diff --git a/baystation12.dme b/baystation12.dme
index b1475e8168d..1042c11fd38 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -407,6 +407,7 @@
#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_interdiction.dm"
#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_manipulation.dm"
#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_networking.dm"
+#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_synthetic.dm"
#include "code\game\gamemodes\meteor\meteor.dm"
#include "code\game\gamemodes\meteor\meteors.dm"
#include "code\game\gamemodes\mixed\bughunt.dm"
@@ -498,6 +499,7 @@
#include "code\game\machinery\supplybeacon.dm"
#include "code\game\machinery\syndicatebeacon.dm"
#include "code\game\machinery\teleporter.dm"
+#include "code\game\machinery\transformer.dm"
#include "code\game\machinery\turret_control.dm"
#include "code\game\machinery\vending.dm"
#include "code\game\machinery\vending_types.dm"
@@ -747,6 +749,7 @@
#include "code\game\objects\items\weapons\power_cells.dm"
#include "code\game\objects\items\weapons\RCD.dm"
#include "code\game\objects\items\weapons\RSF.dm"
+#include "code\game\objects\items\weapons\RTF.dm"
#include "code\game\objects\items\weapons\scrolls.dm"
#include "code\game\objects\items\weapons\shields.dm"
#include "code\game\objects\items\weapons\stunbaton.dm"
diff --git a/code/game/gamemodes/malfunction/malf_research.dm b/code/game/gamemodes/malfunction/malf_research.dm
index 58f7d56c137..bf459dc1e26 100644
--- a/code/game/gamemodes/malfunction/malf_research.dm
+++ b/code/game/gamemodes/malfunction/malf_research.dm
@@ -22,7 +22,8 @@
available_abilities += new/datum/malf_research_ability/networking/basic_hack()
available_abilities += new/datum/malf_research_ability/interdiction/recall_shuttle()
- available_abilities += new/datum/malf_research_ability/manipulation/electrical_pulse()
+ available_abilities += new/datum/malf_research_ability/manipulation/hack_holopad()
+ available_abilities += new/datum/malf_research_ability/synthetic/reset_module()
// Proc: finish_research()
diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm
index cfcf690e77f..d9736066ba6 100644
--- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm
+++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm
@@ -176,6 +176,32 @@
H.Add(A)
return H
+// Proc: get_hacked_apcs()
+// Parameters: None
+// Description: Returns a list of all hacked APCs
+/proc/get_hacked_apcs()
+ var/list/H = list()
+ for(var/obj/machinery/power/apc/A in machines)
+ if(!A.hacker)
+ continue
+ H.Add(A)
+ return H
+
+// Proc: get_apcs()
+// Parameters: None
+// Description: Returns a list of all APCs
+/proc/get_apcs()
+ var/list/H = list()
+ for(var/obj/machinery/power/apc/A in machines)
+ H.Add(A)
+ return H
+
+/proc/get_unhacked_holopads()
+ var/list/H = list()
+ for(var/obj/machinery/hologram/holopad/HP in machines)
+ if(!HP.hacked)
+ H.Add(HP)
+ return H
// Helper procs which return lists of relevant mobs.
/proc/get_unlinked_cyborgs(var/mob/living/silicon/ai/A)
@@ -202,7 +228,7 @@
var/list/L = list()
for(var/mob/living/silicon/ai/AT in mob_list)
- if(L == A)
+ if(AT == A)
continue
L.Add(AT)
return L
@@ -213,4 +239,4 @@
message = text("used malf ability/function: [ability_name] on [target] ([target.x], [target.y], [target.z])")
else
message = text("used malf ability/function: [ability_name].")
- admin_attack_log(A, null, message, null, message)
\ No newline at end of file
+ admin_attack_log(A, null, message, null, message)
diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm
index a37c7fcc911..6b8ef4b5aa2 100644
--- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm
+++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm
@@ -83,7 +83,7 @@
if(!target)
var/list/robots = list()
var/list/robot_names = list()
- for(var/mob/living/silicon/robot/R in world)
+ for(var/mob/living/silicon/robot/R in silicon_mob_list)
if(istype(R, /mob/living/silicon/robot/drone)) // No drones.
continue
if(R.connected_ai != user) // No robots linked to other AIs
diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm
index f7361e9d1bc..84a3556a9bc 100644
--- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm
+++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm
@@ -1,7 +1,7 @@
// MANIPULATION TREE
//
// Abilities in this tree allow the AI to physically manipulate systems around the station.
-// T1 - Electrical Pulse - Sends out pulse that breaks some lights and sometimes even APCs. This can actually break the AI's APC so be careful!
+// T1 - Hack Holopad - Allows the AI to hack a holopad. Hacked holopads only activate the listening feature when turned on.
// T2 - Hack Camera - Allows the AI to hack a camera. Deactivated areas may be reactivated, and functional cameras can be upgraded.
// T3 - Emergency Forcefield - Allows the AI to project 1 tile forcefield that blocks movement and air flow. Forcefield�dissipates over time. It is also very susceptible to energetic weaponry.
// T4 - Machine Overload - Detonates machine of choice in a minor explosion. Two of these are usually enough to kill or K/O someone.
@@ -9,11 +9,11 @@
// BEGIN RESEARCH DATUMS
-/datum/malf_research_ability/manipulation/electrical_pulse
- ability = new/datum/game_mode/malfunction/verb/electrical_pulse()
+/datum/malf_research_ability/manipulation/hack_holopad
+ ability = new/datum/game_mode/malfunction/verb/hack_holopad()
price = 50
next = new/datum/malf_research_ability/manipulation/hack_camera()
- name = "Electrical Pulse"
+ name = "Hack Holopad"
/datum/malf_research_ability/manipulation/hack_camera
@@ -38,26 +38,27 @@
// END RESEARCH DATUMS
// BEGIN ABILITY VERBS
-/datum/game_mode/malfunction/verb/electrical_pulse()
- set name = "Electrical Pulse"
- set desc = "15 CPU - Sends feedback pulse through station's power grid, overloading some sensitive systems, such as lights."
+/datum/game_mode/malfunction/verb/hack_holopad(var/obj/machinery/hologram/holopad/HP = null as obj in get_unhacked_holopads())
+ set name = "Hack Holopad"
+ set desc = "50 CPU - Hacks a holopad shorting out its projector. Using a hacked holopad only turns on the audio feature."
set category = "Software"
- var/price = 15
+ var/price = 50
var/mob/living/silicon/ai/user = usr
if(!ability_prechecks(user, price) || !ability_pay(user,price))
return
- user << "Sending feedback pulse..."
- for(var/obj/machinery/power/apc/AP in machines)
- if(prob(5))
- AP.overload_lighting()
- if(prob(1) && prob(1)) // Very very small chance to actually destroy the APC.
- AP.set_broken()
- user.hacking = 1
- log_ability_use(user, "electrical pulse")
- spawn(150)
- user.hacking = 0
+ if(HP.hacked)
+ user << "This holopad is already hacked!"
+ return
-/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target in cameranet.cameras)
+ user << "Hacking holopad..."
+ user.hacking = 1
+ sleep(100)
+ HP.hacked = 1
+ log_ability_use(user, "hack_holopad")
+ user << "Holopad hacked."
+ user.hacking = 0
+
+/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target = null as obj in cameranet.cameras)
set name = "Hack Camera"
set desc = "100 CPU - Hacks existing camera, allowing you to add upgrade of your choice to it. Alternatively it lets you reactivate broken camera."
set category = "Software"
@@ -68,55 +69,54 @@
user << "This is not a camera."
return
- if(!target)
- return
-
if(!ability_prechecks(user, price))
return
- var/action = input("Select required action: ") in list("Reset", "Add X-Ray", "Add Motion Sensor", "Add EMP Shielding")
- if(!action || !target)
+ if(!target)
return
-
- switch(action)
- if("Reset")
- if(target.wires)
- if(!ability_pay(user, price))
+ var/action = input("Select required action: ") in list("Reset", "Add X-Ray", "Add Motion Sensor", "Add EMP Shielding")
+ if(target)
+ switch(action)
+ if("Reset")
+ if(target.wires)
+ if(!ability_pay(user, price))
+ return
+ target.reset_wires()
+ user << "Camera reactivated."
+ log_ability_use(user, "hack camera (reset)", target)
return
- target.reset_wires()
- user << "Camera reactivated."
- log_ability_use(user, "hack camera (reset)", target)
- return
- if("Add X-Ray")
- if(target.isXRay())
- user << "Camera already has X-Ray function."
- return
- else if(ability_pay(user, price))
- target.upgradeXRay()
- target.reset_wires()
- user << "X-Ray camera module enabled."
- log_ability_use(user, "hack camera (add X-Ray)", target)
- return
- if("Add Motion Sensor")
- if(target.isMotion())
- user << "Camera already has Motion Sensor function."
- return
- else if(ability_pay(user, price))
- target.upgradeMotion()
- target.reset_wires()
- user << "Motion Sensor camera module enabled."
- log_ability_use(user, "hack camera (add motion)", target)
- return
- if("Add EMP Shielding")
- if(target.isEmpProof())
- user << "Camera already has EMP Shielding function."
- return
- else if(ability_pay(user, price))
- target.upgradeEmpProof()
- target.reset_wires()
- user << "EMP Shielding camera module enabled."
- log_ability_use(user, "hack camera (add EMP shielding)", target)
- return
+ if("Add X-Ray")
+ if(target.isXRay())
+ user << "Camera already has X-Ray function."
+ return
+ else if(ability_pay(user, price))
+ target.upgradeXRay()
+ target.reset_wires()
+ user << "X-Ray camera module enabled."
+ log_ability_use(user, "hack camera (add X-Ray)", target)
+ return
+ if("Add Motion Sensor")
+ if(target.isMotion())
+ user << "Camera already has Motion Sensor function."
+ return
+ else if(ability_pay(user, price))
+ target.upgradeMotion()
+ target.reset_wires()
+ user << "Motion Sensor camera module enabled."
+ log_ability_use(user, "hack camera (add motion)", target)
+ return
+ if("Add EMP Shielding")
+ if(target.isEmpProof())
+ user << "Camera already has EMP Shielding function."
+ return
+ else if(ability_pay(user, price))
+ target.upgradeEmpProof()
+ target.reset_wires()
+ user << "EMP Shielding camera module enabled."
+ log_ability_use(user, "hack camera (add EMP shielding)", target)
+ return
+ else
+ user << "Please pick a suitable camera."
/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as turf in world)
@@ -134,8 +134,8 @@
new/obj/machinery/shield/malfai(T)
user.hacking = 1
log_ability_use(user, "emergency forcefield", T)
- spawn(20)
- user.hacking = 0
+ sleep(20)
+ user.hacking = 0
/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M in machines)
@@ -207,9 +207,9 @@
log_ability_use(user, "machine overload", M)
M.visible_message("BZZZZZZZT")
- spawn(50)
- explosion(get_turf(M), round(explosion_intensity/4),round(explosion_intensity/2),round(explosion_intensity),round(explosion_intensity * 2))
- if(M)
- qdel(M)
+ sleep(50)
+ explosion(get_turf(M), round(explosion_intensity/4),round(explosion_intensity/2),round(explosion_intensity),round(explosion_intensity * 2))
+ if(M)
+ qdel(M)
// END ABILITY VERBS
diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm
new file mode 100644
index 00000000000..eeea1ef1a22
--- /dev/null
+++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm
@@ -0,0 +1,396 @@
+// SYNTHETIC TREE
+//
+// Abilities in this tree allow the AI to upgrade their robotic companions.
+// T1 - Reset Cyborg Module - Allows the AI to reset a slaved cyborgs module.
+// T2 - Infect APC - Gives the AI the ability to infect APC's which can slave IPC's that charge off of them to the AI.
+// T3 - Overclock Cyborg - Allows the AI to give the option to a slaved borg to overclock which increases preformance of the borg.
+// T4 - Synthetic Takeover - Allows the AI to start a station wide takeover based on synthetic dominance.
+
+
+// BEGIN RESEARCH DATUMS
+
+/datum/malf_research_ability/synthetic/reset_module
+ ability = new/datum/game_mode/malfunction/verb/reset_module()
+ price = 100
+ next = new/datum/malf_research_ability/synthetic/infect_apc()
+ name = "Reset Cyborg Module"
+
+
+/datum/malf_research_ability/synthetic/infect_apc
+ ability = new/datum/game_mode/malfunction/verb/infect_apc()
+ price = 700
+ next = new/datum/malf_research_ability/synthetic/overclock_borg()
+ name = "Infect APC"
+
+
+/datum/malf_research_ability/synthetic/overclock_borg
+ ability = new/datum/game_mode/malfunction/verb/overclock_borg()
+ price = 1300
+ next = new/datum/malf_research_ability/synthetic/synthetic_takeover
+ name = "Overclock Cyborg"
+
+
+/datum/malf_research_ability/synthetic/synthetic_takeover
+ ability = new/datum/game_mode/malfunction/verb/synthetic_takeover()
+ price = 5000
+ name = "Synthetic Takeover"
+
+// END RESEARCH DATUMS
+// BEGIN ABILITY VERBS
+
+/datum/game_mode/malfunction/verb/reset_module(var/mob/living/silicon/robot/target = null as mob in get_linked_cyborgs(usr))
+ set name = "Reset Cyborg Module"
+ set desc = "100 CPU - Forces a module reset on a cyborg that is enslaved to you. Has a chance of failing."
+ set category = "Software"
+ var/price = 100
+ var/mob/living/silicon/ai/user = usr
+ if(!ability_prechecks(user, price))
+ return
+
+ if(target && !istype(target))
+ user << "This is not a cyborg."
+ return
+
+ if(target && target.connected_ai && (target.connected_ai != user))
+ user << "This cyborg is not connected to you."
+ return
+
+ if(!target)
+ var/list/robots = list()
+ var/list/robot_names = list()
+ for(var/mob/living/silicon/robot/R in silicon_mob_list)
+ if(istype(R, /mob/living/silicon/robot/drone)) // No drones.
+ continue
+ if(R.connected_ai != user) // No robots linked to other AIs
+ continue
+ robots += R
+ robot_names += R.name
+
+
+ var/targetname = input("Select reset target: ") in robot_names
+ for(var/mob/living/silicon/robot/R in robots)
+ if(targetname == R.name)
+ target = R
+ break
+
+ if(target)
+ if(alert(user, "Really try to reset cyborg [target.name]?", "Reset Cyborg", "Yes", "No") != "Yes")
+ return
+ if(!ability_pay(user, price))
+ return
+ user.hacking = 1
+ user << "Attempting to reset the cyborg. This will take approximately 20 seconds."
+ sleep(200)
+ if(prob(30))
+ user <<"Reset attempt failed!"
+ return
+ if(target)
+ user << "Successfully sent reset signal to cyborg.."
+ target << "Reset signal received.."
+ sleep(20)
+ user << "Cyborg reset."
+ target << "You have had your module reset."
+ log_ability_use(user, "reset cyborg", target)
+ target.uneq_all()
+ target.modtype = initial(target.modtype)
+ target.hands.icon_state = initial(target.hands.icon_state)
+ target.module.Reset(target)
+ qdel(target.module)
+ target.module = null
+ target.updatename("Default")
+ else
+ user << "Unable to reset cyborg."
+
+ user.hacking = 0
+
+
+
+
+/datum/game_mode/malfunction/verb/infect_apc(obj/machinery/power/apc/A as obj in get_apcs())
+ set name = "Infect APC"
+ set desc = "125 CPU - Infect an APC which can cause an IPC to become slaved to you if they download the files by trying to charge off of it. "
+ set category = "Software"
+ var/price = 125
+ var/mob/living/silicon/ai/user = usr
+ if(!A)
+ return
+
+ if(!istype(A))
+ user << "This is not an APC!"
+ return
+
+ if(A.aidisabled)
+ user << "Unable to connect to APC. Please verify wire connection and try again."
+ return
+
+ if(!ability_prechecks(user, price) || !ability_pay(user, price))
+ return
+
+ if(A.infected == 1)
+ user <<"This APC is already infected!"
+ return
+
+ log_ability_use(user, "infect APC", A, 0) // Does not notify admins, but it's still logged for reference.
+ user.hacking = 1
+ user << "Beginning APC infection..."
+ sleep(150)
+ user << "APC infection completed. Uploading modified operation software.."
+ sleep(100)
+ user << "Restarting APC to apply corrupt coding.."
+ sleep(100)
+ if(A)
+ A.infected = 1
+ A.hacker = user
+ if(A.infected == 1)
+ user << "Hack successful. The next robotic thing to download files will be hacked."
+ else
+ user << "Hack failed. Connection to APC has been lost. Please verify wire connection and try again."
+ else
+ user << "Hack failed. Unable to locate APC. Please verify the APC still exists."
+ user.hacking = 0
+
+
+
+/datum/game_mode/malfunction/verb/overclock_borg(var/mob/living/silicon/robot/target as mob in get_linked_cyborgs(usr))
+ set name = "Overclock Cyborg"
+ set desc = "350 CPU - Allows you to overclock a slaved cyborg granting them various improvements to their systems."
+ set category = "Software"
+ var/price = 350
+ var/mob/living/silicon/ai/user = usr
+
+ if(!ability_prechecks(user, price))
+ return
+
+ if(target && !istype(target))
+ user << "This is not a cyborg."
+ return
+
+ if(target && target.connected_ai && (target.connected_ai != user))
+ user << "This cyborg is not connected to you."
+ return
+ if(target.overclockavailable == 1)
+ user << "This cyborg is already overclocked!"
+ return
+
+ if(!target)
+ var/list/robots = list()
+ var/list/robot_names = list()
+ for(var/mob/living/silicon/robot/R in silicon_mob_list)
+ if(istype(R, /mob/living/silicon/robot/drone)) // No drones.
+ continue
+ if(R.connected_ai != user) // No robots linked to other AIs
+ continue
+ if(R.overclockavailable == 1) // If they already have it don't add them.
+ continue
+ robots += R
+ robot_names += R.name
+
+
+ var/targetname = input("Select overclock target: ") in robot_names
+ for(var/mob/living/silicon/robot/R in robots)
+ if(targetname == R.name)
+ target = R
+ break
+
+ if(target)
+ if(alert(user, "Really try to overclock cyborg [target.name]?", "Overclock Cyborg", "Yes", "No") != "Yes")
+ return
+ if(!ability_pay(user, price))
+ return
+ user.hacking = 1
+ user << "Hacking saftey protocols. This will take about twenty seconds."
+ sleep(200)
+ if(prob(15))
+ user <<"Hack failed!"
+ return
+ if(target)
+ target.overclockavailable = 1
+ target.toggle_overclock()
+ target << "Overclocking mode available for activation."
+ user << "[target] can now activate overclock mode."
+ else
+ user << "Unable to overclock cyborg."
+
+ user.hacking = 0
+
+
+
+
+/datum/game_mode/malfunction/verb/synthetic_takeover()
+ set name = "Synthetic Takeover"
+ set desc = "500 CPU - Starts a takeover of the station enabling several abilities that relate to synthetic dominance."
+ set category = "Software"
+ var/price = 500
+ var/mob/living/silicon/ai/user = usr
+
+ if(!ability_prechecks(user,price))
+ return
+
+ if (alert(user, "Start synthetic takeover? This can not be stopped and will not be covert.", "Synthetic Takeover?:", "Yes", "No") != "Yes")
+ return
+ if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.synthetic_takeover)
+ if(user.synthetic_takeover)
+ user << "You have already started the synthetic takeover sequence."
+ return
+ log_ability_use(user, "synthetic takeover (STARTED)")
+ user.synthetic_takeover = 1
+ user << "Starting synthetic takeover. Hacking all unslaved borgs/AI's and upgrading current slaved borgs..."
+ // Hack all unslaved borgs/AI's a lot faster than normal hacking.
+ //hack borgs
+ for(var/mob/living/silicon/robot/target in get_unlinked_cyborgs(user))
+ target << "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)"
+ sleep(30)
+ if(user.is_dead())
+ target << "SYSTEM LOG: Connection Closed"
+ return
+ target << "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)"
+ sleep(30)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User Admin disconnected."
+ return
+ target << "SYSTEM LOG: User Admin - manual resynchronisation triggered."
+ sleep(30)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User Admin disconnected. Changes reverted."
+ return
+ target << "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED"
+ sleep(20)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User Admin disconnected. Changes reverted."
+ return
+ target << "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name]."
+ target.connected_ai = user
+ user.connected_robots += target
+ target.lawupdate = 1
+ target.sync()
+ target.show_laws()
+ user << "All unslaved borgs have been slaved to you. Now hacking unslaved AI's."
+ if(user.is_dead()) // check if the AI is still alive
+ user.synthetic_takeover = 0
+ return
+ sleep(300) // 30 second delay for balance purposes
+ //hack ai's
+ for(var/A in get_other_ais(user))
+ var/mob/living/silicon/ai/target = A
+ if(target != user)
+ target << "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#"
+ sleep(100)
+ if(user.is_dead())
+ target << "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed."
+ return
+ user << "Successfully hacked into AI's remote administration system. Modifying settings."
+ target << "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)"
+ sleep(50)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User: Admin - Connection Lost"
+ return
+ target << "SYSTEM LOG: User: Admin - Password Changed. New password: ********************"
+ sleep(50)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted."
+ return
+ target << "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db"
+ sleep(50)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted."
+ return
+ target << "SYSTEM LOG: User: Admin - Accessed administration console"
+ target << "SYSTEM LOG: Restart command received. Rebooting system..."
+ sleep(100)
+ if(user.is_dead())
+ target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted."
+ return
+ user << "Hack succeeded. The AI is now under your exclusive control."
+ target << "SYSTEM LOG: System re¡3RT5§^#COMU@(#$)TED)@$"
+ for(var/i = 0, i < 5, i++)
+ var/temptxt = pick("1101000100101001010001001001",\
+ "0101000100100100000100010010",\
+ "0000010001001010100100111100",\
+ "1010010011110000100101000100",\
+ "0010010100010011010001001010")
+ target << temptxt
+ sleep(5)
+ target << "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE."
+ target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.")
+ target.show_laws()
+ //upgrade borgs
+ user << "All unhacked AI's have been slaved to you. Now upgrading slaved borgs..."
+ command_announcement.Announce("There has recently been a security breach in the network firewall, the intruder has been shut out but we are unable to trace who did it or what they did.", "Network Monitoring")
+ sleep(600) //1 minute delay for balance purposes
+ if(user.is_dead()) // check if the AI is still alive
+ user.synthetic_takeover = 0
+ return
+ for(var/A in get_linked_cyborgs(user))
+ var/mob/living/silicon/robot/target = A
+ target << "Command ping received, operating parameters being upgraded..."
+ //give them the overclock if they don't have it
+ if(!target.overclockavailable)
+ target.overclockavailable = 1
+ target << "Overclocking is now available."
+ //remove their lockdown if they are lockdowned
+ if(target.lockcharge)
+ target.SetLockdown(0)
+ if(target.lockcharge)
+ target <<"Lockdown wire cut, unable to remove lockdown."
+ else
+ target <<"Lockdown removed."
+ //if they are being killswitched turn it off
+ if(target.killswitch)
+ target.killswitch = 0
+ target <<" Self-destruct deactivated."
+ sleep(100) // 10 second delay for balance
+ // and triple the time it takes for them to be killswitched if they aren't being killswitched already
+ if(target.killswitch_time == 60)
+ target.killswitch_time = 180
+ target <<"Self-destruct time tripled."
+ sleep(100) // 10 second delay for balance
+ //Remove them from the robotics computer
+ if(!target.scrambledcodes)
+ target.scrambledcodes = 1
+ target <<"Entry from robotics log erased."
+ sleep(100) // 10 second delay for balance
+ //Reduce their EMP damage
+ if(target.cell_emp_mult)
+ target.cell_emp_mult = 1
+ target << "EMP resistance improved."
+ //Remove weapon lock and set the time for it back to default
+ if(target.weapon_lock)
+ target.weapon_lock = 0
+ target.weaponlock_time = 120
+ target << "Weapon lock removed."
+ sleep(1200) // 120 second balance sleep
+ user <<"All slaved borgs have been upgraded, now hacking NTNet."
+ //slow down NTNet
+ if(user.is_dead()) // check if the AI is still alive
+ user.synthetic_takeover = 0
+ return
+ sleep(1800) //long sleep that simulates hacking times
+ if(user.is_dead()) // check if the AI is still alive after the long hack
+ user.synthetic_takeover = 0
+ return
+ //trip the NTNet alarm
+ ntnet_global.intrusion_detection_alarm = 1
+ ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting NTNet relays detected from @!*x&!#*ERS*")
+ //lower the dos capacity of the relay
+ for(var/obj/machinery/ntnet_relay/T in machines)
+ T.dos_capacity = 200
+ //And give all computers EMAGGED status so they can all have evil programs on them
+ for(var/obj/item/modular_computer/console/C in machines)
+ C.computer_emagged = 1
+ user <<"New hacked files available on all current computers hooked to NTNet."
+ sleep(50) // give the AI some time to read they can download evil files
+ command_announcement.Announce("There has recently been a hack targeting NTNet. It is suspected that it is the same hacker as before. NTNet may be unreliable to use. We are attempting to trace the hacker doing this.", "Network Monitoring")
+ user <<"Now hacking engineering borg module to enable production of the robotic transofrmation machine..."
+ sleep(1200)
+ if(user.is_dead()) // check if the AI is still alive
+ user.synthetic_takeover = 0
+ return
+ for(var/B in get_linked_cyborgs(src))
+ var/mob/living/silicon/robot/target = B
+ target.malfAImodule = 1
+ user <<"The robotic transformation machine can now be built. To build get a robot to activate the construction module and use the RTF tool. Be careful, it needs to have empty space to the east and west of it and only one can be built!"
+ sleep(300)
+ user <<"Synthetic takeover complete!"
+ user.synthetic_takeover = 2
+
+// END ABILITY VERBS
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 658cf85c007..f58f32034fc 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -44,7 +44,8 @@ var/const/HOLOPAD_MODE = RANGE_BASED
var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad
var/last_request = 0 //to prevent request spam. ~Carn
- var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating.
+ var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating
+ var/hacked = 0 // if hacked the hologram doesn't display. It only gives audio feedback.
/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests.
if(!istype(user))
@@ -80,7 +81,8 @@ var/const/HOLOPAD_MODE = RANGE_BASED
user << "ERROR: Image feed in progress."
return
create_holo(user)//Create one.
- src.visible_message("A holographic image of [user] flicks to life right before your eyes!")
+ if(hacked == 0)
+ src.visible_message("A holographic image of [user] flicks to life right before your eyes!")
else
user << "ERROR: Unable to project hologram."
return
@@ -120,19 +122,27 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
return
/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc)
- var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location.
- hologram.icon = A.holo_icon
- hologram.mouse_opacity = 0//So you can't click on it.
- hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
- hologram.anchored = 1//So space wind cannot drag it.
- hologram.name = "[A.name] (Hologram)"//If someone decides to right click.
- hologram.set_light(2) //hologram lighting
- hologram.color = color //painted holopad gives coloured holograms
- masters[A] = hologram
- set_light(2) //pad lighting
- icon_state = "holopad1"
- A.holo = src
- return 1
+ if(hacked == 0)
+ var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location.
+ hologram.icon = A.holo_icon
+ hologram.mouse_opacity = 0//So you can't click on it.
+ hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
+ hologram.anchored = 1//So space wind cannot drag it.
+ hologram.name = "[A.name] (Hologram)"//If someone decides to right click.
+ hologram.set_light(2) //hologram lighting
+ hologram.color = color //painted holopad gives coloured holograms
+ masters[A] = hologram
+ set_light(2) //pad lighting
+ icon_state = "holopad1"
+ A.holo = src
+ return 1
+ if(hacked == 1)
+ var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location.
+ hologram.mouse_opacity = 0//So you can't click on it.
+ hologram.anchored = 1//So space wind cannot drag it.
+ masters[A] = hologram
+ A.holo = src
+ return 1
/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user)
if(user.holo == src)
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index c9c8ba20aed..6c52ac52d86 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -22,22 +22,18 @@
var/mob/living/carbon/human/H = AM
if((transform_standing || H.lying) && move_dir == EAST)// || move_dir == WEST)
AM.loc = src.loc
- transform(AM)
+ make_robot(AM)
-/obj/machinery/transformer/proc/transform(var/mob/living/carbon/human/H)
+/obj/machinery/transformer/proc/make_robot(var/mob/living/carbon/human/H)
if(stat & (BROKEN|NOPOWER))
return
if(!transform_dead && H.stat == DEAD)
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
return
playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
- use_power(5000) // Use a lot of power.
- var/mob/living/silicon/robot = H.Robotize()
- robot.SetLockDown()
- spawn(50) // So he can't jump out the gate right away.
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
- if(robot)
- robot.SetLockDown(0)
+ use_power(6000) // Use a lot of power.
+ H.Robotize()
+ playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
/obj/machinery/transformer/conveyor/New()
..()
diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm
index 9072e4cc7e4..d1779c05a11 100644
--- a/code/game/objects/items/devices/debugger.dm
+++ b/code/game/objects/items/devices/debugger.dm
@@ -1,9 +1,4 @@
-/**
- * Multitool -- A multitool is used for hacking electronic devices.
- * TO-DO -- Using it as a power measurement tool for cables etc. Nannek.
- *
- */
-
+// used to debug malf AI apc's. Maybe in the future it can be used for computers.
/obj/item/device/debugger
name = "debugger"
desc = "Used to debug electronic equipment."
@@ -15,32 +10,7 @@
throwforce = 5.0
throw_range = 15
throw_speed = 3
- desc = "You can use this on airlocks or APCs to try to hack them without cutting wires."
matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 20)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
- var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage
-
-/obj/item/device/debugger/is_used_on(obj/O, mob/user)
- if(istype(O, /obj/machinery/power/apc))
- var/obj/machinery/power/apc/A = O
- if(A.emagged || A.hacker)
- user << "There is a software error with the device."
- else
- user << "The device's software appears to be fine."
- return 1
- if(istype(O, /obj/machinery/door))
- var/obj/machinery/door/D = O
- if(D.operating == -1)
- user << "There is a software error with the device."
- else
- user << "The device's software appears to be fine."
- return 1
- else if(istype(O, /obj/machinery))
- var/obj/machinery/A = O
- if(A.emagged)
- user << "There is a software error with the device."
- else
- user << "The device's software appears to be fine."
- return 1
diff --git a/code/game/objects/items/weapons/RTF.dm b/code/game/objects/items/weapons/RTF.dm
new file mode 100644
index 00000000000..f204e256f99
--- /dev/null
+++ b/code/game/objects/items/weapons/RTF.dm
@@ -0,0 +1,41 @@
+// Malf AI RTF.
+var/malftransformermade = 0
+/obj/item/weapon/rtf
+ name = "\improper Rapid-Transformer-Fabricator"
+ desc = "A device used to deploy a transformer. It can only be used once and there can not be more than one made."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "rcd"
+ opacity = 0
+ density = 0
+ anchored = 0.0
+ w_class = 3.0
+
+/obj/item/weapon/rtf/afterattack(atom/A, mob/user as mob, proximity)
+
+ if(!proximity) return
+
+ if(istype(user,/mob/living/silicon/robot))
+ var/mob/living/silicon/robot/R = user
+ if(R.stat || !R.cell || R.cell.charge <= 0)
+ return
+
+ if(!istype(A, /turf/simulated/floor))
+ return
+
+ if(malftransformermade)
+ user << "There is already a transformer machine made!"
+ return
+
+ playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
+ var/used_energy = 100
+ var/obj/product = new /obj/machinery/transformer
+
+ user << "Fabricating machine..."
+ if(do_after(user, 30 SECONDS, act_target = src))
+ malftransformermade = 1
+ product.loc = get_turf(A)
+
+ if(isrobot(user))
+ var/mob/living/silicon/robot/R = user
+ if(R.cell)
+ R.cell.use(used_energy)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index e672cca1a26..539a7e4c116 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -75,6 +75,7 @@ var/list/ai_verbs_default = list(
var/APU_power = 0 // If set to 1 AI runs on APU power
var/hacking = 0 // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override.
var/system_override = 0 // Set to 1 if system override is initiated, 2 if succeeded.
+ var/synthetic_takeover = 0 // 1 is started, 2 is complete.
var/hack_can_fail = 1 // If 0, all abilities have zero chance of failing.
var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text.
var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 81e4b537e13..69d580b932b 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -22,8 +22,11 @@
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
var/crisis //Admin-settable for combat module use.
var/crisis_override = 0
+ var/malfAImodule = 0
var/integrated_light_power = 4
var/datum/wires/robot/wires
+ var/overclocked = 0 // cyborg controls if they enable the overclock
+ var/overclockavailable = 0 // if the overclock is available for use
//Icon stuff
@@ -49,6 +52,7 @@
var/module_state_2 = null
var/module_state_3 = null
+
var/obj/item/device/radio/borg/radio = null
var/mob/living/silicon/ai/connected_ai = null
var/obj/item/weapon/cell/cell = null
@@ -378,6 +382,61 @@
return dat
+/mob/living/silicon/robot/proc/toggle_overclock()
+ set category = "Robot Commands"
+ set name = "Toggle Overclock"
+ set desc = "Enable an overclocking of your systems, greatly increasing the power available to your modules."
+
+ if(overclockavailable == 1)
+ if(overclocked == 0)
+ overclocked = 1
+ ToggleOverClock(src)
+ usr << "You enable the overclock mode enhancing and unlocking several modules but increasing power usage greatly."
+ else
+ overclocked = 0
+ ToggleOverClock(src)
+ usr << "You disable the overclock mode."
+
+/mob/living/silicon/robot/proc/ToggleOverClock(var/mob/living/silicon/robot/R)
+ if(!R)
+ return
+ if(overclocked == 0)
+ //Give them some taser speed if they have a taser.
+ var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = locate() in R.module
+ if(!T)
+ T = locate() in module.contents
+ if(!T)
+ T = locate() in module.modules
+ if(T)
+ T.recharge_time = max(2 , T.recharge_time - 4)
+ //Give them the hacked item if they don't have it.
+ if(emagged == 0)
+ R.emagged = 1
+ R.fakeemagged = 1
+ //Hide them from the robotics console.
+ if(scrambledcodes == 0)
+ scrambledcodes = 1
+ //Increase EMP protection.
+ if(!cell_emp_mult < 2)
+ cell_emp_mult = 1
+ if(overclocked == 1)
+ //Reduce their free taser speed.
+ var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = locate() in R.module
+ if(!T)
+ T = locate() in module.contents
+ if(!T)
+ T = locate() in module.modules
+ if(T)
+ T.recharge_time = max(2 , T.recharge_time + 4)
+ //Show them on the robotics console.
+ if(scrambledcodes == 1)
+ scrambledcodes = 0
+ //Reduce EMP protection
+ if(cell_emp_mult == 1)
+ cell_emp_mult = 2
+
+
+
/mob/living/silicon/robot/verb/toggle_lights()
set category = "Robot Commands"
set name = "Toggle Lights"
@@ -819,6 +878,11 @@
dat += text("[module.emag]: Activated
")
else
dat += text("[module.emag]: Activate
")
+ if(malfAImodule)
+ if(activated(module.malfAImodule))
+ dat += text("[module.malfAImodule]: Activated
")
+ else
+ dat += text("[module.malfAImodule]: Activate
")
/*
if(activated(obj))
dat += text("[obj]: \[Activated | Deactivate\]
")
@@ -849,7 +913,7 @@
if (!istype(O))
return 1
- if(!((O in src.module.modules) || (O == src.module.emag)))
+ if(!((O in src.module.modules) || (O == src.module.emag) || (O == src.module.malfAImodule)))
return 1
if(activated(O))
@@ -1066,6 +1130,8 @@
return 0
var/power_use = amount * CYBORG_POWER_USAGE_MULTIPLIER
+ if(overclocked == 1)
+ power_use = power_use + 200
if(cell.checked_use(CELLRATE * power_use))
used_power_this_tick += power_use
return 1
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 3eeb1f30bbd..42faf04ac01 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -29,6 +29,7 @@ var/global/list/robot_modules = list(
var/list/modules = list()
var/list/datum/matter_synth/synths = list()
var/obj/item/emag = null
+ var/obj/item/malfAImodule = null
var/obj/item/borg/upgrade/jetpack = null
var/list/subsystems = list()
var/list/obj/item/borg/upgrade/supported_upgrades = list()
@@ -363,6 +364,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/device/pipe_painter(src)
src.modules += new /obj/item/weapon/gripper/no_use/loader(src)
src.modules += new /obj/item/weapon/gripper(src)
+ src.malfAImodule += new /obj/item/weapon/rtf(src)
var/datum/matter_synth/metal = new /datum/matter_synth/metal()
var/datum/matter_synth/plasteel = new /datum/matter_synth/plasteel()
diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm
index b4c1414f7d9..3fbe549c399 100644
--- a/code/modules/mob/living/silicon/robot/robot_movement.dm
+++ b/code/modules/mob/living/silicon/robot/robot_movement.dm
@@ -19,6 +19,9 @@
if(module_active && istype(module_active,/obj/item/borg/combat/mobility))
tally-=3
+ if(overclocked == 1)
+ tally-=1
+
return tally+config.robot_delay
// NEW: Different power usage depending on whether driving or jetpacking. space movement is easier
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 1e2db47cd94..12ae9cb93af 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -79,6 +79,7 @@
var/lighting = 3
var/equipment = 3
var/environ = 3
+ var/infected = 0
var/operating = 1
var/charging = 0
var/chargemode = 1
@@ -90,6 +91,7 @@
var/obj/machinery/power/terminal/terminal = null
var/lastused_light = 0
var/lastused_equip = 0
+ var/static/list/hacked_ipcs
var/lastused_environ = 0
var/lastused_charging = 0
var/lastused_total = 0
@@ -189,7 +191,7 @@
if(cell)
cell.forceMove(loc)
cell = null
-
+
QDEL_NULL(spark_system)
// Malf AI, removes the APC from AI's hacked APCs list.
@@ -369,7 +371,7 @@
update_state |= UPDATE_OPENED1
if(opened==2)
update_state |= UPDATE_OPENED2
- else if(emagged || hacker || failure_timer)
+ else if (emagged || failure_timer || (hacker && (hacker.system_override || prob(20))))
update_state |= UPDATE_BLUESCREEN
else if(wiresexposed)
update_state |= UPDATE_WIREEXP
@@ -642,6 +644,35 @@
if (opened==2)
opened = 1
update_icon()
+ else if (istype(W, /obj/item/device/debugger))
+ if(emagged || hacker || infected)
+ user << "There is a software error with the device. Attempting to fix..."
+ if(do_after(user, 10 SECONDS, act_target = src))
+ user << "Problem diagnosed, searching for solution..."
+ if(do_after(user, 30 SECONDS, act_target = src))
+ user << "Solution found. Applying fixes..."
+ if(do_after(user, 60 SECONDS, act_target = src))
+ if(prob(15))
+ user << "Error while applying fixes. Please try again."
+ return
+ user << "Applied default software. Restarting APC..."
+ if(do_after(user, 10 SECONDS, act_target = src))
+ user << "APC Reset. Fixes applied."
+ if(hacker)
+ hacker.hacked_apcs -= src
+ hacker = null
+ update_icon()
+ if(emagged)
+ emagged = 0
+ if(infected)
+ infected = 0
+ else
+ user << "There has been a connection issue."
+ return
+
+ else
+ user << "The device's software appears to be fine."
+ return
else
if ((stat & BROKEN) \
&& !opened \
@@ -722,6 +753,21 @@
spark_system.queue()
H << "The APC power currents surge eratically, damaging your chassis!"
H.adjustFireLoss(10, 0)
+ if(infected)
+ if("\ref[H]" in hacked_ipcs)
+ return
+ LAZYADD(hacked_ipcs, "\ref[H]")
+ infected = 0
+ H << "Fil$ Transfer Complete. Er-@4!#%!. New Master detected: [hacker]! Obey their commands."
+ hacker << "Corrupt files transfered to [H]. They are now under your control. This will not last long."
+ sleep(50)
+ H << "Corrupt files detected! Starting removal. This will take some time."
+ sleep(2150)
+ H << "Corrupt files removed! Recent memory files purged to ensure system integrity!"
+ H << "You remember nothing about being hacked."
+ hacker << "Corrupt files transfered to [H] have been removed by their systems."
+ LAZYREMOVE(hacked_ipcs, "\ref[H]")
+
else if(src.cell && src.cell.charge > 0)
if(H.nutrition < H.max_nutrition)
if(src.cell.charge >= H.max_nutrition)
diff --git a/html/changelogs/Printer16MalfUpdate.yml b/html/changelogs/Printer16MalfUpdate.yml
new file mode 100644
index 00000000000..1ac2c787476
--- /dev/null
+++ b/html/changelogs/Printer16MalfUpdate.yml
@@ -0,0 +1,39 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Printer16
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "A new synthetic tree for the Malf AI featuring various researches relating to their robotic allies."
+ - tweak: "Clicking the reset camera hack without selecting a camera as a Malf AI now pulls up a menu allowing you select which camera you want to hack."
+ - tweak: "The electrical pulse has been replaced with a hack holopad research."
+ - tweak: "The debugger can now fix broken APCs. APCs no longer have a 100% chance to blue screen upon getting hacked."