From 7c845d0ca021bdb632fb25b529906a827601719d Mon Sep 17 00:00:00 2001
From: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Date: Mon, 20 Jul 2020 20:39:32 -0500
Subject: [PATCH] Refactors some borg inventory code and removes a bunch of
unused references to borgs taking oxy/tox damage (#52265)
* Removing a bunch of oxygen stuff
* breaking works
* completely functional yes
* reverts some old changes
* thanks based shiz
* i said 'thanks' god damn it
* oops
* another one bites the dust
* they told me math was of variable difficulty
---
code/__DEFINES/robots.dm | 5 +
code/game/machinery/computer/aifixer.dm | 6 +-
.../mob/living/silicon/damage_procs.dm | 19 +-
.../mob/living/silicon/pai/pai_defense.dm | 33 --
.../mob/living/silicon/robot/inventory.dm | 464 ++++++++++++------
code/modules/mob/living/silicon/robot/life.dm | 2 -
.../modules/mob/living/silicon/robot/robot.dm | 47 +-
.../mob/living/silicon/robot/robot_defense.dm | 26 -
.../mob/living/silicon/robot/robot_modules.dm | 8 +-
.../mob/living/silicon/silicon_defense.dm | 8 -
.../file_system/programs/airestorer.dm | 6 +-
icons/mob/screen_cyborg.dmi | Bin 26100 -> 26473 bytes
12 files changed, 378 insertions(+), 246 deletions(-)
diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm
index 495d47129c3..87e0be05820 100644
--- a/code/__DEFINES/robots.dm
+++ b/code/__DEFINES/robots.dm
@@ -55,3 +55,8 @@
#define ASSEMBLY_FIFTH_STEP 4
#define BORG_LAMP_CD_RESET -1 //special value to reset cyborg's lamp_cooldown
+
+/// Defines for whether or not module slots are broken.
+#define BORG_MODULE_ALL_DISABLED (1<<0)
+#define BORG_MODULE_TWO_DISABLED (1<<1)
+#define BORG_MODULE_THREE_DISABLED (1<<2)
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index 5de7edc5410..0c1c96a4d53 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -62,11 +62,9 @@
/obj/machinery/computer/aifixer/proc/Fix()
use_power(1000)
- occupier.adjustOxyLoss(-5, 0)
- occupier.adjustFireLoss(-5, 0)
- occupier.adjustToxLoss(-5, 0)
+ occupier.adjustOxyLoss(-5, 0, FALSE)
+ occupier.adjustFireLoss(-5, 0, FALSE)
occupier.adjustBruteLoss(-5, 0)
- occupier.updatehealth()
if(occupier.health >= 0 && occupier.stat == DEAD)
occupier.revive(full_heal = FALSE, admin_revive = FALSE)
if(!occupier.radio_enabled)
diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm
index 15bb5f2f1fa..3f0ce34d971 100644
--- a/code/modules/mob/living/silicon/damage_procs.dm
+++ b/code/modules/mob/living/silicon/damage_procs.dm
@@ -9,9 +9,6 @@
adjustBruteLoss(damage_amount, forced = forced)
if(BURN)
adjustFireLoss(damage_amount, forced = forced)
- if(OXY)
- if(damage < 0 || forced) //we shouldn't be taking oxygen damage through this proc, but we'll let it heal.
- adjustOxyLoss(damage_amount, forced = forced)
return 1
@@ -30,14 +27,26 @@
/mob/living/silicon/setCloneLoss(amount, updating_health = TRUE, forced = FALSE)
return FALSE
-/mob/living/silicon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE)//immune to stamina damage.
+/mob/living/silicon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE) //immune to stamina damage.
return FALSE
/mob/living/silicon/setStaminaLoss(amount, updating_health = TRUE)
return FALSE
-/mob/living/silicon/adjustOrganLoss(slot, amount, maximum = 500)
+/mob/living/silicon/adjustOrganLoss(slot, amount, maximum = 500) //immune to organ damage (no organs, duh)
return FALSE
/mob/living/silicon/setOrganLoss(slot, amount)
return FALSE
+
+/mob/living/silicon/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE) //immune to oxygen damage
+ if(istype(src, /mob/living/silicon/ai)) //ais are snowflakes and use oxyloss for being in AI cards and having no battery
+ return ..()
+
+ return FALSE
+
+/mob/living/silicon/setOxyLoss(amount, updating_health = TRUE, forced = FALSE)
+ if(istype(src, /mob/living/silicon/ai)) //ditto
+ return ..()
+
+ return FALSE
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 3272173da24..e21b73ad1cd 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -86,15 +86,6 @@
/mob/living/silicon/pai/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
return take_holo_damage(amount)
-/mob/living/silicon/pai/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE)
- return FALSE
-
-/mob/living/silicon/pai/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE)
- return FALSE
-
-/mob/living/silicon/pai/adjustCloneLoss(amount, updating_health = TRUE, forced = FALSE)
- return FALSE
-
/mob/living/silicon/pai/adjustStaminaLoss(amount, updating_health, forced = FALSE)
if(forced)
take_holo_damage(amount)
@@ -106,27 +97,3 @@
/mob/living/silicon/pai/getFireLoss()
return emittermaxhealth - emitterhealth
-
-/mob/living/silicon/pai/getToxLoss()
- return FALSE
-
-/mob/living/silicon/pai/getOxyLoss()
- return FALSE
-
-/mob/living/silicon/pai/getCloneLoss()
- return FALSE
-
-/mob/living/silicon/pai/getStaminaLoss()
- return FALSE
-
-/mob/living/silicon/pai/setCloneLoss()
- return FALSE
-
-/mob/living/silicon/pai/setStaminaLoss(amount, updating_health = TRUE)
- return FALSE
-
-/mob/living/silicon/pai/setToxLoss()
- return FALSE
-
-/mob/living/silicon/pai/setOxyLoss()
- return FALSE
diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm
index 60ac3f975cd..e7bed84146c 100644
--- a/code/modules/mob/living/silicon/robot/inventory.dm
+++ b/code/modules/mob/living/silicon/robot/inventory.dm
@@ -1,215 +1,387 @@
//These procs handle putting stuff in your hand. It's probably best to use these rather than setting stuff manually
//as they handle all relevant stuff like adding it to the player's screen and such
-//Returns the thing in our active hand (whatever is in our active module-slot, in this case)
+/**
+ * Returns the thing in our active hand (whatever is in our active module-slot, in this case)
+ */
/mob/living/silicon/robot/get_active_held_item()
return module_active
+/**
+ * Parent proc - triggers when an item/module is unequipped from a cyborg.
+ */
/obj/item/proc/cyborg_unequip(mob/user)
return
-/mob/living/silicon/robot/proc/uneq_module(obj/item/O)
- if(!O)
- return 0
- O.mouse_opacity = MOUSE_OPACITY_OPAQUE
- if(istype(O, /obj/item/borg/sight))
- var/obj/item/borg/sight/S = O
- sight_mode &= ~S.sight_mode
- update_sight()
- else if(istype(O, /obj/item/storage/bag/tray/))
- SEND_SIGNAL(O, COMSIG_TRY_STORAGE_QUICK_EMPTY)
- if(client)
- client.screen -= O
- observer_screen_update(O,FALSE)
+/**
+ * Finds the first available slot and attemps to put item item_module in it.
+ *
+ * Arguments
+ * * item_module - the item being equipped to a slot.
+ */
+/mob/living/silicon/robot/proc/activate_module(obj/item/item_module)
+ if(QDELETED(item_module))
+ CRASH("activate_module called with improper item_module")
- if(module_active == O)
- module_active = null
- if(held_items[1] == O)
- inv1.icon_state = "inv1"
- held_items[1] = null
- else if(held_items[2] == O)
- inv2.icon_state = "inv2"
- held_items[2] = null
- else if(held_items[3] == O)
- inv3.icon_state = "inv3"
- held_items[3] = null
+ if(!(item_module in module.modules))
+ CRASH("activate_module called with item_module not in module.modules")
- if(O.item_flags & DROPDEL)
- O.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly
-
- O.forceMove(module) //Return item to module so it appears in its contents, so it can be taken out again.
- O.cyborg_unequip(src)
-
- hud_used.update_robot_modules_display()
- return 1
-
-/mob/living/silicon/robot/proc/activate_module(obj/item/O)
- . = FALSE
- if(!(O in module.modules))
- return
- if(activated(O))
+ if(activated(item_module))
to_chat(src, "That module is already activated.")
- return
- if(!held_items[1])
- held_items[1] = O
- O.screen_loc = inv1.screen_loc
- . = TRUE
- else if(!held_items[2])
- held_items[2] = O
- O.screen_loc = inv2.screen_loc
- . = TRUE
- else if(!held_items[3])
- held_items[3] = O
- O.screen_loc = inv3.screen_loc
- . = TRUE
- else
- to_chat(src, "You need to disable a module first!")
- if(.)
- O.equipped(src, ITEM_SLOT_HANDS)
- O.mouse_opacity = initial(O.mouse_opacity)
- O.layer = ABOVE_HUD_LAYER
- O.plane = ABOVE_HUD_PLANE
- observer_screen_update(O,TRUE)
- O.forceMove(src)
- if(istype(O, /obj/item/borg/sight))
- var/obj/item/borg/sight/S = O
- sight_mode |= S.sight_mode
- update_sight()
+ return FALSE
+
+ if(disabled_modules & BORG_MODULE_ALL_DISABLED)
+ to_chat(src, "All modules are disabled!")
+ return FALSE
+
+ /// What's the first free slot for the borg?
+ var/first_free_slot = !held_items[1] ? 1 : (!held_items[2] ? 2 : (!held_items[3] ? 3 : null))
+
+ if(!first_free_slot || is_invalid_module_number(first_free_slot))
+ to_chat(src, "Deactivate a module first!")
+ return FALSE
+
+ return equip_module_to_slot(item_module, first_free_slot)
+
+/**
+ * Is passed an item and a module slot. Equips the item to that borg slot.
+ *
+ * Arguments
+ * * item_module - the item being equipped to a slot
+ * * module_num - the slot number being equipped to.
+ */
+/mob/living/silicon/robot/proc/equip_module_to_slot(obj/item/item_module, module_num)
+ switch(module_num)
+ if(1)
+ item_module.screen_loc = inv1.screen_loc
+ if(2)
+ item_module.screen_loc = inv2.screen_loc
+ if(3)
+ item_module.screen_loc = inv3.screen_loc
+
+ held_items[module_num] = item_module
+ item_module.equipped(src, ITEM_SLOT_HANDS)
+ item_module.mouse_opacity = initial(item_module.mouse_opacity)
+ item_module.layer = ABOVE_HUD_LAYER
+ item_module.plane = ABOVE_HUD_PLANE
+ item_module.forceMove(src)
+
+ if(istype(item_module, /obj/item/borg/sight))
+ var/obj/item/borg/sight/borg_sight = item_module
+ sight_mode |= borg_sight.sight_mode
+ update_sight()
+
+ observer_screen_update(item_module, TRUE)
+ return TRUE
+
+/**
+ * Unequips item item_module from slot module_num. Deletes it if delete_after = TRUE.
+ *
+ * Arguments
+ * * item_module - the item being unequipped
+ * * module_num - the slot number being unequipped.
+ */
+/mob/living/silicon/robot/proc/unequip_module_from_slot(obj/item/item_module, module_num)
+ if(QDELETED(item_module))
+ CRASH("unequip_module_from_slot called with improper item_module")
+
+ if(!(item_module in module.modules))
+ CRASH("unequip_module_from_slot called with item_module not in module.modules")
+
+ item_module.mouse_opacity = MOUSE_OPACITY_OPAQUE
+
+ if(istype(item_module, /obj/item/storage/bag/tray/))
+ SEND_SIGNAL(item_module, COMSIG_TRY_STORAGE_QUICK_EMPTY)
+ if(istype(item_module, /obj/item/borg/sight))
+ var/obj/item/borg/sight/borg_sight = item_module
+ sight_mode &= ~borg_sight.sight_mode
+ update_sight()
+
+ if(client)
+ client.screen -= item_module
+
+ if(module_active == item_module)
+ module_active = null
+
+ switch(module_num)
+ if(1)
+ if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
+ inv1.icon_state = initial(inv1.icon_state)
+ if(2)
+ if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
+ inv2.icon_state = initial(inv2.icon_state)
+ if(3)
+ if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
+ inv3.icon_state = initial(inv3.icon_state)
+
+ if(item_module.item_flags & DROPDEL)
+ item_module.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly
+
+ held_items[module_num] = null
+ item_module.cyborg_unequip(src)
+ item_module.forceMove(module) //Return item to module so it appears in its contents, so it can be taken out again.
+
+ observer_screen_update(item_module, FALSE)
+ hud_used.update_robot_modules_display()
+ return TRUE
+
+/**
+ * Breaks the slot number, changing the icon.
+ *
+ * Arguments
+ * * module_num - the slot number being repaired.
+ */
+/mob/living/silicon/robot/proc/break_cyborg_slot(module_num)
+ if(is_invalid_module_number(module_num, TRUE))
+ return FALSE
+
+ if(held_items[module_num]) //If there's a held item, unequip it first.
+ if(!unequip_module_from_slot(held_items[module_num], module_num)) //If we fail to unequip it, then don't continue
+ return FALSE
+
+ switch(module_num)
+ if(1)
+ if(disabled_modules & BORG_MODULE_ALL_DISABLED)
+ return FALSE
+
+ inv1.icon_state = "[initial(inv1.icon_state)] +b"
+ disabled_modules |= BORG_MODULE_ALL_DISABLED
+
+ playsound(src, 'sound/machines/warning-buzzer.ogg', 75, TRUE, TRUE)
+ audible_message("[src] sounds an alarm! \"CRITICAL ERROR: ALL modules OFFLINE.\"")
+ to_chat(src, "CRITICAL ERROR: ALL modules OFFLINE.")
+
+ if(2)
+ if(disabled_modules & BORG_MODULE_TWO_DISABLED)
+ return FALSE
+
+ inv2.icon_state = "[initial(inv2.icon_state)] +b"
+ disabled_modules |= BORG_MODULE_TWO_DISABLED
+
+ playsound(src, 'sound/machines/warning-buzzer.ogg', 60, TRUE, TRUE)
+ audible_message("[src] sounds an alarm! \"SYSTEM ERROR: Module [module_num] OFFLINE.\"")
+ to_chat(src, "SYSTEM ERROR: Module [module_num] OFFLINE.")
+
+ if(3)
+ if(disabled_modules & BORG_MODULE_THREE_DISABLED)
+ return FALSE
+
+ inv3.icon_state = "[initial(inv3.icon_state)] +b"
+ disabled_modules |= BORG_MODULE_THREE_DISABLED
+
+ playsound(src, 'sound/machines/warning-buzzer.ogg', 50, TRUE, TRUE)
+ audible_message("[src] sounds an alarm! \"SYSTEM ERROR: Module [module_num] OFFLINE.\"")
+ to_chat(src, "SYSTEM ERROR: Module [module_num] OFFLINE.")
+
+ return TRUE
-/mob/living/silicon/robot/proc/observer_screen_update(obj/item/I,add = TRUE)
+/**
+ * Breaks all of a cyborg's slots.
+ */
+/mob/living/silicon/robot/proc/break_all_cyborg_slots()
+ for(var/cyborg_slot in 1 to 3)
+ break_cyborg_slot(cyborg_slot)
+
+/**
+ * Repairs the slot number, updating the icon.
+ *
+ * Arguments
+ * * module_num - the module number being repaired.
+ */
+/mob/living/silicon/robot/proc/repair_cyborg_slot(module_num)
+ if(is_invalid_module_number(module_num, TRUE))
+ return FALSE
+
+ switch(module_num)
+ if(1)
+ if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
+ return FALSE
+
+ inv1.icon_state = initial(inv1.icon_state)
+ disabled_modules &= ~BORG_MODULE_ALL_DISABLED
+ if(2)
+ if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
+ return FALSE
+
+ inv2.icon_state = initial(inv2.icon_state)
+ disabled_modules &= ~BORG_MODULE_TWO_DISABLED
+ if(3)
+ if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
+ return FALSE
+
+ inv3.icon_state = initial(inv3.icon_state)
+ disabled_modules &= ~BORG_MODULE_THREE_DISABLED
+
+ to_chat(src, "ERROR CLEARED: Module [module_num] back online.")
+
+ return TRUE
+
+/**
+ * Repairs all slots. Unbroken slots are unaffected.
+ */
+/mob/living/silicon/robot/proc/repair_all_cyborg_slots()
+ for(var/cyborg_slot in 1 to 3)
+ repair_cyborg_slot(cyborg_slot)
+
+/**
+ * Updates the observers's screens with cyborg itemss.
+ * Arguments
+ * * item_module - the item being added or removed from the screen
+ * * add - whether or not the item is being added, or removed.
+ */
+/mob/living/silicon/robot/proc/observer_screen_update(obj/item/item_module, add = TRUE)
if(observers && observers.len)
for(var/M in observers)
var/mob/dead/observe = M
if(observe.client && observe.client.eye == src)
if(add)
- observe.client.screen += I
+ observe.client.screen += item_module
else
- observe.client.screen -= I
+ observe.client.screen -= item_module
else
observers -= observe
if(!observers.len)
observers = null
break
+/**
+ * Unequips the active held item.
+ */
/mob/living/silicon/robot/proc/uneq_active()
- uneq_module(module_active)
+ unequip_module_from_slot(module_active, get_selected_module())
+/**
+ * Unequips all held items.
+ */
/mob/living/silicon/robot/proc/uneq_all()
- for(var/obj/item/I in held_items)
- uneq_module(I)
+ for(var/cyborg_slot in 1 to 3)
+ if(!held_items[cyborg_slot])
+ continue
+ unequip_module_from_slot(held_items[cyborg_slot], cyborg_slot)
-/mob/living/silicon/robot/proc/activated(obj/item/O)
- if(O in held_items)
+/**
+ * Checks if the item is currently in a slot.
+ *
+ * If the item is found in a slot, this returns TRUE. Otherwise, it returns FALSE
+ * Arguments
+ * * item_module - the item being checked
+ */
+/mob/living/silicon/robot/proc/activated(obj/item/item_module)
+ if(item_module in held_items)
return TRUE
return FALSE
-//Helper procs for cyborg modules on the UI.
-//These are hackish but they help clean up code elsewhere.
+/**
+ * Checks if the provided module number is a valid number.
+ *
+ * If the number is between 1 and 3 (if check_all_slots is true) or between 1 and the number of disabled
+ * modules (if check_all_slots is false), then it returns FALSE. Otherwise, it returns TRUE.
+ * Arguments
+ * * module_num - the passed module num that is checked for validity.
+ * * check_all_slots - TRUE = the proc checks all slots | FALSE = the proc only checks un-disabled slots
+ */
+/mob/living/silicon/robot/proc/is_invalid_module_number(module_num, check_all_slots = FALSE)
+ if(!module_num)
+ return TRUE
-//module_selected(module) - Checks whether the module slot specified by "module" is currently selected.
-/mob/living/silicon/robot/proc/module_selected(module) //Module is 1-3
- return module == get_selected_module()
+ /// The number of module slots we're checking
+ var/max_number = 3
+ if(!check_all_slots)
+ if(disabled_modules & BORG_MODULE_ALL_DISABLED)
+ max_number = 0
+ else if(disabled_modules & BORG_MODULE_TWO_DISABLED)
+ max_number = 1
+ else if(disabled_modules & BORG_MODULE_THREE_DISABLED)
+ max_number = 2
-//module_active(module) - Checks whether there is a module active in the slot specified by "module".
-/mob/living/silicon/robot/proc/module_active(module) //Module is 1-3
- if(module < 1 || module > 3)
- return FALSE
+ return module_num < 1 || module_num > max_number
- if(LAZYLEN(held_items) >= module)
- if(held_items[module])
- return TRUE
- return FALSE
-
-//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected.
+/**
+ * Returns the slot number of the selected module, or zero if no modules are selected.
+ */
/mob/living/silicon/robot/proc/get_selected_module()
if(module_active)
return held_items.Find(module_active)
return 0
-//select_module(module) - Selects the module slot specified by "module"
-/mob/living/silicon/robot/proc/select_module(module) //Module is 1-3
- if(module < 1 || module > 3)
- return
+/**
+ * Selects the module in the slot module_num.
+ * Arguments
+ * * module_num - the slot number being selected
+ */
+/mob/living/silicon/robot/proc/select_module(module_num)
+ if(!held_items[module_num])
+ return FALSE
- if(!module_active(module))
- return
-
- switch(module)
+ switch(module_num)
if(1)
- if(module_active != held_items[module])
- inv1.icon_state = "inv1 +a"
- inv2.icon_state = "inv2"
- inv3.icon_state = "inv3"
+ if(module_active != held_items[module_num])
+ inv1.icon_state = "[initial(inv1.icon_state)] +a"
if(2)
- if(module_active != held_items[module])
- inv1.icon_state = "inv1"
- inv2.icon_state = "inv2 +a"
- inv3.icon_state = "inv3"
+ if(module_active != held_items[module_num])
+ inv2.icon_state = "[initial(inv2.icon_state)] +a"
if(3)
- if(module_active != held_items[module])
- inv1.icon_state = "inv1"
- inv2.icon_state = "inv2"
- inv3.icon_state = "inv3 +a"
- module_active = held_items[module]
+ if(module_active != held_items[module_num])
+ inv3.icon_state = "[initial(inv3.icon_state)] +a"
+ module_active = held_items[module_num]
-//deselect_module(module) - Deselects the module slot specified by "module"
-/mob/living/silicon/robot/proc/deselect_module(module) //Module is 1-3
- if(module < 1 || module > 3)
- return
-
- if(!module_active(module))
- return
-
- switch(module)
+/**
+ * Deselects the module in the slot module_num.
+ * Arguments
+ * * module_num - the slot number being de-selected
+ */
+/mob/living/silicon/robot/proc/deselect_module(module_num)
+ switch(module_num)
if(1)
- if(module_active == held_items[module])
- inv1.icon_state = "inv1"
+ if(module_active == held_items[module_num])
+ inv1.icon_state = initial(inv1.icon_state)
if(2)
- if(module_active == held_items[module])
- inv2.icon_state = "inv2"
+ if(module_active == held_items[module_num])
+ inv2.icon_state = initial(inv2.icon_state)
if(3)
- if(module_active == held_items[module])
- inv3.icon_state = "inv3"
+ if(module_active == held_items[module_num])
+ inv3.icon_state = initial(inv3.icon_state)
module_active = null
-//toggle_module(module) - Toggles the selection of the module slot specified by "module".
-/mob/living/silicon/robot/proc/toggle_module(module) //Module is 1-3
- if(module < 1 || module > 3)
- return
+/**
+ * Toggles selection of the module in the slot module_num.
+ * Arguments
+ * * module_num - the slot number being toggled
+ */
+/mob/living/silicon/robot/proc/toggle_module(module_num)
+ if(is_invalid_module_number(module_num))
+ return FALSE
- if(module_selected(module))
- deselect_module(module)
- else
- if(module_active(module))
- select_module(module)
- else
- deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module.
- return
+ if(module_num == get_selected_module())
+ deselect_module(module_num)
+ return TRUE
-//cycle_modules() - Cycles through the list of selected modules.
+ if(module_active != held_items[module_num])
+ deselect_module(get_selected_module())
+
+ return select_module(module_num)
+
+/**
+ * Cycles through the list of enabled modules, deselecting the current one and selecting the next one.
+ */
/mob/living/silicon/robot/proc/cycle_modules()
var/slot_start = get_selected_module()
+ var/slot_num
if(slot_start)
deselect_module(slot_start) //Only deselect if we have a selected slot.
-
- var/slot_num
- if(slot_start == 0)
+ slot_num = slot_start + 1
+ else
slot_num = 1
slot_start = 4
- else
- slot_num = slot_start + 1
while(slot_num != slot_start) //If we wrap around without finding any free slots, just give up.
- if(module_active(slot_num))
- select_module(slot_num)
+ if(select_module(slot_num))
return
slot_num++
if(slot_num > 4) // not >3 otherwise cycling with just one item on module 3 wouldn't work
slot_num = 1 //Wrap around.
-
-
/mob/living/silicon/robot/swap_hand()
cycle_modules()
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index 910a2a6fea0..afad167f231 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -4,7 +4,6 @@
return
..()
- adjustOxyLoss(-10) //we're a robot!
handle_robot_hud_updates()
handle_robot_cell()
@@ -100,4 +99,3 @@
mobility_flags = MOBILITY_FLAGS_DEFAULT
update_transform()
update_action_buttons_icon()
-
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 57fa699c111..9ee28f6aeea 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -24,6 +24,8 @@
var/mob/living/silicon/ai/mainframe = null
var/datum/action/innate/undeployment/undeployment_action = new
+ /// the last health before updating - to check net change in health
+ var/previous_health
//Hud stuff
var/obj/screen/inv1 = null
@@ -41,6 +43,9 @@
var/obj/item/module_active = null
held_items = list(null, null, null) //we use held_items for the module holding, because that makes sense to do!
+ /// For checking which modules are disabled or not.
+ var/disabled_modules
+
var/mutable_appearance/eye_lights
var/mob/living/silicon/ai/connected_ai = null
@@ -110,6 +115,8 @@
ident = rand(1, 999)
+ previous_health = health
+
if(ispath(cell))
cell = new cell(src)
@@ -220,7 +227,6 @@
module.transform_to(modulelist[input_module])
-
/mob/living/silicon/robot/proc/updatename(client/C)
if(shell)
return
@@ -705,21 +711,30 @@
/mob/living/silicon/robot/updatehealth()
..()
- if(health < maxHealth*0.5) //Gradual break down of modules as more damage is sustained
- if(uneq_module(held_items[3]))
- playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, TRUE, TRUE)
- audible_message("[src] sounds an alarm! \"SYSTEM ERROR: Module 3 OFFLINE.\"")
- to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.")
- if(health < 0)
- if(uneq_module(held_items[2]))
- audible_message("[src] sounds an alarm! \"SYSTEM ERROR: Module 2 OFFLINE.\"")
- to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.")
- playsound(loc, 'sound/machines/warning-buzzer.ogg', 60, TRUE, TRUE)
- if(health < -maxHealth*0.5)
- if(uneq_module(held_items[1]))
- audible_message("[src] sounds an alarm! \"CRITICAL ERROR: All modules OFFLINE.\"")
- to_chat(src, "CRITICAL ERROR: All modules OFFLINE.")
- playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, TRUE, TRUE)
+
+ /// the current percent health of the robot (-1 to 1)
+ var/percent_hp = health/maxHealth
+ if(health <= previous_health) //if change in health is negative (we're losing hp)
+ if(percent_hp <= 0.5)
+ break_cyborg_slot(3)
+
+ if(percent_hp <= 0)
+ break_cyborg_slot(2)
+
+ if(percent_hp <= -0.5)
+ break_cyborg_slot(1)
+
+ else //if change in health is positive (we're gaining hp)
+ if(percent_hp >= 0.5)
+ repair_cyborg_slot(3)
+
+ if(percent_hp >= 0)
+ repair_cyborg_slot(2)
+
+ if(percent_hp >= -0.5)
+ repair_cyborg_slot(1)
+
+ previous_health = health
/mob/living/silicon/robot/update_sight()
if(!client)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 358464b81be..bc872ba0608 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -18,7 +18,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
return
adjustBruteLoss(-30)
- updatehealth()
add_fingerprint(user)
visible_message("[user] fixes some of the dents on [src].")
return
@@ -33,8 +32,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
return
if (coil.use(1))
adjustFireLoss(-30)
- adjustToxLoss(-30)
- updatehealth()
user.visible_message("[user] fixes some of the burnt wires on [src].", "You fix some of the burnt wires on [src].")
else
to_chat(user, "You need more cable to repair [src]!")
@@ -268,7 +265,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
damage = rand(5, 35)
damage = round(damage / 2) // borgs receive half damage
adjustBruteLoss(damage)
- updatehealth()
return
@@ -399,25 +395,3 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
updatehealth()
if(prob(75) && Proj.damage > 0)
spark_system.start()
-
-
-/mob/living/silicon/robot/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE)
- . = ..()
- if(isnull(.))
- return
- if(. <= (maxHealth * 0.5))
- if(getOxyLoss() > (maxHealth * 0.5))
- ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
- else if(getOxyLoss() <= (maxHealth * 0.5))
- REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
-
-
-/mob/living/silicon/robot/setOxyLoss(amount, updating_health = TRUE, forced = FALSE)
- . = ..()
- if(isnull(.))
- return
- if(. <= (maxHealth * 0.5))
- if(getOxyLoss() > (maxHealth * 0.5))
- ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
- else if(getOxyLoss() <= (maxHealth * 0.5))
- REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index a9cbaf00e81..3a848165d13 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -161,7 +161,8 @@
/obj/item/robot_module/proc/rebuild_modules() //builds the usable module list from the modules we have
var/mob/living/silicon/robot/R = loc
- var/held_modules = R.held_items.Copy()
+ var/list/held_modules = R.held_items.Copy()
+ var/active_module = R.module_active
R.uneq_all()
modules = list()
for(var/obj/item/I in basic_modules)
@@ -173,7 +174,9 @@
add_module(I, FALSE, FALSE)
for(var/i in held_modules)
if(i)
- R.activate_module(i)
+ R.equip_module_to_slot(i, held_modules.Find(i))
+ if(active_module)
+ R.select_module(held_modules.Find(active_module))
if(R.hud_used)
R.hud_used.update_robot_modules_display()
@@ -225,6 +228,7 @@
R.setDir(SOUTH)
R.anchored = FALSE
R.notransform = FALSE
+ R.updatehealth()
R.update_headlamp(FALSE, BORG_LAMP_CD_RESET)
R.notify_ai(NEW_MODULE)
if(R.hud_used)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 13c4364f73c..5068397f9cd 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -41,14 +41,6 @@
adjustBruteLoss(damage)
if(BURN)
adjustFireLoss(damage)
- if(TOX)
- adjustToxLoss(damage)
- if(OXY)
- adjustOxyLoss(damage)
- if(CLONE)
- adjustCloneLoss(damage)
- if(STAMINA)
- adjustStaminaLoss(damage)
/mob/living/silicon/attack_paw(mob/living/user)
return attack_hand(user)
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index c2a1aa4ca75..c1bb40d1b89 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -72,11 +72,9 @@
restoring = FALSE
return
ai_slot.locked =TRUE
- A.adjustOxyLoss(-5, 0)
- A.adjustFireLoss(-5, 0)
- A.adjustToxLoss(-5, 0)
+ A.adjustOxyLoss(-5, 0, FALSE)
+ A.adjustFireLoss(-5, 0, FALSE)
A.adjustBruteLoss(-5, 0)
- A.updatehealth()
if(A.health >= 0 && A.stat == DEAD)
A.revive(full_heal = FALSE, admin_revive = FALSE)
// Finished restoring
diff --git a/icons/mob/screen_cyborg.dmi b/icons/mob/screen_cyborg.dmi
index 2f87e9796f140fe5091724daa2616f14c02115df..c0c112bdeb952733503d11f22e6fe3b590d3c039 100644
GIT binary patch
delta 4772
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
ObM}n2S*fWj@&5p;e^kBz
delta 4380
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
P&~xXufo7$Kec1m2TX3(d