diff --git a/code/datums/antagonists/datum_traitor.dm b/code/datums/antagonists/datum_traitor.dm
index e2536ad0776..309542ddde7 100644
--- a/code/datums/antagonists/datum_traitor.dm
+++ b/code/datums/antagonists/datum_traitor.dm
@@ -109,7 +109,7 @@
var/mob/living/silicon/ai/A = owner.current
A.set_zeroth_law("")
A.verbs -= /mob/living/silicon/ai/proc/choose_modules
- A.malf_picker.remove_verbs(A)
+ A.malf_picker.remove_malf_verbs(A)
qdel(A.malf_picker)
..()
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 21ccdfd3036..a030361eea0 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -1,51 +1,303 @@
#define DEFAULT_DOOMSDAY_TIMER 4500
+
+//The malf AI action subtype. All malf actions are subtypes of this.
+/datum/action/innate/ai
+ name = "AI Action"
+ desc = "You aren't entirely sure what this does, but it's very beepy and boopy."
+ background_icon_state = "bg_tech_blue"
+ icon_icon = 'icons/mob/actions_AI.dmi'
+ var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time
+ var/uses //If we have multiple uses of the same power
+ var/auto_use_uses = TRUE //If we automatically use up uses on each activation
+ var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules
+
+/datum/action/innate/ai/Grant(mob/living/L)
+ . = ..()
+ if(!isAI(owner))
+ WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!")
+ qdel(src)
+ else
+ owner_AI = owner
+
+/datum/action/innate/ai/IsAvailable()
+ . = ..()
+ if(owner_AI && owner_AI.malf_cooldown > world.time)
+ return
+
+/datum/action/innate/ai/Trigger()
+ . = ..()
+ if(auto_use_uses)
+ adjust_uses(-1)
+ if(cooldown_period)
+ owner_AI.malf_cooldown = world.time + cooldown_period
+
+/datum/action/innate/ai/proc/adjust_uses(amt, silent)
+ uses += amt
+ if(!silent && uses)
+ to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
+ if(!uses)
+ if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
+ to_chat(owner, "[name] has run out of uses!")
+ qdel(src)
+
+//Framework for ranged abilities that can have different effects by left-clicking stuff.
+/datum/action/innate/ai/ranged
+ name = "Ranged AI Action"
+ auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses
+ var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code
+ var/linked_ability_type //The path of our linked ability
+
+/datum/action/innate/ai/ranged/New()
+ if(!linked_ability_type)
+ WARNING("Ranged AI action [name] attempted to spawn without a linked ability!")
+ qdel(src) //uh oh!
+ return
+ linked_ability = new linked_ability_type()
+ linked_ability.attached_action = src
+ ..()
+
+/datum/action/innate/ai/ranged/adjust_uses(amt, silent)
+ uses += amt
+ if(!silent && uses)
+ to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
+ if(!uses)
+ if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
+ to_chat(owner, "[name] has run out of uses!")
+ Remove(owner)
+ QDEL_IN(src, 100) //let any active timers on us finish up
+
+/datum/action/innate/ai/ranged/Destroy()
+ QDEL_NULL(linked_ability)
+ return ..()
+
+/datum/action/innate/ai/ranged/Activate()
+ linked_ability.toggle(owner)
+ return TRUE
+
+//The actual ranged proc holder.
+/obj/effect/proc_holder/ranged_ai
+ var/enable_text = "Hello World!" //Appears when the user activates the ability
+ var/disable_text = "Goodbye Cruel World!" //Context clues!
+ var/datum/action/innate/ai/ranged/attached_action
+
+/obj/effect/proc_holder/ranged_ai/Destroy()
+ QDEL_NULL(attached_action)
+ return ..()
+
+/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user)
+ if(active)
+ remove_ranged_ability(disable_text)
+ else
+ add_ranged_ability(user, enable_text)
+
+
+//The datum and interface for the malf unlock menu, which lets them choose actions to unlock.
+/datum/module_picker
+ var/temp
+ var/processing_time = 50
+ var/list/possible_modules
+
+/datum/module_picker/New()
+ possible_modules = list()
+ for(var/type in typesof(/datum/AI_Module))
+ var/datum/AI_Module/AM = new type
+ if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade)
+ possible_modules += AM
+
+/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI.
+ for(var/datum/AI_Module/AM in possible_modules)
+ for(var/datum/action/A in AI.actions)
+ if(istype(A, initial(AM.power_type)))
+ qdel(A)
+
+/datum/module_picker/proc/use(mob/user)
+ var/list/dat = list()
+ dat += "Select use of processing time: (currently #[processing_time] left.)
"
+ dat += "
"
+ dat += "Install Module:
"
+ dat += "The number afterwards is the amount of processing time it consumes.
"
+ for(var/datum/AI_Module/large/module in possible_modules)
+ dat += "[module.module_name]\[?\] ([module.cost])
"
+ for(var/datum/AI_Module/small/module in possible_modules)
+ dat += "[module.module_name]\[?\] ([module.cost])
"
+ dat += "
"
+ if(temp)
+ dat += "[temp]"
+ var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu")
+ popup.set_content(dat.Join())
+ popup.open()
+
+/datum/module_picker/Topic(href, href_list)
+ ..()
+
+ if(!isAI(usr))
+ return
+ var/mob/living/silicon/ai/A = usr
+
+ if(A.stat == DEAD)
+ to_chat(A, "You are already dead!")
+ return
+
+ for(var/datum/AI_Module/AM in possible_modules)
+ if (href_list[AM.mod_pick_name])
+
+ // Cost check
+ if(AM.cost > processing_time)
+ temp = "You cannot afford this module."
+ break
+
+ var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions
+
+ // Give the power and take away the money.
+ if(AM.upgrade) //upgrade and upgrade() are separate, be careful!
+ AM.upgrade(A)
+ possible_modules -= AM
+ to_chat(A, AM.unlock_text)
+ A.playsound_local(A, AM.unlock_sound, 50, 0)
+ else
+ if(AM.power_type)
+ if(!action) //Unlocking for the first time
+ var/datum/action/AC = new AM.power_type
+ AC.Grant(A)
+ A.current_modules += new AM.type
+ temp = AM.description
+ if(AM.one_purchase)
+ possible_modules -= AM
+ if(AM.unlock_text)
+ to_chat(A, AM.unlock_text)
+ if(AM.unlock_sound)
+ A.playsound_local(A, AM.unlock_sound, 50, 0)
+ else //Adding uses to an existing module
+ action.uses += initial(action.uses)
+ temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!"
+ processing_time -= AM.cost
+
+ if(href_list["showdesc"])
+ if(AM.mod_pick_name == href_list["showdesc"])
+ temp = AM.description
+ use(usr)
+
+
+//The base module type, which holds info about each ability.
/datum/AI_Module
- var/uses = 0
var/module_name
var/mod_pick_name
var/description = ""
var/engaged = 0
var/cost = 5
- var/one_time = 0
+ var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false.
- var/power_type
+ var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade.
+ var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type.
+ var/unlock_text = "Hello World!" //Text shown when an ability is unlocked
+ var/unlock_sound //Sound played when an ability is unlocked
-/datum/AI_Module/large
- uses = 1
+/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades!
+ return
-/datum/AI_Module/small
- uses = 5
+/datum/AI_Module/large //Big, powerful stuff that can only be used once.
+/datum/AI_Module/small //Weak, usually localized stuff with multiple uses.
+
+//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely.
/datum/AI_Module/large/nuke_station
module_name = "Doomsday Device"
mod_pick_name = "nukestation"
description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed."
cost = 130
- one_time = 1
+ one_purchase = TRUE
+ power_type = /datum/action/innate/ai/nuke_station
+ unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time."
- power_type = /mob/living/silicon/ai/proc/nuke_station
-
-/mob/living/silicon/ai/proc/nuke_station()
- set category = "Malfunction"
- set name = "Doomsday Device"
-
- var/turf/T = get_turf(src)
+/datum/action/innate/ai/nuke_station
+ name = "Doomsday Device"
+ desc = "Activates the doomsday device. This is not reversible."
+ button_icon_state = "doomsday_device"
+ auto_use_uses = FALSE
+/datum/action/innate/ai/nuke_station/Activate()
+ var/turf/T = get_turf(owner)
if(!istype(T) || T.z != ZLEVEL_STATION)
- to_chat(src, "You cannot activate the doomsday device while off-station!")
+ to_chat(owner, "You cannot activate the doomsday device while off-station!")
return
+ if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;")
+ return
+ active = TRUE
+ set_us_up_the_bomb(owner)
- to_chat(src, "Doomsday device armed.")
- priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/ai/aimalf.ogg')
+/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb(mob/living/owner)
+ set waitfor = FALSE
+ to_chat(owner, "run -o -a 'selfdestruct'")
+ sleep(5)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Running executable 'selfdestruct'...")
+ sleep(rand(10, 30))
+ if(!owner || QDELETED(owner))
+ return
+ owner.playsound_local(owner, 'sound/misc/bloblarm.ogg', 50, 0)
+ to_chat(owner, "!!! UNAUTHORIZED SELF-DESTRUCT ACCESS !!!")
+ to_chat(owner, "This is a class-3 security violation. This incident will be reported to Central Command.")
+ for(var/i in 1 to 3)
+ sleep(20)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%")
+ sleep(3)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "auth 'akjv9c88asdf12nb' ******************")
+ owner.playsound_local(owner, 'sound/items/timer.ogg', 50, 0)
+ sleep(30)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Credentials accepted. Welcome, akjv9c88asdf12nb.")
+ owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
+ sleep(5)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Arm self-destruct device? (Y/N)")
+ owner.playsound_local(owner, 'sound/misc/compiler-stage1.ogg', 50, 0)
+ sleep(20)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Y")
+ sleep(15)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Confirm arming of self-destruct device? (Y/N)")
+ owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
+ sleep(10)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Y")
+ sleep(rand(15, 25))
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Please repeat password to confirm.")
+ owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
+ sleep(14)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "******************")
+ sleep(40)
+ if(!owner || QDELETED(owner))
+ return
+ to_chat(owner, "Credentials accepted. Transmitting arming signal...")
+ owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
+ sleep(30)
+ if(!owner || QDELETED(owner))
+ return
+ priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
set_security_level("delta")
- nuking = TRUE
- var/obj/machinery/doomsday_device/DOOM = new (src)
- doomsday_device = DOOM
- doomsday_device.start()
- verbs -= /mob/living/silicon/ai/proc/nuke_station
+ var/obj/machinery/doomsday_device/DOOM = new(owner_AI)
+ owner_AI.nuking = TRUE
+ owner_AI.doomsday_device = DOOM
+ owner_AI.doomsday_device.start()
for(var/pinpointer in GLOB.pinpointer_list)
var/obj/item/weapon/pinpointer/P = pinpointer
P.switch_mode_to(TRACK_MALF_AI) //Pinpointers start tracking the AI wherever it goes
+ qdel(src)
/obj/machinery/doomsday_device
icon = 'icons/obj/machines/nuke_terminal.dmi'
@@ -122,46 +374,43 @@
to_chat(world, "The AI cleansed the station of life with the doomsday device!")
SSticker.force_ending = 1
+
+//AI Turret Upgrade: Increases the health and damage of all turrets.
/datum/AI_Module/large/upgrade_turrets
module_name = "AI Turret Upgrade"
mod_pick_name = "turret"
description = "Improves the power and health of all AI turrets. This effect is permanent."
cost = 30
- one_time = 1
+ upgrade = TRUE
+ unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage."
+ unlock_sound = 'sound/items/rped.ogg'
- power_type = /mob/living/silicon/ai/proc/upgrade_turrets
-
-/mob/living/silicon/ai/proc/upgrade_turrets()
- set category = "Malfunction"
- set name = "Upgrade Turrets"
-
- if(!canUseTopic())
- return
-
- src.verbs -= /mob/living/silicon/ai/proc/upgrade_turrets
- //Upgrade AI turrets around the world
+/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI)
for(var/obj/machinery/porta_turret/ai/turret in GLOB.machines)
turret.obj_integrity += 30
turret.lethal_projectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
turret.lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
- to_chat(src, "Turrets upgraded.")
+
+//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset.
/datum/AI_Module/large/lockdown
module_name = "Hostile Station Lockdown"
mod_pick_name = "lockdown"
- description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds."
+ description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \
+ opening all doors on the station."
cost = 30
- one_time = 1
+ one_purchase = TRUE
+ power_type = /datum/action/innate/ai/lockdown
+ unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time."
+ unlock_sound = 'sound/machines/boltsdown.ogg'
- power_type = /mob/living/silicon/ai/proc/lockdown
-
-/mob/living/silicon/ai/proc/lockdown()
- set category = "Malfunction"
- set name = "Initiate Hostile Lockdown"
-
- if(!canUseTopic())
- return
+/datum/action/innate/ai/lockdown
+ name = "Lockdown"
+ desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves."
+ button_icon_state = "lockdown"
+ uses = 1
+/datum/action/innate/ai/lockdown/Activate()
for(var/obj/machinery/door/D in GLOB.airlocks)
if(D.z != ZLEVEL_STATION)
continue
@@ -172,325 +421,364 @@
if(C)
C.post_status("alert", "lockdown")
- verbs -= /mob/living/silicon/ai/proc/lockdown
- minor_announce("Hostile runtime detected in door controllers. Isolation Lockdown protocols are now in effect. Please remain calm.","Network Alert:", 1)
- to_chat(src, "Lockdown Initiated. Network reset in 90 seconds.")
+ minor_announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.","Network Alert:", TRUE)
+ to_chat(owner, "Lockdown initiated. Network reset in 90 seconds.")
addtimer(CALLBACK(GLOBAL_PROC, .proc/minor_announce,
"Automatic system reboot complete. Have a secure day.",
"Network reset:"), 900)
+
+//Destroy RCDs: Detonates all non-cyborg RCDs on the station.
/datum/AI_Module/large/destroy_rcd
module_name = "Destroy RCDs"
mod_pick_name = "rcd"
- description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Cconstruction Devices on the station."
+ description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station."
cost = 25
- one_time = 1
+ one_purchase = TRUE
+ power_type = /datum/action/innate/ai/destroy_rcds
+ unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs."
+ unlock_sound = 'sound/items/timer.ogg'
- power_type = /mob/living/silicon/ai/proc/disable_rcd
-
-/mob/living/silicon/ai/proc/disable_rcd()
- set category = "Malfunction"
- set name = "Destroy RCDs"
- set desc = "Detonate all RCDs on the station, while sparing onboard cyborg RCDs."
- set waitfor = FALSE
-
- if(!canUseTopic() || malf_cooldown > world.time)
- return
+/datum/action/innate/ai/destroy_rcds
+ name = "Destroy RCDs"
+ desc = "Detonate all non-cyborg RCDs on the station."
+ button_icon_state = "detonate_rcds"
+ uses = 1
+ cooldown_period = 100
+/datum/action/innate/ai/destroy_rcds/Activate()
for(var/I in GLOB.rcd_list)
if(!istype(I, /obj/item/weapon/construction/rcd/borg)) //Ensures that cyborg RCDs are spared.
var/obj/item/weapon/construction/rcd/RCD = I
RCD.detonate_pulse()
+ to_chat(owner, "RCD detonation pulse emitted.")
+ owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0)
- to_chat(src, "RCD detonation pulse emitted.")
- malf_cooldown = world.time + 100
+//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right?
/datum/AI_Module/large/mecha_domination
- module_name = "Viral Mech Domination"
+ module_name = "Unlock Mech Domination"
mod_pick_name = "mechjack"
- description = "Hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\
+ description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\
Do not allow the mech to leave the station's vicinity or allow it to be destroyed."
cost = 30
- one_time = 1
+ upgrade = TRUE
+ unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout."
+ unlock_sound = 'sound/mecha/nominal.ogg'
- power_type = /mob/living/silicon/ai/proc/mech_takeover
+/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI)
+ AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk!
-/mob/living/silicon/ai/proc/mech_takeover()
- set name = "Compile Mecha Virus"
- set category = "Malfunction"
- set desc = "Target a mech by clicking it. Click the appropriate command when ready."
- if(stat)
- return
- can_dominate_mechs = 1 //Yep. This is all it does. Honk!
- to_chat(src, "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout.")
- verbs -= /mob/living/silicon/ai/proc/mech_takeover
+//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job.
/datum/AI_Module/large/break_fire_alarms
module_name = "Thermal Sensor Override"
mod_pick_name = "burnpigs"
description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \
Anyone can check the fire alarm's interface and may be tipped off by its status."
- one_time = 1
+ one_purchase = TRUE
cost = 25
+ power_type = /datum/action/innate/ai/break_fire_alarms
+ unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will."
+ unlock_sound = 'goon/sound/machinery/firealarm.ogg'
- power_type = /mob/living/silicon/ai/proc/break_fire_alarms
-
-/mob/living/silicon/ai/proc/break_fire_alarms()
- set name = "Override Thermal Sensors"
- set category = "Malfunction"
-
- if(!canUseTopic())
- return
+/datum/action/innate/ai/break_fire_alarms
+ name = "Override Thermal Sensors"
+ desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless."
+ button_icon_state = "break_fire_alarms"
+ uses = 1
+/datum/action/innate/ai/break_fire_alarms/Activate()
for(var/obj/machinery/firealarm/F in GLOB.machines)
if(F.z != ZLEVEL_STATION)
continue
- F.emagged = 1
- to_chat(src, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.")
- src.verbs -= /mob/living/silicon/ai/proc/break_fire_alarms
+ F.emagged = TRUE
+ to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.")
+ owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0)
+
+//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms.
/datum/AI_Module/large/break_air_alarms
module_name = "Air Alarm Safety Override"
mod_pick_name = "allow_flooding"
description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \
Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality."
- one_time = 1
+ one_purchase = TRUE
cost = 50
+ power_type = /datum/action/innate/ai/break_air_alarms
+ unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard."
+ unlock_sound = 'sound/effects/space_wind.ogg'
- power_type = /mob/living/silicon/ai/proc/break_air_alarms
-
-/mob/living/silicon/ai/proc/break_air_alarms()
- set name = "Disable Air Alarm Safeties"
- set category = "Malfunction"
-
- if(!canUseTopic())
- return
+/datum/action/innate/ai/break_air_alarms
+ name = "Override Air Alarm Safeties"
+ desc = "Enables the Flood setting on all air alarms."
+ button_icon_state = "break_air_alarms"
+ uses = 1
+/datum/action/innate/ai/break_air_alarms/Activate()
for(var/obj/machinery/airalarm/AA in GLOB.machines)
if(AA.z != ZLEVEL_STATION)
continue
- AA.emagged = 1
- to_chat(src, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.")
- src.verbs -= /mob/living/silicon/ai/proc/break_air_alarms
+ AA.emagged = TRUE
+ to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.")
+ owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0)
+
+//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase.
/datum/AI_Module/small/overload_machine
module_name = "Machine Overload"
mod_pick_name = "overload"
- description = "Overloads an electrical machine, causing a small explosion. 2 uses."
- uses = 2
+ description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase."
cost = 20
+ power_type = /datum/action/innate/ai/ranged/overload_machine
+ unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery."
+ unlock_sound = 'sound/effects/comfyfire.ogg' //definitely not comfy, but it's the closest sound to "roaring fire" we have
- power_type = /mob/living/silicon/ai/proc/overload_machine
+/datum/action/innate/ai/ranged/overload_machine
+ name = "Overload Machine"
+ desc = "Overheats a machine, causing a small explosion after a short time."
+ button_icon_state = "overload_machine"
+ uses = 2
+ linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine
-/mob/living/silicon/ai/proc/overload_machine(obj/machinery/M in GLOB.machines)
- set name = "Overload Machine"
- set category = "Malfunction"
+/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M)
+ if(M && !QDELETED(M))
+ explosion(get_turf(M), 0, 2, 3, 0)
+ if(M) //to check if the explosion killed it before we try to delete it
+ qdel(M)
- if(!canUseTopic())
+/obj/effect/proc_holder/ranged_ai/overload_machine
+ active = FALSE
+ ranged_mousepointer = 'icons/effects/overload_machine_target.dmi'
+ enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel."
+ disable_text = "You release your hold on the powernet."
+
+/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
+ if(..())
return
+ if(ranged_ability_user.incapacitated())
+ remove_ranged_ability()
+ return
+ if(!istype(target))
+ to_chat(ranged_ability_user, "You can only overload machines!")
+ return
+ ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0)
+ attached_action.adjust_uses(-1)
+ target.audible_message("You hear a loud electrical buzzing sound coming from [target]!")
+ addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom!
+ remove_ranged_ability("Overcharging machine...")
+ return TRUE
- if (istype(M, /obj/machinery))
- for(var/datum/AI_Module/small/overload_machine/overload in current_modules)
- if(overload.uses > 0)
- overload.uses --
- M.audible_message("You hear a loud electrical buzzing sound coming from [M]!")
- to_chat(src, "Overloading machine circuitry...")
- spawn(50)
- if(M)
- explosion(get_turf(M), 0,2,3,0)
- qdel(M)
- else to_chat(src, "Out of uses.")
- else to_chat(src, "That's not a machine.")
+//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself.
/datum/AI_Module/small/override_machine
module_name = "Machine Override"
mod_pick_name = "override"
- description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. 4 uses."
- uses = 4
+ description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses."
cost = 30
+ power_type = /datum/action/innate/ai/ranged/override_machine
+ unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines."
+ unlock_sound = 'sound/machines/airlock_alien_prying.ogg'
- power_type = /mob/living/silicon/ai/proc/override_machine
+/datum/action/innate/ai/ranged/override_machine
+ name = "Override Machine"
+ desc = "Animates a targeted machine, causing it to attack anyone nearby."
+ button_icon_state = "override_machine"
+ uses = 4
+ linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine
+/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M)
+ if(M && !QDELETED(M))
+ new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1)
-/mob/living/silicon/ai/proc/override_machine(obj/machinery/M in GLOB.machines)
- set name = "Override Machine"
- set category = "Malfunction"
+/obj/effect/proc_holder/ranged_ai/override_machine
+ active = FALSE
+ ranged_mousepointer = 'icons/effects/override_machine_target.dmi'
+ enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel."
+ disable_text = "You release your hold on the powernet."
- if(!canUseTopic())
+/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
+ if(..())
return
+ if(ranged_ability_user.incapacitated())
+ remove_ranged_ability()
+ return
+ if(!istype(target))
+ to_chat(ranged_ability_user, "You can only animate machines!")
+ return
+ if(!target.can_be_overridden())
+ to_chat(ranged_ability_user, "That machine can't be overriden!")
+ return
+ ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0)
+ attached_action.adjust_uses(-1)
+ target.audible_message("You hear a loud electrical buzzing sound coming from [target]!")
+ addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep!
+ remove_ranged_ability("Sending override signal...")
+ return TRUE
- if (istype(M, /obj/machinery))
- if(!M.can_be_overridden())
- to_chat(src, "Can't override this device.")
- for(var/datum/AI_Module/small/override_machine/override in current_modules)
- if(override.uses > 0)
- override.uses --
- M.audible_message("You hear a loud electrical buzzing sound!")
- to_chat(src, "Reprogramming machine behaviour...")
- spawn(50)
- if(M && !QDELETED(M))
- new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, src, 1)
- else to_chat(src, "Out of uses.")
- else to_chat(src, "That's not a machine.")
+//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting.
/datum/AI_Module/large/place_cyborg_transformer
module_name = "Robotic Factory (Removes Shunting)"
mod_pick_name = "cyborgtransformer"
description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside."
cost = 100
- power_type = /mob/living/silicon/ai/proc/place_transformer
- var/list/turfOverlays = list()
+ one_purchase = TRUE
+ power_type = /datum/action/innate/ai/place_transformer
+ unlock_text = "You make contact with Space Amazon and request a robotics factory for delivery."
+ unlock_sound = 'sound/machines/ping.ogg'
-/datum/AI_Module/large/place_cyborg_transformer/New()
- for(var/i=0;i<3;i++)
- var/image/I = image("icon"='icons/turf/overlays.dmi')
- turfOverlays += I
+/datum/action/innate/ai/place_transformer
+ name = "Place Robotics Factory"
+ desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!"
+ button_icon_state = "robotic_factory"
+ uses = 1
+ auto_use_uses = FALSE //So we can attempt multiple times
+ var/list/turfOverlays
+
+/datum/action/innate/ai/place_transformer/New()
..()
+ for(var/i in 1 to 3)
+ var/image/I = image("icon"='icons/turf/overlays.dmi')
+ LAZYADD(turfOverlays, I)
-/mob/living/silicon/ai/proc/place_transformer()
- set name = "Place Robotic Factory"
- set category = "Malfunction"
- if(!canPlaceTransformer())
+/datum/action/innate/ai/place_transformer/Activate()
+ if(!owner_AI.can_place_transformer(src))
return
- var/sure = alert(src, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No")
- if(sure == "Yes")
- if(!canPlaceTransformer())
- return
- var/turf/T = get_turf(eyeobj)
- var/obj/machinery/transformer/conveyor = new(T)
- conveyor.masterAI = src
- playsound(T, 'sound/effects/phasein.ogg', 100, 1)
- var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
- PCT.uses --
- can_shunt = 0
- to_chat(src, "You cannot shunt anymore.")
+ active = TRUE
+ if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No")
+ active = FALSE
+ return
+ if(!owner_AI.can_place_transformer(src))
+ return
+ var/turf/T = get_turf(owner_AI.eyeobj)
+ var/obj/machinery/transformer/conveyor = new(T)
+ conveyor.masterAI = owner
+ playsound(T, 'sound/effects/phasein.ogg', 100, 1)
+ owner_AI.can_shunt = TRUE
+ to_chat(owner, "You are no longer able to shunt your core to APCs.")
+ adjust_uses(-1)
-/mob/living/silicon/ai/proc/canPlaceTransformer()
- if(!eyeobj || !isturf(src.loc) || !canUseTopic())
- return
- var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
- if(!PCT || PCT.uses < 1)
- alert(src, "Out of uses.")
+/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T)
+ if(C && I.loc == T)
+ C.images -= I
+
+/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action)
+ if(!eyeobj || !isturf(loc) || !canUseTopic() || !action)
return
var/turf/middle = get_turf(eyeobj)
var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z))
- var/alert_msg = "There isn't enough room. Make sure you are placing the machine in a clear area and on a floor."
- var/success = 1
- if(turfs.len == 3)
- for(var/n=1;n<4,n++)
- var/fail
- var/turf/T = turfs[n]
- if(!isfloorturf(T))
- fail = 1
- var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z)
- if(!C.visibleTurfs[T])
- alert_msg = "We cannot get camera vision of this location."
- fail = 1
- for(var/atom/movable/AM in T.contents)
- if(AM.density)
- fail = 1
- var/image/I = PCT.turfOverlays[n]
- I.loc = T
- client.images += I
- if(fail)
- success = 0
- I.icon_state = "redOverlay"
- else
- I.icon_state = "greenOverlay"
- spawn(30)
- if(client && (I.loc == T))
- client.images -= I
- if(success)
- return 1
- alert(src, alert_msg)
+ var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor."
+ var/success = TRUE
+ for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled
+ var/turf/T = turfs[n]
+ if(!isfloorturf(T))
+ success = FALSE
+ var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z)
+ if(!C.visibleTurfs[T])
+ alert_msg = "You don't have camera vision of this location!"
+ success = FALSE
+ for(var/atom/movable/AM in T.contents)
+ if(AM.density)
+ alert_msg = "That area must be clear of objects!"
+ success = FALSE
+ var/image/I = action.turfOverlays[n]
+ I.loc = T
+ client.images += I
+ I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively
+ addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30)
+ if(!success)
+ to_chat(src, "[alert_msg]")
+ return success
+
+//Blackout: Overloads a random number of lights across the station. Three uses.
/datum/AI_Module/small/blackout
module_name = "Blackout"
mod_pick_name = "blackout"
- description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. 3 uses."
- uses = 3
+ description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses."
cost = 15
+ power_type = /datum/action/innate/ai/blackout
+ unlock_text = "You hook into the powernet and route bonus power towards the station's lighting."
+ unlock_sound = "sparks"
- power_type = /mob/living/silicon/ai/proc/blackout
+/datum/action/innate/ai/blackout
+ name = "Blackout"
+ desc = "Overloads lights across the station."
+ button_icon_state = "blackout"
+ uses = 3
-/mob/living/silicon/ai/proc/blackout()
- set category = "Malfunction"
- set name = "Blackout"
+/datum/action/innate/ai/blackout/Activate()
+ for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
+ if(prob(30 * apc.overload))
+ apc.overload_lighting()
+ else
+ apc.overload++
+ to_chat(owner, "Overcurrent applied to the powernet.")
+ owner.playsound_local(owner, "sparks", 50, 0)
- if(!canUseTopic())
- return
-
- for(var/datum/AI_Module/small/blackout/blackout in current_modules)
- if(blackout.uses > 0)
- blackout.uses --
- for(var/obj/machinery/power/apc/apc in GLOB.machines)
- if(prob(30*apc.overload))
- apc.overload_lighting()
- else apc.overload++
- to_chat(src, "Overcurrent applied to the powernet.")
- else to_chat(src, "Out of uses.")
+//Reactivate Camera Network: Reactivates up to 30 cameras across the station.
/datum/AI_Module/small/reactivate_cameras
module_name = "Reactivate Camera Network"
mod_pick_name = "recam"
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
- uses = 30
cost = 10
- one_time = 1
+ one_purchase = TRUE
+ power_type = /datum/action/innate/ai/reactivate_cameras
+ unlock_text = "You deploy nanomachines to the cameranet."
+ unlock_sound = 'sound/items/wirecutter.ogg'
- power_type = /mob/living/silicon/ai/proc/reactivate_cameras
+/datum/action/innate/ai/reactivate_cameras
+ name = "Reactivate Cameras"
+ desc = "Reactivates disabled cameras across the station; remaining uses can be used later."
+ button_icon_state = "reactivate_cameras"
+ uses = 30
+ auto_use_uses = FALSE
+ cooldown_period = 30
-/mob/living/silicon/ai/proc/reactivate_cameras()
- set name = "Reactivate Cameranet"
- set category = "Malfunction"
+/datum/action/innate/ai/reactivate_cameras/New()
+ ..()
+ desc = "[desc] There are 30 reactivations remaining."
+ button.desc = desc
- if(!canUseTopic() || malf_cooldown > world.time)
- return
- var/fixedcams = 0 //Tells the AI how many cams it fixed. Stats are fun.
+/datum/action/innate/ai/reactivate_cameras/Activate()
+ var/fixed_cameras = 0
+ for(var/V in GLOB.cameranet.cameras)
+ if(!uses)
+ break
+ var/obj/machinery/camera/C = V
+ if(!C.status || C.view_range != initial(C.view_range))
+ C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc.
+ C.view_range = initial(C.view_range)
+ fixed_cameras++
+ uses-- //Not adjust_uses() so it doesn't automatically delete or show a message
+ to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].")
+ owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0)
+ adjust_uses(0, TRUE) //Checks the uses remaining
+ if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
+ desc = "[initial(desc)] There are [uses] reactivations remaining."
- for(var/datum/AI_Module/small/reactivate_cameras/camera in current_modules)
- for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
- var/initial_range = initial(C.view_range) //To prevent calling the proc twice
- if(camera.uses > 0)
- if(!C.status)
- C.toggle_cam(src, 0) //Reactivates the camera based on status. Badly named proc.
- fixedcams++
- camera.uses--
- if(C.view_range != initial_range)
- C.view_range = initial_range //Fixes cameras with bad focus.
- camera.uses--
- fixedcams++
- //If a camera is both deactivated and has bad focus, it will cost two uses to fully fix!
- else
- to_chat(src, "Out of uses.")
- verbs -= /mob/living/silicon/ai/proc/reactivate_cameras //It is useless now, clean it up.
- break
- to_chat(src, "Diagnostic complete! Operations completed: [fixedcams].")
-
- malf_cooldown = world.time + 30
+//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them x-ray vision.
/datum/AI_Module/large/upgrade_cameras
module_name = "Upgrade Camera Network"
mod_pick_name = "upgradecam"
- description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-Proofing and light-amplified X-ray vision." //I <3 pointless technobabble
+ description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble
//This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR.
- one_time = 1
+ one_purchase = TRUE
cost = 35 //Decent price for omniscience!
+ upgrade = TRUE
+ unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online."
+ unlock_sound = 'sound/items/rped.ogg'
- power_type = /mob/living/silicon/ai/proc/upgrade_cameras
+/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI)
+ AI.see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power.
+ AI.update_sight()
-/mob/living/silicon/ai/proc/upgrade_cameras()
- set name = "Upgrade Cameranet"
- set category = "Malfunction"
-
- if(!canUseTopic())
- return
-
- var/upgradedcams = 0
- see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power.
- update_sight()
-
- for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
+ var/upgraded_cameras = 0
+ for(var/V in GLOB.cameranet.cameras)
+ var/obj/machinery/camera/C = V
if(C.assembly)
var/upgraded = 0
@@ -505,105 +793,22 @@
upgraded = 1
if(upgraded)
- upgradedcams++
+ upgraded_cameras++
- to_chat(src, "OTA firmware distribution complete! Cameras upgraded: [upgradedcams]. Light amplification system online.")
- verbs -= /mob/living/silicon/ai/proc/upgrade_cameras
-
-/datum/module_picker
- var/temp = null
- var/processing_time = 50
- var/list/possible_modules = list()
-
-/datum/module_picker/New()
- for(var/type in typesof(/datum/AI_Module))
- var/datum/AI_Module/AM = new type
- if(AM.power_type != null)
- src.possible_modules += AM
-
-/datum/module_picker/proc/remove_verbs(mob/living/silicon/ai/A)
-
- for(var/datum/AI_Module/AM in possible_modules)
- A.verbs.Remove(AM.power_type)
-
-
-/datum/module_picker/proc/use(mob/user)
- var/dat
- dat = "Select use of processing time: (currently #[src.processing_time] left.)
"
- dat += "
"
- dat += "Install Module:
"
- dat += "The number afterwards is the amount of processing time it consumes.
"
- for(var/datum/AI_Module/large/module in src.possible_modules)
- dat += "[module.module_name]\[?\] ([module.cost])
"
- for(var/datum/AI_Module/small/module in src.possible_modules)
- dat += "[module.module_name]\[?\] ([module.cost])
"
- dat += "
"
- if (src.temp)
- dat += "[src.temp]"
- var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu")
- popup.set_content(dat)
- popup.open()
-
-/datum/module_picker/Topic(href, href_list)
- ..()
-
- if(!isAI(usr))
- return
- var/mob/living/silicon/ai/A = usr
-
- if(A.stat == DEAD)
- to_chat(A, "You are already dead!") //Omae Wa Mou Shindeiru
- return
-
- for(var/datum/AI_Module/AM in possible_modules)
- if (href_list[AM.mod_pick_name])
-
- // Cost check
- if(AM.cost > src.processing_time)
- temp = "You cannot afford this module."
- break
-
- // Add new uses if we can, and it is allowed.
- var/datum/AI_Module/already_AM = locate(AM.type) in A.current_modules
- if(already_AM)
- if(!AM.one_time)
- already_AM.uses += AM.uses
- src.processing_time -= AM.cost
- temp = "Additional use added to [already_AM.module_name]"
- break
- else
- temp = "This module is only needed once."
- break
-
- // Give the power and take away the money.
- A.view_core() //A BYOND bug requires you to be viewing your core before your verbs update
- A.verbs += AM.power_type
- A.current_modules += new AM.type
- temp = AM.description
- src.processing_time -= AM.cost
-
- if(href_list["showdesc"])
- if(AM.mod_pick_name == href_list["showdesc"])
- temp = AM.description
- src.use(usr)
+ unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade()
/datum/AI_Module/large/eavesdrop
module_name = "Enhanced Surveillance"
mod_pick_name = "eavesdrop"
description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations."
cost = 30
- one_time = 1
+ one_purchase = TRUE
+ upgrade = TRUE
+ unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online."
+ unlock_sound = 'sound/items/rped.ogg'
- power_type = /mob/living/silicon/ai/proc/surveillance
+/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI)
+ if(AI.eyeobj)
+ AI.eyeobj.relay_speech = TRUE
-/mob/living/silicon/ai/proc/surveillance()
- set category = "Malfunction"
- set name = "Enhanced Surveillance"
-
- if(eyeobj)
- eyeobj.relay_speech = TRUE
- to_chat(src, "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online.")
- verbs -= /mob/living/silicon/ai/proc/surveillance
-
-
-#undef DEFAULT_DOOMSDAY_TIMER
\ No newline at end of file
+#undef DEFAULT_DOOMSDAY_TIMER
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index 960c354f533..e9d57dcb299 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -16,7 +16,7 @@
var/robot_cell_charge = 5000
var/obj/effect/countdown/transformer/countdown
var/mob/living/silicon/ai/masterAI
-
+
/obj/machinery/transformer/Initialize()
// On us
. = ..()
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index 8532e3f6cfb..d554008c1fa 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -349,6 +349,7 @@ h1.alert, h2.alert {color: #000000;}
.robot {font-family: "Courier New", cursive, sans-serif;}
.command_headset {font-weight: bold; font-size: 24px;}
+.small {font-size: 1;}
.big {font-size: 3;}
.reallybig {font-size: 4;}
.greentext {color: #00FF00; font-size: 3;}
diff --git a/icons/effects/overload_machine_target.dmi b/icons/effects/overload_machine_target.dmi
new file mode 100644
index 00000000000..8bc67cdab6c
Binary files /dev/null and b/icons/effects/overload_machine_target.dmi differ
diff --git a/icons/effects/override_machine_target.dmi b/icons/effects/override_machine_target.dmi
new file mode 100644
index 00000000000..77dbb4ba32b
Binary files /dev/null and b/icons/effects/override_machine_target.dmi differ
diff --git a/icons/mob/actions_AI.dmi b/icons/mob/actions_AI.dmi
new file mode 100644
index 00000000000..3a789984e64
Binary files /dev/null and b/icons/mob/actions_AI.dmi differ
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index 2022db1586b..6f38560b92d 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -143,6 +143,7 @@ h1.alert, h2.alert {color: #000000;}
.robot {font-family: "Courier New", cursive, sans-serif;}
.command_headset {font-weight: bold; font-size: 3;}
+.small {font-size: 1;}
.big {font-size: 3;}
.reallybig {font-size: 4;}
.greentext {color: #00FF00; font-size: 3;}