mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
AI Cyborg-Shells (#27708)
* deployment pt 1 * deploying, redeploying, action buttons, gibbin * locking and unlocking, deletion, action fixes * grant it there too * compile, fix runtimes * runtimes, fixes * dumb chat spam * progress * gib fix * emaggin blowups, fix bugs * give the action button to both the ai and the shell * compile * i said compile! * smash em * uglify * SMASH * click to shell * tummy * make shells their own chilkd instead of defining them with a var * handle death, carding, other things * compile, more undeployments * fix * shell of [name] * fixes, custom title, improvements * fix errors, show the AI's custom core icon * define these * god DAMMIT * runtimes * fix robo console, fix showing laws * malf AIs wont trigger faction_defeated when they pop in a shell * comment out * shunt fixes, hack feedback * typo * add a proc, hopefully fix soc * oopsie * shells wont get the nuke spell * yeah * make sure soc works with the AI too * oops * green eyes * white eye glow, some sleeps to prevent button mashing * add one complete exoskeleton to the maps * Revert "add one complete exoskeleton to the maps" This reverts commit34d32c9319. * Update job_controller.dm * make exoskeleton->cyborg shell creation a proc, allow AI control toggle for cyborg shells * var changed * exploit * lower sleep times * dead shells can be swapped, wont auto-gib on death * let AI know why the shell their clicking on wont be created * improve formar Co-authored-by: jknpj <jknpjr@gmail.com> * improve format Co-authored-by: jknpj <jknpjr@gmail.com> * remove comment Co-authored-by: jknpj <jknpjr@gmail.com> * better emaggin * remove src check from eyes * maybe fix login src check...? * revert * Revert "improve format" This reverts commitfd99140972. * format, adjust color * do this differently, tbc * better format * whoops * handle login without src check, fix runtimes, add more feedback * turn off eye glow when not deployed, grey shade of eye glow, handle malf executions properly * fix indentation * remove src checks * fix Co-authored-by: jknpj <jknpjr@gmail.com>
This commit is contained in:
@@ -74,6 +74,8 @@
|
||||
|
||||
#define isrobot(A) istype(A, /mob/living/silicon/robot)
|
||||
|
||||
#define isshell(A) istype(A, /mob/living/silicon/robot/shell)
|
||||
|
||||
#define isanimal(A) istype(A, /mob/living/simple_animal)
|
||||
|
||||
#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
..()
|
||||
candidates = candidates[CURRENT_LIVING_PLAYERS]
|
||||
for(var/mob/living/player in candidates)
|
||||
if(!isAI(player))
|
||||
if(!isAI(player) && !isshell(player))
|
||||
candidates -= player
|
||||
continue
|
||||
if(player.z == map.zCentcomm)
|
||||
@@ -256,7 +256,14 @@
|
||||
unction = ticker.mode.CreateFaction(/datum/faction/malf, null, 1)
|
||||
if(!candidates || !candidates.len)
|
||||
return 0
|
||||
var/mob/living/silicon/ai/M = pick(candidates)
|
||||
var/mob/living/silicon/P = pick(candidates)
|
||||
var/mob/living/silicon/ai/M
|
||||
if(isshell(P))
|
||||
var/mob/living/silicon/robot/shell/S = P
|
||||
S.undeploy()
|
||||
M = S.mainframe
|
||||
else
|
||||
M = P
|
||||
assigned += M
|
||||
candidates -= M
|
||||
var/datum/role/malfAI/malf = unction.HandleNewMind(M.mind)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
for (var/datum/role/R in members)
|
||||
if(!R.antag.current)
|
||||
continue
|
||||
if(isAI(R.antag.current) && !R.antag.current.isDead())
|
||||
if((isAI(R.antag.current) || isshell(R.antag.current)) && !R.antag.current.isDead())
|
||||
living_ais++
|
||||
if(!living_ais && stage<MALF_CHOOSING_NUKE)
|
||||
command_alert(/datum/command_alert/malf_destroyed)
|
||||
@@ -76,8 +76,11 @@
|
||||
to_chat(malfAI.antag.current, {"<span class='notice'>Congratulations! The station is now under your exclusive control.<br>
|
||||
You may decide to blow up the station. You have 60 seconds to choose.<br>
|
||||
You should now be able to use your Explode spell to interface with the nuclear fission device.</span>"})
|
||||
malfAI.antag.current.add_spell(new /spell/targeted/ai_win, "malf_spell_ready", /obj/abstract/screen/movable/spell_master/malf)
|
||||
|
||||
if(isshell(malfAI.antag.current))
|
||||
var/mob/living/silicon/robot/shell/S = malfAI.antag.current
|
||||
S.mainframe.add_spell(new /spell/targeted/ai_win, "malf_spell_ready", /obj/abstract/screen/movable/spell_master/malf)
|
||||
else
|
||||
malfAI.antag.current.add_spell(new /spell/targeted/ai_win, "malf_spell_ready", /obj/abstract/screen/movable/spell_master/malf)
|
||||
return
|
||||
|
||||
/datum/faction/malf/get_statpanel_addition()
|
||||
|
||||
@@ -312,6 +312,16 @@ var/global/datum/controller/occupations/job_master
|
||||
var/datum/job/master_assistant = GetJob("Assistant")
|
||||
count = (officer.current_positions + warden.current_positions + hos.current_positions)
|
||||
|
||||
var/datum/job/borg = job_master.GetJob("Cyborg") //spawn a completed cyborg shell if no borgs readied up
|
||||
var/borg_count = borg.current_positions
|
||||
if(!borg_count)
|
||||
for(var/obj/effect/landmark/start/sloc in landmarks_list)
|
||||
if(sloc.name != "Cyborg")
|
||||
continue
|
||||
else
|
||||
new /obj/item/robot_parts/robot_suit/mapped(sloc.loc)
|
||||
break
|
||||
|
||||
// For those who wanted to be assistant if their preferences were filled, here you go.
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(player.client.prefs.alternate_option == BE_ASSISTANT)
|
||||
|
||||
@@ -170,7 +170,9 @@ That prevents a few funky behaviors.
|
||||
if(C.contents.len)//If there is an AI on card.
|
||||
to_chat(U, "<span class='danger'>Transfer failed:</span> Existing AI found on this terminal. Remove existing AI to install a new one.")
|
||||
else
|
||||
if(T.mind.GetRole(MALF))
|
||||
if(T.deployed) //send them back!
|
||||
T.shell.undeploy()
|
||||
if(T.mind?.GetRole(MALF))
|
||||
to_chat(U, "<span class='danger'>ERROR:</span> Remote transfer interface disabled.")//Do ho ho ho~
|
||||
return
|
||||
new /obj/structure/AIcore/deactivated(T.loc)//Spawns a deactivated terminal at AI location.
|
||||
@@ -197,6 +199,8 @@ That prevents a few funky behaviors.
|
||||
var/obj/item/device/aicard/C = src
|
||||
var/mob/living/silicon/ai/A = locate() in C//I love locate(). Best proc ever.
|
||||
if(A)//If AI exists on the card. Else nothing since both are empty.
|
||||
if(A.deployed) //send them back!
|
||||
A.shell.undeploy()
|
||||
A.control_disabled = 0
|
||||
A.forceMove(T.loc)//To replace the terminal.
|
||||
A.update_icon()
|
||||
@@ -219,6 +223,8 @@ That prevents a few funky behaviors.
|
||||
|
||||
else
|
||||
for(var/mob/living/silicon/ai/A in C)
|
||||
if(A.deployed) //send them back!
|
||||
A.shell.undeploy()
|
||||
C.icon_state = "aicard"
|
||||
C.name = "inteliCard"
|
||||
C.overlays.len = 0
|
||||
@@ -270,6 +276,8 @@ That prevents a few funky behaviors.
|
||||
|
||||
else
|
||||
for(var/mob/living/silicon/ai/A in C)
|
||||
if(A.deployed) //send them back!
|
||||
A.shell.undeploy()
|
||||
C.icon_state = "aicard"
|
||||
C.name = "inteliCard"
|
||||
C.overlays.len = 0
|
||||
|
||||
@@ -254,7 +254,11 @@
|
||||
// message_admins("<span class='notice'>[key_name_admin(usr)] emagged [R.name] using robotic console!</span>")
|
||||
log_game("[key_name(usr)] emagged [R.name] using robotic console!")
|
||||
R.SetEmagged(TRUE)
|
||||
to_chat(usr, "Hack successful. [R.name] now has access to illegal technology.")
|
||||
var/mob/living/silicon/ai/A = usr
|
||||
if(A.deployed)
|
||||
to_chat(A.shell, "Hack successful. [R.name] now has access to illegal technology.")
|
||||
else
|
||||
to_chat(A, "Hack successful. [R.name] now has access to illegal technology.")
|
||||
if(R.mind.special_role)
|
||||
R.verbs += /mob/living/silicon/robot/proc/ResetSecurityCodes
|
||||
hacking = 0
|
||||
|
||||
@@ -280,6 +280,9 @@
|
||||
else if (isAI(speech.speaker))
|
||||
speech.job = "AI"
|
||||
|
||||
else if (isshell(speech.speaker))
|
||||
speech.job = "AI Shell"
|
||||
|
||||
// --- Cyborg ---
|
||||
else if (isrobot(speech.speaker))
|
||||
speech.job = "Cyborg"
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
var/obj/item/weapon/cell/cell = null
|
||||
var/extension = null //For making borgs start with pre-installed better components. Make the var the end of the path including the "/".
|
||||
|
||||
/obj/item/robot_parts/chest/complete
|
||||
cell = new /obj/item/weapon/cell/high
|
||||
wires = 1.0
|
||||
|
||||
/obj/item/robot_parts/chest/get_cell()
|
||||
return cell
|
||||
|
||||
@@ -59,6 +63,10 @@
|
||||
var/obj/item/device/flash/flash1 = null
|
||||
var/obj/item/device/flash/flash2 = null
|
||||
|
||||
/obj/item/robot_parts/head/complete
|
||||
flash1 = new /obj/item/device/flash
|
||||
flash2 = new /obj/item/device/flash
|
||||
|
||||
/obj/item/robot_parts/robot_suit
|
||||
name = "robot endoskeleton"
|
||||
desc = "A complex metal backbone with standard limb sockets and pseudomuscle anchors."
|
||||
@@ -70,6 +78,15 @@
|
||||
var/obj/item/robot_parts/chest/chest = null
|
||||
var/obj/item/robot_parts/head/head = null
|
||||
var/created_name = ""
|
||||
var/ai_control = 1
|
||||
|
||||
/obj/item/robot_parts/robot_suit/mapped
|
||||
l_arm = new /obj/item/robot_parts/l_arm
|
||||
r_arm = new /obj/item/robot_parts/r_arm
|
||||
l_leg = new /obj/item/robot_parts/l_leg
|
||||
r_leg = new /obj/item/robot_parts/r_leg
|
||||
chest = new /obj/item/robot_parts/chest/complete
|
||||
head = new /obj/item/robot_parts/head/complete
|
||||
|
||||
/obj/item/robot_parts/robot_suit/New()
|
||||
..()
|
||||
@@ -112,7 +129,7 @@
|
||||
feedback_inc("cyborg_frames_built",1)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/obj/item/robot_parts/robot_suit/attackby(obj/item/W as obj, mob/user as mob)
|
||||
..()
|
||||
if(istype(W, /obj/item/stack/sheet/metal) && !l_arm && !r_arm && !l_leg && !r_leg && !chest && !head)
|
||||
@@ -209,47 +226,15 @@
|
||||
if(!user.drop_item(W))
|
||||
return
|
||||
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc))
|
||||
|
||||
for(var/P in M.mommi_assembly_parts) //Let's give back all those mommi creation components
|
||||
for(var/obj/item/L in M.contents)
|
||||
if(L == P)
|
||||
L.forceMove(T)
|
||||
M.contents -= L
|
||||
|
||||
create_robot(M)
|
||||
|
||||
if(!O)
|
||||
return
|
||||
|
||||
O.mmi = W
|
||||
O.invisibility = 0
|
||||
O.custom_name = created_name
|
||||
O.updatename("Default")
|
||||
|
||||
M.brainmob.mind.transfer_to(O)
|
||||
|
||||
if(O.mind && O.mind.special_role)
|
||||
O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
|
||||
|
||||
O.job = "Cyborg"
|
||||
if(chest.extension)
|
||||
O.component_extension = chest.extension
|
||||
O.upgrade_components()
|
||||
O.cell = chest.cell
|
||||
O.cell.forceMove(O)
|
||||
W.forceMove(O) //Should fix cybros run time erroring when blown up. It got deleted before, along with the frame.
|
||||
|
||||
// Since we "magically" installed a cell, we also have to update the correct component.
|
||||
if(O.cell)
|
||||
var/datum/robot_component/cell_component = O.components["power cell"]
|
||||
cell_component.wrapped = O.cell
|
||||
cell_component.installed = 1
|
||||
|
||||
feedback_inc("cyborg_birth",1)
|
||||
|
||||
spawn()
|
||||
O.Namepick()
|
||||
|
||||
qdel(src)
|
||||
|
||||
else
|
||||
to_chat(user, "<span class='notice'>The MMI must go in after everything else!</span>")
|
||||
|
||||
@@ -262,8 +247,109 @@
|
||||
|
||||
src.created_name = t
|
||||
|
||||
if(W.force >= 15) //Smash those skeletons
|
||||
smash()
|
||||
|
||||
return
|
||||
|
||||
/obj/item/robot_parts/robot_suit/attack_hand(mob/user)
|
||||
if(ai_control)
|
||||
to_chat(user, "You disable AI control on the cyborg exoskeleton.")
|
||||
ai_control = 0
|
||||
else
|
||||
to_chat(user, "You enable AI control on the cyborg exoskeleton.")
|
||||
ai_control = 1
|
||||
|
||||
/obj/item/robot_parts/robot_suit/attack_ai(mob/user)
|
||||
if(!isAI(user))
|
||||
return
|
||||
if(!check_completion())
|
||||
return
|
||||
if(!ai_control)
|
||||
to_chat(user, "<span class='warning'>AI control is disabled on that exoskeleton.</span>")
|
||||
return
|
||||
var/mob/living/silicon/ai/A = user
|
||||
if(!A.shell)
|
||||
A.create_shell(src)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You already have a shell. Destroy it to create a new one.</span>")
|
||||
|
||||
/obj/item/robot_parts/robot_suit/bullet_act(var/obj/item/projectile/P)
|
||||
if(P.damage > 10)
|
||||
smash()
|
||||
|
||||
|
||||
/obj/item/robot_parts/robot_suit/proc/smash()
|
||||
robogibs(loc)
|
||||
visible_message("<span class='danger'>[src] blows apart!</span>")
|
||||
var/turf/T = get_turf(src)
|
||||
if(l_arm)
|
||||
l_arm.forceMove(T)
|
||||
l_arm = null
|
||||
if(r_arm)
|
||||
r_arm.forceMove(T)
|
||||
r_arm = null
|
||||
if(l_leg)
|
||||
l_leg.forceMove(T)
|
||||
l_leg = null
|
||||
if(r_leg)
|
||||
r_leg.forceMove(T)
|
||||
r_leg = null
|
||||
if(chest)
|
||||
chest.forceMove(T)
|
||||
chest = null
|
||||
if(head)
|
||||
head.forceMove(T)
|
||||
head = null
|
||||
qdel(src)
|
||||
|
||||
/obj/item/robot_parts/robot_suit/proc/create_robot(var/obj/item/device/mmi/M, is_shell = FALSE)
|
||||
var/robottype
|
||||
if(is_shell)
|
||||
robottype = /mob/living/silicon/robot/shell
|
||||
else
|
||||
robottype = /mob/living/silicon/robot
|
||||
var/mob/living/silicon/robot/O = new robottype(get_turf(loc))
|
||||
if(!O)
|
||||
return
|
||||
if(M)
|
||||
O.mmi = M
|
||||
O.invisibility = 0
|
||||
O.custom_name = created_name
|
||||
O.updatename("Default")
|
||||
|
||||
if(M)
|
||||
M.brainmob.mind.transfer_to(O)
|
||||
|
||||
if(O.mind && O.mind.special_role)
|
||||
O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
|
||||
|
||||
O.job = "Cyborg"
|
||||
if(chest.extension)
|
||||
O.component_extension = chest.extension
|
||||
O.upgrade_components()
|
||||
O.cell = chest.cell
|
||||
O.cell.forceMove(O)
|
||||
if(M)
|
||||
M.forceMove(O) //Should fix cybros run time erroring when blown up. It got deleted before, along with the frame.
|
||||
|
||||
// Since we "magically" installed a cell, we also have to update the correct component.
|
||||
if(O.cell)
|
||||
var/datum/robot_component/cell_component = O.components["power cell"]
|
||||
cell_component.wrapped = O.cell
|
||||
cell_component.installed = 1
|
||||
|
||||
feedback_inc("cyborg_birth",1)
|
||||
|
||||
spawn()
|
||||
if(M)
|
||||
O.Namepick()
|
||||
|
||||
qdel(src)
|
||||
|
||||
return O
|
||||
|
||||
|
||||
/obj/item/robot_parts/chest/attackby(obj/item/W as obj, mob/user as mob)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/cell))
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
if (sterile)
|
||||
to_chat(user, "<span class='danger'>It looks like \the [src]'s has been de-beaked.</span>")
|
||||
return
|
||||
|
||||
/obj/item/clothing/mask/facehugger/headcrab/Attach(mob/living/L)
|
||||
if(escaping)
|
||||
return FALSE
|
||||
|
||||
@@ -41,6 +41,13 @@ var/list/ai_list = list()
|
||||
var/mentions_on = FALSE
|
||||
var/list/holopadoverlays = list()
|
||||
|
||||
//Shell stuff
|
||||
var/mob/living/silicon/robot/shell/shell = null //The shell the AI currently owns
|
||||
var/datum/action/deploy_shell/deploy_action = new
|
||||
var/datum/action/detonate/destroy_action = new
|
||||
var/deployed = 0 //Is the AI currently controlling a borg
|
||||
var/greeted = 0 //Shitty fix for being repeatedly told the AI greeting
|
||||
|
||||
// See VOX_AVAILABLE_VOICES for available values
|
||||
var/vox_voice = "fem";
|
||||
var/vox_corrupted = FALSE
|
||||
@@ -108,6 +115,10 @@ var/list/ai_list = list()
|
||||
|
||||
proc_holder_list = new()
|
||||
|
||||
//Action Buttons
|
||||
deploy_action.Grant(src)
|
||||
destroy_action.Grant(src)
|
||||
|
||||
//Determine the AI's lawset
|
||||
if(L && istype(L,/datum/ai_laws))
|
||||
src.laws = L
|
||||
@@ -152,7 +163,7 @@ var/list/ai_list = list()
|
||||
spawn(1)
|
||||
mind.store_memory("Frequencies list: <br/><b>Command:</b> [COMM_FREQ] <br/> <b>Security:</b> [SEC_FREQ] <br/> <b>Medical:</b> [MED_FREQ] <br/> <b>Science:</b> [SCI_FREQ] <br/> <b>Engineering:</b> [ENG_FREQ] <br/> <b>Service:</b> [SER_FREQ] <b>Cargo:</b> [SUP_FREQ]<br/> <b>AI private:</b> [AIPRIV_FREQ]<br/>")
|
||||
stored_freqs = 1
|
||||
|
||||
greeted = 1
|
||||
job = "AI"
|
||||
ai_list += src
|
||||
..()
|
||||
@@ -848,6 +859,107 @@ var/list/ai_list = list()
|
||||
|
||||
station_holomap.toggleHolomap(src,1)
|
||||
|
||||
/mob/living/silicon/ai/verb/deploy_to_shell() //Pop into a shell if you own one
|
||||
set category = "AI Commands"
|
||||
set name = "Deploy to Shell"
|
||||
|
||||
if(incapacitated())
|
||||
to_chat(src, "<span class='warning'>Not while you're incapacitated.</span>")
|
||||
return
|
||||
if(control_disabled)
|
||||
to_chat(src, "<span class='warning'>Wireless networking module is offline.</span>")
|
||||
return
|
||||
if(istype(loc, /obj/machinery/power/apc))
|
||||
to_chat(src, "<span class='warning'>You can't control a shell while shunted.</span>")
|
||||
return
|
||||
|
||||
if(shell) //If the silicon already has a linked shell, go to that one!
|
||||
sleep(1) //spamming the verb breaks things
|
||||
if (shell.deployed || shell.mainframe != src)
|
||||
return
|
||||
if(mind)
|
||||
to_chat(src, "Taking control of cyborg shell...")
|
||||
deployed = 1
|
||||
mind.transfer_to(shell)
|
||||
shell.deploy_init(src)
|
||||
|
||||
else //Otherwise, lets see if we can create a new shell
|
||||
var/list/potential_shells = list()
|
||||
for(var/obj/item/robot_parts/robot_suit/emptyborg in world) //Looping through the world might not be good
|
||||
if(!cameranet.checkCameraVis(emptyborg)) //Must be visible
|
||||
continue
|
||||
if(!emptyborg.check_completion()) //Must be ready to have a posi/MMI inserted
|
||||
continue
|
||||
if(!emptyborg.ai_control) //AI control was disabled
|
||||
continue
|
||||
potential_shells.Add(emptyborg)
|
||||
if(potential_shells.len == 0)
|
||||
to_chat(src, "<span class='warning'>No potential cyborg shells available.</span>")
|
||||
return
|
||||
var/list/options = list()
|
||||
for(var/obj/item/robot_parts/robot_suit/S in potential_shells)
|
||||
options["Exoskeleton #[potential_shells.Find(S)] in [get_area(S)]"] = S
|
||||
var/choice = input(src, "Which exoskeleton to control?") as null|anything in options
|
||||
if(shell) //no exploits allowed
|
||||
to_chat(src, "<span class='warning'>You already have a shell.</span>")
|
||||
if(choice)
|
||||
create_shell(options[choice])
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/create_shell(var/obj/item/robot_parts/robot_suit/suit)
|
||||
if(mind && !shell)
|
||||
deployed = 1
|
||||
to_chat(src, "Taking control of cyborg shell...")
|
||||
var/mob/living/silicon/robot/shell/R = suit.create_robot(is_shell = 1)
|
||||
shell = R
|
||||
mind.transfer_to(R)
|
||||
R.deploy_init(src)
|
||||
|
||||
/datum/action/deploy_shell
|
||||
name = "Deploy to AI Shell"
|
||||
desc = "Wirelessly control your personal cyborg shell or create a new one from an empty exoskeleton."
|
||||
icon_icon = 'icons/mob/robots.dmi'
|
||||
button_icon_state = "robot"
|
||||
|
||||
/datum/action/deploy_shell/Trigger()
|
||||
var/mob/living/silicon/ai/AI = owner
|
||||
if(!AI)
|
||||
return
|
||||
AI.deploy_to_shell()
|
||||
|
||||
/datum/action/detonate/
|
||||
name = "Destroy shell"
|
||||
desc = "Destroy your current shell and make room for a new one."
|
||||
icon_icon = 'icons/mob/robots.dmi'
|
||||
button_icon_state = "gibup"
|
||||
|
||||
/datum/action/detonate/Trigger()
|
||||
if(istype(owner, /mob/living/silicon/robot/shell)) //Pressing the button as a shell
|
||||
var/mob/living/silicon/robot/shell/R = owner
|
||||
R.mainframe.shell = null
|
||||
R.gib()
|
||||
return TRUE
|
||||
|
||||
else if(istype(owner, /mob/living/silicon/ai)) //Pressing the button as an AI
|
||||
var/mob/living/silicon/ai/R = owner
|
||||
if(R.incapacitated())
|
||||
to_chat(src, "<span class='warning'>Not while you're incapacitated.</span>")
|
||||
return FALSE
|
||||
if(R.control_disabled)
|
||||
to_chat(src, "<span class='warning'>Wireless networking module is offline.</span>")
|
||||
return FALSE
|
||||
if(R.shell)
|
||||
R.shell.gib()
|
||||
return TRUE
|
||||
else
|
||||
to_chat("<span class='warning'>You have no shell.</span>")
|
||||
return FALSE
|
||||
else
|
||||
to_chat("<span class='warning'>You can't do that.</span>")
|
||||
return FALSE
|
||||
|
||||
|
||||
|
||||
//AI_CAMERA_LUMINOSITY
|
||||
|
||||
/mob/living/silicon/ai/proc/light_cameras()
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
/mob/living/silicon/ai/death(gibbed)
|
||||
if(deployed)
|
||||
shell.undeploy()
|
||||
shell.mainframe = null
|
||||
shell.gib()
|
||||
if(stat == DEAD)
|
||||
return
|
||||
if(!gibbed)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up ¬_¬ ~Carn
|
||||
..()
|
||||
|
||||
to_chat(src, "<B>You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).</B>")
|
||||
to_chat(src, "<B>To look at other parts of the station, click on yourself to get a camera menu.</B>")
|
||||
to_chat(src, "<B>While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.</B>")
|
||||
to_chat(src, "To use something, simply click on it.")
|
||||
to_chat(src, {"Use say ":b to speak to your cyborgs through binary."})
|
||||
show_laws()
|
||||
if(ismalf(src))
|
||||
to_chat(src, "<b>These laws may be changed by other players, but you are not required to follow any of them.</b>")
|
||||
|
||||
if(!greeted)
|
||||
to_chat(src, "<B>You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).</B>")
|
||||
to_chat(src, "<B>To look at other parts of the station, click on yourself to get a camera menu.</B>")
|
||||
to_chat(src, "<B>While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.</B>")
|
||||
to_chat(src, "To use something, simply click on it.")
|
||||
to_chat(src, {"Use say ":b to speak to your cyborgs through binary."})
|
||||
show_laws()
|
||||
if(ismalf(src))
|
||||
to_chat(src, "<b>These laws may be changed by other players, but you are not required to follow any of them.</b>")
|
||||
var/datum/runeset/rune_set = global_runesets["blood_cult"]
|
||||
for(var/obj/effect/rune/rune in rune_set.rune_list) //HOLY FUCK WHO THOUGHT LOOPING THROUGH THE WORLD WAS A GOOD IDEA
|
||||
client.images += rune.blood_image
|
||||
@@ -22,9 +23,11 @@
|
||||
O.mode = 1
|
||||
O.emotion = "Neutral"
|
||||
view_core()
|
||||
if (mind && !stored_freqs)
|
||||
if (mind && !stored_freqs && !greeted)
|
||||
to_chat(src, "The various frequencies used by the crew to communicate have been stored in your mind. Use the verb <i>Notes</i> to access them.")
|
||||
spawn(1)
|
||||
mind.store_memory("Frequencies list: <br/><b>Command:</b> [COMM_FREQ] <br/> <b>Security:</b> [SEC_FREQ] <br/> <b>Medical:</b> [MED_FREQ] <br/> <b>Science:</b> [SCI_FREQ] <br/> <b>Engineering:</b> [ENG_FREQ] <br/> <b>Service:</b> [SER_FREQ] <b>Cargo:</b> [SUP_FREQ]<br/> <b>AI private:</b> [AIPRIV_FREQ]<br/>")
|
||||
stored_freqs = 1
|
||||
if(!greeted)
|
||||
greeted = 1
|
||||
client.CAN_MOVE_DIAGONALLY = TRUE
|
||||
|
||||
@@ -89,3 +89,7 @@
|
||||
ghostize() //Somehow their MMI has no brainmob or something even worse happened. Let's just save their soul from this hell.
|
||||
mmi = null
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/shell/Destroy()
|
||||
close_connection()
|
||||
..()
|
||||
|
||||
@@ -38,6 +38,18 @@
|
||||
if(ticker.current_state == GAME_STATE_PLAYING) //Only tell them this if the game has started. We might find an AI master for them before it starts if it hasn't.
|
||||
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
|
||||
|
||||
/mob/living/silicon/robot/shell/show_laws(var/everyone = 0)
|
||||
laws_sanity_check()
|
||||
var/who
|
||||
if (everyone)
|
||||
who = world
|
||||
else
|
||||
who = src
|
||||
|
||||
to_chat(who, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(who)
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/lawsync()
|
||||
laws_sanity_check()
|
||||
var/datum/ai_laws/master = connected_ai ? connected_ai.laws : null
|
||||
@@ -69,7 +81,9 @@
|
||||
laws.supplied[index] = temp
|
||||
|
||||
if(mind)
|
||||
var/datum/role/mastermalf = connected_ai.mind.GetRole(MALF)
|
||||
var/datum/role/mastermalf //Workaround for shelled AIs causing runtimes
|
||||
mastermalf = connected_ai.deployed ? connected_ai.shell.mind.GetRole(MALF) : connected_ai.mind.GetRole(MALF)
|
||||
|
||||
if(mastermalf)
|
||||
var/datum/faction/my_new_faction = mastermalf.faction
|
||||
my_new_faction.HandleRecruitedMind(mind)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/mob/living/silicon/robot/Login()
|
||||
/mob/living/silicon/robot/Login(var/showlaw_override = 0)
|
||||
..()
|
||||
regenerate_icons()
|
||||
show_laws(0)
|
||||
if(!showlaw_override)
|
||||
show_laws(0)
|
||||
if(module)
|
||||
module.UpdateModuleHolder(src)
|
||||
if (mind && !stored_freqs)
|
||||
module.UpdateModuleHolder(src)
|
||||
if (mind && !stored_freqs && !showlaw_override)
|
||||
spawn(1)
|
||||
mind.store_memory("Frequencies list: <br/><b>Command:</b> [COMM_FREQ] <br/> <b>Security:</b> [SEC_FREQ] <br/> <b>Medical:</b> [MED_FREQ] <br/> <b>Science:</b> [SCI_FREQ] <br/> <b>Engineering:</b> [ENG_FREQ] <br/> <b>Service:</b> [SER_FREQ] <b>Cargo:</b> [SUP_FREQ]<br/> <b>AI private:</b> [AIPRIV_FREQ]<br/>")
|
||||
stored_freqs = 1
|
||||
/*if(mind)
|
||||
ticker.mode.remove_revolutionary(mind)
|
||||
*/
|
||||
|
||||
/mob/living/silicon/robot/shell/Login()
|
||||
..(1)
|
||||
|
||||
@@ -557,13 +557,13 @@ var/list/cyborg_list = list()
|
||||
to_chat(user, "The wires get in your way.")
|
||||
else
|
||||
if(prob(50))
|
||||
to_chat(user, "You emag [src]'s interface")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)].")
|
||||
sleep(6)
|
||||
SetEmagged(TRUE)
|
||||
SetEmagged(TRUE)
|
||||
SetLockdown(TRUE)
|
||||
lawupdate = FALSE
|
||||
disconnect_AI()
|
||||
to_chat(user, "You emag [src]'s interface")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overidden.")
|
||||
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
|
||||
clear_supplied_laws()
|
||||
clear_inherent_laws()
|
||||
@@ -1006,8 +1006,10 @@ var/list/cyborg_list = list()
|
||||
overlays.Cut()
|
||||
update_fire()
|
||||
if(!stat && cell != null)
|
||||
eyes = image(icon,"eyes-[icon_state]", overlay_layer)
|
||||
var/icon/eyesicon = icon(icon,"eyes-[icon_state]", overlay_layer)
|
||||
eyes = image(eyesicon,"eyes-[icon_state]", overlay_layer)
|
||||
eyes.plane = overlay_plane
|
||||
|
||||
overlays += eyes
|
||||
|
||||
if(opened)
|
||||
@@ -1342,3 +1344,126 @@ var/list/cyborg_list = list()
|
||||
//Currently only used for borg movement, to avoid awkward situations where borgs with RTG or basic cells are always slowed down
|
||||
/mob/living/silicon/robot/proc/get_percentage_power_for_movement()
|
||||
return clamp(round(cell.maxcharge/4), 0, SILI_LOW_TRIGGER)
|
||||
|
||||
|
||||
//AI Shell Control
|
||||
|
||||
/mob/living/silicon/robot/shell
|
||||
var/deployed = FALSE //Is the shell being controlled right now
|
||||
var/mob/living/silicon/ai/mainframe = null //The AI the shell belongs to.
|
||||
var/datum/action/undeployment/undeployment_action = new
|
||||
var/datum/action/detonate/destroy_action = new
|
||||
braintype = "AI Shell"
|
||||
|
||||
|
||||
/mob/living/silicon/robot/shell/proc/deploy_init(mob/living/silicon/ai/AI) //called right after the AI pops into the shell.
|
||||
deployed = TRUE
|
||||
connected_ai = AI
|
||||
real_name = "Shell of [AI.name]"
|
||||
name = real_name
|
||||
mainframe = AI
|
||||
mainframe.connected_robots |= src
|
||||
lawupdate = TRUE
|
||||
lawsync()
|
||||
if(radio && AI.radio)
|
||||
radio.channels = AI.radio.channels
|
||||
radio.subspace_transmission = TRUE
|
||||
undeployment_action.Grant(src)
|
||||
destroy_action.Grant(src)
|
||||
updateicon()
|
||||
|
||||
/datum/action/undeployment
|
||||
name = "Disconnect from shell"
|
||||
desc = "Stop controlling your shell and resume normal core operations."
|
||||
icon_icon = 'icons/mob/AI.dmi'
|
||||
button_icon_state = "ai"
|
||||
|
||||
/datum/action/undeployment/UpdateButtonIcon()
|
||||
var/mob/living/silicon/robot/shell/R = owner
|
||||
if(R.mainframe)
|
||||
button_icon_state = "[R.mainframe.icon_state]"
|
||||
else
|
||||
button_icon_state = "ai"
|
||||
..()
|
||||
|
||||
/datum/action/undeployment/Trigger()
|
||||
if(!..())
|
||||
return FALSE
|
||||
var/mob/living/silicon/robot/shell/R = owner
|
||||
|
||||
R.undeploy()
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/robot/shell/proc/undeploy()
|
||||
if(!deployed || !mind || !mainframe)
|
||||
return
|
||||
sleep(1) //spamming this breaks things
|
||||
to_chat(src,"Releasing control of cyborg shell...")
|
||||
mind.transfer_to(mainframe)
|
||||
mainframe.deployed = 0
|
||||
deployed = FALSE
|
||||
undeployment_action.Remove(src)
|
||||
destroy_action.Remove(src)
|
||||
if(radio) //Recalculate the radio channel
|
||||
radio.recalculateChannels()
|
||||
if(mainframe.eyeobj)
|
||||
mainframe.eyeobj.forceMove(loc)
|
||||
updateicon()
|
||||
|
||||
/mob/living/silicon/robot/shell/proc/close_connection()
|
||||
if(deployed)
|
||||
undeploy()
|
||||
if(mainframe)
|
||||
mainframe.shell = null
|
||||
mainframe = null
|
||||
|
||||
/mob/living/silicon/robot/shell/connect_AI()
|
||||
to_chat(mainframe, "<span class='notice' style=\"font-family:Courier\">Notice: Connection to cyborg shell re-established.</span>" )
|
||||
SetLockdown(FALSE)
|
||||
|
||||
/mob/living/silicon/robot/shell/disconnect_AI()
|
||||
to_chat(src, "<span class='alert' style=\"font-family:Courier\">Notice: Connection to cyborg shell has been cut.</span>")
|
||||
SetLockdown(TRUE)
|
||||
|
||||
/mob/living/silicon/robot/shell/emag_act(mob/user as mob)
|
||||
if(user != src)
|
||||
spark(src, 5, FALSE)
|
||||
if(!opened)
|
||||
if(locked)
|
||||
if(prob(90))
|
||||
to_chat(user, "You emag the cover lock.")
|
||||
locked = FALSE
|
||||
else
|
||||
to_chat(user, "You fail to emag the cover lock.")
|
||||
if(prob(25))
|
||||
to_chat(src, "<span class='danger'><span style=\"font-family:Courier\">Hack attempt detected.</span>")
|
||||
else
|
||||
to_chat(user, "The cover is already open.")
|
||||
else
|
||||
if(emagged)
|
||||
return TRUE
|
||||
if(wiresexposed)
|
||||
to_chat(user, "The wires get in your way.")
|
||||
else
|
||||
if(prob(50))
|
||||
to_chat(user, "You emag [src]'s interface")
|
||||
message_admins("[key_name_admin(user)] emagged AI-Cyborg shell [key_name_admin(src)] and destroyed it.")
|
||||
sleep(6)
|
||||
gib()
|
||||
else
|
||||
to_chat(user, "You fail to unlock [src]'s interface.")
|
||||
if(prob(25))
|
||||
to_chat(src, "<span class='danger'><span style=\"font-family:Courier\">Hack attempt detected.</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/robot/shell/updateicon(var/overlay_layer = ABOVE_LIGHTING_LAYER, var/overlay_plane = LIGHTING_PLANE)
|
||||
..(overlay_layer, overlay_plane)
|
||||
overlays.Cut()
|
||||
if(!stat && cell != null && deployed)
|
||||
var/icon/eyesicon = icon(icon,"eyes-[icon_state]", overlay_layer)
|
||||
eyesicon.Blend(rgb(255,255,255), ICON_ADD)
|
||||
eyesicon.Blend(rgb(65,65,65), ICON_SUBTRACT)
|
||||
eyes = image(eyesicon,"eyes-[icon_state]", overlay_layer)
|
||||
eyes.plane = overlay_plane
|
||||
|
||||
overlays += eyes
|
||||
@@ -946,8 +946,12 @@
|
||||
src.malfai = usr:parent
|
||||
else
|
||||
src.malfai = usr
|
||||
to_chat(malfai, "Hack complete. The APC is now under your exclusive control. [STATION_Z == z?"You now have [M.apcs] under your control.":"As this APC is not located on the station, it is not contributing to your control of it."]")
|
||||
malfai.handle_regular_hud_updates()
|
||||
|
||||
var/mob/target_malf
|
||||
target_malf = malfai.deployed ? malfai.shell : malfai
|
||||
to_chat(target_malf, "Hack complete. The APC is now under your exclusive control. [STATION_Z == z?"You now have [M.apcs] under your control.":"As this APC is not located on the station, it is not contributing to your control of it."]")
|
||||
target_malf.handle_regular_hud_updates()
|
||||
|
||||
update_icon()
|
||||
|
||||
else if (href_list["occupyapc"])
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
if(ismanifested(M) || iscluwnebanned(M))
|
||||
visible_message("<span class='caution'>The bolt of change doesn't seem to affect [M] in any way.</span>")
|
||||
return
|
||||
if(isshell(M)) //Kick out the AI if its a shell
|
||||
var/mob/living/silicon/robot/shell/R = M
|
||||
R.close_connection()
|
||||
|
||||
if(isAI(M)) //Force the AI back if its in a shell
|
||||
var/mob/living/silicon/ai/R = M
|
||||
if(R.deployed)
|
||||
R.shell.undeploy()
|
||||
R.shell.gib() //Destroy the shell, they wont be needing it anymore
|
||||
|
||||
|
||||
var/mob/living/new_mob
|
||||
// Random chance of fucking up
|
||||
if(type!=null && prob(10))
|
||||
|
||||
Reference in New Issue
Block a user