From 19b1ddb869195b9c913b9c20b51a6e8be7e45dff Mon Sep 17 00:00:00 2001 From: Geeves Date: Sat, 30 May 2020 15:20:16 +0200 Subject: [PATCH] Remote Mech Revision (#8903) The AI can now remotely control mechs in its network. It has one mapped in near its core. Messages received by your old body will now reach your VR body (does not affect Skrell Srom). Exosuit pilots can now interact with elevator panels without having to get out. Robotics and RnD can now create remote controlled mechs. The control centre is in the protolathe, while the exosuit upgrade is in the circuit imprinter. Mechs can no longer be dismantled if it has a pilot in it. Dismantling a mech now takes a while. --- aurorastation.dme | 3 + code/_onclick/hud/_defines.dm | 1 + code/_onclick/hud/ai.dm | 3 +- .../hud/screen_object_types/ai_screen_objs.dm | 10 + .../controllers/subsystems/virtual_reality.dm | 88 +- code/game/jobs/access.dm | 3 + code/game/machinery/deployable.dm | 14 + code/game/machinery/doors/door.dm | 7 + code/game/machinery/doors/windowdoor.dm | 9 +- code/game/machinery/portable_turret.dm | 4 +- code/game/objects/buckling.dm | 11 +- .../objects/structures/vr/_remote_chair.dm | 31 +- code/game/objects/structures/vr/mech_chair.dm | 10 +- .../heavy_vehicle/components/remote.dm | 34 + .../modules/heavy_vehicle/mech_interaction.dm | 45 +- code/modules/heavy_vehicle/mecha.dm | 37 +- code/modules/heavy_vehicle/premade/miner.dm | 1 + .../heavy_vehicle/premade/powerloader.dm | 11 +- code/modules/mob/death.dm | 6 + code/modules/mob/hear_say.dm | 19 +- code/modules/mob/living/carbon/carbon.dm | 4 +- .../mob/living/carbon/carbon_defense.dm | 6 +- code/modules/mob/living/carbon/human/death.dm | 5 - code/modules/mob/living/carbon/human/emote.dm | 2 +- .../mob/living/carbon/human/examine.dm | 4 +- code/modules/mob/living/carbon/human/life.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 8 + code/modules/mob/living/silicon/ai/death.dm | 3 +- .../simple_animal/friendly/spiderbot.dm | 10 +- code/modules/mob/mob_defines.dm | 1 + code/modules/nano/interaction/default.dm | 22 +- .../designs/circuit/mecha_upgrades.dm | 22 + .../designs/protolathe/deployable_kits.dm | 17 + code/modules/turbolift/turbolift_console.dm | 2 + .../geeves-remote_mech_revision.yml | 13 + icons/mob/screen/ai.dmi | Bin 5794 -> 5912 bytes maps/aurora/aurora-3_sublevel.dmm | 3347 +++-------------- 37 files changed, 878 insertions(+), 2937 deletions(-) create mode 100644 code/modules/heavy_vehicle/components/remote.dm create mode 100644 code/modules/research/designs/circuit/mecha_upgrades.dm create mode 100644 code/modules/research/designs/protolathe/deployable_kits.dm create mode 100644 html/changelogs/geeves-remote_mech_revision.yml diff --git a/aurorastation.dme b/aurorastation.dme index 4a5618cee2d..affd1b81c5a 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -1625,6 +1625,7 @@ #include "code\modules\heavy_vehicle\components\frame.dm" #include "code\modules\heavy_vehicle\components\head.dm" #include "code\modules\heavy_vehicle\components\legs.dm" +#include "code\modules\heavy_vehicle\components\remote.dm" #include "code\modules\heavy_vehicle\components\software.dm" #include "code\modules\heavy_vehicle\equipment\_equipment.dm" #include "code\modules\heavy_vehicle\equipment\combat.dm" @@ -2465,6 +2466,7 @@ #include "code\modules\research\designs\circuit\exosuit_circuits.dm" #include "code\modules\research\designs\circuit\hardsuit_circuits.dm" #include "code\modules\research\designs\circuit\machine_circuits.dm" +#include "code\modules\research\designs\circuit\mecha_upgrades.dm" #include "code\modules\research\designs\circuit\misc_electronics.dm" #include "code\modules\research\designs\circuit\shield_designs.dm" #include "code\modules\research\designs\circuit\tcom_designs.dm" @@ -2475,6 +2477,7 @@ #include "code\modules\research\designs\mechfab\prosthetics\internal.dm" #include "code\modules\research\designs\mechfab\robot\robot.dm" #include "code\modules\research\designs\mechfab\robot\robot_upgrades.dm" +#include "code\modules\research\designs\protolathe\deployable_kits.dm" #include "code\modules\research\designs\protolathe\disk_designs.dm" #include "code\modules\research\designs\protolathe\hud_glasses_designs.dm" #include "code\modules\research\designs\protolathe\implant_designs.dm" diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 215bb6caadf..948c07ef0ae 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -132,6 +132,7 @@ #define ui_ai_track_with_camera "SOUTH:6+1,WEST+2:16" #define ui_ai_camera_light "SOUTH:6+1,WEST+3:16" #define ui_ai_sensor "SOUTH:6+1,WEST+4:16" +#define ui_ai_mech "SOUTH:6+1,WEST+5:16" #define ui_ai_core "SOUTH:6,WEST+1:16" #define ui_ai_crew_monitor "SOUTH:6,WEST+2:16" diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 81e462d5a53..a092cc9e905 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -20,9 +20,10 @@ new /obj/screen/ai/take_image, new /obj/screen/ai/view_image, new /obj/screen/ai/sensor_aug, + new /obj/screen/ai/remote_mech, new /obj/screen/ai/move_up, new /obj/screen/ai/move_down, myai.computer ) - mymob.client.screen += adding + other + mymob.client.screen += adding + other \ No newline at end of file diff --git a/code/_onclick/hud/screen_object_types/ai_screen_objs.dm b/code/_onclick/hud/screen_object_types/ai_screen_objs.dm index 44dcbed32cf..56c46610869 100644 --- a/code/_onclick/hud/screen_object_types/ai_screen_objs.dm +++ b/code/_onclick/hud/screen_object_types/ai_screen_objs.dm @@ -180,6 +180,16 @@ var/mob/living/silicon/ai/AI = usr AI.sensor_mode() +/obj/screen/ai/remote_mech + name = "Remote Control Mech" + icon_state = "remote_mech" + screen_loc = ui_ai_mech + +/obj/screen/ai/remote_mech/Click() + if(isAI(usr)) + var/mob/living/silicon/ai/AI = usr + AI.remote_control_mech() + /obj/screen/ai/move_up name = "Move Up" icon_state = "move_up" diff --git a/code/controllers/subsystems/virtual_reality.dm b/code/controllers/subsystems/virtual_reality.dm index 97a107fa1f0..b2cb3c11634 100644 --- a/code/controllers/subsystems/virtual_reality.dm +++ b/code/controllers/subsystems/virtual_reality.dm @@ -6,7 +6,7 @@ flags = SS_NO_FIRE // MECHA - var/list/mechnetworks = list("remotemechs", "prisonmechs") // A list of all the networks a mech can possibly connect to + var/list/mechnetworks = list("remotemechs", "aimechs", "prisonmechs") // A list of all the networks a mech can possibly connect to var/list/list/mechs = list() // A list of lists, containing the mechs and their networks // IPC BODIES @@ -41,35 +41,43 @@ robots[network].Remove(robot) -/mob/living/ +/mob var/mob/living/vr_mob = null // In which mob is our mind var/mob/living/old_mob = null // Which mob is our old mob // Return to our original body -/mob/living/proc/body_return() +/mob/proc/body_return() set name = "Return to Body" set category = "IC" - if(!vr_mob && !old_mob) - return - - if(vr_mob) - ckey = vr_mob.ckey - vr_mob.ckey = null - vr_mob.old_mob = null - vr_mob.languages = list(LANGUAGE_TCB) - vr_mob = null - to_chat(src, span("notice", "System exited safely, we hope you enjoyed your stay.")) if(old_mob) - old_mob.ckey = ckey - ckey = null - old_mob.vr_mob = null - languages = list(LANGUAGE_TCB) - to_chat(old_mob, span("notice", "System exited safely, we hope you enjoyed your stay.")) + ckey_transfer(old_mob) + languages = list(all_languages[LANGUAGE_TCB]) + to_chat(old_mob, SPAN_NOTICE("System exited safely, we hope you enjoyed your stay.")) old_mob = null else - to_chat(src, span("danger", "Interface error, you cannot exit the system at this time.")) - to_chat(src, span("warning", "Ahelp to get back into your body, a bug has occurred.")) + to_chat(src, SPAN_DANGER("Interface error, you cannot exit the system at this time.")) + to_chat(src, SPAN_WARNING("Ahelp to get back into your body, a bug has occurred.")) + +/mob/living/silicon/body_return() + set name = "Return to Body" + set category = "IC" + + if(old_mob) + ckey_transfer(old_mob) + speech_synthesizer_langs = list(all_languages[LANGUAGE_TCB]) + to_chat(old_mob, SPAN_NOTICE("System exited safely, we hope you enjoyed your stay.")) + old_mob = null + else + to_chat(src, SPAN_DANGER("Interface error, you cannot exit the system at this time.")) + to_chat(src, SPAN_WARNING("Ahelp to get back into your body, a bug has occurred.")) + +/mob/living/proc/vr_mob_exit_languages() + languages = list(all_languages[LANGUAGE_TCB]) + +/mob/living/silicon/vr_mob_exit_languages() + ..() + speech_synthesizer_langs = list(all_languages[LANGUAGE_TCB]) // Handles saving of the original mob and assigning the new mob /datum/controller/subsystem/virtualreality/proc/mind_transfer(var/mob/living/M, var/mob/living/target) @@ -78,15 +86,41 @@ M.vr_mob = target target.ckey = new_ckey M.ckey = "@[new_ckey]" - target.verbs += /mob/living/proc/body_return + target.verbs += /mob/proc/body_return + + target.get_vr_name(M) + M.swap_languages(target) - target.languages = M.languages if(target.client) target.client.screen |= global_hud.vr_control - to_chat(target, span("notice", "Connection established, system suite active and calibrated.")) - to_chat(target, span("warning", "To exit this mode, use the \"Return to Body\" verb in the IC tab.")) + to_chat(target, SPAN_NOTICE("Connection established, system suite active and calibrated.")) + to_chat(target, SPAN_WARNING("To exit this mode, use the \"Return to Body\" verb in the IC tab.")) +/mob/living/proc/swap_languages(var/mob/target) + target.languages = languages + if(issilicon(target)) + var/mob/living/silicon/T = target + T.speech_synthesizer_langs = languages + +/mob/living/silicon/swap_languages(mob/target) + target.languages = speech_synthesizer_langs + if(issilicon(target)) + var/mob/living/silicon/T = target + T.speech_synthesizer_langs = speech_synthesizer_langs + +/mob/proc/get_vr_name(var/mob/user) + return + +/mob/living/simple_animal/spiderbot/get_vr_name(mob/user) + real_name = "Remote-Bot ([user.real_name])" + name = real_name + +/mob/proc/ckey_transfer(var/mob/target, var/null_vr_mob = TRUE) + target.ckey = src.ckey + src.ckey = null + if(null_vr_mob) + target.vr_mob = null /datum/controller/subsystem/virtualreality/proc/mech_selection(var/user, var/network) var/list/mech = list() @@ -107,10 +141,10 @@ continue if(mech_pilot.stat == DEAD) continue - mech[mech_pilot.name] = mech_pilot + mech[R.name] = R if(mech.len == 1) - to_chat(user, span("warning", "No active remote mechs are available.")) + to_chat(user, SPAN_WARNING("No active remote mechs are available.")) return var/desc = input("Please select a remote control compatible mech to take over.", "Remote Mech Selection") in mech|null @@ -140,7 +174,7 @@ robot[R.name] = R if(robot.len == 1) - to_chat(user, span("warning", "No active remote robots are available.")) + to_chat(user, SPAN_WARNING("No active remote robots are available.")) return var/desc = input("Please select a remote control robot to take over.", "Remote Robot Selection") in robot|null diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index d9297835d19..35c6d784188 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -236,6 +236,9 @@ var/obj/item/card/id/all_access/ghost_all_access /mob/living/bot/GetIdCard() return botcard +/mob/living/simple_animal/spiderbot/GetIdCard() + return internal_id + /mob/living/carbon/human/GetIdCard() if(wear_id) var/id = wear_id.GetID() diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 7d7d7b001bd..7390eeba42a 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -341,3 +341,17 @@ for reference: w_class = 3 kit_product = /obj/machinery/iv_drip assembly_time = 4 SECONDS + +/obj/item/deployable_kit/remote_mech + name = "mech control centre assembly kit" + desc = "A quick assembly kit to put together a mech control centre." + icon = 'icons/obj/storage.dmi' + icon_state = "barrier_kit" + w_class = ITEMSIZE_LARGE + kit_product = /obj/structure/bed/chair/remote/mech/portable + assembly_time = 20 SECONDS + +/obj/item/deployable_kit/remote_mech/brig + name = "brig mech control centre assembly kit" + desc = "A quick assembly kit to put together a brig mech control centre." + kit_product = /obj/structure/bed/chair/remote/mech/prison/portable \ No newline at end of file diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index a85e190382c..e3f635681eb 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -162,6 +162,13 @@ open() return + if(istype(AM, /mob/living/simple_animal/spiderbot)) + var/mob/living/simple_animal/spiderbot/bot = AM + if(src.check_access(bot.internal_id)) + if(density) + open() + return + if(istype(AM, /obj/structure/bed/chair/wheelchair)) var/obj/structure/bed/chair/wheelchair/wheel = AM if(density) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 06eee8e17ea..a170b6a72bf 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -58,13 +58,20 @@ return ..() /obj/machinery/door/window/CollidedWith(atom/movable/AM as mob|obj) - if (istype(AM, /obj)) + if (istype(AM, /mob/living/bot)) var/mob/living/bot/bot = AM if(istype(bot)) if(density && src.check_access(bot.botcard)) open() addtimer(CALLBACK(src, .proc/close), 50) return + if(istype(AM, /mob/living/simple_animal/spiderbot)) + var/mob/living/simple_animal/spiderbot/bot = AM + if(istype(bot)) + if(density && src.check_access(bot.internal_id)) + open() + addtimer(CALLBACK(src, .proc/close), 50) + return if (!( ROUND_IS_STARTED )) return if (src.operating) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index bf48b9937bd..c2122cee93b 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -582,7 +582,9 @@ var/mob/living/heavy_vehicle/M = L if(!M.pilots?.len) return TURRET_NOT_TARGET - + for(var/mob/pilot in M.pilots) + if(allowed(pilot)) // don't shoot if the mech contains at least one person with access + return TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee /obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 0d775b03c0c..6fbcd464cbd 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -55,16 +55,19 @@ /obj/proc/user_buckle_mob(mob/living/M, mob/user) if(!ROUND_IS_STARTED) - to_chat(user, "You can't buckle anyone in before the game starts.") + to_chat(user, SPAN_WARNING("You can't buckle anyone in before the game starts.")) if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) return + if(!M.can_buckle) + to_chat(user, SPAN_WARNING("\The [M] can't be buckled!")) + return if(M == buckled_mob) return if(istype(M, /mob/living/carbon/slime)) - to_chat(user, "The [M] is too squishy to buckle in.") + to_chat(user, SPAN_WARNING("\The [M] is too squishy to buckle in.")) return - if (buckled_mob) - to_chat(user, "[buckled_mob.name] is already there, unbuckle them first!.") + if(buckled_mob) + to_chat(user, SPAN_WARNING("\The [buckled_mob.name] is already there, unbuckle them first!.")) return add_fingerprint(user) diff --git a/code/game/objects/structures/vr/_remote_chair.dm b/code/game/objects/structures/vr/_remote_chair.dm index 11d2365100d..3ca6bcbc3f0 100644 --- a/code/game/objects/structures/vr/_remote_chair.dm +++ b/code/game/objects/structures/vr/_remote_chair.dm @@ -2,12 +2,34 @@ name = "virtual reality centre" desc = "A comfortable chair with full audio-visual transposition centres." icon_state = "shuttlechair_down" + var/portable = FALSE // can it be moved around 'n shit + var/portable_type can_dismantle = FALSE var/list/remote_network // Which network does this remote control belong to? +/obj/structure/bed/chair/remote/Initialize() + . = ..() + if(portable && portable_type) + name = "portable [name]" + +/obj/structure/bed/chair/remote/examine(mob/user) + ..() + if(portable && portable_type) + to_chat(user, FONT_SMALL(SPAN_NOTICE("Can be packed up by using a wrench on it."))) + /obj/structure/bed/chair/remote/update_icon() return +/obj/structure/bed/chair/remote/attackby(obj/item/W, mob/user) + if(portable && portable_type && W.iswrench()) + user.visible_message(SPAN_NOTICE("\The [user] starts dismantling \the [src]..."), SPAN_NOTICE("You start dismantling \the [src]...")) + if(do_after(user, 20 SECONDS, TRUE, src)) + user.visible_message(SPAN_NOTICE("\The [user] dismantles \the [src]."), SPAN_NOTICE("You dismantle \the [src].")) + var/obj/kit = new portable_type(get_turf(src)) + user.put_in_hands(kit) + return + ..() + /obj/structure/bed/chair/remote/user_buckle_mob(mob/user) ..() if(ishuman(user)) @@ -19,8 +41,9 @@ // Return to our body in the unfortunate event that we get unbuckled while plugged in /obj/structure/bed/chair/remote/user_unbuckle_mob(mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.body_return() - cut_overlays() + if(buckled_mob) + var/mob/M = buckled_mob + if(istype(M) && M.vr_mob) + M.vr_mob.body_return() + cut_overlays() ..() \ No newline at end of file diff --git a/code/game/objects/structures/vr/mech_chair.dm b/code/game/objects/structures/vr/mech_chair.dm index dcd02964ebf..df4bc6e111c 100644 --- a/code/game/objects/structures/vr/mech_chair.dm +++ b/code/game/objects/structures/vr/mech_chair.dm @@ -9,7 +9,15 @@ var/mob/living/carbon/human/H = user SSvirtualreality.mech_selection(H, remote_network) +/obj/structure/bed/chair/remote/mech/portable + portable = TRUE + portable_type = /obj/item/deployable_kit/remote_mech + /obj/structure/bed/chair/remote/mech/prison name = "brig mech control centre" desc = "A comfortable chair with full audio-visual transposition centres. This one gives you access to exosuits attached to the brig network." - remote_network = "prisonmechs" \ No newline at end of file + remote_network = "prisonmechs" + +/obj/structure/bed/chair/remote/mech/prison/portable + portable = TRUE + portable_type = /obj/item/deployable_kit/remote_mech/brig \ No newline at end of file diff --git a/code/modules/heavy_vehicle/components/remote.dm b/code/modules/heavy_vehicle/components/remote.dm new file mode 100644 index 00000000000..b91fd57fbfe --- /dev/null +++ b/code/modules/heavy_vehicle/components/remote.dm @@ -0,0 +1,34 @@ +/obj/item/remote_mecha + name = "standard exosuit remote upgrade" + desc = "A device that, when inserted into an exosuit, allows it to be remotely piloted." + icon = 'icons/obj/modular_components.dmi' + icon_state = "aislot" + origin_tech = list(TECH_BLUESPACE = 3, TECH_MATERIAL = 4, TECH_DATA = 4) + w_class = ITEMSIZE_SMALL + var/mech_remote_network = "remotemechs" + var/hardpoint_lock = FALSE // Whether mechs that receive this upgrade gets locked + var/dummy_path = /mob/living/simple_animal/spiderbot + +/obj/item/remote_mecha/examine(mob/user) + . = ..() + to_chat(user, FONT_SMALL(SPAN_WARNING("This exosuit upgrade cannot be undone if applied!"))) + if(Adjacent(user)) + var/message = "Applying \the [src] will [hardpoint_lock ? "" : "not"] lock the hardpoints[hardpoint_lock ? ", preventing further modification" : ""]." + to_chat(user, FONT_SMALL(SPAN_NOTICE(message))) + +/obj/item/remote_mecha/penal + name = "penal exosuit remote upgrade" + desc = "A device that, when inserted into an exosuit, allows it to be remotely piloted. Intended for prison networks." + mech_remote_network = "prisonmechs" + hardpoint_lock = TRUE + +/obj/item/remote_mecha/penal/examine(mob/user) + . = ..() + if(Adjacent(user)) + to_chat(user, FONT_SMALL(SPAN_NOTICE("Applying \the [src] will additionally add the mech to the security penal network, where they can remotely monitor and shut it down."))) + +/obj/item/remote_mecha/ai + name = "AI exosuit remote upgrade" + desc = "A device that, when inserted into an exosuit, allows it to be remotely piloted by the artificial intelligence." + mech_remote_network = "aimechs" + dummy_path = /mob/living/simple_animal/spiderbot/ai \ No newline at end of file diff --git a/code/modules/heavy_vehicle/mech_interaction.dm b/code/modules/heavy_vehicle/mech_interaction.dm index a7fe552160c..6aff2a05db8 100644 --- a/code/modules/heavy_vehicle/mech_interaction.dm +++ b/code/modules/heavy_vehicle/mech_interaction.dm @@ -346,7 +346,29 @@ else if(user.a_intent != I_HURT) - if(thing.ismultitool()) + if(istype(thing, /obj/item/remote_mecha)) + if(length(pilots)) + to_chat(user, SPAN_WARNING("You can't apply this upgrade while \the [src] has occupants!")) + return + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("You are unable to apply this upgrade while \the [src]'s maintenance protocols are not active.")) + return + user.visible_message(SPAN_NOTICE("\The [user] begins installing \the [thing] into \the [src]..."), SPAN_NOTICE("You begin installing the [thing] into \the [src]...")) + if(do_after(user, 30, TRUE, src)) + if(length(pilots)) + to_chat(user, SPAN_WARNING("You can't apply this upgrade while \the [src] has occupants!")) + return + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("You are unable to apply this upgrade while \the [src]'s maintenance protocols are not active.")) + return + var/obj/item/remote_mecha/RM = thing + user.visible_message(SPAN_NOTICE("\The [user] installs \the [thing] into \the [src]."), SPAN_NOTICE("You install the [thing] into \the [src].")) + remote_network = RM.mech_remote_network + does_hardpoint_lock = RM.hardpoint_lock + dummy_type = RM.dummy_path + become_remote() + qdel(thing) + else if(thing.ismultitool()) if(hardpoints_locked) to_chat(user, "Hardpoint system access is disabled.") return @@ -364,12 +386,23 @@ return else if(thing.iswrench()) - if(!maintenance_protocols) - to_chat(user, "The securing bolts are not visible while maintenance protocols are disabled.") + if(length(pilots)) + to_chat(user, SPAN_WARNING("You can't disassemble \the [src] while it has a pilot!")) + return + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("The securing bolts are not visible while maintenance protocols are disabled.")) + return + user.visible_message(SPAN_NOTICE("\The [user] starts dismantling \the [src]..."), SPAN_NOTICE("You start disassembling \the [src]...")) + if(do_after(user, 30, TRUE, src)) + if(length(pilots)) + to_chat(user, SPAN_WARNING("You can't disassemble \the [src] while it has a pilot!")) + return + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("The securing bolts are not visible while maintenance protocols are disabled.")) + return + user.visible_message(SPAN_NOTICE("\The [user] dismantles \the [src]."), SPAN_NOTICE("You disassemble \the [src].")) + dismantle() return - to_chat(user, "You dismantle \the [src].") - dismantle() - return else if(thing.iswelder()) if(!getBruteLoss()) return diff --git a/code/modules/heavy_vehicle/mecha.dm b/code/modules/heavy_vehicle/mecha.dm index 429a356df20..77ee93a1553 100644 --- a/code/modules/heavy_vehicle/mecha.dm +++ b/code/modules/heavy_vehicle/mecha.dm @@ -1,13 +1,14 @@ // Big stompy robots. /mob/living/heavy_vehicle name = "exosuit" - density = 1 - opacity = 1 - anchored = 1 + density = TRUE + opacity = TRUE + anchored = TRUE status_flags = PASSEMOTES a_intent = I_HURT mob_size = MOB_LARGE mob_push_flags = ALLMOBS + can_buckle = FALSE var/decal var/emp_damage = 0 @@ -24,7 +25,7 @@ // Access updating/container. var/obj/item/card/id/access_card var/list/saved_access = list() - var/sync_access = 1 + var/sync_access = TRUE // Mob currently piloting the mech. var/list/pilots @@ -32,7 +33,9 @@ // Remote control stuff var/remote = FALSE // Spawns a robotic pilot to be remote controlled - var/mob/living/carbon/human/industrial_xion_remote_mech/dummy // The remote controlled dummy + var/does_hardpoint_lock = TRUE + var/mob/living/simple_animal/spiderbot/dummy // The remote controlled dummy + var/dummy_type = /mob/living/simple_animal/spiderbot var/dummy_colour // Visible external components. Not strictly accurately named for non-humanoid machines (submarines) but w/e @@ -56,8 +59,8 @@ var/material/material // Cockpit access vars. - var/hatch_closed = 0 - var/hatch_locked = 0 + var/hatch_closed = FALSE + var/hatch_locked = FALSE var/force_locked = FALSE // Is it possible to unlock the hatch? var/use_air = FALSE @@ -241,16 +244,20 @@ remote = TRUE name = name + " \"[pick("Jaeger", "Reaver", "Templar", "Juggernaut", "Basilisk")]-[rand(0, 999)]\"" - if(remote_network) - SSvirtualreality.add_mech(src, remote_network) - else + if(!remote_network) remote_network = "remotemechs" - SSvirtualreality.add_mech(src, remote_network) + SSvirtualreality.add_mech(src, remote_network) if(hatch_closed) hatch_closed = FALSE - dummy = new /mob/living/carbon/human/industrial_xion_remote_mech(get_turf(src)) + dummy = new dummy_type(get_turf(src)) + dummy.real_name = "Remote-Bot" + dummy.name = dummy.real_name + dummy.mmi = new /obj/item/device/mmi(dummy) // this is literally just because i luck the aesthetics - geeves + dummy.verbs -= /mob/living/proc/ventcrawl + dummy.verbs -= /mob/living/proc/hide + dummy.update_icon() if(dummy_colour) dummy.color = dummy_colour enter(dummy, TRUE) @@ -258,5 +265,7 @@ if(!hatch_closed) hatch_closed = TRUE hatch_locked = TRUE - hardpoints_locked = TRUE - force_locked = TRUE \ No newline at end of file + if(does_hardpoint_lock) + hardpoints_locked = TRUE + force_locked = TRUE + update_icon() \ No newline at end of file diff --git a/code/modules/heavy_vehicle/premade/miner.dm b/code/modules/heavy_vehicle/premade/miner.dm index ec848d4e4c7..e8c308f9b47 100644 --- a/code/modules/heavy_vehicle/premade/miner.dm +++ b/code/modules/heavy_vehicle/premade/miner.dm @@ -19,6 +19,7 @@ name = "remote mining mecha" dummy_colour = "#ffc44f" remote_network = "remotemechs" + does_hardpoint_lock = FALSE /mob/living/heavy_vehicle/premade/miner/remote_prison name = "penal mining mecha" diff --git a/code/modules/heavy_vehicle/premade/powerloader.dm b/code/modules/heavy_vehicle/premade/powerloader.dm index 4b0c1ae537f..f38080412f9 100644 --- a/code/modules/heavy_vehicle/premade/powerloader.dm +++ b/code/modules/heavy_vehicle/premade/powerloader.dm @@ -152,8 +152,17 @@ name = "remote power loader" dummy_colour = "#ffc44f" remote_network = "remotemechs" + does_hardpoint_lock = FALSE /mob/living/heavy_vehicle/premade/ripley/remote_prison name = "penal power loader" dummy_colour = "#302e2b" - remote_network = "prisonmechs" \ No newline at end of file + remote_network = "prisonmechs" + +/mob/living/heavy_vehicle/premade/ripley/remote_ai + name = "stationbound power loader" + e_color = COLOR_GREEN_GRAY + dummy_colour = COLOR_GREEN_GRAY + dummy_type = /mob/living/simple_animal/spiderbot/ai + remote_network = "aimechs" + does_hardpoint_lock = FALSE \ No newline at end of file diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index 932b84f7c7c..e1685796b6a 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -58,6 +58,12 @@ if(!gibbed && deathmessage != "no message") // This is gross, but reliable. Only brains use it. src.visible_message("\The [src.name] [deathmessage]", range = messagerange) + // If we have a remotely controlled mob, we come back to our body to die properly + if(vr_mob) + vr_mob.body_return() + // Alternatively, if we are the remotely controlled mob, just kick our controller out + if(old_mob) + body_return() stat = DEAD update_canmove() diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index b9a433e70b5..e5b3f088813 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -1,7 +1,7 @@ // At minimum every mob has a hear_say proc. /mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) - if(!istype(src, /mob/living/test) && !client) + if(!istype(src, /mob/living/test) && (!client && !vr_mob)) return if(speaker && !istype(speaker, /mob/living/test) && (!speaker.client && istype(src,/mob/abstract/observer) && client.prefs.toggles & CHAT_GHOSTEARS && !(speaker in view(src)))) @@ -21,7 +21,7 @@ italics = 1 sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact - if(sleeping || stat == 1) + if((sleeping && !vr_mob) || stat == 1) hear_sleep(message) return @@ -82,17 +82,20 @@ /mob/proc/on_hear_say(var/message) to_chat(src, message) + if(vr_mob) + to_chat(vr_mob, message) /mob/living/silicon/on_hear_say(var/message) var/time = say_timestamp() to_chat(src, "[time] [message]") + if(vr_mob) + to_chat(vr_mob, "[time] [message]") /mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="") - - if(!client) + if(!client && !vr_mob) return - if(sleeping || stat==1) //If unconscious or sleeping + if((sleeping && !vr_mob) || stat==1) //If unconscious or sleeping hear_sleep(message) return @@ -209,6 +212,8 @@ /mob/proc/on_hear_radio(part_a, speaker_name, track, part_b, formatted) to_chat(src, "[part_a][speaker_name][part_b][formatted]") + if(vr_mob) + to_chat(vr_mob, "[part_a][speaker_name][part_b][formatted]") /mob/abstract/observer/on_hear_radio(part_a, speaker_name, track, part_b, formatted) to_chat(src, "[track][part_a][speaker_name][part_b][formatted]") @@ -216,10 +221,14 @@ /mob/living/silicon/on_hear_radio(part_a, speaker_name, track, part_b, formatted) var/time = say_timestamp() to_chat(src, "[time][part_a][speaker_name][part_b][formatted]") + if(vr_mob) + to_chat(vr_mob, "[time][part_a][speaker_name][part_b][formatted]") /mob/living/silicon/ai/on_hear_radio(part_a, speaker_name, track, part_b, formatted) var/time = say_timestamp() to_chat(src, "[time][part_a][track][part_b][formatted]") + if(vr_mob) + to_chat(vr_mob, "[time][part_a][track][part_b][formatted]") /mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null) if(!client || !speaker) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index c8940312c88..0a7ae9b92a3 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -108,7 +108,7 @@ if(H && show_ssd && !client && !teleop) if(H.bg) to_chat(H, span("danger", "You sense some disturbance to your physical body!")) - else + else if(!vr_mob) visible_message(span("notice", "[M] [action] [src], but they do not respond... Maybe they have S.S.D?")) else if(client && willfully_sleeping) visible_message(span("notice", "[M] [action] [src] waking [t_him] up!")) @@ -271,7 +271,7 @@ if(H && show_ssd && !client && !teleop) if(H.bg) to_chat(H, span("warning", "You sense some disturbance to your physical body, like someone is trying to wake you up.")) - else + else if(!vr_mob) M.visible_message(span("notice", "[M] shakes [src] trying to wake [t_him] up!"), \ span("notice", "You shake [src], but they do not respond... Maybe they have S.S.D?")) else if(lying) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 9233c6526b3..e3d87cb1dc1 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -26,7 +26,7 @@ willfully_sleeping = FALSE else to_chat(H, span("danger", "You sense great disturbance to your physical body!")) - else + else if(!vr_mob) visible_message(span("danger","[src] is hit by [AM], but they do not respond... Maybe they have S.S.D?")) else if(client && willfully_sleeping) visible_message(span("danger", "[src] is hit by [AM] waking [t_him] up!")) @@ -54,7 +54,7 @@ willfully_sleeping = FALSE else to_chat(H, span("danger", "You sense great disturbance to your physical body!")) - else + else if(!vr_mob) visible_message("[P] hit [src], but they do not respond... Maybe they have S.S.D?") else if(client && willfully_sleeping) visible_message("[P] hit [src] waking [t_him] up!") @@ -80,7 +80,7 @@ willfully_sleeping = FALSE else to_chat(H, span("danger", "You sense great disturbance to your physical body!")) - else + else if(!vr_mob) user.visible_message("[user] attacks [src] with [I] waking [t_him] up!", \ "You attack [src] with [I], but they do not respond... Maybe they have S.S.D?") else if(client && willfully_sleeping) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 5ceb04bdafe..a9a5ef152ec 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -114,11 +114,6 @@ return /mob/living/carbon/human/proc/vr_disconnect() - // Come out of VR right before you die, how depressing - geeves - // Also come out of VR if your VR body dies - if(vr_mob || old_mob) - body_return() - if(remote_network) SSvirtualreality.remove_robot(src, remote_network) remote_network = null \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index fcef2143f62..1dc53c4b175 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -324,7 +324,7 @@ if(H && show_ssd && !H.client && !H.teleop) if(H.bg) to_chat(H, span("danger", "You sense some disturbance to your physical body!")) - else + else if(!vr_mob) message = "slaps [M] across the face, but they do not respond... Maybe they have S.S.D?" else if(H.client && H.willfully_sleeping) message = "slaps [M] across the face, waking them up. Ouch!" diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 6dc968cd35e..311620fe70a 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -262,9 +262,9 @@ if(species.show_ssd && (!species.has_organ[BP_BRAIN] || has_brain()) && stat != DEAD) - if(!key) + if(!vr_mob && !key) msg += "[T.He] [T.is] [species.show_ssd]. It doesn't look like [T.he] [T.is] waking up anytime soon.\n" - else if(!client && !bg) + else if(!vr_mob && !client && !bg) msg += "[T.He] [T.is] [species.show_ssd].\n" if(have_client && ((inactivity / 600) > 10)) // inactivity/10/60 > 10 MINUTES msg += "\[Inactive for [round(inactivity / 600)] minutes.\]\n" diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 40e6a4bf331..1ba880b1b5f 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -721,7 +721,7 @@ return 0 //SSD check, if a logged player is awake put them back to sleep! - if(species.show_ssd && !client && !teleop) + if(species.show_ssd && (!client && !vr_mob) && !teleop) Sleeping(2) if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP blinded = 1 diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index ebd3b4bb243..3b6a8843a27 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -20,6 +20,7 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/proc/core, /mob/living/silicon/ai/proc/pick_icon, /mob/living/silicon/ai/proc/sensor_mode, + /mob/living/silicon/ai/proc/remote_control_mech, /mob/living/silicon/ai/proc/show_laws_verb, /mob/living/silicon/ai/proc/toggle_acceleration, /mob/living/silicon/ai/proc/toggle_camera_light, @@ -310,6 +311,7 @@ var/list/ai_verbs_default = list( if(id_card) id_card.registered_name = pickedName id_card.assignment = "AI" + id_card.access = get_all_station_access() id_card.update_name() if(client) @@ -772,6 +774,12 @@ var/list/ai_verbs_default = list( set desc = "Augment visual feed with internal sensor overlays" toggle_sensor_mode() +/mob/living/silicon/ai/proc/remote_control_mech() + set name = "Remote Control Mech" + set category = "AI Commands" + set desc = "Remotely control any active mechs on your AI mech network." + SSvirtualreality.mech_selection(src, "aimechs") + /mob/living/silicon/ai/proc/toggle_hologram_movement() set name = "Toggle Hologram Movement" set category = "AI Commands" diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index e8f55e4df5d..33905de2309 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -16,4 +16,5 @@ card.update_icon() . = ..(gibbed,"gives one shrill beep before falling lifeless.") - density = 1 + density = TRUE + ghostize(FALSE) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm index 56adb46f1c6..a0936a8ac54 100644 --- a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm @@ -12,6 +12,7 @@ var/obj/item/cell/cell = null var/obj/machinery/camera/camera = null var/obj/item/device/mmi/mmi = null + var/obj/item/card/id/internal_id = null var/list/req_access = list(access_robotics) //Access needed to pop out the brain. var/positronic @@ -44,8 +45,9 @@ /mob/living/simple_animal/spiderbot/Initialize() . = ..() - add_language("Ceti Basic") - default_language = all_languages["Ceti Basic"] + add_language(LANGUAGE_TCB) + default_language = all_languages[LANGUAGE_TCB] + internal_id = new /obj/item/card/id(src) verbs |= /mob/living/proc/ventcrawl verbs |= /mob/living/proc/hide @@ -302,3 +304,7 @@ /mob/living/simple_animal/spiderbot/get_bullet_impact_effect_type(var/def_zone) return BULLET_IMPACT_METAL + +/mob/living/simple_animal/spiderbot/ai/Initialize() + . = ..() + internal_id.access = get_all_station_access() \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 5457a2f2d3a..b3135576dbb 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -7,6 +7,7 @@ var/datum/mind/mind var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak + var/can_buckle = TRUE var/obj/screen/flash = null var/obj/screen/blind = null diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm index a9b926285cb..cedf264e4d6 100644 --- a/code/modules/nano/interaction/default.dm +++ b/code/modules/nano/interaction/default.dm @@ -62,6 +62,16 @@ /atom/proc/contents_nano_distance(var/src_object, var/mob/living/user) return user.shared_living_nano_distance(src_object) +/mob/living/heavy_vehicle/contents_nano_distance(src_object, mob/living/user) + if(src_object in contents) + return STATUS_INTERACTIVE + if(hatch_closed && body.pilot_coverage == 100 && !istype(user, /mob/living/simple_animal/spiderbot)) // spiderbots get a pass cuz they need a bone, call it RFID bullshit + to_chat(user, SPAN_WARNING("You can't interact with things outside \the [src] if its hatch is closed!")) + return STATUS_CLOSE + if(!src.Adjacent(src_object)) + return STATUS_CLOSE + return STATUS_INTERACTIVE + /mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object) if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates return STATUS_CLOSE @@ -79,7 +89,10 @@ . = shared_nano_interaction(src_object) if(. != STATUS_CLOSE) if(loc) - . = min(., loc.contents_nano_distance(src_object, src)) + if(istype(loc, /mob/living/heavy_vehicle)) + return loc.contents_nano_distance(src_object, src) + else + . = min(., loc.contents_nano_distance(src_object, src)) if(. == STATUS_INTERACTIVE) return STATUS_UPDATE @@ -87,6 +100,9 @@ . = shared_nano_interaction(src_object) if(. != STATUS_CLOSE) if(loc) - . = min(., loc.contents_nano_distance(src_object, src)) + if(istype(loc, /mob/living/heavy_vehicle)) + . = loc.contents_nano_distance(src_object, src) + else + . = min(., loc.contents_nano_distance(src_object, src)) else - . = min(., shared_living_nano_distance(src_object)) + . = min(., shared_living_nano_distance(src_object)) \ No newline at end of file diff --git a/code/modules/research/designs/circuit/mecha_upgrades.dm b/code/modules/research/designs/circuit/mecha_upgrades.dm new file mode 100644 index 00000000000..c6265f3eaf8 --- /dev/null +++ b/code/modules/research/designs/circuit/mecha_upgrades.dm @@ -0,0 +1,22 @@ +/datum/design/circuit/exosuit_upgrade + design_order = 2.6 + +/datum/design/circuit/exosuit_upgrade/AssembleDesignName() + name = "Exosuit Hardware Upgrade ([name])" + +/datum/design/circuit/exosuit/AssembleDesignDesc() + desc = "Complex circuitry which unlock certain exosuit faculties." + +/datum/design/circuit/exosuit_upgrade/remote + name = "Standard Remote Control" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_MATERIAL = 4) + build_path = /obj/item/remote_mecha + +/datum/design/circuit/exosuit_upgrade/remote/penal + name = "Penal Remote Control" + build_path = /obj/item/remote_mecha/penal + +/datum/design/circuit/exosuit_upgrade/remote/ai + name = "AI Remote Control" + req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 4, TECH_MATERIAL = 4) + build_path = /obj/item/remote_mecha/ai \ No newline at end of file diff --git a/code/modules/research/designs/protolathe/deployable_kits.dm b/code/modules/research/designs/protolathe/deployable_kits.dm new file mode 100644 index 00000000000..c0564c2a5b5 --- /dev/null +++ b/code/modules/research/designs/protolathe/deployable_kits.dm @@ -0,0 +1,17 @@ +/datum/design/item/deployable_kit + design_order = 11 + +/datum/design/item/deployable_kit/AssembleDesignName() + name = "Deployable Kit Design ([name])" + +/datum/design/item/deployable_kit/mech_chair + name = "Remote Mech Centre" + desc = "A deployable kit of a remote mech chair, capable of listening in to standard remote mech networks." + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_MATERIAL = 4) + materials = list(DEFAULT_WALL_MATERIAL = 3000, MATERIAL_SILVER = 750, MATERIAL_URANIUM = 250) + build_path = /obj/item/deployable_kit/remote_mech + +/datum/design/item/deployable_kit/mech_chair/brig + name = "Remote Penal Mech Centre" + desc = "A deployable kit of a remote mech chair, capable of listening in to penal remote mech networks." + build_path = /obj/item/deployable_kit/remote_mech/brig \ No newline at end of file diff --git a/code/modules/turbolift/turbolift_console.dm b/code/modules/turbolift/turbolift_console.dm index 684138671a4..4afba08a21a 100644 --- a/code/modules/turbolift/turbolift_console.dm +++ b/code/modules/turbolift/turbolift_console.dm @@ -100,6 +100,8 @@ /obj/structure/lift/panel/interact(var/mob/user) if(!..()) return + if(istype(user, /mob/living/heavy_vehicle)) // terrible, i know, but it shat out runtimes otherwise + user = usr var/dat = list() dat += "
Lift panel
" diff --git a/html/changelogs/geeves-remote_mech_revision.yml b/html/changelogs/geeves-remote_mech_revision.yml new file mode 100644 index 00000000000..18644e54822 --- /dev/null +++ b/html/changelogs/geeves-remote_mech_revision.yml @@ -0,0 +1,13 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "The AI can now remotely control mechs in its network. It has one mapped in near its core." + - rscadd: "Messages received by your old body will now reach your VR body (does not affect Skrell Srom)." + - bugfix: "Exosuit pilots can now interact with elevator panels without having to get out." + - rscadd: "Robotics and RnD can now create remote controlled mechs. The control centre is in the protolathe, while the exosuit upgrade is in the circuit imprinter." + - tweak: "Mechs can no longer be dismantled if it has a pilot in it." + - tweak: "Dismantling a mech now takes a while." + - bugfix: "Mechs can no longer be buckled to chairs or beds." + - bugfix: "AIs now properly ghost after dying." \ No newline at end of file diff --git a/icons/mob/screen/ai.dmi b/icons/mob/screen/ai.dmi index a6c54ee748072615bb486ac64523f8fbd607a4b1..a8e68d8d3c445724db44dc07e2f38f1ece98ecd6 100644 GIT binary patch delta 5736 zcmV-u7MJOwEtoElB!B*TR9JLGWpiV4X>fFDZ*Bkpc$|&VJ#ND=3O!ugmXg|NK;D3|s%ubd{D#o14k{u<= zq9<2$Pd$>YD+j6_CaP#7PeHHVvCb-OHkHg$s)}c?2hJXCB}csKADTVdM%k;d2@2VW zo4!nkG1?Xuk*Gf)Vde)leVQ1lieCO(#9@p_Hr^<=10zpmCA-YWoG)8eWu;VvY$6x_ zvuC*82N-Un`F{fB8z`U;sc0gv)O!2_T#U9^?&ctIypH(ixzdE0{|a=^wCuv zR5>kZ@YRzSN<;ja`D{<5z@?EEbNSBHaO!?(}vD*gcc=8?Aqfc_UYTc&-vD2BG({r0)tef@6{56537J%+Z8mcKdtYwU+*i!X~qaPmuoi;N?U z-#A+SIDbc{bjB8iFtqJ(y$XR;ji3m2fq$%+*mdLUEq@Y2+s2p`9TB4mA!hN<;1uRy z(T5v*Px;pb`-bo}mz#23~f6W8Oy+teNoA=Ish;L z@NLQgECmwX3p@a@&#b5MmjM&}GdOVIa`(15et(A?nTn9U52Ip%h^Th{E}VjqFt|Fl zl)u?I;P(BlfA%V|*g9J`O! zMVH~n?*RZKrnng^UgV)2wP{V>yag8Lg?|n`zkGj(8iwmX1i$yr`%>Yh3DKkoosc0P zTz_(mBV-i8Q>Z85ri$Y!x1Qv;;1kjCqVrW+>zVJ2k^J4+{4qWNv_Mk(OR^$gD*vs! zm|P)@Y-2B7huw<_Ve;XCwuD2Q6eq>)@n13SzcL2a~06bU!l8NHjaiXFujbC(kvnSt& ziy556e*6W>!=FcZsySaezZ;5fL~6sqdhjVLME1Nb0H7k1y(;miLv(F-y-lr1O!;$1 zK-D|RMDq-LSMlMWif{bLi)|l0&lX{G;b80mBl`lAH*Yc7I14tvAs;glk&S-U{(nM7 zz{sB$H@XO>NNe+A#W#8$>^FYoVf@H@XGIv+Iz92{lLc8}u8WaM9}ugBr@@u1>!K6^ z`fzer-Fc`AX!P91_}@Li1xBCQiHLX|K}&{-$%B%X2i$o-bvHgc&--tu_JmOA6r;55 zN2LwOID&frJB>WeDE$9ZsE6v--G5-ofT;+mB)}|~?l)vX`w(TX6yrx8MxWUj{nO2e zek|DH*9Ty2Y{eEW5&rmn6a^)ql?@k>`0{q$tpR}RirdvJW|6@soi#M#S)_*`s|e;g z!*dUW-1RmSxgQV|$!Wzm%PHf})AXX6i>dIdA|bHFd3@2~Px%fIMd%~@B7e59pQ(Uy zYUhCb$y#uY=6rb&h%mV=FL)98HJ+OaEXjIVFxlI(1^|NseXCS_Y|f_=IR^1{DL*aV zWv>*w-ezo3;9rW?MaU`zWZyBxp9NVFOLIWcZ!yWA2b5De0`hrAwB3!p^7|G+mAwxt zpn2y^_lp}_M*5;<5M6KWL4ObexCKYUIFOHqp=fYBpl^EAi?IneIzptq5M{!I*wT+* zUL}(bM(FB}2rk;g!F7gf^B2W55ahB5zjj1qBB2R>3X%A-1gXYvq6~5X!DtxDpQ{?q>0kj5bxbkH@p$T-sYjiU^HoiwLu=igd$wEVU5TPG(cO*6+~ zVe*0`nsl0A&LiNj3{5a+vRZq#7ImVngtVv?B7eZj!f*qNpq9U4{#q*`pFXj=zV9z@ zY7$=e`8R=I6ngWJ9sf6#M9Ggy{Z0ufPAD& zKVx6f=PXWNM$t$0{0E4SA}Rva>)&B2rH^6ZbIp#wm83+W{D0E%fRR64G|?;+qaD0l z_lQUZC7OabgIHww4cl%eQvCtxg?G94CWN@h9Jk5-fTE$8MVtUA_WWQXLl!>(;P$=l z;PUGw-B*U`?R)WBEcuR*AL`eCb^9LnFFgQ36qMocwjF!lx%^tXFXc}i5zPy)3?V8a zc42emrG+XMY=0@hPd%0u(K^L(8U_FkFYhdYJE{A*00x)Qc~0R5f=m&i3LeA4AO80a zV5Re)`aT|CM(;nzOB~+52c(D)5D0K++krnFvW!Tx-X!wi055j-QdxJSgLJ+cK83-Ji3%%84~bbxn)V!;-# zri$IP-+v*)5qY(~&~!r3zzggE03PBryeM=VweI@GmmlCKjbjS^0Udzzr}M~q@Hs2Q zy#91MJNf+SOy#Aj{&+OnI^4FR*m)3DLR8OR$&-4qt;V0@`ai^}blWkx;6MHF*IN9- z2^2bH~bkVjlX1@(>yGw6`D)Lehbc)h4id`02 z(mt|n^uKruAHeGpg2KbWsjx=&aBzy_uWTImdic99{|`R__-;NI9U_536VucD>8MK+ z&42$w5FZfbrMsSok$r)YeG%qqF5*oVrX%x=d!?wNSIA9SLJWd;zb;{d(Lr-6Aq z!7}_E8Q=y3DDkT_MynRVy4N27j(cyy9;xH6?6BBnp;`n}ej{^2EcTT?T1()}nSZA^ zJc^1ErRM@qJa0Miyrm_0R^D2uL>yidyz_t}|ZBU)BiS0kC8SBj}(1;(S>|eB59FCH_NN z2rE#&_B`PBJvqPP^=I)1ixj!vWPkH#36=62%>mh0AHfBtwc+q{jU2FwC%ESY&H307 zy7M6VR#E;uU^#X9O{5Y#OcPoi=U?~rr~F2mVcunb zx)$|<_Xo^%ki7nMmSu@PGk@s`m{=72@hao5`}~{0-&%8^wdSakU*r#1_K#??KVHjU zF@Nh1*rHan{(voN1@#AHE3#!*$-^T4Or~nth4v?6q&yC2IMN%UXa6fyO5QjkN&kU{ubbMryQBSGRDIpyFeZXRvGC{y~KAI{yq&`Xe*zOat|2%#46#`@^Y%UMyaGO~U;_a5-O1Wr@f2 z04qL_^*ka3G>YJ|{NZ%ac=0)b@cwoW_|K=4K@Amu{eS<8tgX=Bdx_c82lNp5j}QL` zdHmsY_)Vh8`~zeQkgxr#RE$^=<~@Hl`muH>x>1n+7OhH1A1{NIy%pe)_fLrc5ll$d zof)r#Q&bQINo{rY&*$h!qS)^T@ymAIi(C<0L}KHMWF62U`AV4KL6E&t73=`mBijjd z{NX5nRQTy#mUO7g2Pr@Cu=%+UtoD-MyBc)xFVhuXkp*CiU$+Ce9i`$|_5Aw?qJ`=C zHM-Xy$QBxnf-0b#TBBk5doO_+~o5pHcJuLZ{- zh<*tXPFK-WNVB@+HntM`^byYm)1n&{72XtPi(nBw~uLQ93JlLF1o0v3lsPK!TpNn9KeIf^wh;AR^d`|cLO`~pp z$H6l5iRn!OF*uJ`Ff?Y+G01(PsrXc}k8*lzt`lMctm2s$ygwW}HkrtXR#1PyCKW+- z@YkJ!)XiV3KVWSp<#n5c6aH?^l`!1YN+9nKSgAi9^@nQ^KPu!G`6JfNfAjnSC7&rh z7dY>tz+Wnw;ZW;A!k5URxxge>EhZVrbjgC1N<@TDDc; zy76@p|IoHE_87kzLS;hmD*QnF4qZY2jxFksSTTPW72UwO{Q+6=0l{-~K}2XW5(Ye* zF&ETOi~(IGMHWTf<6G+HFAG{j@_GCW3v2MP>v`Idkov==@rz7w05d|A@$Z*B;ERqF zSM}%Pg|`A7e<(}iA>Bi7#I~+Kpw75Jl_6Zp{(vGJktn!U@-T2-cx4Dtgg=Jo6(4@^ zmlSQjmwG9JU;e2{aXSEfA~*|Vnru1Hog1R#lBFd;7r>6^;RUQvgg_w3F)50RiePBl zn7nxlKrXyAA(|55sAmJn_C!>Jjj`$J3_ickM#FNTe{%cih9u=7`P3Ts1uQ&-k7Gm? zkTDTuD8j41!a;Wte)m8yLvG5g$M|pE#pDWreL;@D8U6+!Da0dNUnr^k1d|NW&^aSn zMc=SzC$`D_`SxMqkMJ?-*_r&R81{l2;8R#(3Shz?F|R8sbLD)c~PW*wp}7uhNXgT19gd0AdhRZ8e1!%SA*8WcULf^m;hx_3*6Mi;2$xLLWUW z&N1;kj7T*6hj}n*m`+*-fRE>M9Q<-UCO(#vf9H1)Mb{B2>UTX4Bl`j)`y%uTbODT* z=w|`It-Ejm%of{;S8F@!@=W8;!WC~#;E-(^(DmoL8gzms#h(9aKibbUKMN|p<)G8m zb;II0rN8u`*TW}F6noa|;l24>nv7074+p&-{?z+zYlVbfn*1|{j|K9d_Y`$ z|217L|7OtXI@8s%G?Vhbz`}cU@=D5n2fZFXp3jZUq6&{8sZdO4dTB@7*aSJ4ZsUZ) zGX<&mD0WuWh*0($PAi%`ZxERRd{4Jte?y}cgX3SW3o9Zz#}I+$p%ZrhsO}RA(FDR` zjT(gUX4Nm_`<5$0B%?vek&l9wXby>f-RWw{`bFzPD!@N9qf=A z{}^mIp3FT=$Sv8blY-uB4Z%|W(9WoOy9Ca$mf83(~HklMq22jESD@g7$K2_KI`@HjI95O zZWQEqSy*3DmeB_!1!&;^8I`vk)FPbt`^npYVCEm;oO zdQ~+JbTEQ2f;=Mw5uQoBf1#a>B~5%Rr;J|}c_JXkUpaOjL=}Su@K-*Oi*hC-;P(BP zL;KH`g*88aVqC?wWg{SMhz6pZk4C=!_7!ke!4GPo41qu3t4K6VF?^8Y;3Dzy{%}ct zPn(C2fYOHOvtVMhX?`6v$)B%oejsQdYOOh1YYxg^n?GRP=0J7xBG+vWR5yR?57^>n a;{O3q+aq&3=;HVQ0000fFDZ*Bkpc$|%qy>7!W41{OvDTrh*+NP_Q z!a?C7c?E%$7@LUxAkp^gcQBBxpf?iua7W&q;rseC-eI_nW~JIVC+Okp?cH=6dzqn! z%ctpP;%SzBX@Ww~!)ayOEJvKEolNJsHt6BQbdQRW_W8vMK7Yv;cCu7bVN5A6$x);v zx^hML)Fs)ve8Adaq>481H0#zotYein8*^gGRfUV!1&c>p$q}#mL&c+Ql)MTyK_MG( z)5~-if^G9667&NiCVo)U)5Jhkbo1XV4nsK7c%#$~L>}`>b}7d=pSScMd4}tKfZ;Zp zKLO47ey*|?gMR=36=+FBK~#90?OjiA+(w>#RJxTpcm=QRK}MQQn)v|6!p-(&Pd*uo znM9|>egO-u9QMB0OSif)K)%2ZFlsyZAlQ>E1RI}pcT>i~A0TWmB01RDqwNIPSgnM6 zU=>*`R*_X?@sA`vfM_qxZv_Cmv*3Npe|`Bo0N^^n?>-O6r=GV; z`*Kl?Y`gp2GrRlx-y$B4zf5|JY#S|qbNJWT56c!`7Kh;ER|XduM;gCzwES_7PU(y- z3Sng1;eUD+0;w875$pngteDt!2;&~X^b}TZMfdl(ZCCBOjz!1Q9DF?6=NOUjo z5Wp_8p2lAWOz_X)z=6x%+vfNka%3t(`aX<`1%D!<+WEV13P!@<>ey2LX6Jxgcf0<{ z%fx1jsZ`Vf9pU*HXN)6pRz(^&)Wh3Dl*xt5`Q{G*LK^xtrdwWf9?pVdMBA^o?-7Q zKKxVhO&)r&?c-$uKo}P}1^%Tkogt#%Jew|LxSC5DJ}Ql(zk-v;i4MQ18Fg z$kU9%|38I#sBYW|mJFDRfJy?)f`93LLl(3TQT9qPdFWyMxsCBZU61I;f-Qc10M^E4 zY|#?oPd`LaPy$-na1n_wZ`a)#0JyHWUCm+^8GO=NLnEF=dI++LV6HPf_dv*P?{*^h z1A-zst@vg+W&C-XUQ}~26@FDD1hzblFFO1w-vOcsePF+dE$nA1pq$z{Ab)?d7F?q_ zUmgS^Om52yUPOM4=cWQnvR)QU_O`46z_38yDit4_^QlCRVSHW6Pm6ciE5){VJGLnB zuSDx2WR(K4@0jAxf~<(8IiTpbnB>m`%BdUy`8*@q?#5pEeT$&V-Uk)XymPMm#f?oP zebF+AuDA9e2m#!JqhTD#M}NamG`JnmH$Cdb*n}G$A<|xmGGRh&>Bldxl1T?6bah7r z7wzHTI>WX3i((cCa#e(1J0dcX&;&n)NPJm>RO2^M204IWGz{g>)eK{4E>|4`xS8i! zW+W5^gQ5Y#WDio>vFKog6`oZxDLVw$8D0nf>Z$-bPX;tjx>JbD1%=o&1u^Cx7Hjjf35u1(a}-U%yC$lyda4tohF#` z2>2^Q6U>>c)}F0JooFi|Eoz0xAF#48+`uBJ<*%5()=J1{kFBon`>X4kgx7ukP2d;# z1C~lo{ozjkbrEj6j(`4yb)SDz_+x_+rk)q{SC)t=G1mbA-}e)rl?|3(^chMXnl$yi zZd1;`3H-^?FkFA-RrjZBQ7=;c0Rw+JrX$%7UQ7t__&8c`sy`nfAL%m4*jMy9i{lqj z^ie(kA>yNmia_=HcbH1)BUt!Cv*YhVQle0P>3G1%pDvnc7JrJ-4qmQ$M5KZeO+lPP zEVBHDZPyd2{($ttyWD#dLfm1F+hl)0(NN4HP5=};elU?Ciyr`R^W*OD?AsOHSBBZm zkK?si@*N>R)UW@=&5yBr<^c$zpbYyr?b!Ry*|*YtDSzsSXkK__2vHHS3!5V^EmW~! zO96iBv8;&JDSwXBFa)rFc54OPO5M)|Fg%OSa|SmMWQqt?@CX+E{J*yVE1mz?_wnc~ zdjADpVE^XFK#B+ffdEIg9r)8hF1$1$R?&=6R$eNDBUz8OInwv0isP}uyj%9V4B=oF zn#mN$^rLM4(JSVE@$X?Df6e@k*dH+Trwd5wiOGbRXMa-i6J}l2pUz}ns^gExODLHW zy3A*4Ip7m3O6FD1KW$|3f&C_!L^K323k;ivp0|=!TJl`~$%3>IvUva@829Y%v44#I zjt_j{;IQ`=yNd<(Gx&u?@SND<9uSxI$QDd2!~>u*f4Vx-0p1CU1zWtDDt6O;hYSbg z)%sG?34cKY&(Q|}+{bZvQRp^m-SwNVKETf!#}xVlIsoTS=aKc`b5@9X{ptGseExK% z@={fQJQ{5sZd+0G??sgm)$>>Kq+V{R@#nby_i-%Uc1SMx&p-a17QZk8x&XF44+kV4 zoYLMAk{hx`GaLQN03AVHM_xKW5YTcyT3vT;xv|Z7XB;+sL-N^hT&6FBL(jC@rJdWsxQA1KY;n%Xjbr zye=Uq+#enbYh?F_$2k1P#$m6AfB5SE@H2q#7YorL5-2n=J0<;5>Lf>nBC%k_gxfBA{v!OpzFnTGXG8uJieypW(Wc1qXrn zfG98B_B@R3HyGP*!aU7Iyvf3JWS(=c6jk&JxhYGCVesyEB}_27kK>@$O9@>7GcGrL z6nywWucvfOBi0-uccz$s%*JN4PfigwC4Vm+Kx~CU+E2TqTkn@Pnx-kWV9bJIH^}`c z3eMkErle;n5tpUH1JFD3qTD^>n9Fpno#g});M++YAD zewD^()goB;`UAjW?`_y4b@+`P7P~A|i(txcWKM|1zS2i)37k3e6o&^V!{6=#?_J7q!aDi!UIQ(2A2dv@=?s-9TK6V7Tzua1xc8U!K z6u2uuy<+j&^NdBm{hKy$7UT=66^ri|3#I4>7^;M@k&Uji6(uZBOZTv=0&LB9+w-uU zDBics+ZCUEy9&j0ma#7djeG@1!q8)c{kaRT@Zosm_6KAkss+Wn)C=ApFxNry z`qNpKCHl;yCtzYx@W-o+zklxYZvua7&4JdMqfUO2KVaEEqRIYvEq}%Qtv_IkTG9Fg zwx|`kkN^GJiaz_|sLvpXQHu-T5o^XN={5Rr$kF2>t0cHcMSH`AbI2AAjd)-qSJ#eXKI@ zJ}ruys1>5(?d6a5-PjWQoVslQ0bye|`t}uO}H_$Us(}$$y^Z9c{{6 z+BC%efD-)UfB%-OU0|^D0`n*L=ppc*AN~*W_`~V&n?#fOhsYKnU;9_77_lPEd;VvjOQqg4E=p8o(rv@kutM)&#y*+QdHPz97zYc$MY=LOK`?)H9A z9EW&R?js6BA~ZSO^I|6w9xuX+9bo;--zLB0e?-^BbTp*T24RcX0pfG8*_ntlZoj5O zOMvqycYzlGEs5#+{K;Jaz+tb4-+c7}0Cj)3Jdi~~6GHrT5G?HbSR`cH^Vo_9+zwvO zF2b6yDa^Q6Ac!)wJHThw=ZFhcH5#cvwV2h3f`D!W#7p2YxA8IAPN3@#mltUlqlggK zf9Ao$zK`XCBu5m9uMEtYg@=WB1O%ce*wk7t54{M8dzj;X?Dm`Eek=#n^@l5hGK^?D zPa?tb;_EyCusjVaA&yvZWkf+4e8eLl5JkZ}-GwrUqMw6krjh6~)hL*+7K{$yUI6HX z+sdv5hsKv1n_ZvjW@F#S*7FjP2)8zuFxP@(5JbO(2xk}3Q%JMA*8t(Q4T{(RWMIuX`O zrYXi~k^9pR=8jb&XglBl#U5LsG#G)R41YMnpXpDRh-^6{UtWIGkn-QSgV~KckCO%pWU$>KB5F>whX+}gRJg6Ipt2iF8 z?Cw0_!wRba_prVdo~2g;*m)jo&ZkXGnmAPWMbXbiFvLEQgGofU3voWDd;X?TH^1Xx zmHEWh-gLRyrA2M-?9%1`|P zn^XkV!C!X@QY(L*sMVyrZj*4r->tb4hMM_n6(Q>nSgAi9^@nQ^KPu!G`6JfGUngqy z2b6rK^jzS)ivoYCXp%o4&tC^}S3=kZMZ*^^Rt)FhY3zR^URxxgH6)*6WZT##VmRPh zwpHP}@pTdZ$hI-@7{3`pWkT>O{6PE;y@385Tht%13jR8w9RXSK0l{-~K}2XW5(YfC zV=kzn7z4UWiY$t{$G6nYUlz26O)O_>O@vN&x^!1f2AGSo3}Cb(i>r61@YPV}b3R1q6m~uY)~hsQu`Z%H3IH*P zxqQ(N$QY8pN2Xyu1@Oz_RVY3OhzV)B1Yp}(^L_kb@d{t}K8l$aezI5uB4kAAIE;`u zeh0vqMAtD>)Qg13Mn4MxZrp*p1m?>vxQKthT3hG9Nj85L&Z#v4lPc)uDP?RjQaxjA7c!zYV{ky%vXF(eg=DNQf!Xd7F<45r&Sq3}#WDn5$- zMKvOn{f6U;CeIs0rU2j5ZEB&>ioxOEt_v$7I>!)!=Alz||ETU03eg0@B9Iz{@n+R8 z;`^2>LL{TXC5wC%QUQLtSl}sR#qodXVu3w{1J0MHi72?Y6z(cSo~JeXaJm*;^1ZEC zUosmFi^M|q&pnqsCTV>isG=!keMT!mWP@9ACCDIBBj9+kh}C@4utzEypDY$(&UeUF zJjy~OK41WMHHXu60nAC5pu!t$oWvqzZup(35f~u0T-CP21tL_da5NcoFoJ&(YX~aB z*n0`6Kb#5vcmxbYDBLj+Usu2%w!`gH;~#?!$J2#}Dfy)ooIjsyR|(6p~&(@{u(S%IBC)3c%