From c36105ef888b3e67c389e59b5f0256e51b355694 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Thu, 7 Dec 2023 23:08:17 -0300 Subject: [PATCH 1/5] holy hell --- code/game/objects/items/robot/robot_parts.dm | 1 + .../objects/items/robot/robot_upgrades.dm | 28 +- code/game/turfs/simulated/lava.dm | 1 + code/modules/admin/verbs/borgpanel.dm | 70 +++- code/modules/mining/minebot.dm | 6 +- .../modules/mob/living/silicon/robot/robot.dm | 320 ++++++++---------- .../mob/living/silicon/robot/robot_defense.dm | 70 ++++ .../guns/energy/kinetic_accelerator.dm | 77 ++--- tgui/packages/tgui/interfaces/BorgPanel.js | 57 +++- tgui/packages/tgui/interfaces/NtosRobotact.js | 15 +- 10 files changed, 379 insertions(+), 266 deletions(-) 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..7ff89a9d45 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 => ( -