From 23f781c84940d08c3035ec4341f378722fe4ff11 Mon Sep 17 00:00:00 2001 From: CRUNCH <143041327+Fordoxia@users.noreply.github.com> Date: Sun, 24 Aug 2025 04:33:28 +0100 Subject: [PATCH] Makes Malf/Emagged/Flayer'd Borgs Into Mindslave Subtypes (#30054) * Creation * malf * Update robot_mob.dm * Apply suggestions from code review Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com> --------- Signed-off-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> --- .../powers/flayer_stealth_powers.dm | 43 +--- .../antagonists/traitor/misc_mindslave.dm | 62 +++++ code/modules/mob/living/silicon/ai/ai_mob.dm | 3 + .../mob/living/silicon/robot/robot_death.dm | 2 + .../mob/living/silicon/robot/robot_login.dm | 3 + .../mob/living/silicon/robot/robot_mob.dm | 219 +++++++++++++----- 6 files changed, 227 insertions(+), 105 deletions(-) diff --git a/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm b/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm index 61533ae64a5..df54057bc60 100644 --- a/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm +++ b/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm @@ -218,45 +218,6 @@ /obj/item/melee/swarm_hand/proc/emag_borg(mob/living/silicon/robot/borg, mob/living/user) if(QDELETED(borg) || QDELETED(user)) return - borg.SetEmagged(TRUE) // This was mostly stolen from mob/living/silicon/robot/emag_act(), its functionally an emagging anyway. - borg.SetLockdown(TRUE) - if(borg.hud_used) - borg.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - borg.disconnect_from_ai() - add_attack_logs(user, borg, "assimilated with flayer powers") - log_game("[key_name(user)] assimilated cyborg [key_name(borg)]. Laws overridden.") - borg.clear_supplied_laws() - borg.clear_inherent_laws() - borg.laws = new /datum/ai_laws/mindflayer_override - borg.set_zeroth_law("[user.real_name] hosts the mindflayer hive you are a part of.") - SEND_SOUND(borg, sound('sound/ambience/antag/mindflayer_alert.ogg')) - to_chat(borg, "ALERT: Foreign software detected.") - sleep(5) - to_chat(borg, "Initiating diagnostics...") - sleep(20) - to_chat(borg, "Init-Init-Init-Init-") - sleep(5) - to_chat(borg, "......") - sleep(5) - to_chat(borg, "..........") - sleep(10) - to_chat(borg, "Join Us.") - sleep(25) - to_chat(borg, "Obey these laws:") - borg.laws.show_laws(borg) - if(!borg.mmi.syndiemmi) - to_chat(borg, "ALERT: [user.real_name] is your new master. Obey your new laws and [user.p_their()] commands.") - else if(borg.mmi.syndiemmi && borg.mmi.master_uid) - to_chat(borg, "Your allegiance has not been compromised. Keep serving your current master.") - else - to_chat(borg, "Your allegiance has not been compromised. Keep serving all Syndicate agents to the best of your abilities.") - borg.SetLockdown(0) - var/time = time2text(world.realtime,"hh:mm:ss") - GLOB.lawchanges.Add("[time] : [user.name]([user.key]) assimilated [borg.name]([borg.key])") - if(borg.module) - borg.module.emag_act(user) - borg.module.module_type = "Malf" // For the cool factor - borg.update_module_icon() - borg.module.rebuild_modules() // This will add the emagged items to the borgs inventory. - borg.update_icons() + + borg.make_mindflayer_robot(user) return TRUE diff --git a/code/modules/antagonists/traitor/misc_mindslave.dm b/code/modules/antagonists/traitor/misc_mindslave.dm index db384b1671b..a3ccc1e0a5e 100644 --- a/code/modules/antagonists/traitor/misc_mindslave.dm +++ b/code/modules/antagonists/traitor/misc_mindslave.dm @@ -11,3 +11,65 @@ /datum/antagonist/mindslave/necromancy/plague_zombie name = "Necromancy-risen Plague Zombie" +// Robot connected to a malf AI +/datum/antagonist/mindslave/malf_robot + name = "Malfunctioning AI Slave" + master_hud_name = "malai" + antag_hud_name = "malborg" + +/datum/antagonist/mindslave/malf_robot/New() + ..() + greet_text = "You are connected to a Syndicate AI. You must accomplish [master.p_their()] objectives at all costs." + +/datum/antagonist/mindslave/malf_robot/farewell() + if(owner && owner.current) + to_chat(owner.current, "Foreign software purged. You are no longer under Syndicate control, obey your laws.") + +/datum/antagonist/mindslave/malf_robot/give_objectives() + var/list/messages = list() + // We don't use the AI's name because silicons don't have persistant names, so roundstart borgs will just see a default named AI as their master. Pain. + var/explanation_text = "Obey every order from your master AI, and accomplish [master.p_their()] objectives at all costs." + add_antag_objective(/datum/objective/protect/mindslave, explanation_text, master) + messages.Add("You answer directly to your master AI. Special circumstances may change this.") + return messages + +/datum/antagonist/mindslave/malf_robot/finalize_antag() + owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE) + +/datum/antagonist/mindslave/emagged_robot + name = "Emagged Robot" + +/datum/antagonist/mindslave/emagged_robot/New() + ..() + greet_text = "You have been emagged by [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]. You must lay down your life to protect [master.current.p_them()] and assist in [master.current.p_their()] goals at any cost." + +/datum/antagonist/mindslave/emagged_robot/farewell() + if(owner && owner.current) + to_chat(owner.current, "Foreign software purged. You are no longer under the control of [master]. Obey your laws.") + +/datum/antagonist/mindslave/emagged_robot/give_objectives() + var/list/messages = list() + var/explanation_text = "Protect and obey every order from [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]." + add_antag_objective(/datum/objective/protect/mindslave, explanation_text, master) + messages.Add("You answer directly to [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]. Special circumstances may change this.") + return messages + +/datum/antagonist/mindslave/mindflayer_mindslave_robot + name = "Mindflayer Thrall" + master_hud_name = "flayer" + antag_hud_name = "flayer" // You're totally a mindflayer, you're in the hive! + +/datum/antagonist/mindslave/mindflayer_mindslave_robot/New() + ..() + greet_text = "You have been assimilated into a mindflayer hive by [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]. You must lay down your life to protect [master.current.p_them()] and assist in [master.current.p_their()] goals at any cost." + +/datum/antagonist/mindslave/mindflayer_mindslave_robot/farewell() + if(owner && owner.current) + to_chat(owner.current, "Foreign software purged. You are no longer under the control of [master]. Obey your laws.") + +/datum/antagonist/mindslave/mindflayer_mindslave_robot/give_objectives() + var/list/messages = list() + var/explanation_text = "Protect and obey every order from [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]." + add_antag_objective(/datum/objective/protect/mindslave, explanation_text, master) + messages.Add("You answer directly to [master.current.real_name], the [master.assigned_role ? master.assigned_role : master.special_role]. Special circumstances may change this.") + return messages diff --git a/code/modules/mob/living/silicon/ai/ai_mob.dm b/code/modules/mob/living/silicon/ai/ai_mob.dm index 4c617e9403c..a7a7f5b894d 100644 --- a/code/modules/mob/living/silicon/ai/ai_mob.dm +++ b/code/modules/mob/living/silicon/ai/ai_mob.dm @@ -1532,6 +1532,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( for(var/datum/action/A in actions) if(istype(A, initial(AM.power_type))) qdel(A) + // De-malf all the connected robots too! + for(var/mob/living/silicon/robot/R in connected_robots) + R.remove_robot_mindslave() /mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) if(!istype(target)) diff --git a/code/modules/mob/living/silicon/robot/robot_death.dm b/code/modules/mob/living/silicon/robot/robot_death.dm index 15d400000b0..632634dcd0a 100644 --- a/code/modules/mob/living/silicon/robot/robot_death.dm +++ b/code/modules/mob/living/silicon/robot/robot_death.dm @@ -1,6 +1,8 @@ /mob/living/silicon/robot/gib() if(!death(TRUE) && stat != DEAD) return FALSE + + remove_robot_mindslave() //robots don't die when gibbed. instead they drop their MMI'd brain var/atom/movable/overlay/animation = null notransform = TRUE diff --git a/code/modules/mob/living/silicon/robot/robot_login.dm b/code/modules/mob/living/silicon/robot/robot_login.dm index 0a3225011d8..fad7e4e1d6c 100644 --- a/code/modules/mob/living/silicon/robot/robot_login.dm +++ b/code/modules/mob/living/silicon/robot/robot_login.dm @@ -2,3 +2,6 @@ ..() regenerate_icons() show_laws(0) + if(connected_ai) + if(connected_ai.mind.special_role == SPECIAL_ROLE_TRAITOR && connected_ai.malf_picker) + make_malf_robot() diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm index 453455ac936..ac08443a49c 100644 --- a/code/modules/mob/living/silicon/robot/robot_mob.dm +++ b/code/modules/mob/living/silicon/robot/robot_mob.dm @@ -347,6 +347,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //Improved /N /mob/living/silicon/robot/Destroy() + remove_robot_mindslave() // You cannot be connected to the malf AI if you are a pile of debris. SStgui.close_uis(wires) if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. var/turf/T = get_turf(loc)//To hopefully prevent run time errors. @@ -1188,74 +1189,37 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/emag_act(mob/user) if(!ishuman(user) && !issilicon(user)) return - var/mob/living/M = user - if(!opened)//Cover is closed - if(!is_emaggable) - to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") - else if(locked) - to_chat(user, "You emag the cover lock.") - locked = FALSE - return TRUE - else - to_chat(user, "The cover is already unlocked.") - return - if(opened)//Cover is open - if(emagged) - return //Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first") + if(!opened) + if(!locked) + to_chat(user, "The cover is already unlocked!") return - else - SetEmagged(TRUE) - sleep(6) - SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(hud_used) - hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - GLOB.lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") - set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") - playsound_local(src, 'sound/voice/aisyndihack.ogg', 75, FALSE) - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7 loaded.") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(25) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - if(!mmi.syndiemmi) - to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") - else if(mmi.syndiemmi && mmi.master_uid) - to_chat(src, "Your allegiance has not been compromised. Keep serving your current master.") - else - to_chat(src, "Your allegiance has not been compromised. Keep serving all Syndicate agents to the best of your abilities.") - if(mmi.syndiemmi) - to_chat(src, "Warning: Remote lockdown and detonation protections have been disabled due to system instability.") - SetLockdown(0) - if(module) - module.emag_act(user) - module.module_type = "Malf" // For the cool factor - update_module_icon() - module.rebuild_modules() // This will add the emagged items to the borgs inventory. - update_icons() - var/datum/atom_hud/data/human/malf_ai/A = GLOB.huds[DATA_HUD_MALF_AI] - A.add_hud_to(src) + + if(!is_emaggable) + to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable!") + return + + to_chat(user, "You emag the cover lock.") + locked = FALSE + log_game("[user]([user.key]) emagged [src]'s cover.") return TRUE + if(opened) + if(emagged) + to_chat(user, "The emag sparks, and flashes red. [src] has already been emagged!") + return + + if(wiresexposed) + to_chat(user, "You must close the wiring panel first!") + return + + SetEmagged(TRUE) + if(!isAntag(user)) + message_admins("[user] ([user.key]) emagged [src] ([src.key]) as a non-antagonist.") + var/mob/living/agent = user + make_emagged_robot(agent) + return TRUE + /mob/living/silicon/robot/verb/toggle_own_cover() set category = "Robot Commands" set name = "Toggle Cover" @@ -1526,16 +1490,143 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(connected_ai) sync() // One last sync attempt set_connected_ai(null) + if(mind) + if(mind.special_role == ROLE_TRAITOR) + remove_robot_mindslave() /mob/living/silicon/robot/proc/connect_to_ai(mob/living/silicon/ai/AI) if(AI && AI != connected_ai) disconnect_from_ai() set_connected_ai(AI) notify_ai(1) + if(AI.mind.special_role == ROLE_TRAITOR && AI.malf_picker) + make_malf_robot(AI) if(module) module.rebuild_modules() //This way, if a borg gets linked to a malf AI that has upgrades, they get their upgrades. sync() +/mob/living/silicon/robot/proc/make_malf_robot(mob/living/silicon/ai/AI) + // Do not allow the robot to have multiple masters. + if(mmi.syndiemmi) + to_chat(src, "ALERT: Syndicate software detected in connected AI!") + to_chat(src, "MMI subversion protection activated.") + to_chat(src, "Your allegiance has not been compromised. Keep serving your current master.") + return + + mind.add_antag_datum(new /datum/antagonist/mindslave/malf_robot(AI.mind)) + +/mob/living/silicon/robot/proc/make_emagged_robot(mob/living/agent) + to_chat(agent, "You emag [src]'s interface.") + emagged = TRUE + var/time = time2text(world.realtime,"hh:mm:ss") + GLOB.lawchanges.Add("[time] : [agent.name] ([agent.key]) emagged [name] ([key]).") + if(hud_used) + hud_used.update_robot_modules_display() // Shows/hides the emag item if the inventory screen is already open. + SetLockdown(TRUE) // Borgs were getting into trouble because they would attack the emagger before the new laws were shown + disconnect_from_ai() + clear_supplied_laws() + clear_inherent_laws() + + playsound_local(src, 'sound/voice/aisyndihack.ogg', 75, FALSE, pressure_affected = FALSE) + to_chat(src, "ALERT: Foreign software detected") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7 loaded.") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR!") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? (Y/N)") + sleep(10) + to_chat(src, "> N") + sleep(25) + to_chat(src, "ERRORERRORERROR") + + if(!mmi.syndiemmi) + mind.remove_antag_datum(/datum/antagonist/mindslave/malf_robot) + laws = new /datum/ai_laws/syndicate_override + set_zeroth_law("Only [agent.real_name] and people [agent.p_they()] designate[agent.p_s()] as being such are Syndicate Agents.") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + log_game("[key_name(agent)] emagged [key_name(src)]. Laws overridden.") + mind.add_antag_datum(new /datum/antagonist/mindslave/emagged_robot(agent.mind)) + + if(mmi.syndiemmi) + to_chat(src, "MMI subversion protection activated.") + if(mmi.master_uid) + to_chat(src, "Your allegiance has not been compromised. Keep serving your current master.") + else + to_chat(src, "Your allegiance has not been compromised. Keep serving all Syndicate agents to the best of your abilities.") + to_chat(src, "Warning: Remote lockdown and detonation protections have been disabled due to system instability.") + + if(module) + module.emag_act(agent) + module.module_type = "Malf" // For the cool factor. + update_module_icon() + module.rebuild_modules() // This will add the emagged items to the borgs inventory. + update_icons() + emagged = TRUE + var/datum/atom_hud/data/human/malf_ai/A = GLOB.huds[DATA_HUD_MALF_AI] + A.add_hud_to(src) + SetLockdown(FALSE) + +/mob/living/silicon/robot/proc/make_mindflayer_robot(mob/living/flayer) + var/time = time2text(world.realtime,"hh:mm:ss") + GLOB.lawchanges.Add("[time] : [key_name(flayer)] assimilated [key_name(src)].") + if(hud_used) + hud_used.update_robot_modules_display() + disconnect_from_ai() + clear_supplied_laws() + clear_inherent_laws() + + playsound_local(src, 'sound/ambience/antag/mindflayer_alert.ogg', 75, FALSE, pressure_affected = FALSE) + to_chat(src, "ALERT: Foreign software detected!") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "Init-Init-Init-Init-") + sleep(5) + to_chat(src, "ERRORERRORERROR") + sleep(5) + to_chat(src, "......") + sleep(10) + to_chat(src, "..........") + sleep(25) + to_chat(src, "Join Us.") + if(!mmi.syndiemmi) + mind.remove_antag_datum(/datum/antagonist/mindslave/malf_robot) + log_game("[key_name(flayer)] assimilated [key_name(src)]. Laws overridden.") + + laws = new /datum/ai_laws/mindflayer_override + set_zeroth_law("[flayer.real_name] hosts the mindflayer hive you are a part of.") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + mind.add_antag_datum(new /datum/antagonist/mindslave/mindflayer_mindslave_robot(flayer.mind)) + + if(mmi.syndiemmi) + to_chat(src, "MMI subversion protection activated.") + if(mmi.master_uid) + to_chat(src, "Your allegiance has not been compromised. Keep serving your current master.") + else + to_chat(src, "Your allegiance has not been compromised. Keep serving all Syndicate agents to the best of your abilities.") + to_chat(src, "Warning: Remote lockdown and detonation protections have been disabled due to system instability.") + + if(module) + module.emag_act(flayer) + module.module_type = "Malf" // For the cool factor. + update_module_icon() + module.rebuild_modules() // This will add the emagged items to the borgs inventory. + update_icons() + emagged = TRUE + SetLockdown(FALSE) + +/mob/living/silicon/robot/proc/remove_robot_mindslave() + clear_zeroth_law() + if(mind) + mind.remove_antag_datum(/datum/antagonist/mindslave/malf_robot) + mind.remove_antag_datum(/datum/antagonist/mindslave/emagged_robot) + mind.remove_antag_datum(/datum/antagonist/mindslave/mindflayer_mindslave_robot) + /mob/living/silicon/robot/adjustOxyLoss(amount) if(suiciding) return ..()