From f56493cff34b74ebec60a2292a433798ddf11452 Mon Sep 17 00:00:00 2001 From: Kittynoodle Date: Thu, 20 Jul 2023 16:58:11 -0500 Subject: [PATCH 1/4] super important fix --- code/datums/elements/blocks_explosives.dm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/datums/elements/blocks_explosives.dm b/code/datums/elements/blocks_explosives.dm index 1e867a81c1e8..fe7da7b6f8c5 100644 --- a/code/datums/elements/blocks_explosives.dm +++ b/code/datums/elements/blocks_explosives.dm @@ -1,6 +1,7 @@ /// Apply this element to a movable atom when you want it to block explosions /// It will mirror the blocking down to that movable's turf, keeping explosion work cheap /datum/element/blocks_explosives + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY /datum/element/blocks_explosives/Attach(datum/target) if(!ismovable(target)) @@ -18,8 +19,13 @@ else if(moving_target.loc) block_loc(moving_target.loc, moving_target.explosion_block) -/datum/element/blocks_explosives/Detach(datum/source) +/datum/element/blocks_explosives/Detach(atom/movable/source) . = ..() + if(length(source.locs) > 1) + for(var/atom/location as anything in source.locs) + unblock_loc(location, source.explosion_block) + else if(source.loc) + unblock_loc(source.loc, source.explosion_block) REMOVE_TRAIT(source, TRAIT_BLOCKING_EXPLOSIVES, TRAIT_GENERIC) /// Call this when our blocking well, changes. we'll update our turf(s) with the details From 44dbcbbdaa036b5cffa3d1234638c3ff199319aa Mon Sep 17 00:00:00 2001 From: Kittynoodle Date: Thu, 20 Jul 2023 19:36:26 -0500 Subject: [PATCH 2/4] shields --- code/__HELPERS/maths.dm | 35 + code/game/machinery/modular_shield.dm | 700 ++++++++++++++++++ .../machines/machine_circuitboards.dm | 42 ++ .../research/designs/machine_designs.dm | 50 ++ code/modules/research/techweb/all_nodes.dm | 5 + .../obj/machines/modular_shield_generator.dmi | Bin 0 -> 21832 bytes tgstation.dme | 1 + .../tgui/interfaces/ModularShieldGen.tsx | 127 ++++ 8 files changed, 960 insertions(+) create mode 100644 code/game/machinery/modular_shield.dm create mode 100644 icons/obj/machines/modular_shield_generator.dmi create mode 100644 tgui/packages/tgui/interfaces/ModularShieldGen.tsx diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 5b5144e0cc2d..7e266da95521 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -80,6 +80,41 @@ line += locate(current_x_step, current_y_step, starting_z) return line +/** + * Get a list of turfs in a perimeter given the `center_atom` and `radius`. + * Automatically rounds down decimals and does not accept values less than positive 1 as they dont play well with it. + * Is efficient on large circles but ugly on small ones + * Uses [Jesko`s method to the midpoint circle Algorithm](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm). + */ +/proc/get_perimeter(atom/center, radius) + if(radius < 1) + return + var/rounded_radius = round(radius) + var/x = center.x + var/y = center.y + var/z = center.z + var/t1 = rounded_radius/16 + var/dx = rounded_radius + var/dy = 0 + var/t2 + var/list/perimeter = list() + while(dx >= dy) + perimeter += locate(x + dx, y + dy, z) + perimeter += locate(x - dx, y + dy, z) + perimeter += locate(x + dx, y - dy, z) + perimeter += locate(x - dx, y - dy, z) + perimeter += locate(x + dy, y + dx, z) + perimeter += locate(x - dy, y + dx, z) + perimeter += locate(x + dy, y - dx, z) + perimeter += locate(x - dy, y - dx, z) + dy += 1 + t1 += dy + t2 = t1 - dx + if(t2 > 0) + t1 = t2 + dx -= 1 + return perimeter + ///Format a power value in W, kW, MW, or GW. /proc/display_power(powerused) if(powerused < 1000) //Less than a kW diff --git a/code/game/machinery/modular_shield.dm b/code/game/machinery/modular_shield.dm new file mode 100644 index 000000000000..2d018fdab829 --- /dev/null +++ b/code/game/machinery/modular_shield.dm @@ -0,0 +1,700 @@ +/obj/machinery/modular_shield_generator + name = "Modular Shield Generator" + desc = "A forcefield generator, it seems more stationary than its cousins." + icon = 'icons/obj/machines/modular_shield_generator.dmi' + icon_state = "gen_recovering_closed" + density = TRUE + circuit = /obj/item/circuitboard/machine/modular_shield_generator + processing_flags = START_PROCESSING_ON_INIT + + ///Doesnt actually control it, just tells us if its running or not, you can control by calling procs activate_shields and deactivate_shields + var/active = FALSE + + ///If the generator is currently spawning the forcefield in + var/initiating = FALSE + + ///Determins if we can turn it on or not, no longer recovering when back to max strength + var/recovering = TRUE + + ///Determins max health of the shield + var/max_strength = 40 + + ///Current health of shield + var/stored_strength = 0 //starts at 0 to prevent rebuild abuse + + ///Shield Regeneration when at 100% efficiency + var/max_regeneration = 3 + + ///The regeneration that the shield can support + var/current_regeneration + + ///Determins the max radius the shield can support + var/max_radius = 3 + + ///Current radius the shield is set to, minimum 3 + var/radius = 3 + + ///Determins if we only generate a shield on space turfs or not + var/exterior_only = FALSE + + ///The lazy list of shields that are ours + var/list/deployed_shields + + ///The lazy list of turfs that are within the shield + var/list/inside_shield + + ///The lazy list of machines that are connected to and boosting us + var/list/obj/machinery/modular_shield/module/connected_modules + + ///Regeneration gained from machines connected to us + var/regen_boost = 0 + + ///Max Radius gained from machines connected to us + var/radius_boost = 0 + + ///Max Strength gained from machines connected to us + var/max_strength_boost = 0 + + ///Regeneration gained from our own parts + var/innate_regen = 3 + + ///Max radius gained from our own parts + var/innate_radius = 3 + + ///Max strength gained from our own parts + var/innate_strength = 40 + + ///This is the lazy list of perimeter turfs that we grab when making large shields of 10 or more radius + var/list/list_of_turfs + +/obj/machinery/modular_shield_generator/power_change() + . = ..() + if(!(machine_stat & NOPOWER)) + begin_processing() + return + + deactivate_shields() + end_processing() + +/obj/machinery/modular_shield_generator/RefreshParts() + . = ..() + + innate_regen = initial(innate_regen) + innate_radius = initial(innate_radius) + innate_strength = initial(innate_strength) + + for(var/datum/stock_part/capacitor/new_capacitor in component_parts) + innate_strength += new_capacitor.tier * 10 + + for(var/datum/stock_part/servo/new_servo in component_parts) + innate_regen += new_servo.tier + + for(var/datum/stock_part/micro_laser/new_laser in component_parts) + innate_radius += new_laser.tier * 0.25 + + calculate_regeneration() + calculate_max_strength() + calculate_radius() + + +/obj/machinery/modular_shield_generator/Initialize(mapload) + . = ..() + wires = new /datum/wires/modular_shield_generator(src) + if(mapload && active && anchored) + activate_shields() + +/datum/wires/modular_shield_generator + proper_name = "Modular shield generator" + randomize = FALSE + holder_type = /obj/machinery/modular_shield_generator + +/datum/wires/modular_shield_generator/New(atom/holder) + wires = list(WIRE_HACK) + return ..() + +/datum/wires/modular_shield_generator/on_pulse(wire) + + var/obj/machinery/modular_shield_generator/shield_gen = holder + switch(wire) + if(WIRE_HACK) + shield_gen.toggle_shields() + + return ..() + +///qdels the forcefield and calls calculate regen to update the regen value accordingly +/obj/machinery/modular_shield_generator/proc/deactivate_shields() + active = FALSE + QDEL_LIST(deployed_shields) + deployed_shields = null + LAZYNULL(list_of_turfs) + LAZYNULL(inside_shield) + calculate_regeneration() + +/obj/machinery/modular_shield_generator/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + + if(default_deconstruction_screwdriver(user,"gen_[!(machine_stat & NOPOWER) ? "[recovering ? "recovering_" : "ready_"]" : "no_power_"]open", + "gen_[!(machine_stat & NOPOWER) ? "[recovering ? "recovering_" : "ready_"]" : "no_power_"]closed", tool)) + return TRUE + +/obj/machinery/modular_shield_generator/crowbar_act(mob/living/user, obj/item/tool) + . = ..() + + if(default_deconstruction_crowbar(tool)) + return TRUE + +/obj/machinery/modular_shield_generator/attackby(obj/item/W, mob/user, params) + + if(is_wire_tool(W) && panel_open) + wires.interact(user) + return TRUE + + return ..() + +///toggles the forcefield on and off +/obj/machinery/modular_shield_generator/proc/toggle_shields() + if(initiating) + return + if(active) + deactivate_shields() + return + if (recovering) + return + activate_shields() + + +///generates the forcefield based on the given radius and calls calculate_regen to update the regen value accordingly +/obj/machinery/modular_shield_generator/proc/activate_shields() + if(active || (machine_stat & NOPOWER))//bug or did admin call proc on already active shield gen? + return + if(radius < 0)//what the fuck are admins doing + radius = initial(radius) + active = TRUE + initiating = TRUE + + if(radius >= 10) //the shield is large so we are going to use the midpoint formula and clamp it to the lowest full number in order to save processing power + LAZYADD(inside_shield, circle_range_turfs(src, radius - 1))//in the future we might want to apply an effect to turfs inside the shield + LAZYADD(list_of_turfs, get_perimeter(src, radius)) + + if(exterior_only) + for(var/turf/open/target_tile in list_of_turfs) + if(isfloorturf(target_tile)) + continue + if(locate(/obj/structure/emergency_shield/modular) in target_tile) + continue + var/obj/structure/emergency_shield/modular/deploying_shield = new(target_tile) + deploying_shield.shield_generator = src + LAZYADD(deployed_shields, deploying_shield) + + addtimer(CALLBACK(src, PROC_REF(finish_field)), 2 SECONDS) + calculate_regeneration() + return + + for(var/turf/open/target_tile in list_of_turfs) + if(locate(/obj/structure/emergency_shield/modular) in target_tile) + continue + var/obj/structure/emergency_shield/modular/deploying_shield = new(target_tile) + deploying_shield.shield_generator = src + LAZYADD(deployed_shields, deploying_shield) + + addtimer(CALLBACK(src, PROC_REF(finish_field)), 2 SECONDS) + calculate_regeneration() + return + + //this code only runs on radius less than 10 and gives us a more accurate circle that is more compatible with decimal values + LAZYADD(inside_shield, circle_range_turfs(src, radius - 1))//in the future we might want to apply an effect to the turfs inside the shield + if(exterior_only) + for(var/turf/open/target_tile in circle_range_turfs(src, radius)) + if(isfloorturf(target_tile)) + continue + if(target_tile in inside_shield) + continue + if(locate(/obj/structure/emergency_shield/modular) in target_tile) + continue + var/obj/structure/emergency_shield/modular/deploying_shield = new(target_tile) + deploying_shield.shield_generator = src + LAZYADD(deployed_shields, deploying_shield) + + addtimer(CALLBACK(src, PROC_REF(finish_field)), 2 SECONDS) + calculate_regeneration() + return + + for(var/turf/open/target_tile in circle_range_turfs(src, radius)) + if(target_tile in inside_shield) + continue + if(locate(/obj/structure/emergency_shield/modular) in target_tile) + continue + var/obj/structure/emergency_shield/modular/deploying_shield = new(target_tile) + deploying_shield.shield_generator = src + LAZYADD(deployed_shields, deploying_shield) + + addtimer(CALLBACK(src, PROC_REF(finish_field)), 2 SECONDS) + calculate_regeneration() + + +///After giving people a grace period to react to we up the alpha value and make the forcefield dense +/obj/machinery/modular_shield_generator/proc/finish_field() + + for(var/obj/structure/emergency_shield/modular/current_shield in deployed_shields) + current_shield.density = TRUE + current_shield.alpha = 255 + initiating = FALSE + +/obj/machinery/modular_shield_generator/Destroy() + QDEL_LIST(deployed_shields) + return ..() + +/obj/machinery/modular_shield_generator/update_icon_state() + + icon_state = ("gen_[!(machine_stat & NOPOWER) ? "[recovering ? "recovering_" : "ready_"]" : "no_power_"][(panel_open)?"open" : "closed"]") + return ..() + +//ui stuff +/obj/machinery/modular_shield_generator/ui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ModularShieldGen") + ui.open() + +/obj/machinery/modular_shield_generator/ui_data(mob/user) + + var/list/data = list() + data["max_radius"] = max_radius + data["current_radius"] = radius + data["max_strength"] = max_strength + data["max_regeneration"] = max_regeneration + data["current_regeneration"] = current_regeneration + data["current_strength"] = stored_strength + data["active"] = active + data["recovering"] = recovering + data["exterior_only"] = exterior_only + data["initiating_field"] = initiating + return data + +/obj/machinery/modular_shield_generator/ui_act(action, params) + . = ..() + if(.) + return + switch(action) + if ("set_radius") + if (active) + return + var/change_radius = max(1,(text2num(params["new_radius"]))) + if(change_radius >= 10) + radius = round(change_radius)//if its over 10 we dont allow decimals + return + radius = change_radius + + if ("toggle_shields") + toggle_shields() + + if ("toggle_exterior") + exterior_only = !exterior_only + + +///calculations for the stats supplied by the network of machines that boost us +/obj/machinery/modular_shield_generator/proc/calculate_boost() + + regen_boost = initial(regen_boost) + for (var/obj/machinery/modular_shield/module/charger/new_charger in connected_modules) + regen_boost += new_charger.charge_boost + + calculate_regeneration() + + max_strength_boost = initial(max_strength_boost) + for (var/obj/machinery/modular_shield/module/well/new_well in connected_modules) + max_strength_boost += new_well.strength_boost + + calculate_max_strength() + + radius_boost = initial(radius_boost) + for (var/obj/machinery/modular_shield/module/relay/new_relay in connected_modules) + radius_boost += new_relay.range_boost + + calculate_radius() + +///Calculates the max radius the shield generator can support, modifiers go here +/obj/machinery/modular_shield_generator/proc/calculate_radius() + + max_radius = innate_radius + radius_boost + + if(radius > max_radius)//the generator can no longer function at this capacity + deactivate_shields() + radius = max_radius + +///Calculates the max strength or health of the forcefield, modifiers go here +/obj/machinery/modular_shield_generator/proc/calculate_max_strength() + + max_strength = innate_strength + max_strength_boost + begin_processing() + +///Calculates the regeneration based on the status of the generator and boosts from network, modifiers go here +/obj/machinery/modular_shield_generator/proc/calculate_regeneration() + + max_regeneration = innate_regen + regen_boost + + if(!active) + if(recovering) + current_regeneration = max_regeneration * 0.25 + return + current_regeneration = max_regeneration + return + + //we lose more than half the regeneration rate when generating a shield that is near the max + //radius that we can handle but if we generate a shield with a very small fraction + //of the max radius we can support we get a very small bonus multiplier + current_regeneration = (max_regeneration / (0.5 + (radius * 2)/max_radius)) + + if(!exterior_only) + current_regeneration *= 0.5 + +///Reduces the strength of the shield based on the given integer +/obj/machinery/modular_shield_generator/proc/shield_drain(damage_amount) + stored_strength -= damage_amount + begin_processing() + if (stored_strength < 5) + recovering = TRUE + deactivate_shields() + stored_strength = 0 + update_icon_state() + +/obj/machinery/modular_shield_generator/process(seconds_per_tick) + stored_strength = min((stored_strength + (current_regeneration * seconds_per_tick)),max_strength) + if(stored_strength == max_strength) + if (recovering) + recovering = FALSE + calculate_regeneration() + update_icon_state() + end_processing() //we dont care about continuing to update the alpha, we want to show history of damage to show its unstable + if (active) + var/random_num = rand(1,deployed_shields.len) + var/obj/structure/emergency_shield/modular/random_shield = deployed_shields[random_num] + random_shield.alpha = max(255 * (stored_strength/max_strength), 40) + + + +//Start of other machines +///The general code used for machines that want to connect to the network +/obj/machinery/modular_shield/module + + name = "Modular Shield Debugger" //Filler name and sprite for testing + desc = "This is filler for testing you shouldn`t see this." + icon = 'icons/mecha/mech_bay.dmi' + icon_state = "recharge_port" + density = TRUE + + ///The shield generator we are connected to if we find one or a node provides us one + var/obj/machinery/modular_shield_generator/shield_generator + + ///The node we are connected to if we find one + var/obj/machinery/modular_shield/module/node/connected_node + + ///This is the turf that we are facing and able to search for connections through + var/turf/connected_turf + +/obj/machinery/modular_shield/module/Initialize(mapload) + . = ..() + + connected_turf = get_step(loc, dir) + +/obj/machinery/modular_shield/module/Destroy() + + if(shield_generator) + LAZYREMOVE(shield_generator.connected_modules, (src)) + shield_generator.calculate_boost() + if(connected_node) + LAZYREMOVE(connected_node.connected_through_us, (src)) + return ..() + +/obj/machinery/modular_shield/module/examine(mob/user) + . = ..() + + if(isnull(shield_generator) && isnull(connected_node)) + . += "It can be loosened and rotated with a screwdriver and wrench. It can be connected to a node or generator with a multitool." + return + . += "It can be loosed and rotated with a screwdriver and wrench, rotating it will sever its connection." + +/obj/machinery/modular_shield/module/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + + panel_open = !(panel_open) + tool.play_tool_sound(src, 50) + update_icon_state() + if(panel_open) + balloon_alert(user, "hatch opened") + return TRUE + balloon_alert(user, "hatch closed") + return TRUE + +/obj/machinery/modular_shield/module/multitool_act(mob/living/user, obj/item/tool) + . = ..() + + //rather than automatically checking for connections its probably alot less + //expensive to just make the players manually multi tool sync each part + try_connect(user) + return TRUE + +/obj/machinery/modular_shield/module/wrench_act(mob/living/user, obj/item/tool) + . = ..() + + if(default_change_direction_wrench(user, tool)) + if(shield_generator) + LAZYREMOVE(shield_generator.connected_modules, (src)) + shield_generator.calculate_boost() + shield_generator = null + update_icon_state() + if(connected_node) + LAZYREMOVE(connected_node.connected_through_us, (src)) + connected_node = null + connected_turf = get_step(loc, dir) + return TRUE + +/obj/machinery/modular_shield/module/crowbar_act(mob/living/user, obj/item/tool) + . = ..() + + if(default_deconstruction_crowbar(tool)) + return TRUE + + +/obj/machinery/modular_shield/module/setDir(new_dir) + . = ..() + connected_turf = get_step(loc, dir) + +///checks for a valid machine in front of us and connects to it +/obj/machinery/modular_shield/module/proc/try_connect(user) + + if(shield_generator || connected_node) + balloon_alert(user, "already connected to something!") + return + + shield_generator = (locate(/obj/machinery/modular_shield_generator) in connected_turf) + + if(shield_generator) + + LAZYOR(shield_generator.connected_modules, (src)) + balloon_alert(user, "connected to generator") + update_icon_state() + shield_generator.calculate_boost() + return + + connected_node = (locate(/obj/machinery/modular_shield/module/node) in connected_turf) + + if(connected_node) + + LAZYOR(connected_node.connected_through_us, (src)) + shield_generator = connected_node.shield_generator + if(shield_generator) + LAZYOR(shield_generator.connected_modules, (src)) + balloon_alert(user, "connected to generator") + update_icon_state() + shield_generator.calculate_boost() + return + balloon_alert(user, "connected to node") + return + balloon_alert(user, "no connection!") + + + +/obj/machinery/modular_shield/module/node + + name = "Modular Shield Node" + desc = "A waist high mess of humming pipes and wires that extend the modular shield network." + icon = 'icons/obj/machines/modular_shield_generator.dmi' + icon_state = "node_off_closed" + active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.5 + circuit = /obj/item/circuitboard/machine/modular_shield_node + ///The lazy list of machines that are connected to us and want connection to a generator + var/list/connected_through_us + +/obj/machinery/modular_shield/module/node/update_icon_state() + . = ..() + if(isnull(shield_generator) || (machine_stat & NOPOWER)) + icon_state = "node_off_[panel_open ? "open" : "closed"]" + return + icon_state = "node_on_[panel_open ? "open" : "closed"]" + +/obj/machinery/modular_shield/module/node/setDir(new_dir) + . = ..() + + disconnect_connected_through_us() + if(isnull(shield_generator)) + return + LAZYREMOVE(shield_generator.connected_modules, (src)) + shield_generator.calculate_boost() + shield_generator = null + update_icon_state() + +//after trying to connect to a machine infront of us, we will try to link anything connected to us to a generator +/obj/machinery/modular_shield/module/node/try_connect(user) + . = ..() + + if(isnull(shield_generator)) + return + connect_connected_through_us() + shield_generator.calculate_boost() + +/obj/machinery/modular_shield/module/node/Destroy() + . = ..() + + disconnect_connected_through_us() + for(var/obj/machinery/modular_shield/module/connected in connected_through_us) + connected.connected_node = null + if(shield_generator) + shield_generator.calculate_boost() + +///If we are connected to a shield generator this proc will connect anything connected to us to that generator +/obj/machinery/modular_shield/module/node/proc/connect_connected_through_us() + + if(shield_generator) + for(var/obj/machinery/modular_shield/module/connected in connected_through_us) + LAZYOR(shield_generator.connected_modules, connected) + connected.shield_generator = shield_generator + if(istype(connected, /obj/machinery/modular_shield/module/node)) + var/obj/machinery/modular_shield/module/node/connected_node = connected + connected_node.connect_connected_through_us() + connected.update_icon_state() + + +///This proc disconnects modules connected through us from the shield generator in the event that we lose connection +/obj/machinery/modular_shield/module/node/proc/disconnect_connected_through_us() + + for(var/obj/machinery/modular_shield/module/connected in connected_through_us) + LAZYREMOVE(shield_generator.connected_modules, connected) + if(istype(connected, /obj/machinery/modular_shield/module/node)) + var/obj/machinery/modular_shield/module/node/connected_node = connected + connected_node.disconnect_connected_through_us() + connected.shield_generator = null + connected.update_icon_state() + +/obj/machinery/modular_shield/module/charger + + name = "Modular Shield Charger" + desc = "A machine that somehow fabricates hardlight using electrons." + icon = 'icons/obj/machines/modular_shield_generator.dmi' + icon_state = "charger_off_closed" + + circuit = /obj/item/circuitboard/machine/modular_shield_charger + + ///Amount of regeneration this machine grants the connected generator + var/charge_boost = 0 + +/obj/machinery/modular_shield/module/charger/update_icon_state() + . = ..() + if(isnull(shield_generator) || (machine_stat & NOPOWER)) + icon_state = "charger_off_[panel_open ? "open" : "closed"]" + return + icon_state = "charger_on_[panel_open ? "open" : "closed"]" + +/obj/machinery/modular_shield/module/charger/RefreshParts() + . = ..() + charge_boost = initial(charge_boost) + for(var/datum/stock_part/servo/new_servo in component_parts) + charge_boost += new_servo.tier + + if(shield_generator) + shield_generator.calculate_boost() + +/obj/machinery/modular_shield/module/relay + + name = "Modular Shield Relay" + desc = "It helps the shield generator project farther out." + icon = 'icons/obj/machines/modular_shield_generator.dmi' + icon_state = "relay_off_closed" + + circuit = /obj/item/circuitboard/machine/modular_shield_relay + + ///Amount of max range this machine grants the connected generator + var/range_boost = 0 + +/obj/machinery/modular_shield/module/relay/update_icon_state() + . = ..() + if(isnull(shield_generator) || (machine_stat & NOPOWER)) + icon_state = "relay_off_[panel_open ? "open" : "closed"]" + return + icon_state = "relay_on_[panel_open ? "open" : "closed"]" + +/obj/machinery/modular_shield/module/relay/RefreshParts() + . = ..() + range_boost = initial(range_boost) + for(var/datum/stock_part/micro_laser/new_laser in component_parts) + range_boost += new_laser.tier * 0.25 + + if(shield_generator) + shield_generator.calculate_boost() + +/obj/machinery/modular_shield/module/well + + name = "Modular Shield Well" + desc = "A device used to hold more hardlight for the modular shield generator." + icon = 'icons/obj/machines/modular_shield_generator.dmi' + icon_state = "well_off_closed" + + circuit = /obj/item/circuitboard/machine/modular_shield_well + + ///Amount of max strength this machine grants the connected generator + var/strength_boost = 0 + +/obj/machinery/modular_shield/module/well/RefreshParts() + . = ..() + strength_boost = initial(strength_boost) + for(var/datum/stock_part/capacitor/new_capacitor in component_parts) + strength_boost += new_capacitor.tier * 10 + + if(shield_generator) + shield_generator.calculate_boost() + +/obj/machinery/modular_shield/module/well/update_icon_state() + . = ..() + if(isnull(shield_generator) || (machine_stat & NOPOWER)) + icon_state = "well_off_[panel_open ? "open" : "closed"]" + return + icon_state = "well_on_[panel_open ? "open" : "closed"]" + + +//The shield itself +/obj/structure/emergency_shield/modular + name = "Modular energy shield" + desc = "An energy shield with varying configurations." + color = "#00ffff" + density = FALSE + alpha = 100 + resistance_flags = INDESTRUCTIBLE //the shield itself is indestructible or atleast should be + + ///The shield generator sustaining us + var/obj/machinery/modular_shield_generator/shield_generator + + +/obj/structure/emergency_shield/modular/Initialize(mapload) + . = ..() + AddElement(/datum/element/atmos_sensitive, mapload) + +/obj/structure/emergency_shield/modular/should_atmos_process(datum/gas_mixture/air, exposed_temperature) + return exposed_temperature > (T0C + 400) //starts taking damage from high temps at the same temperature that nonreinforced glass does + +//Damage from atmos +/obj/structure/emergency_shield/modular/atmos_expose(datum/gas_mixture/air, exposed_temperature) + if(isnull(shield_generator)) + qdel(src) + return + + shield_generator.shield_drain(round(air.return_volume() / 400))//400 integer determines how much damage the shield takes from hot atmos (higher value = less damage) + + +//Damage from direct attacks +/obj/structure/emergency_shield/modular/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(damage_type == BRUTE || damage_type == BURN) + if(isnull(shield_generator)) + qdel(src) + return + + shield_generator.shield_drain(damage_amount)//can add or subtract a flat value to buff or nerf crowd damage + +//Damage from emp +/obj/structure/emergency_shield/modular/emp_act(severity) + if(isnull(shield_generator)) + qdel(src) + return + + shield_generator.shield_drain(15 / severity) //Light is 2 heavy is 1, note emp is usually a large aoe, tweak the number if not enough damage diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 12a34feaed33..942aadb70e15 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -158,6 +158,48 @@ req_components = list(/datum/stock_part/capacitor = 1) needs_anchored = FALSE +/obj/item/circuitboard/machine/modular_shield_generator + name = "Modular Shield Generator" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/modular_shield_generator + req_components = list( + /datum/stock_part/manipulator = 1, + /datum/stock_part/micro_laser = 1, + /datum/stock_part/capacitor = 1, + /obj/item/stack/sheet/plasteel = 3,) + +/obj/item/circuitboard/machine/modular_shield_node + name = "Modular Shield Node" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/modular_shield/module/node + req_components = list( + /obj/item/stack/cable_coil = 15, + /obj/item/stack/sheet/plasteel = 2,) + +/obj/item/circuitboard/machine/modular_shield_well + name = "Modular Shield Well" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/modular_shield/module/well + req_components = list( + /datum/stock_part/capacitor = 3, + /obj/item/stack/sheet/plasteel = 2,) + +/obj/item/circuitboard/machine/modular_shield_relay + name = "Modular Shield Relay" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/modular_shield/module/relay + req_components = list( + /datum/stock_part/micro_laser = 3, + /obj/item/stack/sheet/plasteel = 2,) + +/obj/item/circuitboard/machine/modular_shield_charger + name = "Modular Shield Charger" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/modular_shield/module/charger + req_components = list( + /datum/stock_part/manipulator = 3, + /obj/item/stack/sheet/plasteel = 2,) + /obj/item/circuitboard/machine/cell_charger name = "Cell Charger" greyscale_colors = CIRCUIT_COLOR_ENGINEERING diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index ff05b240578f..66e02a7c468f 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -167,6 +167,56 @@ ) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SCIENCE +/datum/design/board/modular_shield_generator + name = "Modular Shield Generator Board" + desc = "The circuit board for a modular shield generator." + id = "modular_shield_generator" + build_path = /obj/item/circuitboard/machine/modular_shield_generator + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/board/modular_shield_node + name = "Modular Shield Node Board" + desc = "The circuit board for a modular shield node." + id = "modular_shield_node" + build_path = /obj/item/circuitboard/machine/modular_shield_node + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/board/modular_shield_relay + name = "Modular Shield Relay Board" + desc = "The circuit board for a modular shield relay." + id = "modular_shield_relay" + build_path = /obj/item/circuitboard/machine/modular_shield_relay + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/board/modular_shield_charger + name = "Modular Shield Charger Board" + desc = "The circuit board for a modular shield charger." + id = "modular_shield_charger" + build_path = /obj/item/circuitboard/machine/modular_shield_charger + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/board/modular_shield_well + name = "Modular Shield Well Board" + desc = "The circuit board for a modular shield well." + id = "modular_shield_well" + build_path = /obj/item/circuitboard/machine/modular_shield_well + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + /datum/design/board/teleconsole name = "Teleporter Console Board" desc = "Allows for the construction of circuit boards used to build a teleporter control console." diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 3b4c95103dfc..2463e5ea3bf5 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -669,6 +669,11 @@ "turbine_compressor", "turbine_rotor", "turbine_stator", + "modular_shield_generator", + "modular_shield_node", + "modular_shield_relay", + "modular_shield_charger", + "modular_shield_well", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500) discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_capacitors = 2500) diff --git a/icons/obj/machines/modular_shield_generator.dmi b/icons/obj/machines/modular_shield_generator.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1a6f3025eab38eca19735babd6fcb4eae12f2e9c GIT binary patch literal 21832 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 C@!7-x literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index 38d39324e5f3..e6bd32143de8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1570,6 +1570,7 @@ #include "code\game\machinery\mechlaunchpad.dm" #include "code\game\machinery\medical_kiosk.dm" #include "code\game\machinery\medipen_refiller.dm" +#include "code\game\machinery\modular_shield.dm" #include "code\game\machinery\navbeacon.dm" #include "code\game\machinery\PDApainter.dm" #include "code\game\machinery\prisongate.dm" diff --git a/tgui/packages/tgui/interfaces/ModularShieldGen.tsx b/tgui/packages/tgui/interfaces/ModularShieldGen.tsx new file mode 100644 index 000000000000..b35c550c7dc8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ModularShieldGen.tsx @@ -0,0 +1,127 @@ +import { useBackend } from '../backend'; +import { Window } from '../layouts'; +import { Stack, Section, ProgressBar, Button, NumberInput, LabeledList } from '../components'; +import { BooleanLike } from 'common/react'; + +type ModularShieldGenData = { + max_strength: number; + current_strength: number; + max_regeneration: number; + current_regeneration: number; + max_radius: number; + current_radius: number; + active: BooleanLike; + recovering: BooleanLike; + exterior_only: BooleanLike; + initiating_field: BooleanLike; +}; + +export const ModularShieldGen = (props, context) => { + const { topLevel } = props; + const { act, data } = useBackend(context); + const { + max_strength, + max_regeneration, + current_regeneration, + max_radius, + current_radius, + current_strength, + active, + exterior_only, + recovering, + initiating_field, + } = data; + + return ( + + + + +
+ + {current_strength}/{max_strength} + +
+
+ + Regeneration {current_regeneration}/{max_regeneration} + +
+ + Radius {current_radius}/{max_radius} + +
+
+
+ +
+ + + + act('set_radius', { + new_radius: value, + }) + } + /> + + + + + +
+
+ + +
+
+
+
+
+ ); +}; From f6c5d1ebf3483a0d8251eef0651f76d6d695fd0f Mon Sep 17 00:00:00 2001 From: Kittynoodle Date: Thu, 20 Jul 2023 21:08:59 -0500 Subject: [PATCH 3/4] balance changes+visible damage --- code/game/machinery/modular_shield.dm | 51 +++++++++++++++++++++------ 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/code/game/machinery/modular_shield.dm b/code/game/machinery/modular_shield.dm index 2d018fdab829..083d5c0474da 100644 --- a/code/game/machinery/modular_shield.dm +++ b/code/game/machinery/modular_shield.dm @@ -29,10 +29,10 @@ var/current_regeneration ///Determins the max radius the shield can support - var/max_radius = 3 + var/max_radius = 5 ///Current radius the shield is set to, minimum 3 - var/radius = 3 + var/radius = 5 ///Determins if we only generate a shield on space turfs or not var/exterior_only = FALSE @@ -59,7 +59,7 @@ var/innate_regen = 3 ///Max radius gained from our own parts - var/innate_radius = 3 + var/innate_radius = 5 ///Max strength gained from our own parts var/innate_strength = 40 @@ -84,10 +84,10 @@ innate_strength = initial(innate_strength) for(var/datum/stock_part/capacitor/new_capacitor in component_parts) - innate_strength += new_capacitor.tier * 10 + innate_strength += new_capacitor.tier * 12.5 - for(var/datum/stock_part/servo/new_servo in component_parts) - innate_regen += new_servo.tier + for(var/datum/stock_part/manipulator/new_manipulator in component_parts) + innate_regen += new_manipulator.tier * 1.2 for(var/datum/stock_part/micro_laser/new_laser in component_parts) innate_radius += new_laser.tier * 0.25 @@ -589,8 +589,8 @@ /obj/machinery/modular_shield/module/charger/RefreshParts() . = ..() charge_boost = initial(charge_boost) - for(var/datum/stock_part/servo/new_servo in component_parts) - charge_boost += new_servo.tier + for(var/datum/stock_part/manipulator/new_manipulator in component_parts) + charge_boost += new_manipulator.tier * 1.2 if(shield_generator) shield_generator.calculate_boost() @@ -639,7 +639,7 @@ . = ..() strength_boost = initial(strength_boost) for(var/datum/stock_part/capacitor/new_capacitor in component_parts) - strength_boost += new_capacitor.tier * 10 + strength_boost += new_capacitor.tier * 12.5 if(shield_generator) shield_generator.calculate_boost() @@ -655,20 +655,31 @@ //The shield itself /obj/structure/emergency_shield/modular name = "Modular energy shield" - desc = "An energy shield with varying configurations." + desc = "An energy shield with varying configurations, the damage it takes puts a strain on its generator." color = "#00ffff" density = FALSE alpha = 100 + explosion_block = INFINITY resistance_flags = INDESTRUCTIBLE //the shield itself is indestructible or atleast should be ///The shield generator sustaining us var/obj/machinery/modular_shield_generator/shield_generator - /obj/structure/emergency_shield/modular/Initialize(mapload) + AddElement(/datum/element/blocks_explosives) . = ..() AddElement(/datum/element/atmos_sensitive, mapload) +//The feedback from getting attacked by an item +/obj/structure/emergency_shield/modular/attacked_by(obj/item/attacking_item, mob/living/user) + . = ..() + visible_message(span_danger("The blow ripples across the field making it more unstable!"), null, null, COMBAT_MESSAGE_RANGE) + +//The feedback from getting attacked by a projectile +/obj/structure/emergency_shield/modular/bullet_act(obj/projectile/P) + . = ..() + visible_message(span_danger("The impact ripples across the field making it more unstable!"), null, null, COMBAT_MESSAGE_RANGE) + /obj/structure/emergency_shield/modular/should_atmos_process(datum/gas_mixture/air, exposed_temperature) return exposed_temperature > (T0C + 400) //starts taking damage from high temps at the same temperature that nonreinforced glass does @@ -698,3 +709,21 @@ return shield_generator.shield_drain(15 / severity) //Light is 2 heavy is 1, note emp is usually a large aoe, tweak the number if not enough damage + +/obj/structure/emergency_shield/modular/ex_act(severity) + if(isnull(shield_generator)) + qdel(src) + return + + switch(severity) + + if(EXPLODE_LIGHT) + shield_generator.shield_drain(20) + return + + if(EXPLODE_HEAVY) + shield_generator.shield_drain(50) + return + + if(EXPLODE_DEVASTATE) + shield_generator.shield_drain(100) From b215f07e1b007536b0ce8bfc9fded7a565b0c92c Mon Sep 17 00:00:00 2001 From: Kittynoodle Date: Tue, 25 Jul 2023 00:59:29 -0500 Subject: [PATCH 4/4] shield changes --- code/game/machinery/modular_shield.dm | 19 +++++++++++++++---- .../machines/machine_circuitboards.dm | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/modular_shield.dm b/code/game/machinery/modular_shield.dm index 083d5c0474da..a0dec02691e1 100644 --- a/code/game/machinery/modular_shield.dm +++ b/code/game/machinery/modular_shield.dm @@ -7,6 +7,16 @@ circuit = /obj/item/circuitboard/machine/modular_shield_generator processing_flags = START_PROCESSING_ON_INIT +/* +Monkestation edits: +changed innate radius to 5 +increased capacitor and manip benifts by 25% +added indicators that the shield is taking damage when hitting it +interacts with explosions +shields now deploy on walls(they wouldn't autogenerate if wall was removed and it looks nicer) +the modular shield components(not generator) are climbable +*/ + ///Doesnt actually control it, just tells us if its running or not, you can control by calling procs activate_shields and deactivate_shields var/active = FALSE @@ -177,7 +187,7 @@ LAZYADD(list_of_turfs, get_perimeter(src, radius)) if(exterior_only) - for(var/turf/open/target_tile in list_of_turfs) + for(var/turf/target_tile in list_of_turfs) if(isfloorturf(target_tile)) continue if(locate(/obj/structure/emergency_shield/modular) in target_tile) @@ -190,7 +200,7 @@ calculate_regeneration() return - for(var/turf/open/target_tile in list_of_turfs) + for(var/turf/target_tile in list_of_turfs) if(locate(/obj/structure/emergency_shield/modular) in target_tile) continue var/obj/structure/emergency_shield/modular/deploying_shield = new(target_tile) @@ -204,7 +214,7 @@ //this code only runs on radius less than 10 and gives us a more accurate circle that is more compatible with decimal values LAZYADD(inside_shield, circle_range_turfs(src, radius - 1))//in the future we might want to apply an effect to the turfs inside the shield if(exterior_only) - for(var/turf/open/target_tile in circle_range_turfs(src, radius)) + for(var/turf/target_tile in circle_range_turfs(src, radius)) if(isfloorturf(target_tile)) continue if(target_tile in inside_shield) @@ -219,7 +229,7 @@ calculate_regeneration() return - for(var/turf/open/target_tile in circle_range_turfs(src, radius)) + for(var/turf/target_tile in circle_range_turfs(src, radius)) if(target_tile in inside_shield) continue if(locate(/obj/structure/emergency_shield/modular) in target_tile) @@ -396,6 +406,7 @@ /obj/machinery/modular_shield/module/Initialize(mapload) . = ..() + AddElement(/datum/element/climbable, climb_time = 1 SECONDS) connected_turf = get_step(loc, dir) /obj/machinery/modular_shield/module/Destroy() diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 942aadb70e15..5277396e6bb8 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -173,7 +173,7 @@ greyscale_colors = CIRCUIT_COLOR_ENGINEERING build_path = /obj/machinery/modular_shield/module/node req_components = list( - /obj/item/stack/cable_coil = 15, + /obj/item/stack/cable_coil = 3, //monke edit 15 to 3 /obj/item/stack/sheet/plasteel = 2,) /obj/item/circuitboard/machine/modular_shield_well