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 zcmZuzc{o(>-#%j(SxdH(Xvh*F>YF7JlC^Bvlclm#WScYAY-N`TV@vk6LTF~JBl}Xu z5(Z_7?CY2r@2Tth{r8?f&h?z@Joo*4*8B4;_rrF2V2Przn52uB!Sl4JQLt8DB9L_1 zNL@O{jjkJ>gw7G;Ud$8*v$4`LJmatb4wYbBYI56lW8 zXsa5!ZX;^kl8c4JKp&?I=Au3&?y*W=U`1Sx!&<__?#)ue?&Py~>Nle~XuP*?;obdkOe~N8i_H(+T%B+*$E1e_^KGf)ZeZ7@V zJw1qg2g)XKU;0r$bg;$^kEL^(ueqo`{vuJwNb%edSLz=W_i^!}sBU|Y-QB?)V~N~a zE{$MJ@{pfQNNYUhVo);4;WFD{@YVSbzrrX$CF@-aUHlBw2OO-?1jOWdrv?ZfzrV^= z3HDq1*K|&x%`(eO9nvE;-KH>!8a-ca@&@N^>D0@2IzpLKeSD&2l-s$&F~z^GF&(uA zal0i#7z0+`p<-%0S2e1*Dl=%+!0r(*a88{j#wXd&KP*JdW25a!m3i= zEefw_`IVB33SWBrOS8{_rF|Bv=ZqqyZYZe%!A{EYf)y1N8593GnPlbAw5hc1Dfz)( z(BK&Ftug~6UM{UdIPTeJyJ0UDv;8+Z1w4rheVG|=-`0Y2`Svk|;$HoKXpD`V&>%HH zP&Ih$sC=nNZ59z){LJvX(#e)E&a_Hv&X-^37kkpYGRke5cL=*PlOsx;+Cw4_=q2!wwpP$rMpbxR}_h z*pXhOd)fx5fUJj=(^s=;MMRv`fL&B?p}1y#ZcNycDY%h^-`Ez;&7q1OIfXqw+P(WtpTpSu zaLXwdZ@p~1>P4sZ{)h?50|?O^(ELVon0nw~s@knFVG53wH(2s-nr`sJpj-{f)Dp!d z=87e4hp0iN(hUOM5~Edi37(iktuNkhKL21KvR$Gw#rN_W?MD?OYias_`ItRSVM#Fu z)yqvbKGrEtVi13AIP}z$YN-;e$(6JjFWF)o+Rozv2h%E7Q?<(;*z{#ZPqPCX^6|aq zgK+ozyHQ5xv(S9irVYK%UDSWj6L0%c(zX|EPJ=4+IS5k95|;W|CGQ07IO^H)n>P|` zzt{FzUhI1r{litB7#puFl!yi|aD%rcQk(RO%v*f@CO5+%;KDKG?109t2E&5%>^u8a zq`nV0Q+Di+^?l`dHv&t|RGD3Mgjb^kac|Er>$-2JnSGt-jE7`2|7s*VU~6(p+0^c9 z6_(oAdKyZ1Ek)G1T>W~P4O_u}o9F4HY+5e{Ezf9d#$6%t%=150!Plj&OsOD(S+iyP zckcA?aM&vWrMab{0#n0S$dpyFE9#C|&w?L#^PS*C`G=b}WINwO#bBovWP&Y6X{)#k zot$mk6)Q_i0B{{G_x|z{L0T58(dLLpE%9Ri%|g#5 z-T5D;M;||aw3q?OpRL(~_SY&%5YVtTr#<=!uRC^RnDkVBfm}aPlS9Uw7?UIF!D;nJ zN~wCK3g|DE0_KINXs8gsBDmx=8Ggk3sLimwsD~Nix{~W#9%D9=V3Tl1!>!=*oe`l( zJH0N<-tZ$~ef;BKW+pK}bP7Dq&M8@>U8AWM_@xL=%K*<>GgxS37r;b2_BP(sA!iK! zQAAco>eN-?qi}ZBhTOPJ^j?y$Ir48#&GqrT3g6#{>m}QvUk`Wk^Nz3k%1(rKD08IA z5&7^z%3k`~0S6nihjC|Q92_0r?Uj{;LjBmhGkkYj21~A$X35K;z9SW&Pjolg5{*l zIfQ6zo=fTPH(+IFZVuR0t$Q$a~-5?!6274I^DRvC3YjB4FoAyBe&!<~B4zkfVN zs{|oaK&hjgU^z={OyA59XN6rP{(9uX{Q0n3P5 z9Sd4}mTtc4X!pn~+U?)u6)RmGac3$BKI-qF7C$M6Vl}9z58`f~(7}lwmCd=)^3+JUleD?0A9_!d7&?{Vg|0K_s^P0m}P7EKX4EdCZ zm_|}Yh`5}vsCD~>i}593L0pd3>TrPW3g@6o`%6--iseI7=VNG z;Q62MMCcn1{pH}|tPzM-*D;Ae#RT+qX(iJa6+mu&J=)R+Q72!5k-Li+Ssks8+SY(<92e-ZYSy)An)*;U;m^DTe?47CyDuxyM zKFFM->}W4D+Lr}~7r&6?+}#}kg)lQy49(=qn})y#Q+S0UW&6I3S#ilHU3?>wV!-oE z_2h=kQZUU%*@kjaA{o7Szt2-nQIW^8!&L;Hqeg1mY?34jIa}u$U7_e>sY(#PhGUux zN9KSKWtL--H3LBs+0@qmMlb}6sbkJKND}CioUu2564voKQ!ivdAos(8rWi@DC12-Q&aAATLF_RgjnDs2su2pv zlCo{7OT!MXr`!gUU`PEPbjLxbA}cE^5#@reNeA<{ygLcY|G^Qqcd+c>BYC1$aL|Y$ zm`W{Ew(<{Y9Awj{9()8U27d8p0u(;kf$&rj>s{iSF z+Tvw_uJPCA2{Ye$bkr!`@F+f6**Fu4NGYV7B1$mfaZe9l8+{!bmcK7a}Jv_!G=!5V+Cq%UNDyX__v3rn>hYYje zZe!&IVmmI}D>?P|^0K+dPtZN;11jHoFCt@KWZ% zrg_fViFWJ`Mp5u!B{O|d?IoKAAz{AgyQ??R;<{?88r`Wl*nZfCL*C5vYu0E4>DDej zw75!rR>l@6dmjX@0014$9ZhwUQB=soyMu^Mr~Z6$yFPt zUgH>HeKF?(IMHa}5YM6E;r+B5Y54!j(G3X!6oh3$^2-rZKGZ)*TVGl#-j&6KGqO7@_Gd4xt#u-P zde^?7pERR#?F4XZbmimyL5AS}3C_s>1jk)P*p%>Gu=T3_VW6joyuAh!%;=GT*MHfa z2L}y*V%|CY6_{@HsR)ia+I?$|o^>CJRsCXs1u-!#qLkg<+6OlBv<5JKYS_9(qjg>(z zO>VUC2o9hW9t{cnF&xtr3UNbn5^}UBoTi&+BB4%r?i4)NB+xxCD+!NCl*3FQZ!0!E z!m~}@BgO`u4R?oTn!~IlEf2~SYI14%$_B!fzu}FJtWqSpxw1e9f{N}Fbhxf(+S=O2 zQaowiINtZ=5r1(sIDa+!M{6&GRTF|$*?}(S52I#IS>u->o7?RKWRZpYpJs72BL_cI z484eBPskqwr6GwnEF(29R~^)IBp`*^@^WmaO~& zhRGLjYDBYx!2|T(zkBWgV&%IfEG}0zw{`6gZdp^-N<*-^sLOvz=x)C9RnOx0^T=$W zL&nSw@CTVebpjk{vT88JFsX9C=Fqqh#s>vcGopf~-%)N*LE7jcJcW7ZAcCXT;xRuz zf2~&vVcg(KS8tQe%sMdBkkG{QPmxF8lCak5bCQxUcE*KW51 P&~xXufo7$Kec1m2TX3(d