diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index 2831e25b10..233ae17a4d 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -133,24 +133,27 @@
#define ui_construct_pull "EAST,CENTER-2:15"
#define ui_construct_health "EAST,CENTER:15"
-// AI
-#define ui_ai_core "SOUTH:6,WEST"
-#define ui_ai_camera_list "SOUTH:6,WEST+1"
-#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
-#define ui_ai_camera_light "SOUTH:6,WEST+3"
-#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
-#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
-#define ui_ai_alerts "SOUTH:6,WEST+6"
-#define ui_ai_announcement "SOUTH:6,WEST+7"
-#define ui_ai_shuttle "SOUTH:6,WEST+8"
-#define ui_ai_state_laws "SOUTH:6,WEST+9"
-#define ui_ai_pda_send "SOUTH:6,WEST+10"
-#define ui_ai_pda_log "SOUTH:6,WEST+11"
-#define ui_ai_take_picture "SOUTH:6,WEST+12"
-#define ui_ai_view_images "SOUTH:6,WEST+13"
-#define ui_ai_sensor "SOUTH:6,WEST+14"
-#define ui_ai_multicam "SOUTH+1:6,WEST+13"
-#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
+//AI
+#define ui_ai_core "BOTTOM:6,RIGHT-4"
+#define ui_ai_shuttle "BOTTOM:6,RIGHT-3"
+#define ui_ai_announcement "BOTTOM:6,RIGHT-2"
+#define ui_ai_state_laws "BOTTOM:6,RIGHT-1"
+#define ui_ai_pda_log "BOTTOM:6,RIGHT"
+#define ui_ai_pda_send "BOTTOM+1:6,RIGHT"
+#define ui_ai_language_menu "BOTTOM+1:8,RIGHT-2:30"
+
+#define ui_ai_crew_monitor "BOTTOM:6,CENTER-1"
+#define ui_ai_crew_manifest "BOTTOM:6,CENTER"
+#define ui_ai_alerts "BOTTOM:6,CENTER+1"
+
+#define ui_ai_view_images "BOTTOM:6,LEFT+4"
+#define ui_ai_camera_list "BOTTOM:6,LEFT+3"
+#define ui_ai_track_with_camera "BOTTOM:6,LEFT+2"
+#define ui_ai_camera_light "BOTTOM:6,LEFT+1"
+#define ui_ai_sensor "BOTTOM:6,LEFT"
+#define ui_ai_multicam "BOTTOM+1:6,LEFT+1"
+#define ui_ai_add_multicam "BOTTOM+1:6,LEFT"
+#define ui_ai_take_picture "BOTTOM+2:6,LEFT"
// pAI
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index 17b5a9ed61..5f8b10fd31 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -194,7 +194,7 @@
// Language menu
using = new /atom/movable/screen/language_menu
- using.screen_loc = ui_borg_language_menu
+ using.screen_loc = ui_ai_language_menu
using.hud = src
static_inventory += using
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index fbb3297f89..760c58b533 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -312,6 +312,7 @@
qdel(O.mmi)
O.mmi = W //and give the real mmi to the borg.
O.updatename()
+ playsound(O.loc, 'sound/voice/liveagain.ogg', 75, TRUE)
SSblackbox.record_feedback("amount", "cyborg_birth", 1)
forceMove(O)
O.robot_suit = src
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 5a8788deba..173b420710 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -8,32 +8,29 @@
icon_state = "cyborg_upgrade"
w_class = WEIGHT_CLASS_SMALL
var/locked = FALSE
- var/installed = 0
- var/require_module = 0
- var/list/module_type
+ var/installed = FALSE
+ var/require_module = FALSE
+ var/list/module_type = null
/// Bitflags listing module compatibility. Used in the exosuit fabricator for creating sub-categories.
var/module_flags = NONE
// if true, is not stored in the robot to be ejected
// if module is reset
var/one_use = FALSE
+ /// Means this is a basetype and should not be used
+ var/abstract_type = /obj/item/borg/upgrade
+ /// Show the amount of this module that is installed
+ var/show_amount = FALSE
/obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R, user = usr)
if(R.stat == DEAD)
- to_chat(user, "[src] will not function on a deceased cyborg.")
+ to_chat(user, span_warning("[src] will not function on a deceased cyborg!"))
return FALSE
if(module_type && !is_type_in_list(R.module, module_type))
- to_chat(R, "Upgrade mounting error! No suitable hardpoint detected.")
- to_chat(user, "There's no mounting point for the module!")
+ to_chat(R, span_alert("Upgrade mounting error! No suitable hardpoint detected."))
+ to_chat(user, span_warning("There's no mounting point for the module!"))
return FALSE
return TRUE
-/*
-This proc gets called by upgrades after installing them. Use this for things that for example need to be moved into a specific borg item,
-as performing this in action() will cause the upgrade to end up in the borg instead of its intended location due to forceMove() being called afterwards..
-*/
-/obj/item/borg/upgrade/proc/afterInstall(mob/living/silicon/robot/R, user = usr)
- return
-
/obj/item/borg/upgrade/proc/deactivate(mob/living/silicon/robot/R, user = usr)
if (!(src in R.upgrades))
return FALSE
@@ -324,12 +321,12 @@ as performing this in action() will cause the upgrade to end up in the borg inst
/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
- ADD_TRAIT(src, TRAIT_LAVA_IMMUNE, type)
+ ADD_TRAIT(R, TRAIT_LAVA_IMMUNE, type)
/obj/item/borg/upgrade/lavaproof/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
if (.)
- REMOVE_TRAIT(src, TRAIT_LAVA_IMMUNE, type)
+ REMOVE_TRAIT(R, TRAIT_LAVA_IMMUNE, type)
/obj/item/borg/upgrade/selfrepair
name = "self-repair module"
@@ -440,6 +437,7 @@ as performing this in action() will cause the upgrade to end up in the borg inst
/obj/item/robot_module/syndicate_medical)
var/list/additional_reagents = list()
module_flags = BORG_MODULE_MEDICAL
+ abstract_type = /obj/item/borg/upgrade/hypospray
/obj/item/borg/upgrade/hypospray/action(mob/living/silicon/robot/R, user = usr)
. = ..()
diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm
index 943f60e752..3cd08ff419 100644
--- a/code/game/turfs/simulated/lava.dm
+++ b/code/game/turfs/simulated/lava.dm
@@ -201,6 +201,7 @@
var/obj/structure/closet/burn_closet = burn_obj
for(var/burn_content in burn_closet.contents)
burn_stuff(burn_content)
+ return
var/mob/living/burn_living = burn_target
burn_living.update_fire()
diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm
index ec03afff36..a41a493feb 100644
--- a/code/modules/admin/verbs/borgpanel.dm
+++ b/code/modules/admin/verbs/borgpanel.dm
@@ -50,15 +50,12 @@
"lockdown" = borg.locked_down,
"scrambledcodes" = borg.scrambledcodes
)
- .["upgrades"] = list()
- for (var/upgradetype in subtypesof(/obj/item/borg/upgrade)-/obj/item/borg/upgrade/hypospray) //hypospray is a dummy parent for hypospray upgrades
- var/obj/item/borg/upgrade/upgrade = upgradetype
- if (initial(upgrade.module_type) && !is_type_in_list(borg.module, initial(upgrade.module_type))) // Upgrade requires a different module
- continue
- var/installed = FALSE
- if (locate(upgradetype) in borg)
- installed = TRUE
- .["upgrades"] += list(list("name" = initial(upgrade.name), "installed" = installed, "type" = upgradetype))
+ var/obj/item/gun/energy/kinetic_accelerator/kinetic_accelerator = locate(/obj/item/gun/energy/kinetic_accelerator) in borg.module
+ if(kinetic_accelerator)
+ .["ka_remaining_capacity"] = kinetic_accelerator.get_remaining_mod_capacity()
+ .["active_upgrades"] = list()
+ for (var/obj/item/borg/upgrade/upgrade as anything in borg.upgrades) // put a non-upgrade here, i dare you.
+ .["active_upgrades"] += list(list("type" = upgrade.type))
.["laws"] = borg.laws ? borg.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) : list()
.["channels"] = list()
for (var/k in GLOB.radiochannels)
@@ -77,6 +74,28 @@
for(var/mob/living/silicon/ai/ai in GLOB.ai_list)
.["ais"] += list(list("name" = ai.name, "ref" = REF(ai), "connected" = (borg.connected_ai == ai)))
+/datum/borgpanel/ui_static_data(mob/user)
+ . = ..()
+ .["upgrades"] = list()
+ for(var/obj/item/borg/upgrade/upgrade as anything in GLOB.borg_upgrades)
+ if(upgrade.type == upgrade.abstract_type)
+ continue
+ var/obj/item/borg/upgrade/modkit/modkit
+ if(istype(upgrade, /obj/item/borg/upgrade/modkit))
+ modkit = upgrade
+ if(modkit.minebot_exclusive)
+ continue
+ modkit = upgrade
+ .["upgrades"] += list(
+ list(
+ "name" = upgrade.name,
+ "type" = upgrade.type,
+ "module_type" = upgrade.module_type,
+ "maximum_of_type" = modkit ? modkit.maximum_of_type : null,
+ "denied_type" = modkit ? modkit.denied_type : null,
+ "cost" = modkit ? modkit.cost : null
+ )
+ )
/datum/borgpanel/ui_act(action, params)
if(..())
@@ -148,17 +167,31 @@
var/upgradepath = text2path(params["upgrade"])
var/obj/item/borg/upgrade/installedupgrade = locate(upgradepath) in borg
if (installedupgrade)
- installedupgrade.deactivate(borg, user)
- borg.upgrades -= installedupgrade
+ qdel(installedupgrade)
message_admins("[key_name_admin(user)] removed the [installedupgrade] upgrade from [ADMIN_LOOKUPFLW(borg)].")
log_admin("[key_name(user)] removed the [installedupgrade] upgrade from [key_name(borg)].")
- qdel(installedupgrade)
else
- var/obj/item/borg/upgrade/upgrade = new upgradepath(borg)
- upgrade.action(borg, user)
- borg.upgrades += upgrade
+ var/obj/item/borg/upgrade/upgrade = new upgradepath()
+ if(!borg.apply_upgrade(upgrade, user, TRUE))
+ to_chat(user, span_danger("Upgrade error."))
+ return
message_admins("[key_name_admin(user)] added the [upgrade] borg upgrade to [ADMIN_LOOKUPFLW(borg)].")
log_admin("[key_name(user)] added the [upgrade] borg upgrade to [key_name(borg)].")
+ if ("add_upgrade")
+ var/upgradepath = text2path(params["upgrade"])
+ var/obj/item/borg/upgrade/upgrade = new upgradepath()
+ if(!borg.apply_upgrade(upgrade, user, TRUE))
+ to_chat(user, span_danger("Upgrade error."))
+ return
+ message_admins("[key_name_admin(user)] added the [upgrade] borg upgrade to [ADMIN_LOOKUPFLW(borg)].")
+ log_admin("[key_name(user)] added the [upgrade] borg upgrade to [key_name(borg)].")
+ if ("remove_upgrade")
+ var/upgradepath = text2path(params["upgrade"])
+ var/obj/item/borg/upgrade/installedupgrade = locate(upgradepath) in borg
+ if (installedupgrade)
+ qdel(installedupgrade)
+ message_admins("[key_name_admin(user)] removed the [installedupgrade] upgrade from [ADMIN_LOOKUPFLW(borg)].")
+ log_admin("[key_name(user)] removed the [installedupgrade] upgrade from [key_name(borg)].")
if ("toggle_radio")
var/channel = params["channel"]
if (channel in borg.radio.channels) // We're removing a channel
@@ -236,3 +269,10 @@
message_admins("[key_name_admin(usr)] added [chosenboard] to [ADMIN_LOOKUPFLW(beepboop)].")
log_admin("[key_name(usr)] added [chosenboard] to [key_name(beepboop)].")
qdel(new_board)
+
+GLOBAL_LIST_INIT(borg_upgrades, populate_borg_upgrades())
+
+/proc/populate_borg_upgrades()
+ . = list()
+ for(var/type in typesof(/obj/item/borg/upgrade))
+ . += new type
diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm
index 69b5de963d..75b647809a 100644
--- a/code/modules/mining/minebot.dm
+++ b/code/modules/mining/minebot.dm
@@ -82,8 +82,7 @@
Field repairs can be done with a welder."
if(stored_gun && stored_gun.max_mod_capacity)
. += "[stored_gun.get_remaining_mod_capacity()]% mod capacity remaining."
- for(var/A in stored_gun.get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in stored_gun.modkits)
. += "There is \a [M] installed, using [M.cost]% capacity."
/mob/living/simple_animal/hostile/mining_drone/welder_act(mob/living/user, obj/item/I)
@@ -136,8 +135,7 @@
if(istype(O, /obj/item/projectile/kinetic))
var/obj/item/projectile/kinetic/K = O
if(K.kinetic_gun)
- for(var/A in K.kinetic_gun.get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in K.kinetic_gun.modkits)
if(istype(M, /obj/item/borg/upgrade/modkit/minebot_passthrough))
return TRUE
if(istype(O, /obj/item/projectile/destabilizer))
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 232c5b4195..77a88960d5 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -28,11 +28,13 @@
inv2 = new /atom/movable/screen/robot/module2()
inv3 = new /atom/movable/screen/robot/module3()
+ ident = rand(1, 999)
+
if(!shell)
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = real_name
aiPDA.ownjob = "Cyborg"
- aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"
+ aiPDA.name = real_name + " ([aiPDA.ownjob])"
previous_health = health
@@ -59,26 +61,29 @@
update_icons()
. = ..()
+ //If this body is meant to be a borg controlled by the AI player
//If this body is meant to be a borg controlled by the AI player
if(shell)
- make_shell()
-
- //MMI stuff. Held togheter by magic. ~Miauw
- else if(!mmi || !mmi.brainmob)
- mmi = new (src)
- mmi.brain = new /obj/item/organ/brain(mmi)
- mmi.brain.organ_flags |= ORGAN_FROZEN
- mmi.brain.name = "[real_name]'s brain"
- mmi.icon_state = "mmi_full"
- mmi.name = "Man-Machine Interface: [real_name]"
- mmi.brainmob = new(mmi)
- mmi.brainmob.name = src.real_name
- mmi.brainmob.real_name = src.real_name
- mmi.brainmob.container = mmi
+ var/obj/item/borg/upgrade/ai/board = new(src)
+ make_shell(board)
+ add_to_upgrades(board)
+ else
+ //MMI stuff. Held togheter by magic. ~Miauw
+ if(!mmi?.brainmob)
+ mmi = new (src)
+ mmi.brain = new /obj/item/organ/brain(mmi)
+ mmi.brain.organ_flags |= ORGAN_FROZEN
+ mmi.brain.name = "[real_name]'s brain"
+ mmi.icon_state = "mmi_full"
+ mmi.name = "[initial(mmi.name)]: [real_name]"
+ mmi.brainmob = new(mmi)
+ mmi.brainmob.name = src.real_name
+ mmi.brainmob.real_name = src.real_name
+ mmi.brainmob.container = mmi
+ mmi.update_appearance()
INVOKE_ASYNC(src, .proc/updatename)
- playsound(loc, 'sound/voice/liveagain.ogg', 75, TRUE)
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
@@ -330,194 +335,145 @@
return FALSE
return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
-/mob/living/silicon/robot/proc/attempt_welder_repair(obj/item/W, mob/user)
- if(!W.tool_behaviour == TOOL_WELDER)
- return
- if(!getBruteLoss())
- to_chat(user, "[src] is already in good condition!")
- return
- if(!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
- return
- user.DelayNextAction(CLICK_CD_MELEE)
- if(src == user)
- to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50))
- return
- adjustBruteLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!do_after(user, 30, target = src))
- return
- adjustBruteLoss(-30)
- updatehealth()
- add_fingerprint(user)
- visible_message("[user] has fixed some of the dents on [src].")
-
-/mob/living/silicon/robot/proc/attempt_cable_repair(obj/item/stack/cable_coil/W, mob/user)
- if (getFireLoss() > 0 || getToxLoss() > 0)
- user.DelayNextAction(CLICK_CD_MELEE)
- if(src == user)
- to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-10)
- adjustToxLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!W.use_tool(src, user, 30, 1))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-30)
- adjustToxLoss(-30)
- updatehealth()
- user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].")
- else
- to_chat(user, "The wires seem fine, there's no need to fix them.")
-
/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
- if(W.tool_behaviour == TOOL_WELDER && (user.a_intent != INTENT_HARM || user == src))
- INVOKE_ASYNC(src, .proc/attempt_welder_repair, W, user)
- return
-
- else if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
- INVOKE_ASYNC(src, .proc/attempt_cable_repair, W, user)
- return
-
- else if(W.tool_behaviour == TOOL_CROWBAR) // crowbar means open or close the cover
- if(opened)
- to_chat(user, "You close the cover.")
- opened = 0
- update_icons()
- else
- if(locked)
- to_chat(user, "The cover is locked and cannot be opened!")
+ if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
+ user.DelayNextAction(CLICK_CD_MELEE)
+ if (getFireLoss() > 0 || getToxLoss() > 0)
+ if(src == user)
+ to_chat(user, span_notice("You start fixing yourself..."))
+ if(!W.use_tool(src, user, 5 SECONDS, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
+ to_chat(user, span_warning("You need more cable to repair [src]!"))
+ return
+ adjustFireLoss(-10)
+ adjustToxLoss(-10)
else
- to_chat(user, "You open the cover.")
- opened = 1
- update_icons()
+ to_chat(user, span_notice("You start fixing [src]..."))
+ if(!W.use_tool(src, user, 3 SECONDS, 1))
+ to_chat(user, span_warning("You need more cable to repair [src]!"))
+ adjustFireLoss(-30)
+ adjustToxLoss(-30)
+ updatehealth()
+ user.visible_message(span_notice("[user] has fixed some of the burnt wires on [src]."), span_notice("You fix some of the burnt wires on [src]."))
+ else
+ to_chat(user, span_warning("The wires seem fine, there's no need to fix them."))
+ return
- else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
+ if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
if(wiresexposed)
- to_chat(user, "Close the cover first!")
+ to_chat(user, span_warning("Close the cover first!"))
else if(cell)
- to_chat(user, "There is a power cell already installed!")
+ to_chat(user, span_warning("There is a power cell already installed!"))
else
if(!user.transferItemToLoc(W, src))
return
cell = W
- to_chat(user, "You insert the power cell.")
+ to_chat(user, span_notice("You insert the power cell."))
update_icons()
diag_hud_set_borgcell()
+ return
- else if(is_wire_tool(W))
+ if(is_wire_tool(W))
if (wiresexposed)
wires.interact(user)
else
- to_chat(user, "You can't reach the wiring!")
+ to_chat(user, span_warning("You can't reach the wiring!"))
+ return
- else if(W.tool_behaviour == TOOL_SCREWDRIVER && opened && !cell) // haxing
- wiresexposed = !wiresexposed
- to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]")
- update_icons()
-
- else if((W.tool_behaviour == TOOL_SCREWDRIVER) && opened && cell) // radio
- if(shell)
- to_chat(user, "You cannot seem to open the radio compartment") //Prevent AI radio key theft
- else if(radio)
- radio.attackby(W,user)//Push it to the radio to let it handle everything
- else
- to_chat(user, "Unable to locate a radio!")
- update_icons()
-
- else if(W.tool_behaviour == TOOL_WRENCH && opened && !cell) //Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
- if(!locked_down)
- to_chat(user, "[src]'s bolts spark! Maybe you should lock them down first!")
- spark_system.start()
- return
- else
- to_chat(user, "You start to unfasten [src]'s securing bolts...")
- if(W.use_tool(src, user, 50, volume=50) && !cell)
- user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!")
- deconstruct()
-
- else if(istype(W, /obj/item/aiModule))
+ if(istype(W, /obj/item/aiModule))
var/obj/item/aiModule/MOD = W
if(!opened)
- to_chat(user, "You need access to the robot's insides to do that!")
+ to_chat(user, span_warning("You need access to the robot's insides to do that!"))
return
if(wiresexposed)
- to_chat(user, "You need to close the wire panel to do that!")
+ to_chat(user, span_warning("You need to close the wire panel to do that!"))
return
if(!cell)
- to_chat(user, "You need to install a power cell to do that!")
+ to_chat(user, span_warning("You need to install a power cell to do that!"))
return
if(shell) //AI shells always have the laws of the AI
- to_chat(user, "[src] is controlled remotely! You cannot upload new laws this way!")
+ to_chat(user, span_warning("[src] is controlled remotely! You cannot upload new laws this way!"))
return
- if(emagged || (connected_ai && lawupdate)) //Can't be sure which, metagamers
- emote("buzz-[user.name]")
+ if(connected_ai && lawupdate)
+ to_chat(user, span_warning("[src] is receiving laws remotely from a synced AI!"))
+ return
+ if(emagged)
+ to_chat(user, span_warning("The law interface glitches out!"))
+ emote("buzz")
return
if(!mind) //A player mind is required for law procs to run antag checks.
- to_chat(user, "[src] is entirely unresponsive!")
+ to_chat(user, span_warning("[src] is entirely unresponsive!"))
return
MOD.install(laws, user) //Proc includes a success mesage so we don't need another one
return
- else if(istype(W, /obj/item/encryptionkey/) && opened)
+ if(istype(W, /obj/item/encryptionkey) && opened)
if(radio)//sanityyyyyy
radio.attackby(W,user)//GTFO, you have your own procs
else
- to_chat(user, "Unable to locate a radio!")
+ to_chat(user, span_warning("Unable to locate a radio!"))
+ return
- else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card
- if(emagged)//still allow them to open the cover
- to_chat(user, "The interface seems slightly damaged.")
+ if (W.GetID()) // trying to unlock the interface with an ID card
if(opened)
- to_chat(user, "You must close the cover to swipe an ID card!")
+ to_chat(user, span_warning("You must close the cover to swipe an ID card!"))
else
if(allowed(usr))
locked = !locked
- to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s cover.")
+ to_chat(user, span_notice("You [ locked ? "lock" : "unlock"] [src]'s cover."))
update_icons()
+ if(emagged)
+ to_chat(user, span_notice("The cover interface glitches out for a split second."))
+ logevent("ChÃ¥vÃis cover lock has been [locked ? "engaged" : "released"]") //ChÃ¥vÃis: see above line
+ else
+ logevent("Chassis cover lock has been [locked ? "engaged" : "released"]")
else
- to_chat(user, "Access denied.")
+ to_chat(user, span_danger("Access denied."))
+ return
- else if(istype(W, /obj/item/borg/upgrade/))
+ if(istype(W, /obj/item/borg/upgrade))
var/obj/item/borg/upgrade/U = W
if(!opened)
- to_chat(user, "You must access the borg's internals!")
- else if(!src.module && U.require_module)
- to_chat(user, "The borg must choose a module before it can be upgraded!")
- else if(U.locked)
- to_chat(user, "The upgrade is locked and cannot be used yet!")
- else
- if(!user.temporarilyRemoveItemFromInventory(U))
- return
- if(U.action(src))
- to_chat(user, "You apply the upgrade to [src].")
- if(U.one_use)
- U.afterInstall(src)
- qdel(U)
- else
- U.forceMove(src)
- upgrades += U
- U.afterInstall(src)
- else
- to_chat(user, "Upgrade error.")
- U.forceMove(drop_location())
+ to_chat(user, span_warning("You must access the cyborg's internals!"))
+ return
+ if(!module && U.require_module)
+ to_chat(user, span_warning("The cyborg must choose a model before it can be upgraded!"))
+ return
+ if(U.locked)
+ to_chat(user, span_warning("The upgrade is locked and cannot be used yet!"))
+ return
+ if(!user.canUnEquip(U))
+ to_chat(user, span_warning("The upgrade is stuck to you and you can't seem to let go of it!"))
+ return
+ apply_upgrade(U, user)
+ return
- else if(istype(W, /obj/item/toner))
+ if(istype(W, /obj/item/toner))
if(toner >= tonermax)
- to_chat(user, "The toner level of [src] is at its highest level possible!")
- else
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- toner = tonermax
- qdel(W)
- to_chat(user, "You fill the toner level of [src] to its max capacity.")
- else
- return ..()
+ to_chat(user, span_warning("The toner level of [src] is at its highest level possible!"))
+ return
+ if(!user.temporarilyRemoveItemFromInventory(W))
+ return
+ toner = tonermax
+ qdel(W)
+ to_chat(user, span_notice("You fill the toner level of [src] to its max capacity."))
+ return
+
+ if(istype(W, /obj/item/flashlight))
+ if(!opened)
+ to_chat(user, span_warning("You need to open the panel to repair the headlamp!"))
+ return
+ if(lamp_functional)
+ to_chat(user, span_warning("The headlamp is already functional!"))
+ return
+ if(!user.temporarilyRemoveItemFromInventory(W))
+ to_chat(user, span_warning("[W] seems to be stuck to your hand. You'll have to find a different light."))
+ return
+ lamp_functional = TRUE
+ qdel(W)
+ to_chat(user, span_notice("You replace the headlamp bulbs."))
+ return
+
+ return ..()
/mob/living/silicon/robot/crowbar_act(mob/living/user, obj/item/I) //TODO: make fucking everything up there in that attackby() proc use the proper tool_act() procs. But honestly, who has time for that? 'cause I know for sure that you, the person reading this, sure as hell doesn't.
var/validbreakout = FALSE
@@ -1064,33 +1020,49 @@
*Checking Exited() to detect if a hat gets up and walks off.
*Drones and pAIs might do this, after all.
*/
-/mob/living/silicon/robot/Exited(atom/A)
- if(hat && hat == A)
+/mob/living/silicon/robot/Exited(atom/movable/gone)
+ . = ..()
+ if(hat == gone)
hat = null
if(!QDELETED(src)) //Don't update icons if we are deleted.
update_icons()
- return ..()
-///Use this to add upgrades to robots. It'll register signals for when the upgrade is moved or deleted, if not single use.
-/mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade, mob/user)
+ if(gone == cell)
+ cell = null
+
+ if(gone == mmi)
+ mmi = null
+
+///Called when a mob uses an upgrade on an open borg. Checks to make sure the upgrade can be applied
+/mob/living/silicon/robot/proc/apply_upgrade(obj/item/borg/upgrade/new_upgrade, mob/user, admin_added)
+ if(!admin_added && isnull(user))
+ return FALSE
if(new_upgrade in upgrades)
return FALSE
- if(!user.temporarilyRemoveItemFromInventory(new_upgrade)) //calling the upgrade's dropped() proc /before/ we add action buttons
+ if(!admin_added && !user.temporarilyRemoveItemFromInventory(new_upgrade)) //calling the upgrade's dropped() proc /before/ we add action buttons
return FALSE
if(!new_upgrade.action(src, user))
- to_chat(user, "Upgrade error.")
- new_upgrade.forceMove(loc) //gets lost otherwise
+ to_chat(user, span_danger("Upgrade error."))
+ if(admin_added)
+ qdel(new_upgrade)
+ else
+ new_upgrade.forceMove(loc) //gets lost otherwise
return FALSE
- to_chat(user, "You apply the upgrade to [src].")
+ to_chat(user, span_notice("You apply the upgrade to [src]."))
+ add_to_upgrades(new_upgrade)
+ return TRUE
+
+///Moves the upgrade inside the robot and registers relevant signals.
+/mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade)
to_chat(src, "----------------\nNew hardware detected...Identified as \"[new_upgrade]\"...Setup complete.\n----------------")
if(new_upgrade.one_use)
logevent("Firmware [new_upgrade] run successfully.")
qdel(new_upgrade)
- return FALSE
+ return
upgrades += new_upgrade
new_upgrade.forceMove(src)
- RegisterSignal(new_upgrade, COMSIG_MOVABLE_MOVED, .proc/remove_from_upgrades)
- RegisterSignal(new_upgrade, COMSIG_PARENT_QDELETING, .proc/on_upgrade_deleted)
+ RegisterSignal(new_upgrade, COMSIG_MOVABLE_MOVED, PROC_REF(remove_from_upgrades))
+ RegisterSignal(new_upgrade, COMSIG_PARENT_QDELETING, PROC_REF(on_upgrade_deleted))
logevent("Hardware [new_upgrade] installed successfully.")
///Called when an upgrade is moved outside the robot. So don't call this directly, use forceMove etc.
@@ -1117,15 +1089,17 @@
* * board - B.O.R.I.S. module board used for transforming the cyborg into AI shell
*/
/mob/living/silicon/robot/proc/make_shell(obj/item/borg/upgrade/ai/board)
- if(!board)
- upgrades |= new /obj/item/borg/upgrade/ai(src)
+ if(isnull(board))
+ stack_trace("make_shell was called without a board argument! This is never supposed to happen!")
+ return FALSE
+
shell = TRUE
braintype = "AI Shell"
name = "Empty AI Shell-[ident]"
real_name = name
GLOB.available_ai_shells |= src
if(!QDELETED(builtInCamera))
- builtInCamera.c_tag = real_name //update the camera name too
+ builtInCamera.c_tag = real_name //update the camera name too
diag_hud_set_aishell()
notify_ai(AI_SHELL)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index d5a1058a61..f8217945db 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -94,6 +94,76 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
apply_status_effect(/datum/status_effect/vtec_disabled, time)
update_movespeed()
+/mob/living/silicon/robot/welder_act(mob/living/user, obj/item/tool)
+ if(user.a_intent == INTENT_HARM)
+ return FALSE
+ . = TRUE
+ if (!getBruteLoss())
+ to_chat(user, span_warning("[src] is already in good condition!"))
+ return
+ if (!tool.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
+ return
+ user.DelayNextAction(CLICK_CD_MELEE)
+ if(src == user)
+ to_chat(user, span_notice("You start fixing yourself..."))
+ if(!tool.use_tool(src, user, 50))
+ return
+ adjustBruteLoss(-10)
+ else
+ to_chat(user, span_notice("You start fixing [src]..."))
+ if(!do_after(user, 3 SECONDS, target = src))
+ return
+ adjustBruteLoss(-30)
+ updatehealth()
+ add_fingerprint(user)
+ visible_message(span_notice("[user] has fixed some of the dents on [src]."))
+
+/mob/living/silicon/robot/crowbar_act(mob/living/user, obj/item/tool)
+ . = TRUE
+ if(opened)
+ to_chat(user, span_notice("You close the cover."))
+ opened = FALSE
+ update_icons()
+ else
+ if(locked)
+ to_chat(user, span_warning("The cover is locked and cannot be opened!"))
+ else
+ to_chat(user, span_notice("You open the cover."))
+ opened = TRUE
+ update_icons()
+
+ return TRUE
+
+/mob/living/silicon/robot/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!opened)
+ return FALSE
+ . = TRUE
+ if(!cell) // haxing
+ wiresexposed = !wiresexposed
+ to_chat(user, span_notice("The wires have been [wiresexposed ? "exposed" : "unexposed"]."))
+ else // radio
+ if(shell)
+ to_chat(user, span_warning("You cannot seem to open the radio compartment!")) //Prevent AI radio key theft
+ else if(radio)
+ radio.screwdriver_act(user, tool) // Push it to the radio to let it handle everything
+ else
+ to_chat(user, span_warning("Unable to locate a radio!"))
+ update_icons()
+
+/mob/living/silicon/robot/wrench_act(mob/living/user, obj/item/tool)
+ if(!(opened && !cell)) // Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
+ return FALSE
+ . = TRUE
+ if(!locked_down)
+ to_chat(user, span_boldannounce("[src]'s bolts spark! Maybe you should lock them down first!"))
+ spark_system.start()
+ return
+ to_chat(user, span_notice("You start to unfasten [src]'s securing bolts..."))
+ if(tool.use_tool(src, user, 5 SECONDS, volume = 50) && !cell)
+ user.visible_message(span_notice("[user] deconstructs [src]!"), span_notice("You unfasten the securing bolts, and [src] falls to pieces!"))
+ deconstruct()
+ return
+
/mob/living/silicon/robot/fire_act()
if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them
IgniteMob()
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 5fd158c028..9c9ccd2b8d 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -57,25 +57,24 @@
. = ..()
if(max_mod_capacity)
. += "[get_remaining_mod_capacity()]% mod capacity remaining."
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
. += "There is \a [M] installed, using [M.cost]% capacity."
/obj/item/gun/energy/kinetic_accelerator/crowbar_act(mob/living/user, obj/item/I)
. = TRUE
if(modkits.len)
- to_chat(user, "You pry the modifications out.")
+ to_chat(user, span_notice("You pry all the modifications out."))
I.play_tool_sound(src, 100)
for(var/obj/item/borg/upgrade/modkit/M in modkits)
- M.uninstall(src)
+ M.forceMove(drop_location()) //uninstallation handled in Exited(), or /mob/living/silicon/robot/remove_from_upgrades() for borgs
else
- to_chat(user, "There are no modifications currently installed.")
+ to_chat(user, span_notice("There are no modifications currently installed."))
-/obj/item/gun/energy/kinetic_accelerator/Exited(atom/movable/AM)
- . = ..()
- if((AM in modkits) && istype(AM, /obj/item/borg/upgrade/modkit))
- var/obj/item/borg/upgrade/modkit/M = AM
- M.uninstall(src, FALSE)
+/obj/item/gun/energy/kinetic_accelerator/Exited(atom/movable/gone)
+ if(gone in modkits)
+ var/obj/item/borg/upgrade/modkit/MK = gone
+ MK.uninstall(src)
+ return ..()
/obj/item/gun/energy/kinetic_accelerator/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/borg/upgrade/modkit))
@@ -86,20 +85,13 @@
/obj/item/gun/energy/kinetic_accelerator/proc/get_remaining_mod_capacity()
var/current_capacity_used = 0
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
current_capacity_used += M.cost
return max_mod_capacity - current_capacity_used
-/obj/item/gun/energy/kinetic_accelerator/proc/get_modkits()
- . = list()
- for(var/A in modkits)
- . += A
-
/obj/item/gun/energy/kinetic_accelerator/proc/modify_projectile(obj/item/projectile/kinetic/K)
K.kinetic_gun = src //do something special on-hit, easy!
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
M.modify_projectile(K)
/obj/item/gun/energy/kinetic_accelerator/cyborg
@@ -259,7 +251,7 @@
if(!target_turf)
target_turf = get_turf(src)
if(kinetic_gun) //hopefully whoever shot this was not very, very unfortunate.
- var/list/mods = kinetic_gun.get_modkits()
+ var/list/mods = kinetic_gun.modkits
for(var/obj/item/borg/upgrade/modkit/M in mods)
M.projectile_strike_predamage(src, target_turf, target, kinetic_gun)
for(var/obj/item/borg/upgrade/modkit/M in mods)
@@ -278,7 +270,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "modkit"
w_class = WEIGHT_CLASS_SMALL
- require_module = 1
+ require_module = TRUE
module_type = list(/obj/item/robot_module/miner)
module_flags = BORG_MODULE_MINER
var/denied_type = null
@@ -298,26 +290,24 @@
else
..()
-/obj/item/borg/upgrade/modkit/afterInstall(mob/living/silicon/robot/R)
- for(var/obj/item/gun/energy/kinetic_accelerator/H in R.module.modules)
- if(install(H, R)) //It worked
- return
- to_chat(R, "Upgrade error - Aborting Kinetic Accelerator linking.") //No applicable KA found, insufficient capacity, or some other problem.
+/obj/item/borg/upgrade/modkit/action(mob/living/silicon/robot/R)
+ . = ..()
+ if (.)
+ for(var/obj/item/gun/energy/kinetic_accelerator/H in R.module.modules)
+ return install(H, usr, FALSE)
-/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
+/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user, transfer_to_loc = TRUE)
. = TRUE
- if(src in KA.modkits) // Sanity check to prevent installing the same modkit twice thanks to occasional click/lag delays.
- return FALSE
if(minebot_upgrade)
if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- to_chat(user, "The modkit you're trying to install is only rated for minebot use.")
+ to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use."))
return FALSE
else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- to_chat(user, "The modkit you're trying to install is not rated for minebot use.")
+ to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use."))
return FALSE
if(denied_type)
var/number_of_denied = 0
- for(var/A in KA.get_modkits())
+ for(var/A in KA.modkits)
var/obj/item/borg/upgrade/modkit/M = A
if(istype(M, denied_type))
number_of_denied++
@@ -326,21 +316,25 @@
break
if(KA.get_remaining_mod_capacity() >= cost)
if(.)
- if(!user.transferItemToLoc(src, KA))
- return FALSE
- to_chat(user, "You install the modkit.")
- playsound(loc, 'sound/items/screwdriver.ogg', 100, 1)
+ if(transfer_to_loc && !user.transferItemToLoc(src, KA))
+ return
+ to_chat(user, span_notice("You install the modkit."))
+ playsound(loc, 'sound/items/screwdriver.ogg', 100, TRUE)
KA.modkits += src
else
- to_chat(user, "The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits.")
+ to_chat(user, span_notice("The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits."))
else
- to_chat(user, "You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits.")
+ to_chat(user, span_notice("You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits."))
. = FALSE
-/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA, forcemove = TRUE)
+/obj/item/borg/upgrade/modkit/deactivate(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if (.)
+ for(var/obj/item/gun/energy/kinetic_accelerator/KA in R.module.modules)
+ uninstall(KA)
+
+/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
KA.modkits -= src
- if(forcemove)
- forceMove(get_turf(KA))
/obj/item/borg/upgrade/modkit/proc/modify_projectile(obj/item/projectile/kinetic/K)
@@ -467,6 +461,7 @@
/obj/item/borg/upgrade/modkit/minebot_passthrough
name = "minebot passthrough"
desc = "Causes kinetic accelerator shots to pass through minebots."
+ denied_type = /obj/item/borg/upgrade/modkit/minebot_passthrough // If you have something cost zero, you can keep adding forever, why
cost = 0
//Tendril-unique modules
diff --git a/tgui/packages/tgui/interfaces/BorgPanel.js b/tgui/packages/tgui/interfaces/BorgPanel.js
index 9e2d97db6e..2c422d9ac0 100644
--- a/tgui/packages/tgui/interfaces/BorgPanel.js
+++ b/tgui/packages/tgui/interfaces/BorgPanel.js
@@ -1,5 +1,5 @@
import { useBackend } from '../backend';
-import { Box, Button, LabeledList, ProgressBar, Section } from '../components';
+import { Box, Button, Icon, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
export const BorgPanel = (props, context) => {
@@ -12,6 +12,9 @@ export const BorgPanel = (props, context) => {
const upgrades = data.upgrades || [];
const ais = data.ais || [];
const laws = data.laws || [];
+
+ const active_upgrades = data.active_upgrades || [];
+ const ka_remaining_capacity = data.ka_remaining_capacity || 0;
return (
{
))}
- {upgrades.map(upgrade => (
-
+ {
+ ka_remaining_capacity !== null
+ && (
+
+ {ka_remaining_capacity}
+
+ )
+ }
{ais.map(ai => (
{
)}
{tab_sub === 2 && (
- {borgUpgrades.map(upgrade => (
-
- {upgrade}
-
- ))}
+ {borgUpgrades.filter((upgrade, index, arr) =>
+ arr.indexOf(upgrade) === index).map(upgrade => {
+ const upgradeCount = borgUpgrades.filter(u =>
+ u === upgrade).length;
+ return (
+
+ {upgrade} {upgradeCount > 1 ? `x${upgradeCount}` : ''}
+
+ );
+ })}
)}
{tab_sub === 3 && (