diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index bd1c5210826..c476232387d 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -78,15 +78,6 @@ var/mob/living/silicon/robot/R = usr R.toggle_ionpulse() -/obj/screen/robot/panel - name = "installed modules" - icon_state = "panel" - -/obj/screen/robot/panel/Click() - if(issilicon(usr)) - var/mob/living/silicon/robot/R = usr - R.installed_modules() - /obj/screen/robot/mov_intent name = "fast/slow toggle" icon_state = "running" @@ -224,16 +215,6 @@ var/x = -4 //Start at CENTER-4,SOUTH+1 var/y = 1 - //Unfortunately adding the emag module to the list of modules has to be here. This is because a borg can - //be emagged before they actually select a module. - or some situation can cause them to get a new module - // - or some situation might cause them to get de-emagged or something. - if(R.emagged || R.weapons_unlock) - if(!(R.module.emag in R.module.modules)) - R.module.modules.Add(R.module.emag) - else - if(R.module.emag in R.module.modules) - R.module.modules.Remove(R.module.emag) - for(var/atom/movable/A in R.module.modules) if( (A != R.module_state_1) && (A != R.module_state_2) && (A != R.module_state_3) ) //Module is not currently active diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm index 38ee8ed6042..f4b8e84204f 100644 --- a/code/datums/components/material_container.dm +++ b/code/datums/components/material_container.dm @@ -130,13 +130,13 @@ /datum/component/material_container/proc/insert_stack(obj/item/stack/S, amt, multiplier = 1) if(isnull(amt)) - amt = S.amount + amt = S.get_amount() if(amt <= 0) return FALSE - if(amt > S.amount) - amt = S.amount + if(amt > S.get_amount()) + amt = S.get_amount() var/material_amt = get_item_material_amount(S) if(!material_amt) diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index 155bbaed8dc..7c05f7806f9 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -47,7 +47,7 @@ /datum/construction/proc/custom_action(step, used_atom, user) if(istype(used_atom, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = used_atom - if(C.amount<4) + if(C.get_amount() < 4) to_chat(user, ("There's not enough cable to finish the task.")) return 0 else @@ -55,7 +55,7 @@ playsound(holder, C.usesound, 50, 1) else if(istype(used_atom, /obj/item/stack)) var/obj/item/stack/S = used_atom - if(S.amount < 5) + if(S.get_amount() < 5) to_chat(user, ("There's not enough material in this stack.")) return 0 else @@ -113,7 +113,7 @@ // STACKS if(istype(used_atom,/obj/item/stack)) var/obj/item/stack/stack=used_atom - if(stack.amount < amount) + if(stack.get_amount() < amount) to_chat(user, "You don't have enough [stack]! You need at least [amount].") return 0 stack.use(amount) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 35e0053822d..29a1b29de97 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1375,46 +1375,20 @@ switch(href_list["silicon"]) if("unemag") var/mob/living/silicon/robot/R = current - if(istype(R)) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has un-emagged [key_name(current)]") - message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") + if(!istype(R)) + return + R.unemag() + log_admin("[key_name(usr)] has un-emagged [key_name(current)]") + message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") if("unemagcyborgs") - if(isAI(current)) - var/mob/living/silicon/ai/ai = current - for(var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") - message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") + if(!isAI(current)) + return + var/mob/living/silicon/ai/ai = current + for(var/mob/living/silicon/robot/R in ai.connected_robots) + R.unemag() + log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") + message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") else if(href_list["common"]) switch(href_list["common"]) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 66df3d4a76c..191bc9638a4 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -413,6 +413,9 @@ /atom/proc/emag_act() return +/atom/proc/unemag() + return + /** * Respond to a radioactive wave hitting this atom * diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index a02c46524c5..80a15be40d5 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -477,11 +477,11 @@ return if(istype(P, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) + if(C.get_amount() >= 5) playsound(loc, C.usesound, 50, 1) to_chat(user, "You start to add cables to the frame.") if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) + if(state == 2 && C.get_amount() >= 5 && C.use(5)) to_chat(user, "You add cables to the frame.") state = 3 icon_state = "3" @@ -502,11 +502,11 @@ return if(istype(P, /obj/item/stack/sheet/glass)) var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) + if(G.get_amount() >= 2) playsound(loc, G.usesound, 50, 1) to_chat(user, "You start to add the glass panel to the frame.") if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) + if(state == 3 && G.get_amount() >= 2 && G.use(2)) to_chat(user, "You put in the glass panel.") state = 4 icon_state = "4" @@ -608,11 +608,11 @@ icon_state = "1" if(istype(P, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) + if(C.get_amount() >= 5) playsound(loc, C.usesound, 50, 1) to_chat(user, "You start to add cables to the frame.") if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) + if(state == 2 && C.get_amount() >= 5 && C.use(5)) to_chat(user, "You add cables to the frame.") state = 3 icon_state = "3" @@ -633,11 +633,11 @@ if(istype(P, /obj/item/stack/sheet/glass)) var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) + if(G.get_amount() >= 2) playsound(loc, G.usesound, 50, 1) to_chat(user, "You start to add the glass panel to the frame.") if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) + if(state == 3 && G.get_amount() >= 2 && G.use(2)) to_chat(user, "You put in the glass panel.") state = 4 icon_state = "4" diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 322141dee0a..a4879448aa0 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -75,11 +75,11 @@ if(1) if(istype(P, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) + if(C.get_amount() >= 5) playsound(src.loc, C.usesound, 50, 1) to_chat(user, "You start to add cables to the frame.") if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 1 && C.amount >= 5 && C.use(5)) + if(state == 1 && C.get_amount() >= 5 && C.use(5)) to_chat(user, "You add cables to the frame.") state = 2 icon_state = "box_1" @@ -193,8 +193,8 @@ playsound(src.loc, P.usesound, 50, 1) if(istype(P, /obj/item/stack)) var/obj/item/stack/S = P - var/camt = min(S.amount, req_components[I]) - var/obj/item/stack/NS = new P.type(src) + var/camt = min(S.get_amount(), req_components[I]) + var/obj/item/stack/NS = new S.merge_type(src) NS.amount = camt NS.update_icon() S.use(camt) diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index cae3e78186e..ab211939804 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -126,7 +126,7 @@ var/obj/item/stack/cable_coil/C = W to_chat(user, "You start adding cables to \the [src]...") playsound(get_turf(src), C.usesound, 50, 1) - if(do_after(user, 20 * C.toolspeed, target = src) && (C.amount >= 2) && (build == 2)) + if(do_after(user, 20 * C.toolspeed, target = src) && (C.get_amount() >= 2) && (build == 2)) C.use(2) to_chat(user, "You've added cables to \the [src].") build++ @@ -146,7 +146,7 @@ var/obj/item/stack/rods/R = W to_chat(user, "You begin to complete \the [src]...") playsound(get_turf(src), R.usesound, 50, 1) - if(do_after(user, 20 * R.toolspeed, target = src) && (R.amount >= 2) && (build == 3)) + if(do_after(user, 20 * R.toolspeed, target = src) && (R.get_amount() >= 2) && (build == 3)) R.use(2) to_chat(user, "You've added the grille to \the [src].") build++ diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 9063a126317..6fa1e79e764 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -7,11 +7,18 @@ use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 1000 + /// A reference to the occupant currently sitting inside the recharger. var/mob/occupant = null + /// What type of circuit board is required to build this machine. var/circuitboard = /obj/item/circuitboard/cyborgrecharger + /// How fast the recharger can give charge to the occupants energy cell. Based on capacitors and the cell the recharger has. var/recharge_speed + /// How much nutrition is given to the occupant. Based on capacitors and the cell the recharger has. Only relevent for IPCs. var/recharge_speed_nutrition + /// How much the occupant is repaired every cycle. Based on what manipulator the recharger has. var/repairs + /// How efficiently the recharger is able to recharge consumable items such as metal, glass, chemicals in sprays, welding tool fuel, etc. + var/consumable_recharge_coeff /obj/machinery/recharge_station/Destroy() go_out() @@ -42,6 +49,7 @@ recharge_speed = 0 recharge_speed_nutrition = 0 repairs = 0 + // The recharge_speed can be anywhere from 200 to 3200, depending on the capacitors and the cell. for(var/obj/item/stock_parts/capacitor/C in component_parts) recharge_speed += C.rating * 100 recharge_speed_nutrition += C.rating * 10 @@ -52,11 +60,14 @@ recharge_speed *= multiplier recharge_speed_nutrition *= multiplier + // This coefficient can be anywhere from 1, to 16. + consumable_recharge_coeff = max(1, recharge_speed / 200) + /obj/machinery/recharge_station/process() if(!(NOPOWER|BROKEN)) return - if(src.occupant) + if(occupant) process_occupant() for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp @@ -133,20 +144,20 @@ return TRUE /obj/machinery/recharge_station/proc/process_occupant() - if(src.occupant) - if(istype(occupant, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = occupant - restock_modules() - if(repairs) - R.heal_overall_damage(repairs, repairs) - if(R.cell) - R.cell.charge = min(R.cell.charge + recharge_speed, R.cell.maxcharge) - else if(istype(occupant, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = occupant - if(H.get_int_organ(/obj/item/organ/internal/cell) && H.nutrition < 450) - H.set_nutrition(min(H.nutrition + recharge_speed_nutrition, 450)) - if(repairs) - H.heal_overall_damage(repairs, repairs, TRUE, 0, 1) + if(isrobot(occupant)) + var/mob/living/silicon/robot/R = occupant + if(R.module) + R.module.recharge_consumables(R, consumable_recharge_coeff) // This will handle all of a cyborg's items. + if(repairs) + R.heal_overall_damage(repairs, repairs) + if(R.cell) + R.cell.charge = min(R.cell.charge + recharge_speed, R.cell.maxcharge) + else if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.get_int_organ(/obj/item/organ/internal/cell) && H.nutrition < 450) + H.set_nutrition(min(H.nutrition + recharge_speed_nutrition, 450)) + if(repairs) + H.heal_overall_damage(repairs, repairs, TRUE, 0, 1) /obj/machinery/recharge_station/proc/go_out() if(!occupant) @@ -157,74 +168,6 @@ use_power = IDLE_POWER_USE return -/obj/machinery/recharge_station/proc/restock_modules() - if(src.occupant) - if(istype(occupant, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = occupant - var/coeff = recharge_speed / 200 - if(R.module && R.module.modules) - var/list/um = R.contents|R.module.modules - // ^ makes sinle list of active (R.contents) and inactive modules (R.module.modules) - for(var/obj/O in um) - // Engineering - if(istype(O,/obj/item/stack/sheet)) - var/obj/item/stack/sheet/S = O - if(S.amount < S.max_amount) - S.amount += round(min(1 * coeff, S.max_amount - S.amount)) - // Security - if(istype(O,/obj/item/flash)) - var/obj/item/flash/F = O - if(F.broken) - F.broken = 0 - F.times_used = 0 - F.icon_state = "flash" - if(istype(O,/obj/item/gun/energy)) - var/obj/item/gun/energy/D = O - if(D.cell.charge < D.cell.maxcharge) - var/obj/item/ammo_casing/energy/E = D.ammo_type[D.select] - D.cell.give(E.e_cost) - D.on_recharge() - D.update_icon() - else - D.charge_tick = 0 - if(istype(O,/obj/item/melee/baton)) - var/obj/item/melee/baton/B = O - if(B.cell) - B.cell.charge = B.cell.maxcharge - //Service - if(istype(O,/obj/item/reagent_containers/food/condiment/enzyme)) - if(O.reagents.get_reagent_amount("enzyme") < 50) - O.reagents.add_reagent("enzyme", 2 * coeff) - //Janitor - if(istype(O, /obj/item/lightreplacer)) - var/obj/item/lightreplacer/LR = O - var/i = 1 - for(1, i <= coeff, i++) - LR.Charge(occupant) - //Fire extinguisher - if(istype(O, /obj/item/extinguisher)) - var/obj/item/extinguisher/ext = O - ext.reagents.check_and_add("water", ext.max_water, 5 * coeff) - //Welding tools - if(istype(O, /obj/item/weldingtool)) - var/obj/item/weldingtool/weld = O - weld.reagents.check_and_add("fuel", weld.maximum_fuel, 2 * coeff) - if(R) - if(R.module) - R.module.respawn_consumable(R) - - //Emagged items for janitor and medical borg - if(R.module.emag) - if(istype(R.module.emag, /obj/item/reagent_containers/spray)) - var/obj/item/reagent_containers/spray/S = R.module.emag - if(S.name == "polyacid spray") - S.reagents.add_reagent("facid", 2 * coeff) - if(S.name == "lube spray") - S.reagents.add_reagent("lube", 2 * coeff) - else if(S.name == "acid synthesizer") - S.reagents.add_reagent("facid", 2 * coeff) - S.reagents.add_reagent("sacid", 2 * coeff) - /obj/machinery/recharge_station/verb/move_eject() set category = "Object" set src in oview(1) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2876527a526..2d6c7767f17 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -699,7 +699,7 @@ else if(istype(W, /obj/item/stack/cable_coil)) if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) var/obj/item/stack/cable_coil/CC = W - if(CC.amount > 1) + if(CC.get_amount() > 1) CC.use(2) clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) to_chat(user, "You replace the fused wires.") diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 01ac7cf8d7d..13b5d6f1be2 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -14,7 +14,7 @@ return 0 else if(istype(used_atom, /obj/item/stack)) var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) + if(S.get_amount() < STANDARD_STACK_AMOUNT) to_chat(user, ("There's not enough material in this stack.")) return 0 else @@ -35,7 +35,7 @@ return 0 else if(istype(used_atom, /obj/item/stack)) var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) + if(S.get_amount() < STANDARD_STACK_AMOUNT) to_chat(user, ("There's not enough material in this stack.")) return 0 else diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index f22fb5cbb81..28a2006e0aa 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -728,3 +728,6 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect if(flags & SLOT_PDA) owner.update_inv_wear_pda() +/// Called on cyborg items that need special charging behavior. Override as needed for specific items. +/obj/item/proc/cyborg_recharge(coeff = 1, emagged = FALSE) + return diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 26bfb6c816d..3799d1d779e 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -176,6 +176,12 @@ ..() new /obj/effect/temp_visual/borgflash(get_turf(src)) +/obj/item/flash/cyborg/cyborg_recharge(coeff, emagged) + if(broken) + broken = FALSE + times_used = 0 + icon_state = "flash" + /obj/item/flash/cameraflash name = "camera" icon = 'icons/obj/items.dmi' diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 4ed9b335584..85e650412f6 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -262,6 +262,10 @@ /obj/item/lightreplacer/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) return +/obj/item/lightreplacer/cyborg/cyborg_recharge(coeff, emagged) + for(var/I in 1 to coeff) + Charge() + #undef LIGHT_OK #undef LIGHT_EMPTY #undef LIGHT_BROKEN diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index d1d4e900543..2cd43506990 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -132,7 +132,7 @@ return else if(iscoil(W) && buildstage == 1) var/obj/item/stack/cable_coil/coil = W - if(coil.amount < 5) + if(coil.get_amount() < 5) to_chat(user, "You need more cable for this!") return if(do_after(user, 10 * coil.toolspeed, target = src) && buildstage == 1) diff --git a/code/game/objects/items/mixing_bowl.dm b/code/game/objects/items/mixing_bowl.dm index 1e1a39d49d0..024274aa973 100644 --- a/code/game/objects/items/mixing_bowl.dm +++ b/code/game/objects/items/mixing_bowl.dm @@ -31,7 +31,7 @@ return 1 if(istype(I, /obj/item/stack)) var/obj/item/stack/S = I - if(S.amount > 1) + if(S.get_amount() > 1) var/obj/item/stack/to_add = S.split(user, 1) to_add.forceMove(src) user.visible_message("[user] adds one of [S] to [src].", "You add one of [S] to [src].") diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 81733bd91e7..bcc5bd35dd9 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -7,19 +7,72 @@ icon = 'icons/obj/module.dmi' icon_state = "cyborg_upgrade" origin_tech = "programming=2" - var/locked = 0 - var/installed = 0 + /// Whether or not the cyborg needs to have a chosen module before they can recieve this upgrade. var/require_module = FALSE + /// The type of module this upgrade is compatible with: Engineering, Medical, etc. var/module_type = null + /// A list of items, and their replacements that this upgrade should replace on installation, in the format of `item_type_to_replace = replacement_item_type`. + var/list/items_to_replace = list() + /// A list of replacement items will need to be placed into a cyborg module's `special_rechargable` list after this upgrade is installed. + var/list/special_rechargables = list() +/** + * Called when someone clicks on a borg with an upgrade in their hand. + * + * Arguments: + * * R - the cyborg that was clicked on with an upgrade. + */ /obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R) + if(!pre_install_checks(R)) + return + if(!do_install(R)) + return + after_install(R) + return TRUE + +/** + * Checks if the upgrade is able to be applied to the cyborg, before actually applying it. + * + * Arguments: + * * R - the cyborg that was clicked on with an upgrade. + */ +/obj/item/borg/upgrade/proc/pre_install_checks(mob/living/silicon/robot/R) if(R.stat == DEAD) - to_chat(usr, "[src] will not function on a deceased cyborg.") - return TRUE + to_chat(usr, "[src] will not function on a deceased cyborg.") + return FALSE if(module_type && !istype(R.module, module_type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return TRUE + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return FALSE + return TRUE + +/** + * Executes code that will modify the cyborg or its module. + * + * Arguments: + * * R - the cyborg we're applying the upgrade to. + */ +/obj/item/borg/upgrade/proc/do_install(mob/living/silicon/robot/R) + return TRUE + +/** + * Executes code after the module has been installed and the cyborg has been modified in some way. + * + * Arguments: + * * R - the cyborg that we've applied the upgrade to. + */ +/obj/item/borg/upgrade/proc/after_install(mob/living/silicon/robot/R) + for(var/item in items_to_replace) + var/replacement_type = items_to_replace[item] + var/obj/item/replacement = new replacement_type(R.module) + R.module.remove_item_from_lists(item) + R.module.basic_modules += replacement + + if(replacement_type in special_rechargables) + R.module.special_rechargables += replacement + + R.module.rebuild_modules() + return TRUE /obj/item/borg/upgrade/reset name = "cyborg module reset board" @@ -27,14 +80,13 @@ icon_state = "cyborg_upgrade1" require_module = TRUE -/obj/item/borg/upgrade/reset/action(mob/living/silicon/robot/R) - if(..()) - return - +/obj/item/borg/upgrade/reset/do_install(mob/living/silicon/robot/R) R.reset_module() - return TRUE +/obj/item/borg/upgrade/reset/after_install(mob/living/silicon/robot/R) + return // We don't need to give them replacement items, or rebuild their module list. It's going to be a blank borg. + /obj/item/borg/upgrade/rename name = "cyborg reclassification board" desc = "Used to rename a cyborg." @@ -44,9 +96,7 @@ /obj/item/borg/upgrade/rename/attack_self(mob/user) heldname = stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN) -/obj/item/borg/upgrade/rename/action(mob/living/silicon/robot/R) - if(..()) - return +/obj/item/borg/upgrade/rename/do_install(mob/living/silicon/robot/R) if(!R.allow_rename) to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.") return 0 @@ -63,7 +113,7 @@ desc = "Used to force a reboot of a disabled-but-repaired cyborg, bringing it back online." icon_state = "cyborg_upgrade1" -/obj/item/borg/upgrade/restart/action(mob/living/silicon/robot/R) +/obj/item/borg/upgrade/restart/do_install(mob/living/silicon/robot/R) if(R.health < 0) to_chat(usr, "You have to repair the cyborg before using this module!") return 0 @@ -88,9 +138,7 @@ require_module = TRUE origin_tech = "engineering=4;materials=5;programming=4" -/obj/item/borg/upgrade/vtec/action(mob/living/silicon/robot/R) - if(..()) - return +/obj/item/borg/upgrade/vtec/do_install(mob/living/silicon/robot/R) if(R.speed < 0) to_chat(R, "A VTEC unit is already installed!") to_chat(usr, "There's no room for another VTEC unit!") @@ -108,10 +156,7 @@ require_module = TRUE module_type = /obj/item/robot_module/security -/obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R) - if(..()) - return - +/obj/item/borg/upgrade/disablercooler/do_install(mob/living/silicon/robot/R) var/obj/item/gun/energy/disabler/cyborg/T = locate() in R.module.modules if(!T) to_chat(usr, "There's no disabler in this unit!") @@ -131,10 +176,7 @@ icon_state = "cyborg_upgrade3" origin_tech = "engineering=4;powerstorage=4" -/obj/item/borg/upgrade/thrusters/action(mob/living/silicon/robot/R) - if(..()) - return - +/obj/item/borg/upgrade/thrusters/do_install(mob/living/silicon/robot/R) if(R.ionpulse) to_chat(usr, "This unit already has ion thrusters installed!") return @@ -149,20 +191,9 @@ origin_tech = "engineering=4;materials=5" require_module = TRUE module_type = /obj/item/robot_module/miner - -/obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R) - if(..()) - return - - for(var/obj/item/pickaxe/drill/cyborg/D in R.module.modules) - qdel(D) - for(var/obj/item/shovel/S in R.module.modules) - qdel(S) - - R.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(R.module) - R.module.rebuild() - - return TRUE + items_to_replace = list( + /obj/item/pickaxe/drill/cyborg = /obj/item/pickaxe/drill/cyborg/diamond + ) /obj/item/borg/upgrade/soh name = "mining cyborg satchel of holding" @@ -171,18 +202,9 @@ origin_tech = "engineering=4;materials=4;bluespace=4" require_module = TRUE module_type = /obj/item/robot_module/miner - -/obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R) - if(..()) - return - - for(var/obj/item/storage/bag/ore/cyborg/S in R.module.modules) - qdel(S) - - R.module.modules += new /obj/item/storage/bag/ore/holding(R.module) - R.module.rebuild() - - return TRUE + items_to_replace = list( + /obj/item/storage/bag/ore/cyborg = /obj/item/storage/bag/ore/holding + ) /obj/item/borg/upgrade/abductor_engi name = "engineering cyborg abductor upgrade" @@ -191,33 +213,17 @@ origin_tech = "engineering=6;materials=6;abductor=3" require_module = TRUE module_type = /obj/item/robot_module/engineering - -/obj/item/borg/upgrade/abductor_engi/action(mob/living/silicon/robot/R) - if(..()) - return - - for(var/obj/item/weldingtool/largetank/cyborg/W in R.module.modules) - qdel(W) - for(var/obj/item/screwdriver/cyborg/S in R.module.modules) - qdel(S) - for(var/obj/item/wrench/cyborg/E in R.module.modules) - qdel(E) - for(var/obj/item/crowbar/cyborg/C in R.module.modules) - qdel(C) - for(var/obj/item/wirecutters/cyborg/I in R.module.modules) - qdel(I) - for(var/obj/item/multitool/cyborg/M in R.module.modules) - qdel(M) - - R.module.modules += new /obj/item/weldingtool/abductor(R.module) - R.module.modules += new /obj/item/wrench/abductor(R.module) - R.module.modules += new /obj/item/screwdriver/abductor(R.module) - R.module.modules += new /obj/item/crowbar/abductor(R.module) - R.module.modules += new /obj/item/wirecutters/abductor(R.module) - R.module.modules += new /obj/item/multitool/abductor(R.module) - R.module.rebuild() - - return TRUE + items_to_replace = list( + /obj/item/weldingtool = /obj/item/weldingtool/abductor, + /obj/item/wrench = /obj/item/wrench/abductor, + /obj/item/screwdriver = /obj/item/screwdriver/abductor, + /obj/item/crowbar = /obj/item/crowbar/abductor, + /obj/item/wirecutters = /obj/item/wirecutters/abductor, + /obj/item/multitool = /obj/item/multitool/abductor + ) + special_rechargables = list( + /obj/item/weldingtool/abductor + ) /obj/item/borg/upgrade/abductor_medi name = "medical cyborg abductor upgrade" @@ -226,39 +232,16 @@ origin_tech = "biotech=6;materials=6;abductor=3" require_module = TRUE module_type = /obj/item/robot_module/medical - -/obj/item/borg/upgrade/abductor_medi/action(mob/living/silicon/robot/R) - if(..()) - return - - for(var/obj/item/scalpel/laser/laser1/L in R.module.modules) - qdel(L) - for(var/obj/item/hemostat/H in R.module.modules) - qdel(H) - for(var/obj/item/retractor/E in R.module.modules) - qdel(E) - for(var/obj/item/bonegel/B in R.module.modules) - qdel(B) - for(var/obj/item/FixOVein/F in R.module.modules) - qdel(F) - for(var/obj/item/bonesetter/S in R.module.modules) - qdel(S) - for(var/obj/item/circular_saw/C in R.module.modules) - qdel(C) - for(var/obj/item/surgicaldrill/D in R.module.modules) - qdel(D) - - R.module.modules += new /obj/item/scalpel/laser/laser3(R.module) //no abductor laser scalpel, so next best thing. - R.module.modules += new /obj/item/hemostat/alien(R.module) - R.module.modules += new /obj/item/retractor/alien(R.module) - R.module.modules += new /obj/item/bonegel/alien(R.module) - R.module.modules += new /obj/item/FixOVein/alien(R.module) - R.module.modules += new /obj/item/bonesetter/alien(R.module) - R.module.modules += new /obj/item/circular_saw/alien(R.module) - R.module.modules += new /obj/item/surgicaldrill/alien(R.module) - R.module.rebuild() - - return TRUE + items_to_replace = list( + /obj/item/scalpel/laser/laser1 = /obj/item/scalpel/laser/laser3, // No abductor laser scalpel, so next best thing. + /obj/item/hemostat = /obj/item/hemostat/alien, + /obj/item/retractor = /obj/item/retractor/alien, + /obj/item/bonegel = /obj/item/bonegel/alien, + /obj/item/FixOVein = /obj/item/FixOVein/alien, + /obj/item/bonesetter = /obj/item/bonesetter/alien, + /obj/item/circular_saw = /obj/item/circular_saw/alien, + /obj/item/surgicaldrill = /obj/item/surgicaldrill/alien + ) /obj/item/borg/upgrade/syndicate name = "safety override module" @@ -267,13 +250,11 @@ origin_tech = "combat=6;materials=6" require_module = TRUE -/obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/R) - if(..()) - return +/obj/item/borg/upgrade/syndicate/do_install(mob/living/silicon/robot/R) if(R.weapons_unlock) - to_chat(R, "Warning: Safety Overide Protocols have be disabled.") - return - R.weapons_unlock = 1 + return // They already had the safety override upgrade, or they're a cyborg type which has this by default. + R.weapons_unlock = TRUE + to_chat(R, "Warning: Safety Overide Protocols have be disabled.") return TRUE /obj/item/borg/upgrade/lavaproof @@ -284,9 +265,7 @@ require_module = TRUE module_type = /obj/item/robot_module/miner -/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R) - if(..()) - return +/obj/item/borg/upgrade/lavaproof/do_install(mob/living/silicon/robot/R) if(istype(R)) R.weather_immunities += "lava" return TRUE @@ -303,10 +282,7 @@ var/powercost = 10 var/mob/living/silicon/robot/cyborg -/obj/item/borg/upgrade/selfrepair/action(mob/living/silicon/robot/R) - if(..()) - return - +/obj/item/borg/upgrade/selfrepair/do_install(mob/living/silicon/robot/R) var/obj/item/borg/upgrade/selfrepair/U = locate() in R if(U) to_chat(usr, "This unit is already equipped with a self-repair module.") diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index f9d02c8f515..a0bd2a3f708 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -17,13 +17,18 @@ var/healverb = "bandage" /obj/item/stack/medical/attack(mob/living/M, mob/user) + if(get_amount() <= 0) + if(is_cyborg) + to_chat(user, "You don't have enough energy to dispense more [singular_name]\s!") + return TRUE + if(!iscarbon(M) && !isanimal(M)) to_chat(user, "[src] cannot be applied to [M]!") - return 1 + return TRUE if(!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") - return 1 + return TRUE if(ishuman(M)) @@ -167,7 +172,12 @@ heal_brute = 25 stop_bleeding = 0 +/obj/item/stack/medical/bruise_pack/advanced/cyborg + energy_type = /datum/robot_energy_storage/medical/adv_brute_kit + is_cyborg = TRUE +/obj/item/stack/medical/bruise_pack/advanced/cyborg/syndicate + energy_type = /datum/robot_energy_storage/medical/adv_brute_kit/syndicate //Ointment// @@ -208,6 +218,13 @@ icon_state = "burnkit" heal_burn = 25 +/obj/item/stack/medical/ointment/advanced/cyborg + energy_type = /datum/robot_energy_storage/medical/adv_burn_kit + is_cyborg = TRUE + +/obj/item/stack/medical/ointment/advanced/cyborg/syndicate + energy_type = /datum/robot_energy_storage/medical/adv_burn_kit/syndicate + //Medical Herbs// /obj/item/stack/medical/bruise_pack/comfrey name = "\improper Comfrey leaf" @@ -283,6 +300,13 @@ H.handle_splints() use(1) +/obj/item/stack/medical/splint/cyborg + energy_type = /datum/robot_energy_storage/medical/splint + is_cyborg = TRUE + +/obj/item/stack/medical/splint/cyborg/syndicate + energy_type = /datum/robot_energy_storage/medical/splint/syndicate + /obj/item/stack/medical/splint/tribal name = "tribal splints" icon_state = "tribal_splint" diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm index ff469ba3c59..11a5dc4c39c 100644 --- a/code/game/objects/items/stacks/nanopaste.dm +++ b/code/game/objects/items/stacks/nanopaste.dm @@ -63,3 +63,16 @@ to_chat(user, "Nothing to fix here.") else to_chat(user, "[src] won't work on that.") + +/obj/item/stack/nanopaste/cyborg + energy_type = /datum/robot_energy_storage/medical/nanopaste + is_cyborg = TRUE + +/obj/item/stack/nanopaste/cyborg/attack(mob/living/M, mob/user) + if(get_amount() <= 0) + to_chat(user, "You don't have enough energy to dispense more [name]!") + else + return ..() + +/obj/item/stack/nanopaste/cyborg/syndicate + energy_type = /datum/robot_energy_storage/medical/nanopaste/syndicate diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 3911ab51cc7..f9ec5cff198 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -21,10 +21,16 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \ hitsound = 'sound/weapons/grenadelaunch.ogg' toolspeed = 1 usesound = 'sound/items/deconstruct.ogg' + merge_type = /obj/item/stack/rods /obj/item/stack/rods/cyborg + energy_type = /datum/robot_energy_storage/rods + is_cyborg = TRUE materials = list() +/obj/item/stack/rods/cyborg/update_icon() + return // icon_state should always be a full stack of rods. + /obj/item/stack/rods/ten amount = 10 diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 0e0bb55df85..91ededd189c 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -40,6 +40,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ amount = 50 /obj/item/stack/sheet/glass/cyborg + energy_type = /datum/robot_energy_storage/glass + is_cyborg = TRUE materials = list() /obj/item/stack/sheet/glass/New(loc, amount) @@ -48,15 +50,15 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ /obj/item/stack/sheet/glass/attackby(obj/item/W, mob/user, params) ..() - if(istype(W,/obj/item/stack/cable_coil)) + if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/CC = W - if(CC.amount < 5) + if(CC.get_amount() < 5) to_chat(user, "There is not enough wire in this coil. You need 5 lengths.") return CC.use(5) to_chat(user, "You attach wire to the [name].") new /obj/item/stack/light_w(user.loc) - src.use(1) + use(1) else if( istype(W, /obj/item/stack/rods) ) var/obj/item/stack/rods/V = W var/obj/item/stack/sheet/rglass/RG = new (user.loc) @@ -97,9 +99,6 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ merge_type = /obj/item/stack/sheet/rglass point_value = 4 -/obj/item/stack/sheet/rglass/cyborg - materials = list() - /obj/item/stack/sheet/rglass/New(loc, amount) recipes = GLOB.reinforced_glass_recipes ..() @@ -109,6 +108,11 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \ new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/plasmabasic, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ )) +/obj/item/stack/sheet/rglass/cyborg + energy_type = /datum/robot_energy_storage/rglass + is_cyborg = TRUE + materials = list() + /obj/item/stack/sheet/plasmaglass name = "plasma glass" desc = "A very strong and very resistant sheet of a plasma-glass alloy." diff --git a/code/game/objects/items/stacks/sheets/light.dm b/code/game/objects/items/stacks/sheets/light.dm index 4eadda37c32..ddbe453a324 100644 --- a/code/game/objects/items/stacks/sheets/light.dm +++ b/code/game/objects/items/stacks/sheets/light.dm @@ -13,25 +13,16 @@ flags = CONDUCT max_amount = 60 -/obj/item/stack/light_w/attackby(obj/item/O as obj, mob/user as mob, params) +/obj/item/stack/light_w/attackby(obj/item/I, mob/user, params) ..() - if(istype(O,/obj/item/wirecutters)) - var/obj/item/stack/cable_coil/CC = new/obj/item/stack/cable_coil(user.loc) + if(istype(I, /obj/item/wirecutters)) + var/obj/item/stack/cable_coil/CC = new(user.loc) CC.amount = 5 - amount-- new/obj/item/stack/sheet/glass(user.loc) - if(amount <= 0) - user.unEquip(src, 1) - qdel(src) + use(1) - if(istype(O,/obj/item/stack/sheet/metal)) - var/obj/item/stack/sheet/metal/M = O - M.amount-- - if(M.amount <= 0) - user.unEquip(src, 1) - qdel(M) - amount-- - new/obj/item/stack/tile/light(user.loc) - if(amount <= 0) - user.unEquip(src, 1) - qdel(src) + if(istype(I, /obj/item/stack/sheet/metal)) + var/obj/item/stack/sheet/metal/M = I + M.use(1) + new /obj/item/stack/tile/light(user.loc) + use(1) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index a004cfd0de8..84c59310418 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -110,6 +110,8 @@ GLOBAL_LIST_INIT(metal_recipes, list( point_value = 2 /obj/item/stack/sheet/metal/cyborg + energy_type = /datum/robot_energy_storage/metal + is_cyborg = TRUE materials = list() /obj/item/stack/sheet/metal/fifty @@ -162,6 +164,10 @@ GLOBAL_LIST_INIT(plasteel_recipes, list( recipes = GLOB.plasteel_recipes return ..() +/obj/item/stack/sheet/wood/cyborg + energy_type = /datum/robot_energy_storage/wood + is_cyborg = TRUE + /* * Wood */ diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index b57c3435132..6a2af11f0dc 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -10,7 +10,16 @@ */ /obj/item/stack origin_tech = "materials=1" - var/list/recipes = list() // /datum/stack_recipe + /// Whether this stack is a `/cyborg` subtype or not. + var/is_cyborg = FALSE + /// The energy storage datum that will be used with this stack. Used only with `/cyborg` type stacks. + var/datum/robot_energy_storage/source + /// Which `robot_energy_storage` to choose when this stack is created in cyborgs. Used only with `/cyborg` type stacks. + var/energy_type + /// How much energy using 1 sheet from the stack costs. Used only with `/cyborg` type stacks. + var/cost = 1 + /// A list of recipes buildable with this stack. + var/list/recipes = list() var/singular_name var/amount = 1 var/to_transfer = 0 @@ -52,15 +61,27 @@ /obj/item/stack/examine(mob/user) . = ..() - if(in_range(user, src)) + if(!in_range(user, src)) + return + + if(is_cyborg) if(singular_name) - . += "There are [amount] [singular_name]\s in the stack." + . += "There is enough energy for [get_amount()] [singular_name]\s." else - . += "There are [amount] [name]\s in the stack." - . +="Alt-click to take a custom amount." + . += "There is enough energy for [get_amount()]." + return + + if(singular_name) + . += "There are [amount] [singular_name]\s in the stack." + else + . += "There are [amount] [name]\s in the stack." + . +="Alt-click to take a custom amount." /obj/item/stack/proc/add(newamount) - amount += newamount + if(is_cyborg) + source.add_charge(newamount * cost) + else + amount += newamount update_icon() /obj/item/stack/attack_self(mob/user) @@ -87,8 +108,10 @@ if(!recipes) return - if(amount <= 0) + if(get_amount() <= 0) user << browse(null, "window=stack") + if(is_cyborg) + to_chat(user, "You don't have enough energy to dispense more [name]!") return user.set_machine(src) //for correct work of onclose @@ -98,7 +121,7 @@ var/datum/stack_recipe_list/srl = recipe_list[recipes_sublist] recipe_list = srl.recipes - var/t1 = "Amount Left: [amount]
" + var/t1 = "Amount Left: [get_amount()]
" for(var/i in 1 to recipe_list.len) var/E = recipe_list[i] if(isnull(E)) @@ -114,7 +137,7 @@ if(istype(E, /datum/stack_recipe)) var/datum/stack_recipe/R = E - var/max_multiplier = round(amount / R.req_amount) + var/max_multiplier = round(get_amount() / R.req_amount) var/title var/can_build = 1 can_build = can_build && (max_multiplier > 0) @@ -154,7 +177,7 @@ list_recipes(usr, text2num(href_list["sublist"])) if(href_list["make"]) - if(amount < 1) + if(amount < 0 && !is_cyborg) qdel(src) //Never should happen var/list/recipes_list = recipes @@ -167,8 +190,8 @@ if(!multiplier || multiplier <= 0 || multiplier > 50) // Href exploit checks multiplier = 1 - if(amount < R.req_amount * multiplier) - if(R.req_amount * multiplier>1) + if(get_amount() < R.req_amount * multiplier) + if(R.req_amount * multiplier > 1) to_chat(usr, "You haven't got enough [src] to build \the [R.req_amount * multiplier] [R.title]\s!") else to_chat(usr, "You haven't got enough [src] to build \the [R.title]!") @@ -195,7 +218,7 @@ if(!do_after(usr, R.time, target = usr)) return 0 - if(amount < R.req_amount * multiplier) + if(get_amount() < R.req_amount * multiplier) return var/atom/O @@ -205,6 +228,7 @@ O = new R.result_type(usr.drop_location()) O.setDir(usr.dir) use(R.req_amount * multiplier) + updateUsrDialog() R.post_build(src, O) @@ -231,6 +255,8 @@ /obj/item/stack/use(used, check = TRUE) if(check && zero_amount()) return FALSE + if(is_cyborg) + return source.use_charge(used * cost) if(amount < used) return FALSE amount -= used @@ -240,6 +266,8 @@ return TRUE /obj/item/stack/proc/get_amount() + if(is_cyborg) + return round(source.energy / cost) return amount /obj/item/stack/proc/get_max_amount() @@ -258,7 +286,7 @@ return F /obj/item/stack/attack_hand(mob/user) - if(user.is_in_inactive_hand(src) && amount > 1) + if(user.is_in_inactive_hand(src) && get_amount() > 1) change_stack(user, 1) if(src && usr.machine == src) spawn(0) @@ -272,6 +300,8 @@ return if(!in_range(src, user)) return + if(is_cyborg) + return if(!ishuman(usr)) return if(amount < 1) @@ -305,11 +335,9 @@ // Returns TRUE if the stack amount is zero. // Also qdels the stack gracefully if it is. /obj/item/stack/proc/zero_amount() + if(is_cyborg) + return source.energy < cost if(amount < 1) - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(locate(src) in R.module.modules) - R.module.modules -= src if(ismob(loc)) var/mob/living/L = loc // At this stage, stack code is so horrible and atrocious, I wouldn't be all surprised ghosts can somehow have stacks. If this happens, then the world deserves to burn. L.unEquip(src, TRUE) @@ -325,7 +353,10 @@ if(QDELETED(S) || QDELETED(src) || S == src) //amusingly this can cause a stack to consume itself, let's not allow that. return FALSE var/transfer = get_amount() - transfer = min(transfer, S.max_amount - S.amount) + if(S.is_cyborg) + transfer = min(transfer, round((S.source.max_energy - S.source.energy) / S.cost)) + else + transfer = min(transfer, S.max_amount - S.amount) if(transfer <= 0) return if(pulledby) @@ -333,6 +364,7 @@ S.copy_evidences(src) S.add(transfer) use(transfer) + return transfer /obj/item/stack/proc/copy_evidences(obj/item/stack/from) blood_DNA = from.blood_DNA diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 4eb77144528..d148123be94 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -61,8 +61,13 @@ icon_state = "tile-wood" origin_tech = "biotech=1" turf_type = /turf/simulated/floor/wood + merge_type = /obj/item/stack/tile/wood resistance_flags = FLAMMABLE +/obj/item/stack/tile/wood/cyborg + energy_type = /datum/robot_energy_storage/wood_tile + is_cyborg = TRUE + //Carpets /obj/item/stack/tile/carpet name = "carpet" @@ -101,6 +106,10 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF +/obj/item/stack/tile/plasteel/cyborg + energy_type = /datum/robot_energy_storage/metal_tile + is_cyborg = TRUE + //Light /obj/item/stack/tile/light name = "light tiles" diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm index 41801fe0287..8ab4c12cf0f 100644 --- a/code/game/objects/items/tools/welder.dm +++ b/code/game/objects/items/tools/welder.dm @@ -177,6 +177,9 @@ update_torch() ..() +/obj/item/weldingtool/cyborg_recharge(coeff, emagged) + if(reagents.check_and_add("fuel", maximum_fuel, 2 * coeff)) + update_icon() /obj/item/weldingtool/largetank name = "industrial welding tool" diff --git a/code/game/objects/items/weapons/RCL.dm b/code/game/objects/items/weapons/RCL.dm index 27583a7edd3..02827d3bcef 100644 --- a/code/game/objects/items/weapons/RCL.dm +++ b/code/game/objects/items/weapons/RCL.dm @@ -29,7 +29,7 @@ return else if(loaded.amount < max_amount) - var/amount = min(loaded.amount + C.amount, max_amount) + var/amount = min(loaded.amount + C.get_amount(), max_amount) C.use(amount - loaded.amount) loaded.amount = amount else diff --git a/code/game/objects/items/weapons/alien_specific.dm b/code/game/objects/items/weapons/alien_specific.dm index 8f6f0b2a82f..f78232653a8 100644 --- a/code/game/objects/items/weapons/alien_specific.dm +++ b/code/game/objects/items/weapons/alien_specific.dm @@ -29,6 +29,7 @@ desc = "squirts smokey liquids." icon = 'icons/mob/alien.dmi' icon_state = "borg-spray-smoke" + list_reagents = list("water" = 50) /obj/item/reagent_containers/spray/alien/smoke/afterattack(atom/A as mob|obj, mob/user as mob) if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) @@ -45,17 +46,29 @@ smoke.start() playsound(user.loc, 'sound/effects/bamf.ogg', 50, 2) +/obj/item/reagent_containers/spray/alien/smoke/cyborg_recharge(coeff, emagged) + reagents.check_and_add("water", volume, 2 * coeff) + /obj/item/reagent_containers/spray/alien/acid name = "acid synthesizer" desc = "squirts burny liquids." icon = 'icons/mob/alien.dmi' icon_state = "borg-spray-acid" + list_reagents = list("facid" = 125, "sacid" = 125) + +/obj/item/reagent_containers/spray/alien/acid/cyborg_recharge(coeff, emagged) + reagents.check_and_add("facid", volume / 2, 2 * coeff) // Volume / 2 here becuase there should be an even amount of both chems. + reagents.check_and_add("sacid", volume / 2, 2 * coeff) /obj/item/reagent_containers/spray/alien/stun name = "paralytic toxin synthesizer" desc = "squirts viagra." icon = 'icons/mob/alien.dmi' icon_state = "borg-spray-stun" + list_reagents = list("ether" = 250) + +/obj/item/reagent_containers/spray/alien/stun/cyborg_recharge(coeff, emagged) + reagents.check_and_add("ether", volume, 2 * coeff) //SKREEEEEEEEEEEE tool diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index ecc485737a4..0a3e8cd4a70 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -180,3 +180,6 @@ sleep(2) else return ..() + +/obj/item/extinguisher/cyborg_recharge(coeff, emagged) + reagents.check_and_add("water", max_water, 5 * coeff) diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index f2b0b475e30..f93f7220c75 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -314,7 +314,7 @@ //Therefore, make a new stack internally that has the remainder. // -Sayu - if(S.amount > S.max_amount) + if(S.get_amount() > S.max_amount) var/obj/item/stack/sheet/temp = new S.type(src) temp.amount = S.amount - S.max_amount S.amount = S.max_amount diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 0df5817f598..a7d4bc4be3b 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -206,6 +206,11 @@ return TRUE ..() +/obj/item/melee/baton/cyborg_recharge(coeff, emagged) + if(cell) + cell.charge = cell.maxcharge + update_icon() + //Makeshift stun baton. Replacement for stun gloves. /obj/item/melee/baton/cattleprod name = "stunprod" diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index e6341f14ed7..437721f383a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2810,7 +2810,7 @@ if(R.module) R.module.modules += I I.loc = R.module - R.module.rebuild() + R.module.rebuild_modules() R.activate_module(I) R.module.fix_modules() diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 13e2d7bcb16..bfeaaa5c374 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -961,15 +961,13 @@ if(1) if(iscoil(I)) var/obj/item/stack/cable_coil/coil = I - if(coil.amount < 5) + if(coil.get_amount() < 5) to_chat(user, "You need more cable for this!") return to_chat(user, "You wire \the [src]!") playsound(get_turf(src), coil.usesound, 50, 1) - coil.amount -= 5 - if(!coil.amount) - qdel(coil) + coil.use(5) buildstage = 2 update_icon() diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index f6dbb48ed63..0fc71ecf4c9 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -245,7 +245,7 @@ var/obj/item/stack/SD while(amt > 0) S = locate(A) in surroundings - if(S.amount >= amt) + if(S.get_amount() >= amt) if(!locate(S.type) in Deletion) SD = new S.type() Deletion += SD diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index 897e11443ff..9d9e81be9af 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -114,6 +114,9 @@ icon_state = "enzyme" list_reagents = list("enzyme" = 50) +/obj/item/reagent_containers/food/condiment/enzyme/cyborg_recharge(coeff, emagged) + reagents.check_and_add("enzyme", volume, 2 * coeff) // Only recharge if the current amount of enzyme is under `volume`. + /obj/item/reagent_containers/food/condiment/sugar name = "sugar bottle" desc = "Tasty spacey sugar!" diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm index 72782f532a6..12f09adeb10 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm @@ -115,7 +115,7 @@ return 1 if(istype(O,/obj/item/stack)) var/obj/item/stack/S = O - if(S.amount > 1) + if(S.get_amount() > 1) var/obj/item/stack/to_add = S.split(user, 1) to_add.forceMove(src) user.visible_message("[user] adds one of [S] to [src].", "You add one of [S] to [src].") diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index fe05b61d867..8a98f7e30d2 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -307,7 +307,7 @@ return FALSE if(istype(I, /obj/item/stack)) var/obj/item/stack/S = I - if(S.amount == 0) + if(!S.get_amount()) qdel(I) return FALSE if(put_in_active_hand(I)) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 16e81f53276..d1156b5c81d 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -179,62 +179,6 @@ to_chat(user, "Nothing on \the [T] is useful to you.") return -//PRETTIER TOOL LIST. -/mob/living/silicon/robot/drone/installed_modules() - - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - module = new /obj/item/robot_module/drone(src) - - var/dat = "Drone modules\n" - dat += {"Close -
-
- Activated Modules -
- Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
- Module 2: [module_state_2 ? "
[module_state_2]" : "No Module"]
- Module 3: [module_state_3 ? "
[module_state_3]" : "No Module"]
-
- Installed Modules

"} - - - var/tools = "Tools and devices
" - var/resources = "
Resources
" - - for(var/O in module.modules) - - var/module_string = "" - - if(!O) - module_string += text("Resource depleted
") - else if(activated(O)) - module_string += text("[O]: Activated
") - else - module_string += text("[O]:
Activate
") - - if((istype(O,/obj/item) || istype(O,/obj/item)) && !(istype(O,/obj/item/stack/cable_coil))) - tools += module_string - else - resources += module_string - - dat += tools - - if(emagged) - if(!module.emag) - dat += text("Resource depleted
") - else if(activated(module.emag)) - dat += text("[module.emag]: Activated
") - else - dat += text("[module.emag]: Activate
") - - dat += resources - - src << browse(dat, "window=robotmod&can_close=0") - //Putting the decompiler here to avoid doing list checks every tick. /mob/living/silicon/robot/drone/use_power() diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 32ce86e582f..3357bddc925 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -40,7 +40,7 @@ return 1 /mob/living/silicon/robot/proc/activate_module(obj/item/O) - if(!(locate(O) in src.module.modules) && O != src.module.emag) + if(!(locate(O) in module.modules) && !(O in module.emag_modules)) return if(activated(O)) to_chat(src, "Already activated") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index c67d64a72b1..9bacb40fff2 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -482,6 +482,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( speed = 0 // Remove upgrades. ionpulse = FALSE magpulse = FALSE + weapons_unlock = FALSE add_language("Robot Talk", TRUE) if("lava" in weather_immunities) // Remove the lava-immunity effect given by a printable upgrade weather_immunities -= "lava" @@ -635,17 +636,27 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( else stat(null, text("No Cell Inserted!")) +/mob/living/silicon/robot/proc/show_gps_coords() + if(locate(/obj/item/gps/cyborg) in module.modules) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + +/mob/living/silicon/robot/proc/show_stack_energy() + for(var/storage in module.storages) // Storages should only contain `/datum/robot_energy_storage` + var/datum/robot_energy_storage/R = storage + stat(null, "[R.statpanel_name]: [R.energy] / [R.max_energy]") // update the status screen display /mob/living/silicon/robot/Stat() ..() - statpanel("Status") - if(client.statpanel == "Status") - show_cell_power() - var/total_user_contents = GetAllContents() - if(locate(/obj/item/gps/cyborg) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") + if(!statpanel("Status")) + return // They aren't looking at the status panel. + + show_cell_power() + + if(module) + show_gps_coords() + show_stack_energy() /mob/living/silicon/robot/restrained() return 0 @@ -774,16 +785,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( 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.drop_item()) return if(U.action(src)) - user.visible_message("[user] applied [U] to [src].", "You apply [U] to [src].") + user.visible_message("[user] applied [U] to [src].", "You apply [U] to [src].") U.forceMove(src) - else - to_chat(user, "Upgrade error.") else if(istype(W, /obj/item/mmi_radio_upgrade)) if(!opened) @@ -910,7 +917,19 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( spark_system.start() ..() -/mob/living/silicon/robot/emag_act(user as mob) +// Here so admins can unemag borgs. +/mob/living/silicon/robot/unemag() + SetEmagged(FALSE) + if(!module) + return + uneq_all() + module.module_type = initial(module.module_type) + update_module_icon() + module.unemag() + clear_supplied_laws() + laws = new /datum/ai_laws/crewsimov + +/mob/living/silicon/robot/emag_act(mob/user) if(!ishuman(user) && !issilicon(user)) return var/mob/living/M = user @@ -925,7 +944,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(emagged) + return //Prevents the X has hit Y with Z message also you cant emag them twice if(wiresexposed) to_chat(user, "You must close the panel first") return @@ -933,11 +953,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( sleep(6) SetEmagged(TRUE) SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(src.hud_used) - src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. + if(hud_used) + hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. disconnect_from_ai() to_chat(user, "You emag [src]'s interface.") -// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") clear_supplied_laws() clear_inherent_laws() @@ -963,17 +982,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( laws.show_laws(src) to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") SetLockdown(0) - if(src.module && istype(src.module, /obj/item/robot_module/miner)) - for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) - qdel(D) - src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) - src.module.rebuild() - if(src.module && istype(src.module, /obj/item/robot_module/medical)) - for(var/obj/item/borg_defib/F in src.module.modules) - F.safety = 0 if(module) + module.emag_act() module.module_type = "Malf" // For the cool factor update_module_icon() + module.rebuild_modules() // This will add the emagged items to the borgs inventory. update_icons() return @@ -1041,51 +1054,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it return -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = {"Close -
-
- Activated Modules -
- - - - -
Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

- Installed Modules

- - "} - for(var/obj in module.modules) - if(!obj) - dat += text("") - else if(activated(obj)) - dat += text("") - else - dat += text("") - if(emagged || weapons_unlock) - if(activated(module.emag)) - dat += text("") - else - dat += text("") - dat += "
Resource depleted
[obj]Activated
[obj]Activate
[module.emag]Activated
[module.emag]Activate
" -/* - if(activated(obj)) - dat += text("[obj]: \[Activated | Deactivate\]
") - else - dat += text("[obj]: \[Activate | Deactivated\]
") -*/ - var/datum/browser/popup = new(src, "robotmod", "Modules") - popup.set_content(dat) - popup.open() - - /mob/living/silicon/robot/Topic(href, href_list) if(..()) return 1 @@ -1111,7 +1079,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return 1 activate_module(O) - installed_modules() //Show alerts window if user clicked on "Show alerts" in chat if(href_list["showalerts"]) @@ -1134,7 +1101,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( to_chat(src, "Module isn't activated.") else to_chat(src, "Module isn't activated") - installed_modules() return 1 return 1 diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 29fd2a0235c..71d8d5ef19b 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -6,77 +6,213 @@ item_state = "electronic" flags = CONDUCT + /// A list of all currently usable and created modules the robot currently has access too. var/list/modules = list() - var/obj/item/emag = null - var/list/subsystems = list() + /// A list of module-specific, non-emag modules the borg will gain when this module is chosen. + var/list/basic_modules = list() + /// A list of modules the robot gets when emagged. + var/list/emag_modules = list() + /// A list of modules that require special recharge handling. Examples include things like flashes, sprays and welding tools. + var/list/special_rechargables = list() + /// A list of all "energy stacks", i.e metal, glass, brute kits, splints, etc. + var/list/storages = list() + /// Special actions this module will gain when chosen such as meson vision, or thermal vision. var/list/module_actions = list() - - var/module_type = "NoMod" // For icon usage - - var/list/stacktypes - var/channels = list() + /// Available tools given in the verb tab such as a crew monitor, or power monitor. + var/list/subsystems = list() + /// Radio channels the module owner has access to. + var/list/channels = list() + /// Special items that are able to be removed from the robot owner via crowbar. var/list/custom_removals = list() - - -/obj/item/robot_module/emp_act(severity) - if(modules) - for(var/obj/O in modules) - O.emp_act(severity) - if(emag) - emag.emp_act(severity) - ..() + /// For icon usage. + var/module_type = "NoMod" /obj/item/robot_module/New() ..() - add_default_robot_items() - emag = new /obj/item/toy/sword(src) - emag.name = "Placeholder Emag Item" + + // Creates new objects from the type lists. + for(var/i in basic_modules) + var/obj/item/I = new i(src) + basic_modules += I + basic_modules -= i + + // Even though these are created here the robot won't be able to see and equip them until they actually get emagged/hacked. + for(var/i in emag_modules) + var/obj/item/I = new i(src) + emag_modules += I + emag_modules -= i + + // Flashes need a special recharge, and since basically every module uses it, add it here. + // Even if the module doesn't use a flash, it wont cause any issues to have it in this list. + special_rechargables += /obj/item/flash/cyborg + + // This is done so we can loop through this list later and call cyborg_recharge() on the items while the borg is recharging. + var/all_modules = basic_modules | emag_modules + for(var/path in special_rechargables) + var/obj/item/I = locate(path) in all_modules + if(I) // If it exists, add the object reference. + special_rechargables += I + special_rechargables -= path // No matter what, remove the path from the list. + + // Add all the modules into the robot's inventory. Without this, their inventory will be blank. + rebuild_modules() + + +/obj/item/robot_module/emp_act(severity) + . = ..() + for(var/object in modules) + var/obj/O = object + O.emp_act(severity) /obj/item/robot_module/Destroy() + // These can all contain actual objects, so we need to null them out. QDEL_LIST(modules) - QDEL_NULL(emag) + QDEL_LIST(basic_modules) + QDEL_LIST(emag_modules) + QDEL_LIST(storages) + QDEL_LIST(special_rechargables) return ..() -// By default, all robots will get the items in this proc, unless you override it for your specific module. See: ../robot_module/drone -/obj/item/robot_module/proc/add_default_robot_items() - modules += new /obj/item/flash/cyborg(src) +/** + * Searches through the various module lists for the given `item_type`, deletes and removes the item from all supplied lists, if the item is found. + * + * NOTE: be careful with using this proc, as it irreversibly removes entries from a borg's module list. + * This is safe to do with upgrades because the only way to revert upgrades currently is either to rebuild the borg or use a reset module, which allows the lists to regenerate. + * + * Arugments: + * * item_type - the type of item to search for. Also accepts objects themselves. + */ +/obj/item/robot_module/proc/remove_item_from_lists(item_or_item_type) + var/list/lists = list( + basic_modules, + emag_modules, + storages, + special_rechargables + ) + for(var/_list in lists) + for(var/item in _list) + var/obj/item/I = item + if(!istype(I, item_or_item_type)) + continue + if(!QDELETED(I)) + qdel(I) + _list -= I + +// Here for admin debugging purposes only. /obj/item/robot_module/proc/fix_modules() - for(var/obj/item/I in modules) + for(var/item in modules) + var/obj/item/I = item I.flags |= NODROP I.mouse_opacity = MOUSE_OPACITY_OPAQUE - if(emag) - emag.flags |= NODROP - emag.mouse_opacity = MOUSE_OPACITY_OPAQUE -/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) - if(!stacktypes || !stacktypes.len) +/** + * Returns a `robot_energy_strage` datum of type `storage_type`. If one already exists, it returns that one, otherwise it create a new one. + * + * Arguments: + * * storage_type - the subtype of `datum/robot_energy_storage` to fetch or create. + */ +/obj/item/robot_module/proc/get_or_create_estorage(storage_type) + for(var/e_storage in storages) + var/datum/robot_energy_storage/S = e_storage + if(istype(S, storage_type)) + return S + return new storage_type(src) + +/** + * Adds the item `I` to our `modules` list, and sets up an `/datum/robot_energy_storage` if its a stack. + * + * Arugments: + * * I - the item to add to our modules. + * * requires_rebuild - if adding this item requires `rebuild_modules()` to be called. + */ +/obj/item/robot_module/proc/add_module(obj/item/I, requires_rebuild) + if(I in modules) // No duplicate items. return - var/stack_respawned = 0 - for(var/T in stacktypes) - var/O = locate(T) in modules - var/obj/item/stack/S = O + if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I - if(!S) - S = new T(src) - modules += S - S.amount = 1 - stack_respawned = 1 + if(S.energy_type) + S.source = get_or_create_estorage(S.energy_type) + S.is_cyborg = TRUE - if(S && S.amount < stacktypes[T]) - S.amount++ - if(stack_respawned && istype(R) && R.hud_used) + // Here for safety. Using a cyborg stack without a `source` will create hundreds of runtimes. + if(!S.source) + qdel(S) + return + + if(I.loc != src) + I.forceMove(src) + + modules += I + I.flags |= NODROP + I.mouse_opacity = MOUSE_OPACITY_OPAQUE + + if(requires_rebuild) + rebuild_modules() + return I + +/** + * Builds the usable module list from the modules we have in `basic_modules` and `emag_modules` + */ +/obj/item/robot_module/proc/rebuild_modules() + var/mob/living/silicon/robot/R = loc + R.uneq_all() + modules = list() + + // By this point these lists should only contain items. It's safe to use typeless loops here. + for(var/item in basic_modules) + add_module(item, FALSE) + + if(R.emagged || R.weapons_unlock) + for(var/item in emag_modules) + add_module(item, FALSE) + + if(R.hud_used) R.hud_used.update_robot_modules_display() -/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module - var/list/temp_list = modules - modules = list() - for(var/obj/O in temp_list) - if(!QDELETED(O)) //so items getting deleted don't stay in module list and haunt you - modules += O +/** + * Handles the recharging of all borg stack items and any items which are in the `special_rechargables` list. + * + * Arguments: + * * R - the owner of this module. + * * coeff - a coefficient which can be used to modify the recharge rate of consumables. + */ +/obj/item/robot_module/proc/recharge_consumables(mob/living/silicon/robot/R, coeff = 1) + for(var/e_storage in storages) + var/datum/robot_energy_storage/E = e_storage + E.add_charge(max(1, coeff * E.recharge_rate)) + for(var/item in special_rechargables) + var/obj/item/I = item + I.cyborg_recharge(coeff, R.emagged) +/** + * Called when the robot owner of this module has the `unemag()` proc called on them, which is only via admin means. + * + * Deletes this module's emag items, and recreates them. + */ +/obj/item/robot_module/unemag() + . = ..() + for(var/item in emag_modules) + var/obj/item/old_item = item + var/obj/item/new_item = new old_item.type(src) + emag_modules += new_item + if(old_item in special_rechargables) // If the old item was in this list, make sure the new one goes into it as well. + special_rechargables += new_item + special_rechargables -= old_item + modules -= old_item + emag_modules -= old_item + qdel(old_item) + rebuild_modules() + +/** + * Adds all of the languages this module is suppose to know and/or speak. + * + * Arugments: + * * R - the owner of this module. + */ /obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) //full set of languages R.add_language("Galactic Common", 1) @@ -94,8 +230,9 @@ R.add_language("Chittin", 0) R.add_language("Bubblish", 0) R.add_language("Orluum", 0) - R.add_language("Clownish",0) + R.add_language("Clownish", 0) +/// Adds anything in `subsystems` to the robot's verbs, and grants any actions that are in `module_actions`. /obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) R.verbs |= subsystems for(var/A in module_actions) @@ -103,6 +240,7 @@ act.Grant(R) R.module_actions += act +/// Removes any verbs from the robot that are in `subsystems`, and removes any actions that are in `module_actions`. /obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) R.verbs -= subsystems for(var/datum/action/A in R.module_actions) @@ -117,6 +255,7 @@ /obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed) return +// Standard cyborg module. /obj/item/robot_module/standard // if station is fine, assist with constructing station goal room, cleaning, and repairing cables chewed by rats // if medical crisis, assist by providing basic healthcare, retrieving corpses, and monitoring crew lifesigns @@ -125,221 +264,214 @@ name = "generalist robot module" module_type = "Standard" subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor, /mob/living/silicon/proc/subsystem_crew_monitor) - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/standard/New() - ..() - // sec - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - // janitorial - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/lightreplacer/cyborg(src) - // eng - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/extinguisher(src) // for firefighting, and propulsion in space - modules += new /obj/item/weldingtool/largetank/cyborg(src) - // mining - modules += new /obj/item/pickaxe(src) - modules += new /obj/item/t_scanner/adv_mining_scanner(src) - modules += new /obj/item/storage/bag/ore/cyborg(src) - // med - modules += new /obj/item/healthanalyzer(src) - modules += new /obj/item/reagent_containers/borghypo/basic(src) - modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way - emag = new /obj/item/melee/energy/sword/cyborg(src) - for(var/G in stacktypes) - var/obj/item/stack/sheet/M = new G(src) - M.amount = stacktypes[G] - modules += M - fix_modules() - + basic_modules = list( + // sec + /obj/item/flash/cyborg, + /obj/item/restraints/handcuffs/cable/zipties/cyborg, + // janitorial + /obj/item/soap/nanotrasen, + /obj/item/lightreplacer/cyborg, + // eng + /obj/item/crowbar/cyborg, + /obj/item/wrench/cyborg, + /obj/item/extinguisher, // for firefighting, and propulsion in space + /obj/item/weldingtool/largetank/cyborg, + // mining + /obj/item/pickaxe, + /obj/item/t_scanner/adv_mining_scanner, + /obj/item/storage/bag/ore/cyborg, + // med + /obj/item/healthanalyzer, + /obj/item/reagent_containers/borghypo/basic, + /obj/item/roller_holder, // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/cable_coil/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg + ) + emag_modules = list(/obj/item/melee/energy/sword/cyborg) + special_rechargables = list( + /obj/item/extinguisher, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/lightreplacer/cyborg + ) +// Medical cyborg module. /obj/item/robot_module/medical name = "medical robot module" module_type = "Medical" subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 6, - /obj/item/stack/medical/ointment/advanced = 6, - /obj/item/stack/medical/splint = 6, - /obj/item/stack/nanopaste = 6 - ) + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/healthanalyzer/advanced, + /obj/item/robotanalyzer, + /obj/item/reagent_scanner/adv, + /obj/item/borg_defib, + /obj/item/handheld_defibrillator, + /obj/item/roller_holder, + /obj/item/reagent_containers/borghypo, + /obj/item/scalpel/laser/laser1, + /obj/item/hemostat, + /obj/item/retractor, + /obj/item/circular_saw, + /obj/item/surgicaldrill, + /obj/item/bonesetter, + /obj/item/bonegel, + /obj/item/FixOVein, + /obj/item/extinguisher/mini, + /obj/item/reagent_containers/glass/beaker/large, + /obj/item/reagent_containers/dropper, + /obj/item/reagent_containers/syringe, + /obj/item/stack/medical/bruise_pack/advanced/cyborg, + /obj/item/stack/medical/ointment/advanced/cyborg, + /obj/item/stack/medical/splint/cyborg, + /obj/item/stack/nanopaste/cyborg, + /obj/item/gripper/medical + ) + emag_modules = list(/obj/item/reagent_containers/spray/cyborg_facid) + special_rechargables = list(/obj/item/reagent_containers/spray/cyborg_facid, /obj/item/extinguisher/mini) -/obj/item/robot_module/medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/robotanalyzer(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo(src) - modules += new /obj/item/reagent_containers/glass/beaker/large(src) - modules += new /obj/item/reagent_containers/dropper(src) - modules += new /obj/item/reagent_containers/syringe(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) +// Disable safeties on the borg's defib. +/obj/item/robot_module/medical/emag_act() + . = ..() + for(var/obj/item/borg_defib/F in modules) + F.safety = FALSE - emag = new /obj/item/reagent_containers/spray(src) +// Enable safeties on the borg's defib. +/obj/item/robot_module/medical/unemag() + for(var/obj/item/borg_defib/F in modules) + F.safety = TRUE + return ..() - emag.reagents.add_reagent("facid", 250) - emag.name = "Polyacid spray" +// Fluorosulphuric acid spray bottle. +/obj/item/reagent_containers/spray/cyborg_facid + name = "Polyacid spray" + list_reagents = list("facid" = 250) - fix_modules() - -/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/spray/PS = emag - PS.reagents.add_reagent("facid", 2) - ..() +/obj/item/reagent_containers/spray/cyborg_facid/cyborg_recharge(coeff, emagged) + if(emagged) + reagents.check_and_add("facid", volume, 2 * coeff) +// Engineering cyborg module. /obj/item/robot_module/engineering name = "engineering robot module" module_type = "Engineer" subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) - module_actions = list( - /datum/action/innate/robot_sight/meson, + module_actions = list(/datum/action/innate/robot_sight/meson) + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/rcd/borg, + /obj/item/rpd, + /obj/item/extinguisher, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/screwdriver/cyborg, + /obj/item/wrench/cyborg, + /obj/item/crowbar/cyborg, + /obj/item/wirecutters/cyborg, + /obj/item/multitool/cyborg, + /obj/item/t_scanner, + /obj/item/analyzer, + /obj/item/geiger_counter/cyborg, + /obj/item/holosign_creator/engineering, + /obj/item/gripper, + /obj/item/matter_decompiler, + /obj/item/floor_painter, + /obj/item/areaeditor/blueprints/cyborg, + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg, + /obj/item/stack/cable_coil/cyborg, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/sheet/rglass/cyborg ) - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/engineering/New() - ..() - modules += new /obj/item/rcd/borg(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/geiger_counter/cyborg(src) - modules += new /obj/item/holosign_creator/engineering(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/floor_painter(src) - modules += new /obj/item/areaeditor/blueprints/cyborg(src) - emag = new /obj/item/borg/stun(src) - - for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. - var/obj/item/stack/sheet/M = new G(src) - M.amount = stacktypes[G] - modules += M - - fix_modules() + emag_modules = list(/obj/item/borg/stun) + special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg) /obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed) var/obj/item/gripper/G = locate(/obj/item/gripper) in modules if(G) G.drop_gripped_item(silent = TRUE) +// Security cyborg module. /obj/item/robot_module/security name = "security robot module" module_type = "Security" subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/restraints/handcuffs/cable/zipties/cyborg, + /obj/item/melee/baton/loaded, + /obj/item/gun/energy/disabler/cyborg, + /obj/item/holosign_creator/security, + /obj/item/clothing/mask/gas/sechailer/cyborg + ) + emag_modules = list(/obj/item/gun/energy/laser/cyborg) + special_rechargables = list(/obj/item/melee/baton/loaded, /obj/item/gun/energy/disabler/cyborg) -/obj/item/robot_module/security/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/gun/energy/disabler/cyborg(src) - modules += new /obj/item/holosign_creator/security(src) - modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) - emag = new /obj/item/gun/energy/laser/cyborg(src) - - fix_modules() - +// Janitor cyborg module. /obj/item/robot_module/janitor name = "janitorial robot module" module_type = "Janitor" + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/soap/nanotrasen, + /obj/item/storage/bag/trash/cyborg, + /obj/item/mop/advanced/cyborg, + /obj/item/lightreplacer/cyborg, + /obj/item/holosign_creator, + /obj/item/extinguisher/mini + ) + emag_modules = list(/obj/item/reagent_containers/spray/cyborg_lube) + special_rechargables = list( + /obj/item/lightreplacer, + /obj/item/reagent_containers/spray/cyborg_lube, + /obj/item/extinguisher/mini + ) -/obj/item/robot_module/janitor/New() - ..() - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/storage/bag/trash/cyborg(src) - modules += new /obj/item/mop/advanced/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/holosign_creator(src) - modules += new /obj/item/extinguisher/mini(src) - emag = new /obj/item/reagent_containers/spray(src) +/obj/item/reagent_containers/spray/cyborg_lube + name = "Lube spray" + list_reagents = list("lube" = 250) - emag.reagents.add_reagent("lube", 250) - emag.name = "Lube spray" - - fix_modules() - -/obj/item/robot_module/janitor/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/spray/S = emag - S.reagents.add_reagent("lube", 5) - ..() +/obj/item/reagent_containers/spray/cyborg_lube/cyborg_recharge(coeff, emagged) + if(emagged) + reagents.check_and_add("lube", volume, 2 * coeff) +// Service cyborg module. /obj/item/robot_module/butler name = "service robot module" module_type = "Service" + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/handheld_chem_dispenser/booze, + /obj/item/handheld_chem_dispenser/soda, + /obj/item/pen, + /obj/item/razor, + /obj/item/instrument/piano_synth, + /obj/item/healthanalyzer/advanced, + /obj/item/reagent_scanner/adv, + /obj/item/rsf/cyborg, + /obj/item/reagent_containers/dropper/cyborg, + /obj/item/lighter/zippo, + /obj/item/storage/bag/tray/cyborg, + /obj/item/reagent_containers/food/drinks/shaker + ) + emag_modules = list(/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer) + special_rechargables = list( + /obj/item/reagent_containers/food/condiment/enzyme, + /obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer + ) -/obj/item/robot_module/butler/New() - ..() - modules += new /obj/item/handheld_chem_dispenser/booze(src) - modules += new /obj/item/handheld_chem_dispenser/soda(src) +/obj/item/rsf/cyborg + matter = 30 - modules += new /obj/item/pen(src) - modules += new /obj/item/razor(src) - modules += new /obj/item/instrument/piano_synth(src) - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) +// This is a special type of beer given when emagged, one sip and the target falls asleep. +/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer + name = "Mickey Finn's Special Brew" + list_reagents = list("beer2" = 50) - var/obj/item/rsf/M = new /obj/item/rsf(src) - M.matter = 30 - modules += M - - modules += new /obj/item/reagent_containers/dropper/cyborg(src) - modules += new /obj/item/lighter/zippo(src) - modules += new /obj/item/storage/bag/tray/cyborg(src) - modules += new /obj/item/reagent_containers/food/drinks/shaker(src) - emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) - - var/datum/reagents/R = new/datum/reagents(50) - emag.reagents = R - R.my_atom = emag - R.add_reagent("beer2", 50) - emag.name = "Mickey Finn's Special Brew" - - fix_modules() - -/obj/item/robot_module/butler/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag - B.reagents.add_reagent("beer2", 5) - ..() +/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer/cyborg_recharge(coeff, emagged) + if(emagged) + reagents.check_and_add("beer2", volume, 5) /obj/item/robot_module/butler/add_languages(mob/living/silicon/robot/R) //full set of languages @@ -368,267 +500,352 @@ /obj/item/robot_module/miner name = "miner robot module" module_type = "Miner" - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) + module_actions = list(/datum/action/innate/robot_sight/meson) custom_removals = list("KA modkits") + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/storage/bag/ore/cyborg, + /obj/item/pickaxe/drill/cyborg, + /obj/item/shovel, + /obj/item/weldingtool/mini, + /obj/item/extinguisher/mini, + /obj/item/storage/bag/sheetsnatcher/borg, + /obj/item/t_scanner/adv_mining_scanner/cyborg, + /obj/item/gun/energy/kinetic_accelerator/cyborg, + /obj/item/gps/cyborg + ) + emag_modules = list(/obj/item/borg/stun, /obj/item/pickaxe/drill/cyborg/diamond) + special_rechargables = list(/obj/item/extinguisher/mini, /obj/item/weldingtool/mini) -/obj/item/robot_module/miner/New() - ..() - modules += new /obj/item/storage/bag/ore/cyborg(src) - modules += new /obj/item/pickaxe/drill/cyborg(src) - modules += new /obj/item/shovel(src) - modules += new /obj/item/weldingtool/mini(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) - modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) - modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) - modules += new /obj/item/gps/cyborg(src) - emag = new /obj/item/borg/stun(src) +// Replace their normal drill with a diamond drill. +/obj/item/robot_module/miner/emag_act() + . = ..() + for(var/obj/item/pickaxe/drill/cyborg/D in modules) + // Make sure we don't remove the diamond drill If they already have a diamond drill from the borg upgrade. + if(!istype(D, /obj/item/pickaxe/drill/cyborg/diamond)) + qdel(D) + basic_modules -= D // Remove it from this list so it doesn't get added in the rebuild. - fix_modules() +// Readd the normal drill +/obj/item/robot_module/miner/unemag() + var/obj/item/pickaxe/drill/cyborg/C = new(src) + basic_modules += C + return ..() +// This makes it so others can crowbar out KA upgrades from the miner borg. /obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) - if(component_id == "KA modkits") - for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) - D.attackby(W, user) - return TRUE - return ..() + if(component_id == "KA modkits") + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) + D.attackby(W, user) + return TRUE + return ..() +// Deathsquad cyborg module. /obj/item/robot_module/deathsquad name = "NT advanced combat module" module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, + module_actions = list(/datum/action/innate/robot_sight/thermal) + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/melee/energy/sword/cyborg, + /obj/item/gun/energy/pulse/cyborg, + /obj/item/crowbar/cyborg ) -/obj/item/robot_module/deathsquad/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/pulse/cyborg(src) - modules += new /obj/item/crowbar(src) - emag = null - - fix_modules() - +// Sydicate assault cyborg module. /obj/item/robot_module/syndicate name = "syndicate assault robot module" module_type = "Malf" // cuz it looks cool + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/melee/energy/sword/cyborg, + /obj/item/gun/energy/printer, + /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg, + /obj/item/card/emag, + /obj/item/crowbar/cyborg, + /obj/item/pinpointer/operative + ) -/obj/item/robot_module/syndicate/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/printer(src) - modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - +// Sydicate medical cyborg module. /obj/item/robot_module/syndicate_medical name = "syndicate medical robot module" module_type = "Malf" - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 25, - /obj/item/stack/medical/ointment/advanced = 25, - /obj/item/stack/medical/splint = 25, - /obj/item/stack/nanopaste = 25 + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/healthanalyzer/advanced, + /obj/item/reagent_scanner/adv, + /obj/item/bodyanalyzer/borg/syndicate, + /obj/item/borg_defib, + /obj/item/handheld_defibrillator, + /obj/item/roller_holder, + /obj/item/reagent_containers/borghypo/syndicate, + /obj/item/scalpel/laser/laser1, + /obj/item/hemostat, + /obj/item/retractor, + /obj/item/melee/energy/sword/cyborg/saw, //Energy saw -- primary weapon + /obj/item/surgicaldrill, + /obj/item/bonesetter, + /obj/item/bonegel, + /obj/item/FixOVein, + /obj/item/card/emag, + /obj/item/crowbar/cyborg, + /obj/item/pinpointer/operative, + /obj/item/stack/medical/bruise_pack/advanced/cyborg/syndicate, + /obj/item/stack/medical/ointment/advanced/cyborg/syndicate, + /obj/item/stack/medical/splint/cyborg/syndicate, + /obj/item/stack/nanopaste/cyborg/syndicate, + /obj/item/gun/medbeam, + /obj/item/extinguisher/mini, + /obj/item/gripper/medical ) + special_rechargables = list(/obj/item/extinguisher/mini) -/obj/item/robot_module/syndicate_medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo/syndicate(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - modules += new /obj/item/gun/medbeam(src) - modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - +// Sydicate engineer/sabotuer cyborg module. /obj/item/robot_module/syndicate_saboteur name = "engineering robot module" //to disguise in examine module_type = "Malf" - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/syndicate_saboteur/New() - ..() - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/borg_chameleon(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/rcd/borg/syndicate, + /obj/item/rpd, + /obj/item/extinguisher, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/screwdriver/cyborg, + /obj/item/wrench/cyborg, + /obj/item/crowbar/cyborg, + /obj/item/wirecutters/cyborg, + /obj/item/multitool/cyborg, + /obj/item/t_scanner, + /obj/item/analyzer, + /obj/item/gripper, + /obj/item/melee/energy/sword/cyborg, + /obj/item/card/emag, + /obj/item/borg_chameleon, + /obj/item/pinpointer/operative, + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg, + /obj/item/stack/cable_coil/cyborg, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/sheet/rglass/cyborg + ) + special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg) /obj/item/robot_module/destroyer name = "destroyer robot module" module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, + module_actions = list(/datum/action/innate/robot_sight/thermal) + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/gun/energy/immolator/multi/cyborg, // See comments on /robot_module/combat below + /obj/item/melee/baton/loaded, // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats + /obj/item/restraints/handcuffs/cable/zipties/cyborg, + /obj/item/pickaxe/drill/jackhammer, // for breaking walls to execute flanking moves + /obj/item/borg/destroyer/mobility ) - -/obj/item/robot_module/destroyer/New() - ..() - - modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // See comments on /robot_module/combat below - modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves - modules += new /obj/item/borg/destroyer/mobility(src) - emag = null - fix_modules() - + special_rechargables = list(/obj/item/melee/baton/loaded) /obj/item/robot_module/combat name = "combat robot module" module_type = "Malf" - module_actions = list() - -/obj/item/robot_module/combat/New() - ..() - modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot. - // Borg gets 40 shots of this weapon. Gamma Sec ERT gets 10. - // So, borg has way more burst damage, but also takes way longer to recharge / get back in the fight once depleted. Has to find a borg recharger and sit in it for ages. - // Organic gamma sec ERT carries alternate weapons, including a box of flashbangs, and can load up on a huge number of guns from science. Borg cannot do either. - // Overall, gamma borg has higher skill floor but lower skill ceiling. - modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves - emag = null - fix_modules() - + basic_modules = list( + /obj/item/flash/cyborg, + /obj/item/gun/energy/immolator/multi/cyborg, // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot. + // Borg gets 40 shots of this weapon. Gamma Sec ERT gets 10. + // So, borg has way more burst damage, but also takes way longer to recharge / get back in the fight once depleted. Has to find a borg recharger and sit in it for ages. + // Organic gamma sec ERT carries alternate weapons, including a box of flashbangs, and can load up on a huge number of guns from science. Borg cannot do either. + // Overall, gamma borg has higher skill floor but lower skill ceiling. + /obj/item/melee/baton/loaded, // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats + /obj/item/restraints/handcuffs/cable/zipties/cyborg, + /obj/item/pickaxe/drill/jackhammer // for breaking walls to execute flanking moves + ) + special_rechargables = list(/obj/item/melee/baton/loaded) +// Xenomorph cyborg module. /obj/item/robot_module/alien/hunter name = "alien hunter module" module_type = "Standard" - module_actions = list( - /datum/action/innate/robot_sight/thermal/alien, + module_actions = list(/datum/action/innate/robot_sight/thermal/alien) + basic_modules = list( + /obj/item/melee/energy/alien/claws, + /obj/item/flash/cyborg/alien, + /obj/item/reagent_containers/spray/alien/stun, + /obj/item/reagent_containers/spray/alien/smoke, + ) + emag_modules = list(/obj/item/reagent_containers/spray/alien/acid) + special_rechargables = list( + /obj/item/reagent_containers/spray/alien/acid, + /obj/item/reagent_containers/spray/alien/stun, + /obj/item/reagent_containers/spray/alien/smoke ) -/obj/item/robot_module/alien/hunter/New() - ..() - modules += new /obj/item/melee/energy/alien/claws(src) - modules += new /obj/item/flash/cyborg/alien(src) - var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) - S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. - modules += S - var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) - S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. - modules += A - emag = new /obj/item/reagent_containers/spray/alien/acid(src) - emag.reagents.add_reagent("facid", 125) - emag.reagents.add_reagent("sacid", 125) - - fix_modules() - /obj/item/robot_module/alien/hunter/add_languages(mob/living/silicon/robot/R) - ..() + . = ..() R.add_language("xenocommon", 1) +// Maintenance drone module. /obj/item/robot_module/drone name = "drone module" module_type = "Engineer" - stacktypes = list( - /obj/item/stack/sheet/wood = 10, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/tile/wood = 20, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20, - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 30 - ) - -/obj/item/robot_module/drone/New() - ..() - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) - modules += new /obj/item/soap(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/rpd(src) - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/drone/add_default_robot_items() - return - -/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) - var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules - C.reagents.add_reagent("cleaner", 3) - ..() - + basic_modules = list( + /obj/item/weldingtool/largetank/cyborg, + /obj/item/screwdriver/cyborg, + /obj/item/wrench/cyborg, + /obj/item/crowbar/cyborg, + /obj/item/wirecutters/cyborg, + /obj/item/multitool/cyborg, + /obj/item/lightreplacer/cyborg, + /obj/item/gripper, + /obj/item/matter_decompiler, + /obj/item/reagent_containers/spray/cleaner/drone, + /obj/item/soap, + /obj/item/t_scanner, + /obj/item/rpd, + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg, + /obj/item/stack/cable_coil/cyborg, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/sheet/rglass/cyborg, + /obj/item/stack/sheet/wood/cyborg, + /obj/item/stack/tile/wood/cyborg + ) + special_rechargables = list( + /obj/item/reagent_containers/spray/cleaner/drone, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/lightreplacer/cyborg + ) /obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed) var/obj/item/gripper/G = locate(/obj/item/gripper) in modules if(G) G.drop_gripped_item(silent = TRUE) -//checks whether this item is a module of the robot it is located in. +/// Checks whether this item is a module of the robot it is located in. /obj/item/proc/is_robot_module() if(!istype(loc, /mob/living/silicon/robot)) - return 0 + return FALSE var/mob/living/silicon/robot/R = loc return (src in R.module.modules) + +/** + * # The robot_energy_storage datum + * + * Used to handle robot stack items, such as metal, wood, nanopaste, etc. + * + * To make things simple, the default `cost` of using 1 item from a borg stack, is 1. + * So then for example, when we have a `max_energy` of 50, the borg can use 50 of that item before it runs out. + * + * The `recharge_rate` will be affected by the charge rate of a borg recharger, depending on the level of parts. By default it is 1. + * This amount will be given every 2 seconds. So at round start, rechargers will give 1 energy back every 2 seconds, to each stack the borg has. + */ +/datum/robot_energy_storage + /// The name of the energy storage. + var/name = "Generic energy storage" + /// The name that will be displayed in the status panel. + var/statpanel_name = "Statpanel name" + /// The max amount of energy the stack can hold at once. + var/max_energy = 50 + /// The amount of energy the stack will regain while charging. + var/recharge_rate = 1 + /// Current amount of energy. + var/energy + +/datum/robot_energy_storage/New(obj/item/robot_module/R = null) + if(!energy) + energy = max_energy + if(R) + R.storages += src + +/** + * Called whenever the cyborg uses one of its stacks. Subtract the amount used from this datum's `energy` variable. + * + * Arguments: + * * amount - the number to subtract from the `energy` var. + */ +/datum/robot_energy_storage/proc/use_charge(amount) + if(energy < amount) + return FALSE // If we have more energy that we're about to drain, return + + energy -= amount + return TRUE + +/** + * Called whenever the cyborg is recharging and gains charge on its stack, or when clicking on other same-type stacks in the world. + * + * Arguments: + * * amount - the number to add to the `energy` var. + */ +/datum/robot_energy_storage/proc/add_charge(amount) + energy = min(energy + amount, max_energy) + +/datum/robot_energy_storage/metal + name = "Metal Synthesizer" + statpanel_name = "Metal" + +/datum/robot_energy_storage/metal_tile + name = "Floor tile Synthesizer" + statpanel_name = "Floor tiles" + max_energy = 60 + +/datum/robot_energy_storage/rods + name = "Rod Synthesizer" + statpanel_name = "Rods" + +/datum/robot_energy_storage/glass + name = "Glass Synthesizer" + statpanel_name = "Glass" + +/datum/robot_energy_storage/rglass + name = "Reinforced glass Synthesizer" + statpanel_name = "Reinforced glass" + +/datum/robot_energy_storage/wood + name = "Wood Synthesizer" + statpanel_name = "Wood" + +/datum/robot_energy_storage/wood_tile + name = "Wooden tile Synthesizer" + statpanel_name = "Wooden tiles" + max_energy = 60 + +/datum/robot_energy_storage/cable + name = "Cable Synthesizer" + statpanel_name = "Cable" + +// For the medical stacks, even though the recharge rate is 0, it will be set to 1 by default because of a `max()` proc. +// It will always take ~12 seconds to fully recharge these stacks beacuse of this. This time does not apply to the syndicate storages. +/datum/robot_energy_storage/medical + name = "Medical Synthesizer" + max_energy = 6 + recharge_rate = 0 + +/datum/robot_energy_storage/medical/splint + name = "Splint Synthesizer" + statpanel_name = "Splints" + +/datum/robot_energy_storage/medical/splint/syndicate + max_energy = 25 + +/datum/robot_energy_storage/medical/adv_burn_kit + name = "Burn kit Synthesizer" + statpanel_name = "Burn kits" + +/datum/robot_energy_storage/medical/adv_burn_kit/syndicate + max_energy = 25 + +/datum/robot_energy_storage/medical/adv_brute_kit + name = "Trauma kit Synthesizer" + statpanel_name = "Brute kits" + +/datum/robot_energy_storage/medical/adv_brute_kit/syndicate + max_energy = 25 + +/datum/robot_energy_storage/medical/nanopaste + name = "Nanopaste Synthesizer" + statpanel_name = "Nanopaste" + +/datum/robot_energy_storage/medical/nanopaste/syndicate + max_energy = 25 diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 3c8db1f50df..96ad65666ca 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -596,7 +596,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai /obj/item/stack/cable_coil/examine(mob/user) . = ..() - if(in_range(user, src)) + if(in_range(user, src) && !is_cyborg) if(get_amount() == 1) . += "A short piece of power cable." else if(get_amount() == 2) @@ -612,10 +612,10 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = W // Cable merging is handled by parent proc - if(C.amount >= MAXCOIL) + if(C.get_amount() >= MAXCOIL) to_chat(user, "The coil is as long as it will get.") return - if( (C.amount + src.amount <= MAXCOIL) ) + if((C.get_amount() + get_amount() <= MAXCOIL)) to_chat(user, "You join the cable coils together.") return else @@ -861,7 +861,11 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai return color /obj/item/stack/cable_coil/cyborg - name = "cyborg cable coil" + energy_type = /datum/robot_energy_storage/cable + is_cyborg = TRUE + +/obj/item/stack/cable_coil/cyborg/update_icon() + return // icon_state should always be a full cable /obj/item/stack/cable_coil/cyborg/attack_self(mob/user) var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 6da11d80c22..f830debdd17 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -149,7 +149,7 @@ return var/obj/item/stack/cable_coil/C = I - if(C.amount < 10) + if(C.get_amount() < 10) to_chat(user, "You need more wires.") return diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 00dd4dbff60..9dcd6eaf6dc 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -220,7 +220,7 @@ if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) var/obj/item/stack/sheet/S = W if(S.use(2)) - glass_type = W.type + glass_type = S.merge_type playsound(loc, S.usesound, 50, 1) user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") if(tracker) diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 2247a1a6c9c..140c14171eb 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -55,3 +55,12 @@ /obj/item/gun/energy/disabler/cyborg/newshot() ..() robocharge() + +/obj/item/gun/energy/disabler/cyborg/cyborg_recharge(coeff, emagged) + if(cell.charge < cell.maxcharge) + var/obj/item/ammo_casing/energy/E = ammo_type[select] + cell.give(E.e_cost * coeff) + on_recharge() + update_icon() + else + charge_tick = 0 diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 828040aae80..1002ff117c1 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -131,6 +131,9 @@ volume = 50 list_reagents = list("cleaner" = 50) +/obj/item/reagent_containers/spray/cleaner/drone/cyborg_recharge(coeff, emagged) + reagents.check_and_add("cleaner", volume, 3 * coeff) + //spray tan /obj/item/reagent_containers/spray/spraytan name = "spray tan"