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 ..()