diff --git a/cev_eris.dme b/cev_eris.dme index f399f799d4b..2de7485a2b4 100644 --- a/cev_eris.dme +++ b/cev_eris.dme @@ -2382,6 +2382,7 @@ #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\powernet.dm" +#include "code\modules\power\shipside_machinery.dm" #include "code\modules\power\smes.dm" #include "code\modules\power\smes_construction.dm" #include "code\modules\power\solar.dm" diff --git a/code/__DEFINES/lrange_scanner.dm b/code/__DEFINES/lrange_scanner.dm index 46628c7be0e..6e726c4c3bf 100644 --- a/code/__DEFINES/lrange_scanner.dm +++ b/code/__DEFINES/lrange_scanner.dm @@ -1,5 +1,5 @@ -#define ENERGY_UPKEEP_SCANNER (35 KILOWATTS) // Base upkeep -#define ENERGY_PER_SCAN (100 MEGAWATTS) // Energy cost to launch a scan (yes it's in Watt and not in Joules...) +#define ENERGY_UPKEEP_SCANNER (350 KILOWATTS) // Base upkeep +#define ENERGY_PER_SCAN (500 MEGAWATTS) // Energy cost to launch a scan (yes it's in Watt and not in Joules...) #define SCANNER_OFF 0 // The shield is offline #define SCANNER_DISCHARGING 1 // The shield is shutting down and discharging. diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm index 5a36b56168e..cc62a2c4cdb 100644 --- a/code/__HELPERS/turfs.dm +++ b/code/__HELPERS/turfs.dm @@ -28,6 +28,15 @@ return FALSE return TRUE +//A coppy of a proc above because sometimes you actually need to check if turf is clear no matter if it has wires or pipes +/proc/turf_clear_ignore_cables(turf/T) + if (T.density) + return FALSE + for(var/atom/A in T) + if(A.density) + return FALSE + return TRUE + /proc/clear_interior(var/turf/T) if (turf_clear(T)) if (!turf_is_external(T)) diff --git a/code/controllers/subsystems/staverbs.dm b/code/controllers/subsystems/staverbs.dm index b8c02ee28f5..eeb7f902b6f 100644 --- a/code/controllers/subsystems/staverbs.dm +++ b/code/controllers/subsystems/staverbs.dm @@ -180,3 +180,53 @@ SUBSYSTEM_DEF(statverbs) user.visible_message( SPAN_NOTICE("You stop repairing [target_name]."), ) + +/datum/statverb/connect_conduit //Connects or disconnects conduits of a shield generator or long range scanner + name = "Connect conduit" + required_stat = STAT_MEC + minimal_stat = STAT_LEVEL_ADEPT + +/datum/statverb/connect_conduit/action(mob/user, obj/machinery/power/conduit/conduit) + var/timer = 30 * (1 - user.stats.getStat(STAT_MEC) / 100) SECONDS + if(!conduit.base) //we try to connect it + var/turf/T = get_step(conduit, conduit.dir) + var/obj/machinery/power/shipside/target = locate(/obj/machinery/power/shipside/) in T + if(!target) + user.visible_message(self_message = SPAN_NOTICE("There is nothing to [conduit] to.")) + return FALSE + else + if(!target.tendrils_deployed && target.tendrils.len > 0) + if(!target.toggle_tendrils(TRUE)) //fail if conduits are not deployed and can not be deployed + return + if(target.tendrils.len < 1) //no conduits? + target.tendrils_deployed = TRUE + var/datum/repeating_sound/wrenchsound = new(30, timer, 0.15, conduit, 'sound/items/Ratchet.ogg', 80, 1) + user.visible_message(SPAN_NOTICE("[user] starts to connect various pipes and wires between [conduit] and [target]."), + "You start to connect various pipes and wires between [conduit] and [target].") + if(do_mob(user, conduit, timer)) + wrenchsound.stop() + qdel(wrenchsound) + conduit.connect(target) + user.visible_message(SPAN_NOTICE("[user] successfully connected [conduit] to the [target]!"), + "You successfully conneced [conduit] to the [target]!") + else + wrenchsound.stop() + qdel(wrenchsound) + user.visible_message(SPAN_NOTICE("[user] stopped connecting [conduit] and [target]."), + "You stopped connecting [conduit] and [target].") + else //disconnection + var/datum/repeating_sound/wrenchsound = new(30, timer, 0.15, conduit, 'sound/items/Ratchet.ogg', 80, 1) + user.visible_message(SPAN_NOTICE("[user] attempts to disconnect [conduit] from the [conduit.base]."), + "You attempt to disconnect [conduit] from the [conduit.base].") + if(do_mob(user, conduit, timer)) + wrenchsound.stop() + qdel(wrenchsound) + user.visible_message(SPAN_NOTICE("[user] successfully disconnected [conduit] from the [conduit.base]!"), + "You successfully disconneced [conduit] from the [conduit.base]!") + if(conduit.base.tendrils_deployed == TRUE) + conduit.disconnect() + else + wrenchsound.stop() + qdel(wrenchsound) + user.visible_message(SPAN_NOTICE("[user] stopped connecting [conduit] and [conduit.base]."), + "You stopped connecting [conduit] and [conduit.base].") \ No newline at end of file diff --git a/code/datums/wires/lrange_scanner.dm b/code/datums/wires/lrange_scanner.dm index 17703b29b0e..9775906569d 100644 --- a/code/datums/wires/lrange_scanner.dm +++ b/code/datums/wires/lrange_scanner.dm @@ -1,5 +1,5 @@ /datum/wires/long_range_scanner - holder_type = /obj/machinery/power/long_range_scanner/ + holder_type = /obj/machinery/power/shipside/long_range_scanner/ wire_count = 5 descriptions = list( new /datum/wire_description(SCANNER_WIRE_POWER, "Power"), @@ -14,13 +14,13 @@ var/const/SCANNER_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to rest var/const/SCANNER_WIRE_NOTHING = 16 // A blank wire that doesn't have any specific function /datum/wires/long_range_scanner/CanUse() - var/obj/machinery/power/long_range_scanner/S = holder + var/obj/machinery/power/shipside/long_range_scanner/S = holder if(S.panel_open) return 1 return 0 /datum/wires/long_range_scanner/UpdateCut(index, mended) - var/obj/machinery/power/long_range_scanner/S = holder + var/obj/machinery/power/shipside/long_range_scanner/S = holder switch(index) if(SCANNER_WIRE_POWER) S.input_cut = !mended diff --git a/code/datums/wires/shield_generator.dm b/code/datums/wires/shield_generator.dm index 8e4e5fd02bd..0c3dc57a86b 100644 --- a/code/datums/wires/shield_generator.dm +++ b/code/datums/wires/shield_generator.dm @@ -1,5 +1,5 @@ /datum/wires/shield_generator - holder_type = /obj/machinery/power/shield_generator/ + holder_type = /obj/machinery/power/shipside/shield_generator/ wire_count = 5 descriptions = list( new /datum/wire_description(SHIELDGEN_WIRE_POWER, "Main power"), @@ -13,13 +13,13 @@ var/const/SHIELDGEN_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to re var/const/SHIELDGEN_WIRE_NOTHING = 16 // A blank wire that doesn't have any specific function /datum/wires/shield_generator/CanUse() - var/obj/machinery/power/shield_generator/S = holder + var/obj/machinery/power/shipside/shield_generator/S = holder if(S.panel_open) return 1 return 0 /datum/wires/shield_generator/UpdateCut(index, mended) - var/obj/machinery/power/shield_generator/S = holder + var/obj/machinery/power/shipside/shield_generator/S = holder switch(index) if(SHIELDGEN_WIRE_POWER) S.input_cut = !mended diff --git a/code/game/gamemodes/events/space_weather.dm b/code/game/gamemodes/events/space_weather.dm index 2ecc9bbec1e..c86107ab124 100644 --- a/code/game/gamemodes/events/space_weather.dm +++ b/code/game/gamemodes/events/space_weather.dm @@ -149,7 +149,7 @@ SSevent.change_parallax(GLOB.random_parallax) /datum/event/harmonic_feedback/tick() //around two seconds - for(var/obj/machinery/power/shield_generator/G in GLOB.machines) + for(var/obj/machinery/power/shipside/shield_generator/G in GLOB.machines) if(G.running != SHIELD_OFF) G.take_shield_damage(250, SHIELD_DAMTYPE_SPECIAL, "UNKNOWN") // ALRIGHT LISTEN WEATHER CAN LAST AT MAXIMUM 450 TICKS, AVERAGE AT 375 SHIELD HITS // BY DEFAULT 1000 DAMAGE EQUALS 1 PERCENT, SO 0.25% * 375 = 93.75%, THIS WILL KNOCK SHIELDS OUT IF THEY'RE NOT AT FULL CAPACITY OR NOT CHARGING WELL diff --git a/code/game/gamemodes/scores.dm b/code/game/gamemodes/scores.dm index 80097318c85..438d7ae5371 100644 --- a/code/game/gamemodes/scores.dm +++ b/code/game/gamemodes/scores.dm @@ -180,7 +180,7 @@ GLOBAL_VAR_INIT(score_technomancer_faction_item_loss, 0) if(smes_count == 0) GLOB.all_smes_powered = FALSE - for(var/obj/machinery/power/shield_generator/S in GLOB.machines) + for(var/obj/machinery/power/shipside/shield_generator/S in GLOB.machines) if(!isStationLevel(S.z)) continue smes_count++ if(!S.running) continue diff --git a/code/game/objects/items/weapons/circuitboards/machinery/lrange_scanner.dm b/code/game/objects/items/weapons/circuitboards/machinery/lrange_scanner.dm index 1aad3f148f4..454d1ceb221 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/lrange_scanner.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/lrange_scanner.dm @@ -5,11 +5,24 @@ /obj/item/electronics/circuitboard/long_range_scanner name = T_BOARD("long range scanner") board_type = "machine" - build_path = /obj/machinery/power/long_range_scanner + build_path = /obj/machinery/power/shipside/long_range_scanner matter = list(MATERIAL_GLASS = 3, MATERIAL_GOLD = 3) - origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4) + origin_tech = list(TECH_BLUESPACE = 5, TECH_ENGINEERING = 4) req_components = list( - /obj/item/stock_parts/capacitor = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/smes_coil = 1, + /obj/item/stock_parts/scanning_module = 2, + /obj/item/bluespace_crystal = 1, + /obj/item/stock_parts/subspace/filter = 1, /obj/item/stock_parts/console_screen = 1) + + +/obj/item/electronics/circuitboard/scanner_conduit + name = T_BOARD("scanner conduit") + board_type = "machine" + build_path = /obj/machinery/power/conduit/scanner_conduit + matter = list(MATERIAL_GLASS = 3, MATERIAL_GOLD = 3) + origin_tech = list(TECH_BLUESPACE = 3, TECH_POWER = 4) + req_components = list( + /obj/item/stock_parts/subspace/crystal = 1, + /obj/item/stock_parts/subspace/amplifier = 1, + /obj/item/stock_parts/smes_coil = 1, + /obj/item/stock_parts/capacitor = 2) \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm index d780cda67c0..3677a7f54b7 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm @@ -5,15 +5,28 @@ /obj/item/electronics/circuitboard/shield_generator name = T_BOARD("hull shield generator") board_type = "machine" - build_path = /obj/machinery/power/shield_generator + build_path = /obj/machinery/power/shipside/shield_generator matter = list(MATERIAL_GLASS = 2, MATERIAL_GOLD = 1) - origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4) + origin_tech = list(TECH_ENGINEERING = 4, TECH_BLUESPACE = 4) req_components = list( - /obj/item/stock_parts/capacitor = 1, /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/smes_coil = 1, + /obj/item/stock_parts/subspace/crystal = 1, //might be changed to the Catalyst in furure Techno updates + /obj/item/stock_parts/subspace/transmitter = 1, /obj/item/stock_parts/console_screen = 1) + +/obj/item/electronics/circuitboard/shield_conduit + name = T_BOARD("shield conduit") + board_type = "machine" + build_path = /obj/machinery/power/conduit/shield_conduit + matter = list(MATERIAL_GLASS = 2, MATERIAL_GOLD = 1) + origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4) + req_components = list( + /obj/item/stock_parts/capacitor = 4, + /obj/item/stock_parts/smes_coil = 1, + /obj/item/stack/cable_coil = 10) + + /obj/item/electronics/circuitboard/shield_diffuser name = T_BOARD("shield diffuser") board_type = "machine" diff --git a/code/modules/long_range_scanner/hull.dm b/code/modules/long_range_scanner/hull.dm index c2ae02576ee..351ba6471c9 100644 --- a/code/modules/long_range_scanner/hull.dm +++ b/code/modules/long_range_scanner/hull.dm @@ -1,9 +1,10 @@ //The main hull long range scanner. -/obj/machinery/power/long_range_scanner/hull +/obj/machinery/power/shipside/long_range_scanner/hull name = "long range scanner core" //This subtype comes pre-deployed and partially charged -/obj/machinery/power/long_range_scanner/hull/installed/Initialize() +/obj/machinery/power/shipside/long_range_scanner/hull/installed/Initialize() . = ..() - anchored = toggle_tendrils(TRUE) + anchored = TRUE + spawn_tendrils() current_energy = max_energy * 0.30 diff --git a/code/modules/long_range_scanner/long_range_scanner.dm b/code/modules/long_range_scanner/long_range_scanner.dm index be1c8ca0d31..3d6159045cb 100644 --- a/code/modules/long_range_scanner/long_range_scanner.dm +++ b/code/modules/long_range_scanner/long_range_scanner.dm @@ -5,13 +5,14 @@ #define PASSIVE_SCAN_PERIOD 3 SECONDS #define PULSE_PROGRESS_TIME 30 // in decisecond #define ACTIVE_SCAN_RANGE 10 -#define ACTIVE_SCAN_DURATION 30 SECONDS +#define ACTIVE_SCAN_DURATION 10 SECONDS var/list/ship_scanners = list() -/obj/machinery/power/long_range_scanner +/obj/machinery/power/shipside/long_range_scanner name = "long range scanner" desc = "An advanced long range scanner with heavy-duty capacitor, capable of scanning celestial anomalies at large distances." + description_info = "Can be moved by retracting the power conduits with the appropiate right-click verb" icon = 'icons/obj/machines/conduit_of_soul.dmi' icon_state = "core_inactive" density = TRUE @@ -19,78 +20,64 @@ var/list/ship_scanners = list() circuit = /obj/item/electronics/circuitboard/long_range_scanner - - - var/needs_update = FALSE //If true, will update in process - var/datum/wires/long_range_scanner/wires - var/list/event_log = list() // List of relevant events for this shield - var/max_log_entries = 200 // A safety to prevent players generating endless logs and maybe endangering server memory + list/event_log = list() // List of relevant events for this scanner + max_log_entries = 200 // A safety to prevent players generating endless logs and maybe endangering server memory - var/scanner_modes = 0 // Enabled scanner mode flags var/as_duration_multiplier = 1.0 // Active scan duration multiplier (improve internal components) var/as_energy_multiplier = 1.0 // Active scan energy cost multiplier (improve internal components) - var/max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this scanner. - var/current_energy = 0 // Current stored energy. - var/running = SCANNER_OFF // Whether the scanner is enabled or not. - var/input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The shield will try to input this value per tick at most - var/upkeep_power_usage = 0 // Upkeep power usage last tick. - var/power_usage = 0 // Total power usage last tick. - var/overloaded = 0 // Whether the field has overloaded and shut down to regenerate. - var/offline_for = 0 // The scanner will be inoperable for this duration in ticks. - var/input_cut = 0 // Whether the input wire is cut. - var/mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. - var/ai_control_disabled = 0 // Whether the AI control is disabled. - var/emergency_shutdown = FALSE // Whether the scanner is currently recovering from an emergency shutdown - var/list/default_modes = list() - var/generatingShield = FALSE //true when shield tiles are in process of being generated - - var/obj/effect/overmap/ship/linked_ship = null // To access position of Eris on the overmap - - var/list/tendrils = list() - var/list/tendril_dirs = list(NORTH, EAST, WEST) - var/tendrils_deployed = FALSE // Whether the dummy capacitors are currently extended - - -/obj/machinery/power/long_range_scanner/update_icon() - cut_overlays() + max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this scanner. + var/input_maxcap = 0 // Maximal level of input by the scanner. Set by RefreshParts() + current_energy = 0 // Current stored energy. + running = SCANNER_OFF // Whether the scanner is enabled or not. + input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The scanner will try to input this value per tick at most + upkeep_power_usage = 0 // Upkeep power usage last tick. + power_usage = 0 // Total power usage last tick. + offline_for = 0 // The scanner will be inoperable for this duration in ticks. + input_cut = 0 // Whether the input wire is cut. + mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. + ai_control_disabled = 0 // Whether the AI control is disabled. + emergency_shutdown = FALSE // Whether the scanner is currently recovering from an emergency shutdown + + obj/effect/overmap/ship/linked_ship = null // To access position of Eris on the overmap + + list/tendrils = list() + list/tendril_dirs = list() + tendrils_deployed = FALSE // Whether the capacitors are currently extended + + +/obj/machinery/power/shipside/long_range_scanner/update_icon() if(running) set_light(1, 1, "#82C2D8") - icon_state = "core_warmup" - spawn(20) + flick("core_warmup", src) + icon_state = "core_active" + spawn(6) set_light(2, 2, "#82C2D8") - icon_state = "core_active" else set_light(1, 1, "#82C2D8") - icon_state = "core_shutdown" - spawn(20) + flick("core_shutdown", src) + icon_state = "core_inactive" + spawn(5) set_light(0) - icon_state = "core_inactive" - for (var/obj/machinery/scanner_conduit/S in tendrils) + for (var/obj/machinery/power/conduit/scanner_conduit/S in tendrils) if (running) S.dim_light() - S.icon_state = "warmup" - S.update_icon() - spawn(20) + flick("warmup", S) + S.icon_state = "speen" + spawn(19) S.bright_light() - S.icon_state = "speen" - S.update_icon() - else S.dim_light() - S.icon_state = "shutdown" - S.update_icon() - spawn(20) + flick("shutdown", S) + S.icon_state = "inactive" + spawn(5) S.no_light() - S.icon_state = "inactive" - S.update_icon() -/obj/machinery/power/long_range_scanner/Initialize() +/obj/machinery/power/shipside/long_range_scanner/Initialize() . = ..() - connect_to_network() wires = new(src) ship_scanners += src var/obj/effect/overmap/ship/S = map_sectors["[z]"] @@ -100,8 +87,7 @@ var/list/ship_scanners = list() // Link to Eris object on the overmap linked_ship = (locate(/obj/effect/overmap/ship/eris) in GLOB.ships) -/obj/machinery/power/long_range_scanner/Destroy() - toggle_tendrils(FALSE) +/obj/machinery/power/shipside/long_range_scanner/Destroy() QDEL_NULL(wires) ship_scanners -= src var/obj/effect/overmap/ship/S = map_sectors["[z]"] @@ -110,28 +96,49 @@ var/list/ship_scanners = list() . = ..() -/obj/machinery/power/long_range_scanner/RefreshParts() +/obj/machinery/power/shipside/long_range_scanner/RefreshParts() max_energy = 0 - for(var/obj/item/stock_parts/smes_coil/S in component_parts) - max_energy += (S.ChargeCapacity / CELLRATE) - current_energy = between(0, current_energy, max_energy) - - // Better micro lasers increase the duration of the active scan mode - as_duration_multiplier = 1.0 + 0.5 * max_part_rating(/obj/item/stock_parts/micro_laser) - as_duration_multiplier = between(initial(as_duration_multiplier), as_duration_multiplier, 10.0) + input_maxcap = 0 + for(var/obj/machinery/power/conduit/scanner_conduit/SC in tendrils) + for(var/obj/item/stock_parts/smes_coil/S in SC.component_parts) + max_energy += (S.ChargeCapacity / CELLRATE) / 3 //Divide by 3 because three default conduits + input_maxcap += S.IOCapacity //Around 2.25 MEGAWATTS with default parts + current_energy = between(0, current_energy, max_energy) //Yes, same as the shieldgen. + input_cap = between(0, input_cap, input_maxcap) + + // Better scanners increase the duration of the active scan mode + var/scan_rating = 0 + as_duration_multiplier = max(1, 1.0 * tendrils.len) + for(var/obj/item/stock_parts/scanning_module/S in component_parts) + scan_rating += S.rating + as_duration_multiplier *= scan_rating / 2 + for(var/obj/item/bluespace_crystal/artificial/A in component_parts) + as_duration_multiplier /= 2 //Halves duration if you use cheep crystal. Miser pays twice! // Better capacitors diminish the energy consumption of the active scan mode - as_energy_multiplier = 1.0 - 0.1 * max_part_rating(/obj/item/stock_parts/capacitor) - as_energy_multiplier = between(0.0, as_energy_multiplier, initial(as_energy_multiplier)) - + as_energy_multiplier = 1.15 + for(var/obj/machinery/power/conduit/scanner_conduit/SC in tendrils) + as_energy_multiplier -= 0.05 * SC.rating // Shuts down the long range scanner -/obj/machinery/power/long_range_scanner/proc/shutdown_scanner() +/obj/machinery/power/shipside/long_range_scanner/shutdown_machine() running = SCANNER_OFF update_icon() +/obj/machinery/power/shipside/long_range_scanner/spawn_tendrils(dirs = list(NORTH, EAST, WEST)) + for (var/D in dirs) + var/turf/T = get_step(src, D) + var/obj/machinery/power/conduit/scanner_conduit/tendril = locate(T) + if(!tendril) + tendril = new(T) + tendril.connect(src) + tendril.face_atom(src) + tendril.anchored = TRUE + tendrils_deployed = TRUE + build_tendril_dirs() + update_icon() -/obj/machinery/power/long_range_scanner/Process() +/obj/machinery/power/shipside/long_range_scanner/Process() upkeep_power_usage = 0 power_usage = 0 @@ -145,77 +152,39 @@ var/list/ship_scanners = list() // We are shutting down, therefore our stored energy disperses faster than usual. else if(running == SCANNER_DISCHARGING) if (offline_for <= 0) - shutdown_scanner() //We've finished the winding down period and now turn off + shutdown_machine() //We've finished the winding down period and now turn off offline_for += 30 //Another minute before it can be turned back on again return - upkeep_power_usage = ENERGY_UPKEEP_SCANNER + if(running == SCANNER_RUNNING) + upkeep_power_usage = ENERGY_UPKEEP_SCANNER - if(powernet && !input_cut && (running == SCANNER_RUNNING || running == SCANNER_OFF)) + if(tendrils_deployed && !input_cut && (running == SCANNER_RUNNING || running == SCANNER_OFF)) var/energy_buffer = 0 - energy_buffer = draw_power(min(upkeep_power_usage, input_cap)) + for(var/obj/machinery/power/conduit/scanner_conduit/SC in tendrils) + energy_buffer += SC.draw_power(input_cap / tendrils.len) power_usage += round(energy_buffer) - - if(energy_buffer < upkeep_power_usage) - current_energy -= round(upkeep_power_usage - energy_buffer) // If we don't have enough energy from the grid, take it from the internal battery instead. - - // Now try to recharge our internal energy. - var/energy_to_demand - if(input_cap) - energy_to_demand = between(0, max_energy - current_energy, input_cap - upkeep_power_usage) - else - energy_to_demand = max(0, max_energy - current_energy) - energy_buffer = draw_power(energy_to_demand) - power_usage += energy_buffer - current_energy += round(energy_buffer) + current_energy += energy_buffer - upkeep_power_usage //if grid energy is lower than upkeep - negative number will be added else current_energy -= round(upkeep_power_usage) // We are shutting down, or we lack external power connection. Use energy from internal source instead. if(current_energy <= 0) energy_failure() - if (charge_level() > 5) - overloaded = 0 +/obj/machinery/power/shipside/long_range_scanner/proc/energy_failure() + offline_for += 150 + shutdown_machine() + emergency_shutdown = TRUE + if (current_energy < 0) + current_energy = 0 -/obj/machinery/power/long_range_scanner/attackby(obj/item/O as obj, mob/user as mob) - // Prevents dismantle-rebuild tactics to reset the emergency shutdown timer. - if(running) - to_chat(user, "Turn off \the [src] first!") - return - if(offline_for) - to_chat(user, "Wait until \the [src] cools down from emergency shutdown first!") - return - - if(default_deconstruction(O, user)) - return - if(default_part_replacement(O, user)) - return - - //TODO: Implement unwrenching in a proper centralised location. Having to copypaste this around sucks - if(QUALITY_BOLT_TURNING in O.tool_qualities) - wrench(user, O) - return - - if(istool(O)) - return src.attack_hand(user) - -/obj/machinery/power/long_range_scanner/proc/energy_failure() - if(running == SCANNER_DISCHARGING) - shutdown_scanner() - else - if (current_energy < 0) - current_energy = 0 - overloaded = 1 - - -/obj/machinery/power/long_range_scanner/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = NANOUI_FOCUS) +/obj/machinery/power/shipside/long_range_scanner/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = NANOUI_FOCUS) var/data[0] data["running"] = running data["logs"] = get_logs() - data["overloaded"] = overloaded data["max_energy"] = round(max_energy / 1000000, 0.1) data["current_energy"] = round(current_energy / 1000000, 0.1) data["input_cap_kw"] = round(input_cap / 1000) @@ -233,19 +202,19 @@ var/list/ship_scanners = list() ui.set_auto_update(1) -/obj/machinery/power/long_range_scanner/attack_hand(var/mob/user) +/obj/machinery/power/shipside/long_range_scanner/attack_hand(var/mob/user) nano_ui_interact(user) if(panel_open) wires.Interact(user) -/obj/machinery/power/long_range_scanner/CanUseTopic(var/mob/user) +/obj/machinery/power/shipside/long_range_scanner/CanUseTopic(var/mob/user) if(issilicon(user) && !Adjacent(user) && ai_control_disabled) return STATUS_UPDATE return ..() -/obj/machinery/power/long_range_scanner/Topic(href, href_list) +/obj/machinery/power/shipside/long_range_scanner/Topic(href, href_list) if(..()) return 1 if(!anchored) @@ -263,13 +232,17 @@ var/list/ship_scanners = list() log_event(EVENT_DISABLED, src) if(href_list["start_generator"]) + if(tendrils_deployed == FALSE) + visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it needs to have it's conduits deployed first to operate")) + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 100, 1, 5) + return running = SCANNER_RUNNING update_icon() log_event(EVENT_ENABLED, src) - offline_for = 3 //This is to prevent cases where you startup the shield and then turn it off again immediately while spamclicking + offline_for = 3 //This is to prevent cases where you startup the scanner and then turn it off again immediately while spamclicking . = 1 - // Instantly drops the shield, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge. + // Instantly drops the scanner, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge. if(href_list["emergency_shutdown"]) if(!running) return @@ -281,7 +254,7 @@ var/list/ship_scanners = list() var/temp_integrity = charge_level() offline_for += 300 //5 minutes, given that procs happen every 2 seconds - shutdown_scanner() + shutdown_machine() emergency_shutdown = TRUE log_event(EVENT_DISABLED, src) if(prob(temp_integrity - 50) * 1.75) @@ -293,28 +266,23 @@ var/list/ship_scanners = list() return 1 if(href_list["set_input_cap"]) - var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num) + var/new_cap = round(input(usr, "Enter new input cap (in kW). Current maximal input cap is [input_maxcap / 1000] kW", "Scanner Power Control", round(input_cap / 1000)) as num) if(!new_cap) - input_cap = 0 return - input_cap = max(0, new_cap) * 1000 + input_cap = between(1, new_cap, input_maxcap / 1000) * 1000 log_event(EVENT_RECONFIGURED, src) . = 1 nano_ui_interact(usr) -/obj/machinery/power/long_range_scanner/proc/charge_level() +/obj/machinery/power/shipside/long_range_scanner/proc/charge_level() if(max_energy) return (current_energy / max_energy) * 100 return 0 -// Checks whether specific flags are enabled -/obj/machinery/power/long_range_scanner/proc/check_flag(var/flag) - return (scanner_modes & flag) - -/obj/machinery/power/long_range_scanner/proc/get_logs() +/obj/machinery/power/shipside/long_range_scanner/proc/get_logs() var/list/all_logs = list() for(var/i = event_log.len; i > 1; i--) all_logs.Add(list(list( @@ -322,30 +290,17 @@ var/list/ship_scanners = list() ))) return all_logs - -/obj/machinery/power/long_range_scanner/proc/wrench(var/user, var/obj/item/O) - if(O.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(anchored) - to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) - anchored = FALSE - else - if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! - to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) - anchored = TRUE - return - -//This proc keeps an internal log of shield impacts, activations, deactivations, and a vague log of config changes -/obj/machinery/power/long_range_scanner/proc/log_event(var/event_type, var/atom/origin_atom) +//This proc keeps an internal log of scanner activations, deactivations, and a vague log of config changes +/obj/machinery/power/shipside/long_range_scanner/log_event(var/event_type, var/atom/origin_atom) var/logstring = "[stationtime2text()]: " switch (event_type) if (EVENT_ENABLED to EVENT_RECONFIGURED) switch (event_type) if (EVENT_ENABLED) - logstring += "Shield powered up" + logstring += "Scanner powered up" if (EVENT_DISABLED) - logstring += "Shield powered down" + logstring += "Scanner powered down" if (EVENT_RECONFIGURED) logstring += "Configuration altered" else @@ -373,125 +328,54 @@ var/list/ship_scanners = list() if (event_log.len > max_log_entries) event_log.Cut(1,2) -/obj/machinery/scanner_conduit +/obj/machinery/power/shipside/long_range_scanner/proc/consume_energy_scan() + if(current_energy > round(ENERGY_PER_SCAN * as_energy_multiplier)) + current_energy -= round(ENERGY_PER_SCAN * as_energy_multiplier) + return TRUE + return FALSE + + +/obj/machinery/power/conduit/scanner_conduit name = "scanner conduit" icon = 'icons/obj/machines/conduit_of_soul.dmi' icon_state = "inactive" - desc = "A combined conduit and capacitor that transfers and stores massive amounts of energy." + desc = "A combined conduit and capacitor that transfers and stores massive amounts of energy while also increasing the efficiency of connected long range scanner." density = TRUE anchored = FALSE //Will be set true just after deploying - var/obj/machinery/power/long_range_scanner/scanner - -/obj/machinery/scanner_conduit/proc/connect(sca) - scanner = sca - -/obj/machinery/scanner_conduit/proc/no_light() + circuit = /obj/item/electronics/circuitboard/scanner_conduit + var/rating //average rating of all capacitors + +/obj/machinery/power/conduit/scanner_conduit/no_light() set_light(0) -/obj/machinery/scanner_conduit/proc/dim_light() +/obj/machinery/power/conduit/scanner_conduit/proc/dim_light() set_light(1, 1, "#82C2D8") -/obj/machinery/scanner_conduit/proc/bright_light() +/obj/machinery/power/conduit/scanner_conduit/bright_light() set_light(2, 2, "#82C2D8") -/obj/machinery/scanner_conduit/Destroy() - if(scanner) - scanner.toggle_tendrils(FALSE) - if(scanner.running != SCANNER_OFF && !scanner.emergency_shutdown) - scanner.offline_for += 300 - scanner.shutdown_scanner() - scanner.emergency_shutdown = TRUE - scanner.log_event(EVENT_DISABLED, scanner) +/obj/machinery/power/conduit/scanner_conduit/RefreshParts() + rating = 0 + for(var/obj/item/stock_parts/capacitor/C in component_parts) + rating += C.rating + rating /= 2 . = ..() -/obj/machinery/power/long_range_scanner/wrench(user, obj/item/I) - if(running != SCANNER_OFF) - to_chat(usr, SPAN_NOTICE("Scanner has to be toggled off first!")) - return - if(tendrils_deployed) - to_chat(usr, SPAN_NOTICE("Retract conduits first!")) - return - if(I.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(anchored) - to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) - toggle_tendrils(FALSE) - anchored = FALSE - else - if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! - to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) - anchored = TRUE - return - -/obj/machinery/power/long_range_scanner/verb/toggle_tendrils_verb() - set category = "Object" - set name = "Toggle conduits" - set src in view(1) - - if(running != SCANNER_OFF) - to_chat(usr, SPAN_NOTICE("Scanner has to be toggled off first!")) - return - toggle_tendrils() - -/obj/machinery/power/long_range_scanner/proc/toggle_tendrils(on = null) - var/target_state - if (!isnull(on)) - target_state = on - else - target_state = tendrils_deployed ? FALSE : TRUE //Otherwise we're toggling - - if (target_state == tendrils_deployed) - return - //If we're extending them - if (target_state == TRUE) - if(!anchored) - visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it needs to be properly anchored to deploy")) - playsound(src.loc, "/sound/machines/buzz-two", 100, 1, 5) - tendrils_deployed = FALSE - update_icon() - return FALSE - for (var/D in tendril_dirs) - var/turf/T = get_step(src, D) - var/obj/machinery/scanner_conduit/SC = locate(/obj/machinery/scanner_conduit) in T - if(SC) - continue - if (!turf_clear(T)) - visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it lacks the space to deploy")) - playsound(src.loc, "/sound/machines/buzz-two", 100, 1, 5) - tendrils_deployed = FALSE - update_icon() - return FALSE - - //Now deploy - for (var/D in tendril_dirs) - var/turf/T = get_step(src, D) - var/obj/machinery/scanner_conduit/SC = locate(/obj/machinery/scanner_conduit) in T - if(!SC) SC = new(T) - SC.connect(src) - tendrils.Add(SC) - SC.face_atom(src) - SC.anchored = TRUE - tendrils_deployed = TRUE - update_icon() - - to_chat(usr, SPAN_NOTICE("You deployed [src] conduits.")) - return TRUE - - else if (target_state == FALSE) - for (var/obj/machinery/scanner_conduit/SC in tendrils) - tendrils.Remove(SC) - qdel(SC) - tendrils_deployed = FALSE - update_icon() - - to_chat(usr, SPAN_NOTICE("You retracted [src] conduits.")) +/obj/machinery/power/conduit/scanner_conduit/disconnect() + if(!base) return FALSE - -/obj/machinery/power/long_range_scanner/proc/consume_energy_scan() - if(current_energy > round(ENERGY_PER_SCAN * as_energy_multiplier)) - current_energy -= round(ENERGY_PER_SCAN * as_energy_multiplier) - return TRUE - return FALSE + if(base.running != 0 && !base.emergency_shutdown) + base.offline_for += 300 + base.shutdown_machine() + base.emergency_shutdown = TRUE + base.log_event(EVENT_DISABLED, base) + base.tendrils.Remove(src) + base.build_tendril_dirs() + base.RefreshParts() + base.update_icon() + base = null + no_light() + disconnect_from_network() #undef EVENT_ENABLED #undef EVENT_DISABLED diff --git a/code/modules/modular_computers/file_system/programs/engineering/shield_control.dm b/code/modules/modular_computers/file_system/programs/engineering/shield_control.dm index 9d1019172b3..96e9dc644ca 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/shield_control.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/shield_control.dm @@ -23,7 +23,7 @@ /datum/nano_module/shield_control name = "Shield control" - var/obj/machinery/power/shield_generator/hull/gen = null + var/obj/machinery/power/shipside/shield_generator/hull/gen = null var/multigen = FALSE //Set true if multiple active hull shield generators are detected onstation var/genloc = ""//A string that describes the location of our connected shield generator @@ -35,11 +35,11 @@ /datum/nano_module/shield_control/proc/connect_to_generator() var/n = 0 gen = null - for (var/obj/machinery/power/shield_generator/hull/G in world) + for (var/obj/machinery/power/shipside/shield_generator/hull/G in world) //Check that the generator is on the same vessel as us. //This allows antag ships/stations to have their own shield generators and consoles if (is_matching_vessel(G, nano_host())) - if (G.anchored && G.tendrils_deployed) //Only look at those that are wrenched in and setup + if (G.anchored && G.tendrils_deployed && !G.ai_control_disabled) //Only look at those that are wrenched in and setup gen = G //It's a good enough candidate, we're connected! n++ @@ -121,6 +121,7 @@ //If the generator has been unwrenched we also lose connection playsound_host('sound/machines/buzz-two.ogg', 50) gen = null //Cut our connection, and we'll be unable to reconnect + genloc = "" //Clear the location too! return @@ -159,7 +160,7 @@ var/temp_integrity = gen.field_integrity() gen.offline_for += 300 //5 minutes, given that procs happen every 2 seconds - gen.shutdown_field() + gen.shutdown_machine() gen.emergency_shutdown = TRUE gen.log_event(EVENT_DISABLED, nano_host()) if(prob(temp_integrity - 50) * 1.75) @@ -180,11 +181,10 @@ . = 1 if(href_list["set_input_cap"]) - var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(gen.input_cap / 1000)) as num) + var/new_cap = round(input(usr, "Enter new input cap (in kW). Current maximal input cap is [gen.input_maxcap / 1000] kW", "Generator Power Control", round(gen.input_cap / 1000)) as num) if(!new_cap) - gen.input_cap = 0 return - gen.input_cap = max(0, new_cap) * 1000 + gen.input_cap = between(1, new_cap, gen.input_maxcap / 1000) * 1000 gen.log_event(EVENT_RECONFIGURED, nano_host()) . = 1 diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm index c856aa5a52d..da4f0ebcdde 100644 --- a/code/modules/nano/modules/nano_module.dm +++ b/code/modules/nano/modules/nano_module.dm @@ -174,8 +174,9 @@ //Allows computer programs to play sounds from the console /datum/nano_module/proc/playsound_host(soundin, vol as num, vary, extrarange as num, falloff, var/is_global, var/use_pressure = TRUE) - if (!host) - return + var/atom/nhost = nano_host() + if (!nhost) + return FALSE - var/turf/T = get_turf(host) + var/turf/T = get_turf(nhost) playsound(T, soundin, vol, vary, extrarange, falloff, is_global,use_pressure) diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index 0b0b146d636..9345016f8e4 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -14,7 +14,7 @@ var/obj/machinery/computer/helm/nav_control var/list/engines = list() // contains /datum/ship_engine - var/list/scanners = list() // contains /obj/machinery/power/long_range_scanner + var/list/scanners = list() // contains /obj/machinery/power/shipside/long_range_scanner var/engines_state = 1 //global on/off toggle for all engines var/thrust_limit = 1 //global thrust limit for all engines, 0..1 var/triggers_events = 1 @@ -57,7 +57,7 @@ E.linked = src //testing("Engines console at level [E.z] linked to overmap object '[name]'.") - for(var/obj/machinery/power/long_range_scanner/LRS in ship_scanners) + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in ship_scanners) if (LRS.z in map_z) //testing("Scanner at level [LRS.z] linked to overmap object '[name]'.") scanners |= LRS @@ -87,7 +87,7 @@ E.linked = src //testing("Engines console at level [E.z] linked to overmap object '[name]'.") - for(var/obj/machinery/power/long_range_scanner/LRS in ship_scanners) + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in ship_scanners) if (LRS.z in map_z) //testing("Scanner at level [LRS.z] linked to overmap object '[name]'.") scanners |= LRS @@ -251,14 +251,16 @@ if(pulsing) // Should not happen but better to check return - var/obj/machinery/power/long_range_scanner/enough_LRS = null - for(var/obj/machinery/power/long_range_scanner/LRS in scanners) // Among all ship's scanners get one with enough energy + var/obj/machinery/power/shipside/long_range_scanner/enough_LRS = null + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in scanners) // Among all ship's scanners get one with enough energy if(LRS.running && (LRS.current_energy > round(ENERGY_PER_SCAN * LRS.as_energy_multiplier))) enough_LRS = LRS - if(enough_LRS) - enough_LRS.consume_energy_scan() + if(!enough_LRS) + nav_control.visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it fails to find any sensors with enough power to pulse")) + playsound(nav_control.loc, 'sound/machines/buzz-two.ogg', 100, 1, 5) + if(enough_LRS.consume_energy_scan()) pulsing = TRUE scan_range = ACTIVE_SCAN_RANGE spawn(ACTIVE_SCAN_DURATION * enough_LRS.as_duration_multiplier) @@ -269,7 +271,7 @@ /obj/effect/overmap/ship/proc/can_scan() - for(var/obj/machinery/power/long_range_scanner/LRS in scanners) + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in scanners) . |= (LRS.running) /obj/effect/overmap/ship/proc/can_pulse() @@ -278,7 +280,7 @@ return FALSE // Check if one of the ship's scanners has enough energy to pulse - for(var/obj/machinery/power/long_range_scanner/LRS in scanners) + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in scanners) . |= (LRS.running && (LRS.current_energy > round(ENERGY_PER_SCAN * LRS.as_energy_multiplier))) /obj/effect/overmap/ship/proc/can_scan_poi() @@ -286,7 +288,7 @@ if(!is_still()) // Ship must be immobile return FALSE - for(var/obj/machinery/power/long_range_scanner/LRS in scanners) + for(var/obj/machinery/power/shipside/long_range_scanner/LRS in scanners) . |= (LRS.running) /obj/effect/overmap/ship/proc/scan_poi() diff --git a/code/modules/power/shipside_machinery.dm b/code/modules/power/shipside_machinery.dm new file mode 100644 index 00000000000..3b18c4dc765 --- /dev/null +++ b/code/modules/power/shipside_machinery.dm @@ -0,0 +1,285 @@ +//File created to collect all of the shared code between shield generator, long range scanner and their conduits +//Maybe used for future uses too + +/obj/machinery/power/shipside + name = "heavy shipside machinery" + desc = "A heavy-duty unknown piece of machinery used for the basic functions of the ship." + description_info = "Shouldn't really appear anywhere, please notify admins" + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "baygenerator0" + density = TRUE + anchored = FALSE + + circuit = null + + var/list/event_log = list() // List of relevant events for this machine + var/max_log_entries = 200 // A safety to prevent players generating endless logs and maybe endangering server memory + + var/max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this machinery. + var/current_energy = 0 // Current stored energy. + var/running = null // Whether the machinery is enabled or not. + var/input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The machine will try to input this value per tick at most + var/upkeep_power_usage = 0 // Upkeep power usage last tick. + var/power_usage = 0 // Total power usage last tick. + var/overloaded = 0 // Whether the machinery has overloaded and shut down to regenerate. + var/offline_for = 0 // The machinery will be inoperable for this duration in ticks. + var/input_cut = 0 // Whether the input wire is cut. + var/mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. + var/ai_control_disabled = 0 // Whether the AI control is disabled. + var/emergency_shutdown = FALSE // Whether the machinery is currently recovering from an emergency shutdown + + var/obj/effect/overmap/ship/linked_ship = null // To access position of Eris on the overmap + + var/list/tendrils = list() + var/list/tendril_dirs = list() + var/tendrils_deployed = FALSE // Whether the capacitors are currently extended + + +/obj/machinery/power/shipside/Destroy() + for(var/obj/machinery/power/conduit/C in tendrils) + C.disconnect() + . = ..() + +/obj/machinery/power/shipside/proc/shutdown_machine() + return + +/obj/machinery/power/shipside/proc/build_tendril_dirs() + tendril_dirs = list() + if(tendrils.len < 1) + return FALSE + for (var/obj/machinery/power/conduit/C in tendrils) + tendril_dirs.Add(turn(C.dir, 180)) + return TRUE + +/obj/machinery/power/shipside/proc/spawn_tendrils(dirs = list(NORTH, EAST, WEST)) + for (var/D in dirs) + var/turf/T = get_step(src, D) + var/obj/machinery/power/conduit/tendril = locate(T) + if(!tendril) + tendril = new(T) + tendril.connect(src) + tendril.face_atom(src) + tendril.anchored = TRUE + tendrils_deployed = TRUE + build_tendril_dirs() + update_icon() + +/obj/machinery/power/shipside/proc/log_event(var/event_type, var/atom/origin_atom) + return + +/obj/machinery/power/shipside/verb/toggle_tendrils_verb() + set category = "Object" + set name = "Toggle conduits" + set src in view(1) + + if(running != 0) + to_chat(usr, SPAN_NOTICE("[src] has to be toggled off first!")) + return + toggle_tendrils() + +/obj/machinery/power/shipside/proc/toggle_tendrils(forced_state) + var/target_state + if (isnull(forced_state)) + target_state = tendrils_deployed ? FALSE : TRUE + else + target_state = forced_state + + if (target_state == tendrils_deployed) + return + //If we're extending them + if (target_state == TRUE) + if(!anchored) + visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it needs to be properly anchored to deploy")) + playsound(src.loc, 'sound/machines/buzz-two.ogg', 100, 1, 5) + tendrils_deployed = FALSE + update_icon() + return FALSE + if(!build_tendril_dirs()) + visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it has no conduits to deploy")) + playsound(src.loc, 'sound/machines/buzz-two.ogg', 100, 1, 5) + return FALSE + for (var/D in tendril_dirs) + var/turf/T = get_step(src, D) + if (!turf_clear_ignore_cables(T)) + visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it lacks the space to deploy")) + playsound(src.loc, 'sound/machines/buzz-two.ogg', 100, 1, 5) + tendrils_deployed = FALSE + update_icon() + return FALSE + + //Now deploy + for (var/obj/machinery/power/conduit/C in tendrils) + var/turf/T = get_step(src, turn(C.dir, 180)) + C.forceMove(T) + C.connect(src) + C.anchored = TRUE + C.connect_to_network() + tendrils_deployed = TRUE + update_icon() + + to_chat(usr, SPAN_NOTICE("You deployed [src] conduits.")) + return TRUE + + else if (target_state == FALSE) + for (var/obj/machinery/power/conduit/C in tendrils) + C.disconnect_from_network() + C.forceMove(src) + tendrils_deployed = FALSE + update_icon() + + to_chat(usr, SPAN_NOTICE("You retracted [src] conduits.")) + return FALSE + +/obj/machinery/power/shipside/attackby(obj/item/O as obj, mob/user as mob) + // Prevents dismantle-rebuild tactics to reset the emergency shutdown timer. + if(running) + to_chat(user, "Turn off \the [src] first!") + return + if(offline_for) + to_chat(user, "Wait until \the [src] cools down from emergency shutdown first!") + return + + if(default_deconstruction(O, user)) + return + if(default_part_replacement(O, user)) + return + + //TODO: Implement unwrenching in a proper centralised location. Having to copypaste this around sucks + if(QUALITY_BOLT_TURNING in O.tool_qualities) + wrench(user, O) + return + + if(istool(O)) + return src.attack_hand(user) + +/obj/machinery/power/shipside/proc/wrench(user, obj/item/I) + if(running != 0) + to_chat(usr, SPAN_NOTICE("[src] has to be toggled off first!")) + return + if(tendrils_deployed) + to_chat(usr, SPAN_NOTICE("Retract conduits first!")) + return + if(I.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + if(anchored) + to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) + toggle_tendrils(FALSE) + anchored = FALSE + else + if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! + to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) + anchored = TRUE + return + + +/obj/machinery/power/conduit //Da parent of all the conduits, shouldn't really appear anywhere + name = "general conduit" + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "shieldsparkles" + desc = "A combined conduit and capacitor that transfers and stores massive amounts of energy." + density = TRUE + anchored = FALSE //Will be set true just after deploying + circuit = null + var/obj/machinery/power/shipside/base + +/obj/machinery/power/conduit/Initialize() + . = ..() + add_statverb(/datum/statverb/connect_conduit) + +/obj/machinery/power/conduit/proc/connect(target) + if(base || !target) + return FALSE + base = target + base.tendrils.Add(src) + anchored = TRUE + connect_to_network() + base.RefreshParts() + base.build_tendril_dirs() + base.update_icon() + +/obj/machinery/power/conduit/proc/disconnect() + if(!base) + return FALSE + if(base.running != 0 && !base.emergency_shutdown) + base.offline_for += 300 + base.shutdown_machine() + base.emergency_shutdown = TRUE + base.log_event(0, base) + base.tendrils.Remove(src) + base.build_tendril_dirs() + base.RefreshParts() + base.update_icon() + base = null + no_light() + disconnect_from_network() + +/obj/machinery/power/conduit/proc/no_light() + set_light(0) + +/obj/machinery/power/conduit/proc/bright_light() + set_light(2, 2, "#ff0000") + +/obj/machinery/power/conduit/Destroy() + if(base) + disconnect() + . = ..() + +/obj/machinery/power/conduit/on_deconstruction() + disconnect() + . = ..() + +/obj/machinery/power/conduit/RefreshParts() + . = ..() + if(base) + base.RefreshParts() + +/obj/machinery/power/conduit/attackby(obj/item/O as obj, mob/user as mob) + // Prevents whatever unholy things can happen if you touch conduits mid-work. + if(base) + if(base.running) + to_chat(user, "Turn off \the [base] first!") + return + if(base.offline_for) + to_chat(user, "Wait until \the [base] cools down from emergency shutdown first!") + return + + if(default_deconstruction(O, user)) + return + if(default_part_replacement(O, user)) + if(base) + base.RefreshParts() + return + + //TODO: Implement unwrenching in a proper centralised location. Having to copypaste this around sucks + if(QUALITY_BOLT_TURNING in O.tool_qualities) + wrench(user, O) + return + + if(istool(O)) + return src.attack_hand(user) + +/obj/machinery/power/conduit/proc/wrench(user, obj/item/I) + if(base) + to_chat(usr, SPAN_NOTICE("Disconnect [src] from the [base] first!")) + return + if(I.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + if(anchored) + to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) + anchored = FALSE + else + if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! + to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) + anchored = TRUE + return + +/obj/machinery/power/conduit/verb/rotate() //copied from emitter.dm + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if (src.anchored || usr.stat) + to_chat(usr, "It is fastened to the floor!") + return 0 + src.set_dir(turn(src.dir, 90)) + return 1 \ No newline at end of file diff --git a/code/modules/research/designs/circuits.dm b/code/modules/research/designs/circuits.dm index efd8a0c10b5..f5c38fe0f0e 100644 --- a/code/modules/research/designs/circuits.dm +++ b/code/modules/research/designs/circuits.dm @@ -385,16 +385,16 @@ category = CAT_MISC /datum/design/research/circuit/shield/hull - name = "hull" + name = "hull shield generator" + desc = "Allows for the construction of a shield conduit circuit board." build_path = /obj/item/electronics/circuitboard/shield_generator sort_string = "VAAAB" -/* -/datum/design/research/circuit/shield/capacitor - name = "capacitor" - desc = "Allows for the construction of a shield capacitor circuit board." - req_tech = list(TECH_MAGNET = 3, TECH_POWER = 4) - build_path = /obj/item/electronics/circuitboard/shield_cap - sort_string = "VAAAC"*/ + +/datum/design/research/circuit/shield/conduit + name = "shield conduit" + desc = "Allows for the construction of a shield conduit circuit board." + build_path = /obj/item/electronics/circuitboard/shield_conduit + sort_string = "VAAAC" // Long range scanner /datum/design/research/circuit/lrange_scanner @@ -404,19 +404,24 @@ /datum/design/research/circuit/lrange_scanner/hull name = "long range scanner" build_path = /obj/item/electronics/circuitboard/long_range_scanner - sort_string = "VAAAC" + sort_string = "VAAAD" + +/datum/design/research/circuit/lrange_scanner/conduit + name = "long range scanner conduit" + build_path = /obj/item/electronics/circuitboard/scanner_conduit + sort_string = "VAAAE" //BS /datum/design/research/circuit/telesci/console name = "TeleSci Console" build_path = /obj/item/electronics/circuitboard/telesci_console - sort_string = "VAAAD" + sort_string = "VAAAF" category = CAT_BLUE /datum/design/research/circuit/telesci/hub name = "TeleSci Pad" build_path = /obj/item/electronics/circuitboard/telesci_pad - sort_string = "VAAAE" + sort_string = "VAAAG" category = CAT_BLUE /datum/design/research/circuit/bssilk/console @@ -428,7 +433,7 @@ /datum/design/research/circuit/bssilk/hub name = "Bluespace Snare Hub" build_path = /obj/item/electronics/circuitboard/bssilk_hub - sort_string = "VAAAG" + sort_string = "VAAAH" category = CAT_BLUE /datum/design/research/circuit/teleporter/station diff --git a/code/modules/research/nodes/bluespace.dm b/code/modules/research/nodes/bluespace.dm index 2e7fda3b491..9581a32cf52 100644 --- a/code/modules/research/nodes/bluespace.dm +++ b/code/modules/research/nodes/bluespace.dm @@ -120,7 +120,8 @@ required_tech_levels = list() cost = 1500 - unlocks_designs = list(/datum/design/research/circuit/shield/hull) + unlocks_designs = list( /datum/design/research/circuit/shield/hull, + /datum/design/research/circuit/shield/conduit) /datum/technology/teleportation name = "Teleportation" @@ -135,8 +136,7 @@ required_tech_levels = list() cost = 1500 - unlocks_designs = list(/datum/design/research/circuit/teleconsole, - /datum/design/research/circuit/lrange_scanner/hull) + unlocks_designs = list( /datum/design/research/circuit/teleconsole) /datum/technology/adv_spatial_scan name = "Advanced Spatial Analyzing" @@ -157,7 +157,9 @@ /datum/design/research/circuit/bssilk/console, /datum/design/research/item/bs_snare, /datum/design/research/circuit/teleporter/station, - /datum/design/research/circuit/teleporter/hub + /datum/design/research/circuit/teleporter/hub, + /datum/design/research/circuit/lrange_scanner/hull, + /datum/design/research/circuit/lrange_scanner/conduit ) /datum/technology/bluespace_tools diff --git a/code/modules/shield_generators/hull.dm b/code/modules/shield_generators/hull.dm index 6c0669c83d8..0ee965d7570 100644 --- a/code/modules/shield_generators/hull.dm +++ b/code/modules/shield_generators/hull.dm @@ -1,11 +1,12 @@ //The main hull shield. Moving a few variables here to make it easier to branch off the parent for shortrange bubble shields and such -/obj/machinery/power/shield_generator/hull +/obj/machinery/power/shipside/shield_generator/hull name = "hull shield core" report_integrity = TRUE //This subtype comes pre-deployed and partially charged -/obj/machinery/power/shield_generator/hull/installed/Initialize() +/obj/machinery/power/shipside/shield_generator/hull/installed/Initialize() . = ..() - anchored = toggle_tendrils(TRUE) + anchored = TRUE + spawn_tendrils() current_energy = max_energy * 0.30 diff --git a/code/modules/shield_generators/shield.dm b/code/modules/shield_generators/shield.dm index a12141b9c68..8d0b58f1e94 100644 --- a/code/modules/shield_generators/shield.dm +++ b/code/modules/shield_generators/shield.dm @@ -27,7 +27,7 @@ layer = BELOW_OBJ_LAYER density = TRUE invisibility = 0 - var/obj/machinery/power/shield_generator/gen = null + var/obj/machinery/power/shipside/shield_generator/gen = null var/disabled_for = 0 var/diffused_for = 0 var/floorOnly = FALSE @@ -308,31 +308,31 @@ Like for example singulo act and whatever. // Shield collision checks below -/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) return 1 // Other mobs -/mob/living/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) return !gen.check_flag(MODEFLAG_NONHUMANS) // Human mobs -/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) if(isSynthetic()) return !gen.check_flag(MODEFLAG_ANORGANIC) return !gen.check_flag(MODEFLAG_HUMANOIDS) // Silicon mobs -/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) return !gen.check_flag(MODEFLAG_ANORGANIC) // Generic objects. Also applies to bullets and meteors. -/obj/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/obj/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) return !gen.check_flag(MODEFLAG_HYPERKINETIC) // Beams -/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shipside/shield_generator/gen) return !gen.check_flag(MODEFLAG_PHOTONIC) diff --git a/code/modules/shield_generators/shield_generator.dm b/code/modules/shield_generators/shield_generator.dm index 0e31c383263..a07367a3517 100644 --- a/code/modules/shield_generators/shield_generator.dm +++ b/code/modules/shield_generators/shield_generator.dm @@ -6,7 +6,7 @@ #define EVENT_DISABLED 5 #define EVENT_RECONFIGURED 6 -/obj/machinery/power/shield_generator +/obj/machinery/power/shipside/shield_generator name = "advanced shield generator" desc = "A heavy-duty shield generator and capacitor, capable of generating energy shields at large distances." description_info = "Can be moved by retracting the power conduits with the appropiate right-click verb" @@ -22,36 +22,37 @@ var/datum/wires/shield_generator/wires var/list/field_segments = list() // List of all shield segments owned by this generator. var/list/damaged_segments = list() // List of shield segments that have failed and are currently regenerating. - var/list/event_log = list() // List of relevant events for this shield - var/max_log_entries = 200 // A safety to prevent players generating endless logs and maybe endangering server memory + list/event_log = list() // List of relevant events for this shield + max_log_entries = 200 // A safety to prevent players generating endless logs and maybe endangering server memory var/shield_modes = 0 // Enabled shield mode flags var/mitigation_em = 0 // Current EM mitigation var/mitigation_physical = 0 // Current Physical mitigation var/mitigation_heat = 0 // Current Burn mitigation var/mitigation_max = 0 // Maximal mitigation reachable with this generator. Set by RefreshParts() - var/max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this generator. - var/current_energy = 0 // Current stored energy. + var/input_maxcap = 0 // Maximal level of input by the generator. Set by RefreshParts() + max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this generator. + current_energy = 0 // Current stored energy. var/field_radius = 200 // Current field radius. //200 is default for hull shield - var/running = SHIELD_OFF // Whether the generator is enabled or not. - var/input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The shield will try to input this value per tick at most - var/upkeep_power_usage = 0 // Upkeep power usage last tick. + running = SHIELD_OFF // Whether the generator is enabled or not. + input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The shield will try to input this value per tick at most + upkeep_power_usage = 0 // Upkeep power usage last tick. var/upkeep_multiplier = 1 // Multiplier of upkeep values. var/upkeep_star_multiplier = 1 // Multiplier of upkeep values due to proximity with the star at the center of the overmap var/upkeep_star_multiplier_max = 4 // Maximum upkeep multiplier when the ship is right on top of the star var/upkeep_star_multiplier_safe = 50// Distance from star above which shields are no longer impacted (multiplier = 1) - var/power_usage = 0 // Total power usage last tick. - var/overloaded = 0 // Whether the field has overloaded and shut down to regenerate. - var/offline_for = 0 // The generator will be inoperable for this duration in ticks. - var/input_cut = 0 // Whether the input wire is cut. - var/mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. - var/ai_control_disabled = 0 // Whether the AI control is disabled. + power_usage = 0 // Total power usage last tick. + overloaded = 0 // Whether the field has overloaded and shut down to regenerate. + offline_for = 0 // The generator will be inoperable for this duration in ticks. + input_cut = 0 // Whether the input wire is cut. + mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. + ai_control_disabled = 0 // Whether the AI control is disabled. var/list/mode_list = null // A list of shield_mode datums. - var/emergency_shutdown = FALSE // Whether the generator is currently recovering from an emergency shutdown + emergency_shutdown = FALSE // Whether the generator is currently recovering from an emergency shutdown var/list/default_modes = list() var/generatingShield = FALSE //true when shield tiles are in process of being generated - var/obj/effect/overmap/ship/linked_ship = null // To access position of Eris on the overmap + obj/effect/overmap/ship/linked_ship = null // To access position of Eris on the overmap // The shield mode flags which should be enabled on this generator by default @@ -81,12 +82,12 @@ var/report_delay = 20 SECONDS //We will wait this amount of time after taking a hit before sending a report. var/report_scheduled = FALSE // - var/list/tendrils = list() - var/list/tendril_dirs = list(NORTH, EAST, WEST) - var/tendrils_deployed = FALSE // Whether the dummy capacitors are currently extended + list/tendrils = list() + list/tendril_dirs = list() + tendrils_deployed = FALSE // Whether the capacitors are currently extended -/obj/machinery/power/shield_generator/update_icon() +/obj/machinery/power/shipside/shield_generator/update_icon() overlays.Cut() if(running) icon_state = "generator1" @@ -99,7 +100,7 @@ var/I = image(icon,"capacitor_connected", dir = D) overlays += I - for (var/obj/machinery/shield_conduit/S in tendrils) + for (var/obj/machinery/power/conduit/shield_conduit/S in tendrils) if (running) S.icon_state = "conduit_1" S.bright_light() @@ -109,9 +110,8 @@ -/obj/machinery/power/shield_generator/Initialize() +/obj/machinery/power/shipside/shield_generator/Initialize() . = ..() - connect_to_network() wires = new(src) //Add all allowed modes to our mode list for users to select @@ -128,9 +128,8 @@ // Link to Eris object on the overmap linked_ship = (locate(/obj/effect/overmap/ship/eris) in GLOB.ships) -/obj/machinery/power/shield_generator/Destroy() - toggle_tendrils(FALSE) - shutdown_field() +/obj/machinery/power/shipside/shield_generator/Destroy() + shutdown_machine() field_segments = null damaged_segments = null mode_list = null @@ -138,22 +137,26 @@ . = ..() -/obj/machinery/power/shield_generator/RefreshParts() +/obj/machinery/power/shipside/shield_generator/RefreshParts() max_energy = 0 - for(var/obj/item/stock_parts/smes_coil/S in component_parts) - max_energy += (S.ChargeCapacity / CELLRATE) + input_maxcap = 0 + for(var/obj/machinery/power/conduit/shield_conduit/SC in tendrils) + for(var/obj/item/stock_parts/smes_coil/S in SC.component_parts) + max_energy += (S.ChargeCapacity / CELLRATE) / 3 //Divide by 3 because three default conduits + input_maxcap += S.IOCapacity //Around 2.25 MEGAWATTS with default parts current_energy = between(0, current_energy, max_energy) + input_cap = between(0, input_cap, input_maxcap) mitigation_max = MAX_MITIGATION_BASE - for(var/obj/item/stock_parts/capacitor/C in component_parts) - mitigation_max += MAX_MITIGATION_RESEARCH * C.rating + for(var/obj/item/stock_parts/micro_laser/L in component_parts) //Mitigation can theoretically reach 100% but only if One Star laser is used (0 energy per hit). + mitigation_max += MAX_MITIGATION_RESEARCH * L.rating //It could reach >100% (GAIN charge when hit) if tier 6 laser is used (currently not in game, admemes only) mitigation_em = between(0, mitigation_em, mitigation_max) mitigation_physical = between(0, mitigation_physical, mitigation_max) mitigation_heat = between(0, mitigation_heat, mitigation_max) // Shuts down the shield, removing all shield segments and unlocking generator settings. -/obj/machinery/power/shield_generator/proc/shutdown_field() +/obj/machinery/power/shipside/shield_generator/shutdown_machine() for(var/obj/effect/shield/S in field_segments) qdel(S) @@ -163,9 +166,21 @@ mitigation_heat = 0 update_icon() +/obj/machinery/power/shipside/shield_generator/spawn_tendrils(dirs = list(NORTH, EAST, WEST)) + for (var/D in dirs) + var/turf/T = get_step(src, D) + var/obj/machinery/power/conduit/shield_conduit/tendril = locate(T) + if(!tendril) + tendril = new(T) + tendril.connect(src) + tendril.face_atom(src) + tendril.anchored = TRUE + tendrils_deployed = TRUE + build_tendril_dirs() + update_icon() // Generates the field objects. Deletes existing field, if applicable. -/obj/machinery/power/shield_generator/proc/regenerate_field() +/obj/machinery/power/shipside/shield_generator/proc/regenerate_field() needs_update = FALSE if (generatingShield) return @@ -224,7 +239,7 @@ // Recalculates and updates the upkeep multiplier -/obj/machinery/power/shield_generator/proc/update_upkeep_multiplier() +/obj/machinery/power/shipside/shield_generator/proc/update_upkeep_multiplier() var/new_upkeep = 1 for(var/datum/shield_mode/SM in mode_list) if(check_flag(SM.mode_flag)) @@ -233,14 +248,14 @@ upkeep_multiplier = new_upkeep // Recalculates and updates the upkeep star multiplier -/obj/machinery/power/shield_generator/proc/update_upkeep_star_multiplier() +/obj/machinery/power/shipside/shield_generator/proc/update_upkeep_star_multiplier() var/distance = sqrt((linked_ship.x - GLOB.maps_data.overmap_size/2)**2 + (linked_ship.y - GLOB.maps_data.overmap_size/2)**2) // Distance from star if(distance>upkeep_star_multiplier_safe) // Above safe distance, no impact on shields upkeep_star_multiplier = 1 else // Otherwise shields are impacted depending on proximity to the star upkeep_star_multiplier = 1 + (upkeep_star_multiplier_max - 1) * ((upkeep_star_multiplier_safe - distance) / upkeep_star_multiplier_safe) -/obj/machinery/power/shield_generator/Process() +/obj/machinery/power/shipside/shield_generator/Process() upkeep_power_usage = 0 power_usage = 0 @@ -254,7 +269,7 @@ // We are shutting down, therefore our stored energy disperses faster than usual. else if(running == SHIELD_DISCHARGING) if (offline_for <= 0) - shutdown_field() //We've finished the winding down period and now turn off + shutdown_machine() //We've finished the winding down period and now turn off offline_for += 30 //Another minute before it can be turned back on again return @@ -266,23 +281,12 @@ upkeep_power_usage = round((field_segments.len - damaged_segments.len) * ENERGY_UPKEEP_PER_TILE * upkeep_multiplier * upkeep_star_multiplier) - if(powernet && !input_cut && (running == SHIELD_RUNNING || running == SHIELD_OFF)) + if(tendrils_deployed && !input_cut && (running == SHIELD_RUNNING || running == SHIELD_OFF)) var/energy_buffer = 0 - energy_buffer = draw_power(min(upkeep_power_usage, input_cap)) + for(var/obj/machinery/power/conduit/shield_conduit/SC in tendrils) + energy_buffer += SC.draw_power(input_cap / tendrils.len) power_usage += round(energy_buffer) - - if(energy_buffer < upkeep_power_usage) - current_energy -= round(upkeep_power_usage - energy_buffer) // If we don't have enough energy from the grid, take it from the internal battery instead. - - // Now try to recharge our internal energy. - var/energy_to_demand - if(input_cap) - energy_to_demand = between(0, max_energy - current_energy, input_cap - upkeep_power_usage) - else - energy_to_demand = max(0, max_energy - current_energy) - energy_buffer = draw_power(energy_to_demand) - power_usage += energy_buffer - current_energy += round(energy_buffer) + current_energy += energy_buffer - upkeep_power_usage //if grid energy is lower than upkeep - negative number will be added else current_energy -= round(upkeep_power_usage) // We are shutting down, or we lack external power connection. Use energy from internal source instead. @@ -299,32 +303,9 @@ regenerate_field() -/obj/machinery/power/shield_generator/attackby(obj/item/O as obj, mob/user as mob) - // Prevents dismantle-rebuild tactics to reset the emergency shutdown timer. - if(running) - to_chat(user, "Turn off \the [src] first!") - return - if(offline_for) - to_chat(user, "Wait until \the [src] cools down from emergency shutdown first!") - return - - if(default_deconstruction(O, user)) - return - if(default_part_replacement(O, user)) - return - - //TODO: Implement unwrenching in a proper centralised location. Having to copypaste this around sucks - if(QUALITY_BOLT_TURNING in O.tool_qualities) - wrench(user, O) - return - - if(istool(O)) - return src.attack_hand(user) - - -/obj/machinery/power/shield_generator/proc/energy_failure() +/obj/machinery/power/shipside/shield_generator/proc/energy_failure() if(running == SHIELD_DISCHARGING) - shutdown_field() + shutdown_machine() else if (current_energy < 0) current_energy = 0 @@ -333,7 +314,7 @@ S.fail(1) -/obj/machinery/power/shield_generator/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = NANOUI_FOCUS) +/obj/machinery/power/shipside/shield_generator/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = NANOUI_FOCUS) var/data[0] data["running"] = running @@ -366,7 +347,7 @@ //Sorts the mode list so that currently active ones are at the top -/obj/machinery/power/shield_generator/proc/sort_modes() +/obj/machinery/power/shipside/shield_generator/proc/sort_modes() var/list/temp = list() for (var/A in mode_list) var/datum/shield_mode/SM = A //This late casting is an optimisation trick @@ -385,18 +366,18 @@ mode_list.Add(temp) -/obj/machinery/power/shield_generator/attack_hand(var/mob/user) +/obj/machinery/power/shipside/shield_generator/attack_hand(var/mob/user) nano_ui_interact(user) if(panel_open) wires.Interact(user) -/obj/machinery/power/shield_generator/CanUseTopic(var/mob/user) +/obj/machinery/power/shipside/shield_generator/CanUseTopic(var/mob/user) if(issilicon(user) && !Adjacent(user) && ai_control_disabled) return STATUS_UPDATE return ..() -/obj/machinery/power/shield_generator/Topic(href, href_list) +/obj/machinery/power/shipside/shield_generator/Topic(href, href_list) if(..()) return 1 if(!anchored) @@ -414,6 +395,10 @@ log_event(EVENT_DISABLED, src) if(href_list["start_generator"]) + if(tendrils_deployed == FALSE) + visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it needs to have it's conduits deployed first to operate")) + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 100, 1, 5) + return running = SHIELD_RUNNING regenerate_field() log_event(EVENT_ENABLED, src) @@ -432,7 +417,7 @@ var/temp_integrity = field_integrity() offline_for += 300 //5 minutes, given that procs happen every 2 seconds - shutdown_field() + shutdown_machine() emergency_shutdown = TRUE log_event(EVENT_DISABLED, src) if(prob(temp_integrity - 50) * 1.75) @@ -453,11 +438,10 @@ . = 1 if(href_list["set_input_cap"]) - var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num) + var/new_cap = round(input(usr, "Enter new input cap (in kW). Current maximal input cap is [input_maxcap / 1000] kW", "Generator Power Control", round(input_cap / 1000)) as num) if(!new_cap) - input_cap = 0 return - input_cap = max(0, new_cap) * 1000 + input_cap = between(1, new_cap, input_maxcap / 1000) * 1000 log_event(EVENT_RECONFIGURED, src) . = 1 @@ -469,14 +453,14 @@ nano_ui_interact(usr) -/obj/machinery/power/shield_generator/proc/field_integrity() +/obj/machinery/power/shipside/shield_generator/proc/field_integrity() if(max_energy) return (current_energy / max_energy) * 100 return 0 // Takes specific amount of damage -/obj/machinery/power/shield_generator/proc/take_shield_damage(var/damage, var/shield_damtype, var/atom/damager = null) +/obj/machinery/power/shipside/shield_generator/proc/take_shield_damage(var/damage, var/shield_damtype, var/atom/damager = null) var/energy_to_use = damage * ENERGY_PER_HP // Even if the shield isn't currently modulating, it can still use old modulation buildup to reduce damage @@ -536,11 +520,11 @@ // Checks whether specific flags are enabled -/obj/machinery/power/shield_generator/proc/check_flag(var/flag) +/obj/machinery/power/shipside/shield_generator/proc/check_flag(var/flag) return (shield_modes & flag) -/obj/machinery/power/shield_generator/proc/toggle_flag(var/flag) +/obj/machinery/power/shipside/shield_generator/proc/toggle_flag(var/flag) shield_modes ^= flag update_upkeep_multiplier() for(var/obj/effect/shield/S in field_segments) @@ -558,7 +542,7 @@ sort_modes() -/obj/machinery/power/shield_generator/proc/get_flag_descriptions() +/obj/machinery/power/shipside/shield_generator/proc/get_flag_descriptions() var/list/all_flags = list() for(var/datum/shield_mode/SM in mode_list) all_flags.Add(list(list( @@ -570,7 +554,7 @@ ))) return all_flags -/obj/machinery/power/shield_generator/proc/get_logs() +/obj/machinery/power/shipside/shield_generator/proc/get_logs() var/list/all_logs = list() for(var/i = event_log.len; i > 1; i--) all_logs.Add(list(list( @@ -578,7 +562,7 @@ ))) return all_logs -/obj/machinery/power/shield_generator/proc/fieldtype_square() +/obj/machinery/power/shipside/shield_generator/proc/fieldtype_square() var/list/out = list() var/list/base_turfs = get_base_turfs() @@ -603,7 +587,7 @@ CHECK_TICK return out -/obj/machinery/power/shield_generator/proc/fieldtype_hull() +/obj/machinery/power/shipside/shield_generator/proc/fieldtype_hull() var/list/turf/valid_turfs = list() var/list/base_turfs = get_base_turfs() @@ -621,7 +605,7 @@ return valid_turfs // Returns a list of turfs from which a field will propagate. If multi-Z mode is enabled, this will return a "column" of turfs above and below the generator. -/obj/machinery/power/shield_generator/proc/get_base_turfs() +/obj/machinery/power/shipside/shield_generator/proc/get_base_turfs() var/list/turfs = list() var/turf/T = get_turf(src) @@ -649,7 +633,7 @@ return turfs -/obj/machinery/power/shield_generator/proc/handle_reporting() +/obj/machinery/power/shipside/shield_generator/proc/handle_reporting() if (report_scheduled) return @@ -658,7 +642,7 @@ report_damage() //This proc sends reports for shield damage -/obj/machinery/power/shield_generator/proc/report_damage() +/obj/machinery/power/shipside/shield_generator/proc/report_damage() var/do_report = FALSE //We only report if this is true report_scheduled = FALSE //Reset this regardless of what we do here @@ -699,20 +683,9 @@ command_announcement.Announce(span(spanclass, "[prefix]Shield integrity at [round(field_integrity())]%"), "Shield Status Report", msg_sanitized = TRUE) -/obj/machinery/power/shield_generator/proc/wrench(var/user, var/obj/item/O) - if(O.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(anchored) - to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) - anchored = FALSE - else - if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! - to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) - anchored = TRUE - return //This proc keeps an internal log of shield impacts, activations, deactivations, and a vague log of config changes -/obj/machinery/power/shield_generator/proc/log_event(var/event_type, var/atom/origin_atom) +/obj/machinery/power/shipside/shield_generator/log_event(var/event_type, var/atom/origin_atom) var/logstring = "[stationtime2text()]: " switch (event_type) if(EVENT_DAMAGE_PHYSICAL to EVENT_DAMAGE_SPECIAL) @@ -771,131 +744,50 @@ if (event_log.len > max_log_entries) event_log.Cut(1,2) -/obj/machinery/shield_conduit +/obj/machinery/power/conduit/shield_conduit //Most of the stuff is moved to the shipside_machinery.dm name = "shield conduit" icon = 'icons/obj/machines/shielding.dmi' icon_state = "conduit_0" desc = "A combined conduit and capacitor that transfers and stores massive amounts of energy." density = TRUE anchored = FALSE //Will be set true just after deploying - var/obj/machinery/power/shield_generator/generator - -/obj/machinery/shield_conduit/proc/connect(gen) - generator = gen + circuit = /obj/item/electronics/circuitboard/shield_conduit + var/input_modifier = 0 //Modifier for the power use. Updated by RefreshParts() -/obj/machinery/shield_conduit/proc/no_light() +/obj/machinery/power/conduit/shield_conduit/no_light() set_light(0) -/obj/machinery/shield_conduit/proc/bright_light() +/obj/machinery/power/conduit/shield_conduit/bright_light() set_light(2, 2, "#8AD55D") -/obj/machinery/shield_conduit/Destroy() - if(generator) - generator.toggle_tendrils(FALSE) - if(generator.running != SHIELD_OFF && !generator.emergency_shutdown) - generator.offline_for += 300 - generator.shutdown_field() - generator.emergency_shutdown = TRUE - generator.log_event(EVENT_DISABLED, generator) +/obj/machinery/power/conduit/shield_conduit/RefreshParts() + input_modifier = 0 + for(var/obj/item/stock_parts/capacitor/C in component_parts) + input_modifier += C.rating / 4 . = ..() -/obj/machinery/power/shield_generator/wrench(user, obj/item/I) - if(running != SHIELD_OFF) - to_chat(usr, SPAN_NOTICE("Generator has to be toggled off first!")) - return - if(tendrils_deployed) - to_chat(usr, SPAN_NOTICE("Retract conduits first!")) - return - if(I.use_tool(user, src, WORKTIME_FAST, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(anchored) - to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!")) - toggle_tendrils(FALSE) - anchored = FALSE - else - if(istype(get_turf(src), /turf/space)) return //No wrenching these in space! - to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!")) - anchored = TRUE - return - -/obj/machinery/power/shield_generator/verb/toggle_tendrils_verb() - set category = "Object" - set name = "Toggle conduits" - set src in view(1) - - if(running != SHIELD_OFF) - to_chat(usr, SPAN_NOTICE("Generator has to be toggled off first!")) - return - toggle_tendrils() - -/obj/machinery/power/shield_generator/proc/toggle_tendrils(on = null) - var/target_state - if (!isnull(on)) - target_state = on - else - target_state = tendrils_deployed ? FALSE : TRUE //Otherwise we're toggling - - if (target_state == tendrils_deployed) - return - //If we're extending them - if (target_state == TRUE) - if(!anchored) - visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it needs to be properly anchored to deploy")) - playsound(src.loc, "/sound/machines/buzz-two", 100, 1, 5) - tendrils_deployed = FALSE - update_icon() - return FALSE - for (var/D in tendril_dirs) - var/turf/T = get_step(src, D) - var/obj/machinery/shield_conduit/SC = locate(/obj/machinery/shield_conduit) in T - if(SC) - continue - if (!turf_clear(T)) - visible_message(SPAN_DANGER("The [src] buzzes an insistent warning as it lacks the space to deploy")) - playsound(src.loc, "/sound/machines/buzz-two", 100, 1, 5) - tendrils_deployed = FALSE - update_icon() - return FALSE - - //Now deploy - for (var/D in tendril_dirs) - var/turf/T = get_step(src, D) - var/obj/machinery/shield_conduit/SC = locate(/obj/machinery/shield_conduit) in T - if(!SC) SC = new(T) - SC.connect(src) - tendrils.Add(SC) - SC.face_atom(src) - SC.anchored = TRUE - tendrils_deployed = TRUE - update_icon() - - allowed_modes |= MODEFLAG_MULTIZ - allowed_modes |= MODEFLAG_HULL +/obj/machinery/power/conduit/shield_conduit/draw_power(amount) //manual fuckery with the powernet surely won't result in a disaster later on *clueless* + if(!powernet) + return 0 + var/draw = between(0, amount, surplus() * input_modifier) + powernet.load += draw / input_modifier + return draw - to_chat(usr, SPAN_NOTICE("You deployed [src] conduits.")) - return TRUE - - else if (target_state == FALSE) - for (var/obj/machinery/shield_conduit/SC in tendrils) - tendrils.Remove(SC) - qdel(SC) - tendrils_deployed = FALSE - update_icon() - - allowed_modes.Remove(MODEFLAG_MULTIZ) - allowed_modes.Remove(MODEFLAG_HULL) - to_chat(usr, SPAN_NOTICE("You retracted [src] conduits.")) +/obj/machinery/power/conduit/disconnect() + if(!base) return FALSE - - mode_list = list() - for(var/st in subtypesof(/datum/shield_mode/)) - var/datum/shield_mode/SM = new st() - if (locate(SM.mode_flag) in allowed_modes) - mode_list.Add(SM) - - //Enable all modes in the default modes list - for (var/DM in default_modes) - toggle_flag(DM) + if(base.running != 0 && !base.emergency_shutdown) + base.offline_for += 300 + base.shutdown_machine() + base.emergency_shutdown = TRUE + base.log_event(EVENT_DISABLED, base) + base.tendrils.Remove(src) + base.build_tendril_dirs() + base.RefreshParts() + base.update_icon() + base = null + no_light() + disconnect_from_network() #undef EVENT_DAMAGE_PHYSICAL #undef EVENT_DAMAGE_EM diff --git a/maps/CEVEris/_CEV_Eris.dmm b/maps/CEVEris/_CEV_Eris.dmm index 51ed06b2e9c..2680ffb8233 100644 --- a/maps/CEVEris/_CEV_Eris.dmm +++ b/maps/CEVEris/_CEV_Eris.dmm @@ -47729,10 +47729,16 @@ /area/eris/command/mbo) "cmk" = ( /obj/structure/table/rack, -/obj/item/electronics/circuitboard/communications, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 10 }, +/obj/item/electronics/circuitboard/shield_conduit{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/electronics/circuitboard/communications{ + pixel_y = -2 + }, /obj/structure/cable/green{ d1 = 1; d2 = 2; @@ -79651,6 +79657,10 @@ /turf/wall/low/with_glass/smart, /area/eris/engineering/gravity_generator) "dPl" = ( +/obj/structure/cable/green{ + d2 = 8; + icon_state = "0-8" + }, /turf/floor/tiled/steel/gray_platform, /area/eris/engineering/shield_generator) "dPm" = ( @@ -79728,11 +79738,22 @@ /turf/floor/tiled/steel/gray_perforated, /area/eris/engineering/shield_generator) "dPx" = ( +/obj/machinery/power/shipside/shield_generator/hull/installed, +/obj/structure/cable/green{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, /obj/structure/cable/green{ + d1 = 1; d2 = 2; - icon_state = "0-2" + icon_state = "1-2" }, -/obj/machinery/power/shield_generator/hull/installed, /turf/floor/tiled/steel/gray_platform, /area/eris/engineering/shield_generator) "dPy" = ( @@ -99727,11 +99748,22 @@ /turf/floor/reinforced/almost_airless, /area/eris/engineering/propulsion/left) "kaS" = ( +/obj/machinery/power/shipside/long_range_scanner/hull/installed, /obj/structure/cable/green{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, +/obj/structure/cable/green{ + d1 = 1; d2 = 2; - icon_state = "0-2" + icon_state = "1-2" }, -/obj/machinery/power/long_range_scanner/hull/installed, /turf/floor/tiled/steel/gray_platform, /area/eris/engineering/long_range_scanner) "kaZ" = ( @@ -100029,6 +100061,13 @@ /obj/structure/kitchenspike, /turf/floor/tiled/white/brown_perforated, /area/eris/crew_quarters/kitchen_freezer) +"kFz" = ( +/obj/structure/cable/green{ + d2 = 8; + icon_state = "0-8" + }, +/turf/floor/tiled/steel/gray_platform, +/area/eris/engineering/long_range_scanner) "kFA" = ( /obj/machinery/door/firedoor, /turf/wall/low/with_glass/smart, @@ -101791,6 +101830,13 @@ /obj/machinery/atmospherics/pipe/simple/visible/yellow, /turf/floor/plating/under, /area/eris/maintenance/section4deck5port) +"orh" = ( +/obj/structure/cable/green{ + d2 = 2; + icon_state = "0-2" + }, +/turf/floor/tiled/steel/gray_platform, +/area/eris/engineering/shield_generator) "orM" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -104704,6 +104750,10 @@ /area/eris/crew_quarters/kitchen_storage) "tCG" = ( /obj/machinery/camera/network/engineering, +/obj/structure/cable/green{ + d2 = 4; + icon_state = "0-4" + }, /turf/floor/tiled/steel/gray_platform, /area/eris/engineering/long_range_scanner) "tEu" = ( @@ -105426,6 +105476,13 @@ }, /turf/floor/tiled/steel/gray_perforated, /area/eris/engineering/long_range_scanner) +"uQK" = ( +/obj/structure/cable/green{ + d2 = 4; + icon_state = "0-4" + }, +/turf/floor/tiled/steel/gray_platform, +/area/eris/engineering/shield_generator) "uUD" = ( /obj/machinery/atmospherics/valve/open{ dir = 4; @@ -106853,6 +106910,13 @@ /obj/structure/disposalpipe/up, /turf/floor/plating/under, /area/eris/maintenance/section3deck4port) +"xDD" = ( +/obj/structure/cable/green{ + d2 = 2; + icon_state = "0-2" + }, +/turf/floor/tiled/steel/gray_platform, +/area/eris/engineering/long_range_scanner) "xDO" = ( /obj/item/device/radio/intercom{ dir = 4; @@ -292259,7 +292323,7 @@ dXi abF dKs dKs -dPl +uQK ezX dOT eQb @@ -292460,7 +292524,7 @@ dXi dXi abF dKs -dPl +orh dPx dPA dPG @@ -293677,7 +293741,7 @@ abF abF abF peB -xoq +xDD kaS sbX oAL @@ -293880,7 +293944,7 @@ abF abF peB peB -xoq +kFz uPp sBN oUD