diff --git a/byond-extools.dll b/byond-extools.dll new file mode 100644 index 00000000..a83ea1e5 Binary files /dev/null and b/byond-extools.dll differ diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 10b815e5..fc6147c4 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -1,13 +1,28 @@ -#define TICK_LIMIT_RUNNING 80 +/// Percentage of tick to leave for master controller to run +#define MAPTICK_MC_MIN_RESERVE 70 +/// internal_tick_usage is updated every tick by extools +#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100) +/// Tick limit while running normally +#define TICK_BYOND_RESERVE 2 +#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE)) +/// Tick limit used to resume things in stoplag #define TICK_LIMIT_TO_RUN 70 +/// Tick limit for MC while running #define TICK_LIMIT_MC 70 -#define TICK_LIMIT_MC_INIT_DEFAULT 98 +/// Tick limit while initializing +#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE) -#define TICK_USAGE world.tick_usage //for general usage -#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked +/// for general usage of tick_usage +#define TICK_USAGE world.tick_usage +/// to be used where the result isn't checked +#define TICK_USAGE_REAL world.tick_usage +/// Returns true if tick_usage is above the limit #define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit ) +/// runs stoplag if tick_usage is above the limit #define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) +/// Returns true if tick usage is above 95, for high priority usage #define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 ) +/// runs stoplag if tick_usage is above 95, for high priority usage #define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index f2ef5638..a45cf49b 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -131,8 +131,15 @@ #define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) - - +// SSair run section +#define SSAIR_PIPENETS 1 +#define SSAIR_ATMOSMACHINERY 2 +#define SSAIR_REACTQUEUE 3 +#define SSAIR_EXCITEDGROUPS 4 +#define SSAIR_HIGHPRESSURE 5 +#define SSAIR_HOTSPOTS 6 +#define SSAIR_SUPERCONDUCTIVITY 7 +#define SSAIR_REBUILD_PIPENETS 8 #define COMPILE_OVERLAYS(A)\ if (TRUE) {\ diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 38147262..3d4dbf01 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -27,3 +27,5 @@ GLOBAL_VAR(bible_icon_state) GLOBAL_VAR(bible_item_state) GLOBAL_VAR(holy_weapon_type) GLOBAL_VAR(holy_armor_type) + +GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 76a3b1eb..70acff1c 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -35,6 +35,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/sleep_delta = 1 + ///Only run ticker subsystems for the next n ticks. + var/skip_ticks = 0 + var/make_runtime = 0 var/initializations_finished_with_no_players_logged_in //I wonder what this could be? @@ -334,7 +337,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new new/datum/controller/failsafe() // (re)Start the failsafe. //now do the actual stuff - if (!queue_head || !(iteration % 3)) + if (!skip_ticks) var/checking_runlevel = current_runlevel if(cached_runlevel != checking_runlevel) //resechedule subsystems @@ -380,6 +383,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration++ last_run = world.time + if (skip_ticks) + skip_ticks-- src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) current_ticklimit = TICK_LIMIT_RUNNING if (processing * sleep_delta <= world.tick_lag) @@ -443,10 +448,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new while (queue_node) if (ran && TICK_USAGE > TICK_LIMIT_RUNNING) break - queue_node_flags = queue_node.flags queue_node_priority = queue_node.queued_priority + if (!(queue_node_flags & SS_TICKER) && skip_ticks) + queue_node = queue_node.queue_next + continue //super special case, subsystems where we can't make them pause mid way through //if we can't run them this tick (without going over a tick) //we bump up their priority and attempt to run them next tick @@ -454,14 +461,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // in those cases, so we just let them run) if (queue_node_flags & SS_NO_TICK_CHECK) if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker) - queue_node.queued_priority += queue_priority_count * 0.1 - queue_priority_count -= queue_node_priority - queue_priority_count += queue_node.queued_priority - current_tick_budget -= queue_node_priority - queue_node = queue_node.queue_next + if (!(queue_node_flags & SS_BACKGROUND)) + queue_node.queued_priority += queue_priority_count * 0.1 + queue_priority_count -= queue_node_priority + queue_priority_count += queue_node.queued_priority + current_tick_budget -= queue_node_priority + queue_node = queue_node.queue_next continue - if ((queue_node_flags & SS_BACKGROUND) && !bg_calc) + if (!bg_calc && (queue_node_flags & SS_BACKGROUND)) current_tick_budget = queue_priority_count_bg bg_calc = TRUE @@ -514,7 +522,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.paused_ticks = 0 queue_node.paused_tick_usage = 0 - if (queue_node_flags & SS_BACKGROUND) //update our running total + if (bg_calc) //update our running total queue_priority_count_bg -= queue_node_priority else queue_priority_count -= queue_node_priority @@ -582,6 +590,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new log_world("MC: SoftReset: Finished.") . = 1 +/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick. +/datum/controller/master/proc/laggy_byond_map_update_incoming() + if (!skip_ticks) + skip_ticks = 1 /datum/controller/master/stat_entry(msg) diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 853dae5e..6100601e 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -1,11 +1,3 @@ -#define SSAIR_PIPENETS 1 -#define SSAIR_ATMOSMACHINERY 2 -#define SSAIR_REACTQUEUE 3 -#define SSAIR_EXCITEDGROUPS 4 -#define SSAIR_HIGHPRESSURE 5 -#define SSAIR_HOTSPOTS 6 -#define SSAIR_SUPERCONDUCTIVITY 7 - SUBSYSTEM_DEF(air) name = "Atmospherics" init_order = INIT_ORDER_AIR @@ -20,6 +12,7 @@ SUBSYSTEM_DEF(air) var/cost_hotspots = 0 var/cost_superconductivity = 0 var/cost_pipenets = 0 + var/cost_rebuilds = 0 var/cost_atmos_machinery = 0 var/list/excited_groups = list() @@ -27,6 +20,7 @@ SUBSYSTEM_DEF(air) var/list/turf_react_queue = list() var/list/hotspots = list() var/list/networks = list() + var/list/pipenets_needing_rebuilt = list() var/list/obj/machinery/atmos_machinery = list() var/list/pipe_init_dirs_cache = list() @@ -39,7 +33,7 @@ SUBSYSTEM_DEF(air) var/list/currentrun = list() - var/currentpart = SSAIR_PIPENETS + var/currentpart = SSAIR_REBUILD_PIPENETS var/map_loading = TRUE var/list/queued_for_activation @@ -52,6 +46,7 @@ SUBSYSTEM_DEF(air) msg += "HS:[round(cost_hotspots,1)]|" msg += "SC:[round(cost_superconductivity,1)]|" msg += "PN:[round(cost_pipenets,1)]|" + msg += "RB:[round(cost_rebuilds,1)]|" msg += "AM:[round(cost_atmos_machinery,1)]" msg += "} " msg += "AT:[active_turfs.len]|" @@ -77,6 +72,18 @@ SUBSYSTEM_DEF(air) /datum/controller/subsystem/air/fire(resumed = 0) var/timer = TICK_USAGE_REAL + if(currentpart == SSAIR_REBUILD_PIPENETS) + var/list/pipenet_rebuilds = pipenets_needing_rebuilt + for(var/thing in pipenet_rebuilds) + var/obj/machinery/atmospherics/AT = thing + AT.build_network() + cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + pipenets_needing_rebuilt.Cut() + if(state != SS_RUNNING) + return + resumed = FALSE + currentpart = SSAIR_PIPENETS + if(currentpart == SSAIR_PIPENETS || !resumed) process_pipenets(resumed) cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) @@ -137,9 +144,7 @@ SUBSYSTEM_DEF(air) if(state != SS_RUNNING) return resumed = 0 - currentpart = SSAIR_PIPENETS - - + currentpart = SSAIR_REBUILD_PIPENETS /datum/controller/subsystem/air/proc/process_pipenets(resumed = 0) if (!resumed) @@ -156,6 +161,9 @@ SUBSYSTEM_DEF(air) if(MC_TICK_CHECK) return +/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine) + if(istype(atmos_machine, /obj/machinery/atmospherics)) + pipenets_needing_rebuilt += atmos_machine /datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0) var/seconds = wait * 0.1 diff --git a/code/game/world.dm b/code/game/world.dm index 5bd39779..f3c6611c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -7,9 +7,8 @@ GLOBAL_VAR(restart_counter) //This happens after the Master subsystem new(s) (it's a global datum) //So subsystems globals exist, but are not initialised /world/New() - var/extools = world.GetConfig("env", "EXTOOLS_DLL") || (world.system_type == MS_WINDOWS ? "./byond-extools.dll" : "./libbyond-extools.so") - if (fexists(extools)) - call(extools, "maptick_initialize")() + if(fexists("byond-extools.dll")) + call("byond-extools.dll", "maptick_initialize")() enable_debugger() log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!") diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index f6bbe7b3..aa74e6dc 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -1,341 +1,342 @@ -// Quick overview: -// -// Pipes combine to form pipelines -// Pipelines and other atmospheric objects combine to form pipe_networks -// Note: A single pipe_network represents a completely open space -// -// Pipes -> Pipelines -// Pipelines + Other Objects -> Pipe network - -#define PIPE_VISIBLE_LEVEL 2 -#define PIPE_HIDDEN_LEVEL 1 - -/obj/machinery/atmospherics - anchored = TRUE - move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues. - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - layer = GAS_PIPE_HIDDEN_LAYER //under wires - resistance_flags = FIRE_PROOF - max_integrity = 200 - obj_flags = CAN_BE_HIT | ON_BLUEPRINTS - var/nodealert = 0 - var/can_unwrench = 0 - var/initialize_directions = 0 - var/pipe_color - var/piping_layer = PIPING_LAYER_DEFAULT - var/pipe_flags = NONE - - var/global/list/iconsetids = list() - var/global/list/pipeimages = list() - - var/image/pipe_vision_img = null - - var/device_type = 0 - var/list/obj/machinery/atmospherics/nodes - - var/construction_type - var/pipe_state //icon_state as a pipe item - var/on = FALSE - -/obj/machinery/atmospherics/examine(mob/user) - . = ..() - if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user)) - var/mob/living/L = user - if(L.ventcrawler) - . += "Alt-click to crawl through it." - -/obj/machinery/atmospherics/New(loc, process = TRUE, setdir) - if(!isnull(setdir)) - setDir(setdir) - if(pipe_flags & PIPING_CARDINAL_AUTONORMALIZE) - normalize_cardinal_directions() - nodes = new(device_type) - if (!armor) - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) - ..() - if(process) - SSair.atmos_machinery += src - SetInitDirections() - -/obj/machinery/atmospherics/Destroy() - for(var/i in 1 to device_type) - nullifyNode(i) - - SSair.atmos_machinery -= src - - dropContents() - if(pipe_vision_img) - qdel(pipe_vision_img) - - return ..() - //return QDEL_HINT_FINDREFERENCE - -/obj/machinery/atmospherics/proc/destroy_network() - return - -/obj/machinery/atmospherics/proc/build_network() - // Called to build a network from this node - return - -/obj/machinery/atmospherics/proc/nullifyNode(i) - if(nodes[i]) - var/obj/machinery/atmospherics/N = nodes[i] - N.disconnect(src) - nodes[i] = null - -/obj/machinery/atmospherics/proc/getNodeConnects() - var/list/node_connects = list() - node_connects.len = device_type - - for(var/i in 1 to device_type) - for(var/D in GLOB.cardinals) - if(D & GetInitDirections()) - if(D in node_connects) - continue - node_connects[i] = D - break - return node_connects - -/obj/machinery/atmospherics/proc/normalize_cardinal_directions() - switch(dir) - if(SOUTH) - setDir(NORTH) - if(WEST) - setDir(EAST) - -//this is called just after the air controller sets up turfs -/obj/machinery/atmospherics/proc/atmosinit(list/node_connects) - if(!node_connects) //for pipes where order of nodes doesn't matter - node_connects = getNodeConnects() - - for(var/i in 1 to device_type) - for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i])) - if(can_be_node(target, i)) - nodes[i] = target - break - update_icon() - -/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) - piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer - update_icon() - -/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration) - return connection_check(target, piping_layer) - -//Find a connecting /obj/machinery/atmospherics in specified direction -/obj/machinery/atmospherics/proc/findConnecting(direction, prompted_layer) - for(var/obj/machinery/atmospherics/target in get_step(src, direction)) - if(target.initialize_directions & get_dir(target,src)) - if(connection_check(target, prompted_layer)) - return target - -/obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer) - if(isConnectable(target, given_layer) && target.isConnectable(src, given_layer) && (target.initialize_directions & get_dir(target,src))) - return TRUE - return FALSE - -/obj/machinery/atmospherics/proc/isConnectable(obj/machinery/atmospherics/target, given_layer) - if(isnull(given_layer)) - given_layer = piping_layer - if((target.piping_layer == given_layer) || (target.pipe_flags & PIPING_ALL_LAYER)) - return TRUE - return FALSE - -/obj/machinery/atmospherics/proc/pipeline_expansion() - return nodes - -/obj/machinery/atmospherics/proc/SetInitDirections() - return - -/obj/machinery/atmospherics/proc/GetInitDirections() - return initialize_directions - -/obj/machinery/atmospherics/proc/returnPipenet() - return - -/obj/machinery/atmospherics/proc/returnPipenetAir() - return - -/obj/machinery/atmospherics/proc/setPipenet() - return - -/obj/machinery/atmospherics/proc/replacePipenet() - return - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - if(istype(reference, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = reference - P.destroy_network() - nodes[nodes.Find(reference)] = null - update_icon() - -/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pipe)) //lets you autodrop - var/obj/item/pipe/pipe = W - if(user.dropItemToGround(pipe)) - pipe.setPipingLayer(piping_layer) //align it with us - return TRUE - else - return ..() - -/obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I) - if(!can_unwrench(user)) - return ..() - - var/turf/T = get_turf(src) - if (level==1 && isturf(T) && T.intact) - to_chat(user, "You must remove the plating first!") - return TRUE - - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - add_fingerprint(user) - - var/unsafe_wrenching = FALSE - var/internal_pressure = int_air.return_pressure()-env_air.return_pressure() - - to_chat(user, "You begin to unfasten \the [src]...") - - if (internal_pressure > 2*ONE_ATMOSPHERE) - to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") - unsafe_wrenching = TRUE //Oh dear oh dear - - if(I.use_tool(src, user, 20, volume=50)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You unfasten \the [src].", \ - "You hear ratchet.") - investigate_log("was REMOVED by [key_name(usr)]", INVESTIGATE_ATMOS) - - //You unwrenched a pipe full of pressure? Let's splat you into the wall, silly. - if(unsafe_wrenching) - unsafe_pressure_release(user, internal_pressure) - deconstruct(TRUE) - return TRUE - -/obj/machinery/atmospherics/proc/can_unwrench(mob/user) - return can_unwrench - -// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure. -/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null) - if(!user) - return - if(!pressures) - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - pressures = int_air.return_pressure() - env_air.return_pressure() - - user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") - - // if get_dir(src, user) is not 0, target is the edge_target_turf on that dir - // otherwise, edge_target_turf uses a random cardinal direction - // range is pressures / 250 - // speed is pressures / 1250 - user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinals)), pressures / 250, pressures / 1250) - -/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) - if(!(flags_1 & NODECONSTRUCT_1)) - if(can_unwrench) - var/obj/item/pipe/stored = new construction_type(loc, null, dir, src) - stored.setPipingLayer(piping_layer) - if(!disassembled) - stored.obj_integrity = stored.max_integrity * 0.5 - transfer_fingerprints_to(stored) - ..() - -/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255), piping_layer=2) - - //Add identifiers for the iconset - if(iconsetids[iconset] == null) - iconsetids[iconset] = num2text(iconsetids.len + 1) - - //Generate a unique identifier for this image combination - var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]_[piping_layer]" - - if((!(. = pipeimages[identifier]))) - var/image/pipe_overlay - pipe_overlay = . = pipeimages[identifier] = image(iconset, iconstate, dir = direction) - pipe_overlay.color = col - PIPING_LAYER_SHIFT(pipe_overlay, piping_layer) - -/obj/machinery/atmospherics/on_construction(obj_color, set_layer) - if(can_unwrench) - add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY) - pipe_color = obj_color - setPipingLayer(set_layer) - var/turf/T = get_turf(src) - level = T.intact ? 2 : 1 - atmosinit() - var/list/nodes = pipeline_expansion() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmosinit() - A.addMember(src) - build_network() - -/obj/machinery/atmospherics/Entered(atom/movable/AM) - if(istype(AM, /mob/living)) - var/mob/living/L = AM - L.ventcrawl_layer = piping_layer - return ..() - -/obj/machinery/atmospherics/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - deconstruct(FALSE) - return ..() - -#define VENT_SOUND_DELAY 30 - -/obj/machinery/atmospherics/relaymove(mob/living/user, direction) - direction &= initialize_directions - if(!direction || !(direction in GLOB.cardinals)) //cant go this way. - return - - if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug - return - - var/obj/machinery/atmospherics/target_move = findConnecting(direction, user.ventcrawl_layer) - if(target_move) - if(target_move.can_crawl_through()) - if(is_type_in_typecache(target_move, GLOB.ventcrawl_machinery)) - user.forceMove(target_move.loc) //handle entering and so on. - user.visible_message("You hear something squeezing through the ducts...", "You climb out the ventilation system.") - else - var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets() - if(pipenetdiff.len) - user.update_pipe_vision(target_move) - user.forceMove(target_move) - user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement - if(world.time - user.last_played_vent > VENT_SOUND_DELAY) - user.last_played_vent = world.time - playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) - else if(is_type_in_typecache(src, GLOB.ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent - user.forceMove(loc) - user.visible_message("You hear something squeezing through the ducts...", "You climb out the ventilation system.") - - user.canmove = FALSE - addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1) - - -/obj/machinery/atmospherics/AltClick(mob/living/L) - if(is_type_in_typecache(src, GLOB.ventcrawl_machinery)) - return L.handle_ventcrawl(src) - return ..() - - -/obj/machinery/atmospherics/proc/can_crawl_through() - return TRUE - -/obj/machinery/atmospherics/proc/returnPipenets() - return list() - -/obj/machinery/atmospherics/update_remote_sight(mob/user) - user.sight |= (SEE_TURFS|BLIND) - -//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it. -/obj/machinery/atmospherics/proc/can_see_pipes() - return TRUE - -/obj/machinery/atmospherics/proc/update_layer() +// Quick overview: +// +// Pipes combine to form pipelines +// Pipelines and other atmospheric objects combine to form pipe_networks +// Note: A single pipe_network represents a completely open space +// +// Pipes -> Pipelines +// Pipelines + Other Objects -> Pipe network + +#define PIPE_VISIBLE_LEVEL 2 +#define PIPE_HIDDEN_LEVEL 1 + +/obj/machinery/atmospherics + anchored = TRUE + move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues. + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + layer = GAS_PIPE_HIDDEN_LAYER //under wires + resistance_flags = FIRE_PROOF + max_integrity = 200 + obj_flags = CAN_BE_HIT | ON_BLUEPRINTS + var/nodealert = 0 + var/can_unwrench = 0 + var/initialize_directions = 0 + var/pipe_color + var/piping_layer = PIPING_LAYER_DEFAULT + var/pipe_flags = NONE + + var/global/list/iconsetids = list() + var/global/list/pipeimages = list() + + var/image/pipe_vision_img = null + + var/device_type = 0 + var/list/obj/machinery/atmospherics/nodes + + var/construction_type + var/pipe_state //icon_state as a pipe item + var/on = FALSE + +/obj/machinery/atmospherics/examine(mob/user) + . = ..() + if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user)) + var/mob/living/L = user + if(L.ventcrawler) + . += "Alt-click to crawl through it." + +/obj/machinery/atmospherics/New(loc, process = TRUE, setdir) + if(!isnull(setdir)) + setDir(setdir) + if(pipe_flags & PIPING_CARDINAL_AUTONORMALIZE) + normalize_cardinal_directions() + nodes = new(device_type) + if (!armor) + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) + ..() + if(process) + SSair.atmos_machinery += src + SetInitDirections() + +/obj/machinery/atmospherics/Destroy() + for(var/i in 1 to device_type) + nullifyNode(i) + + SSair.atmos_machinery -= src + SSair.pipenets_needing_rebuilt -= src + + dropContents() + if(pipe_vision_img) + qdel(pipe_vision_img) + + return ..() + //return QDEL_HINT_FINDREFERENCE + +/obj/machinery/atmospherics/proc/destroy_network() + return + +/obj/machinery/atmospherics/proc/build_network() + // Called to build a network from this node + return + +/obj/machinery/atmospherics/proc/nullifyNode(i) + if(nodes[i]) + var/obj/machinery/atmospherics/N = nodes[i] + N.disconnect(src) + nodes[i] = null + +/obj/machinery/atmospherics/proc/getNodeConnects() + var/list/node_connects = list() + node_connects.len = device_type + + for(var/i in 1 to device_type) + for(var/D in GLOB.cardinals) + if(D & GetInitDirections()) + if(D in node_connects) + continue + node_connects[i] = D + break + return node_connects + +/obj/machinery/atmospherics/proc/normalize_cardinal_directions() + switch(dir) + if(SOUTH) + setDir(NORTH) + if(WEST) + setDir(EAST) + +//this is called just after the air controller sets up turfs +/obj/machinery/atmospherics/proc/atmosinit(list/node_connects) + if(!node_connects) //for pipes where order of nodes doesn't matter + node_connects = getNodeConnects() + + for(var/i in 1 to device_type) + for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i])) + if(can_be_node(target, i)) + nodes[i] = target + break + update_icon() + +/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) + piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer + update_icon() + +/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration) + return connection_check(target, piping_layer) + +//Find a connecting /obj/machinery/atmospherics in specified direction +/obj/machinery/atmospherics/proc/findConnecting(direction, prompted_layer) + for(var/obj/machinery/atmospherics/target in get_step(src, direction)) + if(target.initialize_directions & get_dir(target,src)) + if(connection_check(target, prompted_layer)) + return target + +/obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer) + if(isConnectable(target, given_layer) && target.isConnectable(src, given_layer) && (target.initialize_directions & get_dir(target,src))) + return TRUE + return FALSE + +/obj/machinery/atmospherics/proc/isConnectable(obj/machinery/atmospherics/target, given_layer) + if(isnull(given_layer)) + given_layer = piping_layer + if((target.piping_layer == given_layer) || (target.pipe_flags & PIPING_ALL_LAYER)) + return TRUE + return FALSE + +/obj/machinery/atmospherics/proc/pipeline_expansion() + return nodes + +/obj/machinery/atmospherics/proc/SetInitDirections() + return + +/obj/machinery/atmospherics/proc/GetInitDirections() + return initialize_directions + +/obj/machinery/atmospherics/proc/returnPipenet() + return + +/obj/machinery/atmospherics/proc/returnPipenetAir() + return + +/obj/machinery/atmospherics/proc/setPipenet() + return + +/obj/machinery/atmospherics/proc/replacePipenet() + return + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + if(istype(reference, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = reference + P.destroy_network() + nodes[nodes.Find(reference)] = null + update_icon() + +/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pipe)) //lets you autodrop + var/obj/item/pipe/pipe = W + if(user.dropItemToGround(pipe)) + pipe.setPipingLayer(piping_layer) //align it with us + return TRUE + else + return ..() + +/obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I) + if(!can_unwrench(user)) + return ..() + + var/turf/T = get_turf(src) + if (level==1 && isturf(T) && T.intact) + to_chat(user, "You must remove the plating first!") + return TRUE + + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + add_fingerprint(user) + + var/unsafe_wrenching = FALSE + var/internal_pressure = int_air.return_pressure()-env_air.return_pressure() + + to_chat(user, "You begin to unfasten \the [src]...") + + if (internal_pressure > 2*ONE_ATMOSPHERE) + to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") + unsafe_wrenching = TRUE //Oh dear oh dear + + if(I.use_tool(src, user, 20, volume=50)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You unfasten \the [src].", \ + "You hear ratchet.") + investigate_log("was REMOVED by [key_name(usr)]", INVESTIGATE_ATMOS) + + //You unwrenched a pipe full of pressure? Let's splat you into the wall, silly. + if(unsafe_wrenching) + unsafe_pressure_release(user, internal_pressure) + deconstruct(TRUE) + return TRUE + +/obj/machinery/atmospherics/proc/can_unwrench(mob/user) + return can_unwrench + +// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure. +/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null) + if(!user) + return + if(!pressures) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + pressures = int_air.return_pressure() - env_air.return_pressure() + + user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") + + // if get_dir(src, user) is not 0, target is the edge_target_turf on that dir + // otherwise, edge_target_turf uses a random cardinal direction + // range is pressures / 250 + // speed is pressures / 1250 + user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinals)), pressures / 250, pressures / 1250) + +/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + if(can_unwrench) + var/obj/item/pipe/stored = new construction_type(loc, null, dir, src) + stored.setPipingLayer(piping_layer) + if(!disassembled) + stored.obj_integrity = stored.max_integrity * 0.5 + transfer_fingerprints_to(stored) + ..() + +/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255), piping_layer=2) + + //Add identifiers for the iconset + if(iconsetids[iconset] == null) + iconsetids[iconset] = num2text(iconsetids.len + 1) + + //Generate a unique identifier for this image combination + var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]_[piping_layer]" + + if((!(. = pipeimages[identifier]))) + var/image/pipe_overlay + pipe_overlay = . = pipeimages[identifier] = image(iconset, iconstate, dir = direction) + pipe_overlay.color = col + PIPING_LAYER_SHIFT(pipe_overlay, piping_layer) + +/obj/machinery/atmospherics/on_construction(obj_color, set_layer) + if(can_unwrench) + add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY) + pipe_color = obj_color + setPipingLayer(set_layer) + var/turf/T = get_turf(src) + level = T.intact ? 2 : 1 + atmosinit() + var/list/nodes = pipeline_expansion() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmosinit() + A.addMember(src) + build_network() + +/obj/machinery/atmospherics/Entered(atom/movable/AM) + if(istype(AM, /mob/living)) + var/mob/living/L = AM + L.ventcrawl_layer = piping_layer + return ..() + +/obj/machinery/atmospherics/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + deconstruct(FALSE) + return ..() + +#define VENT_SOUND_DELAY 30 + +/obj/machinery/atmospherics/relaymove(mob/living/user, direction) + direction &= initialize_directions + if(!direction || !(direction in GLOB.cardinals)) //cant go this way. + return + + if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug + return + + var/obj/machinery/atmospherics/target_move = findConnecting(direction, user.ventcrawl_layer) + if(target_move) + if(target_move.can_crawl_through()) + if(is_type_in_typecache(target_move, GLOB.ventcrawl_machinery)) + user.forceMove(target_move.loc) //handle entering and so on. + user.visible_message("You hear something squeezing through the ducts...", "You climb out the ventilation system.") + else + var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets() + if(pipenetdiff.len) + user.update_pipe_vision(target_move) + user.forceMove(target_move) + user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement + if(world.time - user.last_played_vent > VENT_SOUND_DELAY) + user.last_played_vent = world.time + playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) + else if(is_type_in_typecache(src, GLOB.ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent + user.forceMove(loc) + user.visible_message("You hear something squeezing through the ducts...", "You climb out the ventilation system.") + + user.canmove = FALSE + addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1) + + +/obj/machinery/atmospherics/AltClick(mob/living/L) + if(is_type_in_typecache(src, GLOB.ventcrawl_machinery)) + return L.handle_ventcrawl(src) + return ..() + + +/obj/machinery/atmospherics/proc/can_crawl_through() + return TRUE + +/obj/machinery/atmospherics/proc/returnPipenets() + return list() + +/obj/machinery/atmospherics/update_remote_sight(mob/user) + user.sight |= (SEE_TURFS|BLIND) + +//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it. +/obj/machinery/atmospherics/proc/can_see_pipes() + return TRUE + +/obj/machinery/atmospherics/proc/update_layer() layer = initial(layer) + (piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_LCHANGE \ No newline at end of file diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm index 9fd0850f..bf0f221b 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm @@ -119,7 +119,7 @@ if(node2) node2.atmosinit() node2.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) return TRUE diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index ff2a655a..3defa76a 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -144,8 +144,8 @@ for(var/i in 1 to device_type) var/datum/pipeline/parent = parents[i] if(!parent) - throw EXCEPTION("Component is missing a pipenet! Rebuilding...") - build_network() + stack_trace("Component is missing a pipenet! Rebuilding...") + SSair.add_to_rebuild_queue(src) parent.update = 1 /obj/machinery/atmospherics/components/returnPipenets() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 0a54503b..766b7a24 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -1,446 +1,446 @@ -/obj/machinery/atmospherics/components/unary/cryo_cell - name = "cryo cell" - icon = 'icons/obj/cryogenics.dmi' - icon_state = "pod-off" - density = TRUE - max_integrity = 350 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) - layer = ABOVE_WINDOW_LAYER - state_open = FALSE - circuit = /obj/item/circuitboard/machine/cryo_tube - pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY - occupant_typecache = list(/mob/living/carbon, /mob/living/simple_animal) - - var/autoeject = FALSE - var/volume = 100 - - var/efficiency = 1 - var/sleep_factor = 0.00125 - var/unconscious_factor = 0.001 - var/heat_capacity = 20000 - var/conduction_coefficient = 0.3 - - var/obj/item/reagent_containers/glass/beaker = null - var/reagent_transfer = 0 - - var/obj/item/radio/radio - var/radio_key = /obj/item/encryptionkey/headset_med - var/radio_channel = RADIO_CHANNEL_MEDICAL - - var/running_anim = FALSE - - var/escape_in_progress = FALSE - var/message_cooldown - var/breakout_time = 300 - -/obj/machinery/atmospherics/components/unary/cryo_cell/Initialize() - . = ..() - initialize_directions = dir - - radio = new(src) - radio.keyslot = new radio_key - radio.subspace_transmission = TRUE - radio.canhear_range = 0 - radio.recalculateChannels() - -/obj/machinery/atmospherics/components/unary/cryo_cell/on_construction() - ..(dir, dir) - -/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts() - var/C - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - C += M.rating - - efficiency = initial(efficiency) * C - sleep_factor = initial(sleep_factor) * C - unconscious_factor = initial(unconscious_factor) * C - heat_capacity = initial(heat_capacity) / C - conduction_coefficient = initial(conduction_coefficient) * C - -/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy() - QDEL_NULL(radio) - QDEL_NULL(beaker) - return ..() - -/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target) - ..() - if(beaker) - beaker.ex_act(severity, target) - -/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A) - ..() - if(A == beaker) - beaker = null - updateUsrDialog() - -/obj/machinery/atmospherics/components/unary/cryo_cell/on_deconstruction() - if(beaker) - beaker.forceMove(drop_location()) - beaker = null - -/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon() - cut_overlays() - - if(panel_open) - add_overlay("pod-panel") - - if(state_open) - icon_state = "pod-open" - return - - if(occupant) - var/image/occupant_overlay - - if(ismonkey(occupant)) // Monkey - occupant_overlay = image(CRYOMOBS, "monkey") - else if(isalienadult(occupant)) - if(isalienroyal(occupant)) // Queen and prae - occupant_overlay = image(CRYOMOBS, "alienq") - else if(isalienhunter(occupant)) // Hunter - occupant_overlay = image(CRYOMOBS, "alienh") - else if(isaliensentinel(occupant)) // Sentinel - occupant_overlay = image(CRYOMOBS, "aliens") - else // Drone or other - occupant_overlay = image(CRYOMOBS, "aliend") - - else if(ishuman(occupant) || islarva(occupant) || (isanimal(occupant) && !ismegafauna(occupant))) // Mobs that are smaller than cryotube - occupant_overlay = image(occupant.icon, occupant.icon_state) - occupant_overlay.copy_overlays(occupant) - - else - occupant_overlay = image(CRYOMOBS, "generic") - - occupant_overlay.dir = SOUTH - occupant_overlay.pixel_y = 22 - - if(on && !running_anim && is_operational()) - icon_state = "pod-on" - running_anim = TRUE - run_anim(TRUE, occupant_overlay) - else - icon_state = "pod-off" - add_overlay(occupant_overlay) - add_overlay("cover-off") - - else if(on && is_operational()) - icon_state = "pod-on" - add_overlay("cover-on") - else - icon_state = "pod-off" - add_overlay("cover-off") - -/obj/machinery/atmospherics/components/unary/cryo_cell/proc/run_anim(anim_up, image/occupant_overlay) - if(!on || !occupant || !is_operational()) - running_anim = FALSE - return - cut_overlays() - if(occupant_overlay.pixel_y != 23) // Same effect as occupant_overlay.pixel_y == 22 || occupant_overlay.pixel_y == 24 - anim_up = occupant_overlay.pixel_y == 22 // Same effect as if(occupant_overlay.pixel_y == 22) anim_up = TRUE ; if(occupant_overlay.pixel_y == 24) anim_up = FALSE - if(anim_up) - occupant_overlay.pixel_y++ - else - occupant_overlay.pixel_y-- - add_overlay(occupant_overlay) - add_overlay("cover-on") - addtimer(CALLBACK(src, .proc/run_anim, anim_up, occupant_overlay), 7, TIMER_UNIQUE) - -/obj/machinery/atmospherics/components/unary/cryo_cell/process() - ..() - - if(!on) - return - if(!is_operational()) - on = FALSE - update_icon() - return - if(!occupant) - return - - var/mob/living/mob_occupant = occupant - - if(mob_occupant.stat == DEAD) // We don't bother with dead people. - return - - if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people. - on = FALSE - update_icon() - playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors. - var/msg = "Patient fully restored." - if(autoeject) // Eject if configured. - msg += " Auto ejecting patient now." - open_machine() - radio.talk_into(src, msg, radio_channel) - return - - var/datum/gas_mixture/air1 = airs[1] - - if(air1.gases.len) - if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic. - mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000) - mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000) - if(beaker) - if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic. - beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents. - beaker.reagents.reaction(occupant, VAPOR) - air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this - GAS_GARBAGE_COLLECT(air1.gases) - if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker). - reagent_transfer = 0 - - return 1 - -/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos() - ..() - - if(!on) - return - - var/datum/gas_mixture/air1 = airs[1] - - if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work. - on = FALSE - update_icon() - return - - if(occupant) - var/mob/living/mob_occupant = occupant - var/cold_protection = 0 - var/temperature_delta = air1.temperature - mob_occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant. - - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - cold_protection = H.get_thermal_protection(air1.temperature, TRUE) - - if(abs(temperature_delta) > 1) - var/air_heat_capacity = air1.heat_capacity() - - var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity)) - - air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB) - mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB) - - air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic. - GAS_GARBAGE_COLLECT(air1.gases) - -/obj/machinery/atmospherics/components/unary/cryo_cell/power_change() - ..() - update_icon() - -/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user) - if(message_cooldown <= world.time) - message_cooldown = world.time + 50 - to_chat(user, "[src]'s door won't budge!") - -/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0) - if(!state_open && !panel_open) - on = FALSE - ..() - for(var/mob/M in contents) //only drop mobs - M.forceMove(get_turf(src)) - if(isliving(M)) - var/mob/living/L = M - L.update_canmove() - occupant = null - update_icon() - -/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user) - if((isnull(user) || istype(user)) && state_open && !panel_open) - ..(user) - return occupant - -/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user) - user.changeNext_move(CLICK_CD_BREAKOUT) - user.last_special = world.time + CLICK_CD_BREAKOUT - user.visible_message("You see [user] kicking against the glass of [src]!", \ - "You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)", \ - "You hear a thump from [src].") - if(do_after(user, breakout_time, target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src ) - return - user.visible_message("[user] successfully broke out of [src]!", \ - "You successfully break out of [src]!") - open_machine() - -/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user) - . = ..() - if(occupant) - if(on) - . += "Someone's inside [src]!" - else - . += "You can barely make out a form floating in [src]." - else - . += "[src] seems empty." - -/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) - return - if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious()) - close_machine(target) - else - user.visible_message("[user] starts shoving [target] inside [src].", "You start shoving [target] inside [src].") - if (do_after(user, 25, target=target)) - close_machine(target) - -/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/glass)) - . = 1 //no afterattack - if(beaker) - to_chat(user, "A beaker is already loaded into [src]!") - return - if(!user.transferItemToLoc(I, src)) - return - beaker = I - user.visible_message("[user] places [I] in [src].", \ - "You place [I] in [src].") - var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list) - log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]") - return - if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \ - || default_change_direction_wrench(user, I) \ - || default_pry_open(I) \ - || default_deconstruction_crowbar(I)) - update_icon() - return - else if(istype(I, /obj/item/screwdriver)) - to_chat(user, "You can't access the maintenance panel while the pod is " \ - + (on ? "active" : (occupant ? "full" : "open")) + ".") - return - return ..() - -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state) - ui.open() - -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data() - var/list/data = list() - data["isOperating"] = on - data["hasOccupant"] = occupant ? TRUE : FALSE - data["isOpen"] = state_open - data["autoEject"] = autoeject - - data["occupant"] = list() - if(occupant) - var/mob/living/mob_occupant = occupant - data["occupant"]["name"] = mob_occupant.name - switch(mob_occupant.stat) - if(CONSCIOUS) - data["occupant"]["stat"] = "Conscious" - data["occupant"]["statstate"] = "good" - if(SOFT_CRIT) - data["occupant"]["stat"] = "Conscious" - data["occupant"]["statstate"] = "average" - if(UNCONSCIOUS) - data["occupant"]["stat"] = "Unconscious" - data["occupant"]["statstate"] = "average" - if(DEAD) - data["occupant"]["stat"] = "Dead" - data["occupant"]["statstate"] = "bad" - data["occupant"]["health"] = round(mob_occupant.health, 1) - data["occupant"]["maxHealth"] = mob_occupant.maxHealth - data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD - data["occupant"]["bruteLoss"] = round(mob_occupant.getBruteLoss(), 1) - data["occupant"]["oxyLoss"] = round(mob_occupant.getOxyLoss(), 1) - data["occupant"]["toxLoss"] = round(mob_occupant.getToxLoss(), 1) - data["occupant"]["fireLoss"] = round(mob_occupant.getFireLoss(), 1) - data["occupant"]["bodyTemperature"] = round(mob_occupant.bodytemperature, 1) - if(mob_occupant.bodytemperature < TCRYO) - data["occupant"]["temperaturestatus"] = "good" - else if(mob_occupant.bodytemperature < T0C) - data["occupant"]["temperaturestatus"] = "average" - else - data["occupant"]["temperaturestatus"] = "bad" - - var/datum/gas_mixture/air1 = airs[1] - data["cellTemperature"] = round(air1.temperature, 1) - - data["isBeakerLoaded"] = beaker ? TRUE : FALSE - var/beakerContents = list() - if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents += list(list("name" = R.name, "volume" = R.volume)) - data["beakerContents"] = beakerContents - return data - -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params) - if(..()) - return - switch(action) - if("power") - if(on) - on = FALSE - else if(!state_open) - on = TRUE - . = TRUE - if("door") - if(state_open) - close_machine() - else - open_machine() - . = TRUE - if("autoeject") - autoeject = !autoeject - . = TRUE - if("ejectbeaker") - if(beaker) - beaker.forceMove(drop_location()) - if(Adjacent(usr) && !issilicon(usr)) - usr.put_in_hands(beaker) - beaker = null - . = TRUE - update_icon() - -/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open) - on = !on - update_icon() - return ..() - -/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user) - . = ..() - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - if(state_open) - close_machine() - else - open_machine() - update_icon() - return TRUE - -/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) - return // we don't see the pipe network while inside cryo. - -/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) - -/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through() - return // can't ventcrawl in or out of cryo. - -/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes() - return 0 // you can't see the pipe network when inside a cryo cell. - -/obj/machinery/atmospherics/components/unary/cryo_cell/return_temperature() - var/datum/gas_mixture/G = airs[1] - - if(G.total_moles() > 10) - return G.temperature - return ..() - -/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W) - . = ..() - if(.) - SetInitDirections() - var/obj/machinery/atmospherics/node = nodes[1] - if(node) - node.disconnect(src) - nodes[1] = null - nullifyPipenet(parents[1]) - atmosinit() - node = nodes[1] - if(node) - node.atmosinit() - node.addMember(src) - build_network() - -#undef CRYOMOBS +/obj/machinery/atmospherics/components/unary/cryo_cell + name = "cryo cell" + icon = 'icons/obj/cryogenics.dmi' + icon_state = "pod-off" + density = TRUE + max_integrity = 350 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) + layer = ABOVE_WINDOW_LAYER + state_open = FALSE + circuit = /obj/item/circuitboard/machine/cryo_tube + pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY + occupant_typecache = list(/mob/living/carbon, /mob/living/simple_animal) + + var/autoeject = FALSE + var/volume = 100 + + var/efficiency = 1 + var/sleep_factor = 0.00125 + var/unconscious_factor = 0.001 + var/heat_capacity = 20000 + var/conduction_coefficient = 0.3 + + var/obj/item/reagent_containers/glass/beaker = null + var/reagent_transfer = 0 + + var/obj/item/radio/radio + var/radio_key = /obj/item/encryptionkey/headset_med + var/radio_channel = RADIO_CHANNEL_MEDICAL + + var/running_anim = FALSE + + var/escape_in_progress = FALSE + var/message_cooldown + var/breakout_time = 300 + +/obj/machinery/atmospherics/components/unary/cryo_cell/Initialize() + . = ..() + initialize_directions = dir + + radio = new(src) + radio.keyslot = new radio_key + radio.subspace_transmission = TRUE + radio.canhear_range = 0 + radio.recalculateChannels() + +/obj/machinery/atmospherics/components/unary/cryo_cell/on_construction() + ..(dir, dir) + +/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts() + var/C + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + C += M.rating + + efficiency = initial(efficiency) * C + sleep_factor = initial(sleep_factor) * C + unconscious_factor = initial(unconscious_factor) * C + heat_capacity = initial(heat_capacity) / C + conduction_coefficient = initial(conduction_coefficient) * C + +/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy() + QDEL_NULL(radio) + QDEL_NULL(beaker) + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target) + ..() + if(beaker) + beaker.ex_act(severity, target) + +/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A) + ..() + if(A == beaker) + beaker = null + updateUsrDialog() + +/obj/machinery/atmospherics/components/unary/cryo_cell/on_deconstruction() + if(beaker) + beaker.forceMove(drop_location()) + beaker = null + +/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon() + cut_overlays() + + if(panel_open) + add_overlay("pod-panel") + + if(state_open) + icon_state = "pod-open" + return + + if(occupant) + var/image/occupant_overlay + + if(ismonkey(occupant)) // Monkey + occupant_overlay = image(CRYOMOBS, "monkey") + else if(isalienadult(occupant)) + if(isalienroyal(occupant)) // Queen and prae + occupant_overlay = image(CRYOMOBS, "alienq") + else if(isalienhunter(occupant)) // Hunter + occupant_overlay = image(CRYOMOBS, "alienh") + else if(isaliensentinel(occupant)) // Sentinel + occupant_overlay = image(CRYOMOBS, "aliens") + else // Drone or other + occupant_overlay = image(CRYOMOBS, "aliend") + + else if(ishuman(occupant) || islarva(occupant) || (isanimal(occupant) && !ismegafauna(occupant))) // Mobs that are smaller than cryotube + occupant_overlay = image(occupant.icon, occupant.icon_state) + occupant_overlay.copy_overlays(occupant) + + else + occupant_overlay = image(CRYOMOBS, "generic") + + occupant_overlay.dir = SOUTH + occupant_overlay.pixel_y = 22 + + if(on && !running_anim && is_operational()) + icon_state = "pod-on" + running_anim = TRUE + run_anim(TRUE, occupant_overlay) + else + icon_state = "pod-off" + add_overlay(occupant_overlay) + add_overlay("cover-off") + + else if(on && is_operational()) + icon_state = "pod-on" + add_overlay("cover-on") + else + icon_state = "pod-off" + add_overlay("cover-off") + +/obj/machinery/atmospherics/components/unary/cryo_cell/proc/run_anim(anim_up, image/occupant_overlay) + if(!on || !occupant || !is_operational()) + running_anim = FALSE + return + cut_overlays() + if(occupant_overlay.pixel_y != 23) // Same effect as occupant_overlay.pixel_y == 22 || occupant_overlay.pixel_y == 24 + anim_up = occupant_overlay.pixel_y == 22 // Same effect as if(occupant_overlay.pixel_y == 22) anim_up = TRUE ; if(occupant_overlay.pixel_y == 24) anim_up = FALSE + if(anim_up) + occupant_overlay.pixel_y++ + else + occupant_overlay.pixel_y-- + add_overlay(occupant_overlay) + add_overlay("cover-on") + addtimer(CALLBACK(src, .proc/run_anim, anim_up, occupant_overlay), 7, TIMER_UNIQUE) + +/obj/machinery/atmospherics/components/unary/cryo_cell/process() + ..() + + if(!on) + return + if(!is_operational()) + on = FALSE + update_icon() + return + if(!occupant) + return + + var/mob/living/mob_occupant = occupant + + if(mob_occupant.stat == DEAD) // We don't bother with dead people. + return + + if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people. + on = FALSE + update_icon() + playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors. + var/msg = "Patient fully restored." + if(autoeject) // Eject if configured. + msg += " Auto ejecting patient now." + open_machine() + radio.talk_into(src, msg, radio_channel) + return + + var/datum/gas_mixture/air1 = airs[1] + + if(air1.gases.len) + if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic. + mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000) + mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000) + if(beaker) + if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic. + beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents. + beaker.reagents.reaction(occupant, VAPOR) + air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this + GAS_GARBAGE_COLLECT(air1.gases) + if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker). + reagent_transfer = 0 + + return 1 + +/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos() + ..() + + if(!on) + return + + var/datum/gas_mixture/air1 = airs[1] + + if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work. + on = FALSE + update_icon() + return + + if(occupant) + var/mob/living/mob_occupant = occupant + var/cold_protection = 0 + var/temperature_delta = air1.temperature - mob_occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant. + + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + cold_protection = H.get_thermal_protection(air1.temperature, TRUE) + + if(abs(temperature_delta) > 1) + var/air_heat_capacity = air1.heat_capacity() + + var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity)) + + air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB) + mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB) + + air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic. + GAS_GARBAGE_COLLECT(air1.gases) + +/obj/machinery/atmospherics/components/unary/cryo_cell/power_change() + ..() + update_icon() + +/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + +/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0) + if(!state_open && !panel_open) + on = FALSE + ..() + for(var/mob/M in contents) //only drop mobs + M.forceMove(get_turf(src)) + if(isliving(M)) + var/mob/living/L = M + L.update_canmove() + occupant = null + update_icon() + +/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user) + if((isnull(user) || istype(user)) && state_open && !panel_open) + ..(user) + return occupant + +/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user) + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message("You see [user] kicking against the glass of [src]!", \ + "You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)", \ + "You hear a thump from [src].") + if(do_after(user, breakout_time, target = src)) + if(!user || user.stat != CONSCIOUS || user.loc != src ) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open_machine() + +/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user) + . = ..() + if(occupant) + if(on) + . += "Someone's inside [src]!" + else + . += "You can barely make out a form floating in [src]." + else + . += "[src] seems empty." + +/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) + return + if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious()) + close_machine(target) + else + user.visible_message("[user] starts shoving [target] inside [src].", "You start shoving [target] inside [src].") + if (do_after(user, 25, target=target)) + close_machine(target) + +/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/glass)) + . = 1 //no afterattack + if(beaker) + to_chat(user, "A beaker is already loaded into [src]!") + return + if(!user.transferItemToLoc(I, src)) + return + beaker = I + user.visible_message("[user] places [I] in [src].", \ + "You place [I] in [src].") + var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list) + log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]") + return + if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \ + || default_change_direction_wrench(user, I) \ + || default_pry_open(I) \ + || default_deconstruction_crowbar(I)) + update_icon() + return + else if(istype(I, /obj/item/screwdriver)) + to_chat(user, "You can't access the maintenance panel while the pod is " \ + + (on ? "active" : (occupant ? "full" : "open")) + ".") + return + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state) + ui.open() + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data() + var/list/data = list() + data["isOperating"] = on + data["hasOccupant"] = occupant ? TRUE : FALSE + data["isOpen"] = state_open + data["autoEject"] = autoeject + + data["occupant"] = list() + if(occupant) + var/mob/living/mob_occupant = occupant + data["occupant"]["name"] = mob_occupant.name + switch(mob_occupant.stat) + if(CONSCIOUS) + data["occupant"]["stat"] = "Conscious" + data["occupant"]["statstate"] = "good" + if(SOFT_CRIT) + data["occupant"]["stat"] = "Conscious" + data["occupant"]["statstate"] = "average" + if(UNCONSCIOUS) + data["occupant"]["stat"] = "Unconscious" + data["occupant"]["statstate"] = "average" + if(DEAD) + data["occupant"]["stat"] = "Dead" + data["occupant"]["statstate"] = "bad" + data["occupant"]["health"] = round(mob_occupant.health, 1) + data["occupant"]["maxHealth"] = mob_occupant.maxHealth + data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD + data["occupant"]["bruteLoss"] = round(mob_occupant.getBruteLoss(), 1) + data["occupant"]["oxyLoss"] = round(mob_occupant.getOxyLoss(), 1) + data["occupant"]["toxLoss"] = round(mob_occupant.getToxLoss(), 1) + data["occupant"]["fireLoss"] = round(mob_occupant.getFireLoss(), 1) + data["occupant"]["bodyTemperature"] = round(mob_occupant.bodytemperature, 1) + if(mob_occupant.bodytemperature < TCRYO) + data["occupant"]["temperaturestatus"] = "good" + else if(mob_occupant.bodytemperature < T0C) + data["occupant"]["temperaturestatus"] = "average" + else + data["occupant"]["temperaturestatus"] = "bad" + + var/datum/gas_mixture/air1 = airs[1] + data["cellTemperature"] = round(air1.temperature, 1) + + data["isBeakerLoaded"] = beaker ? TRUE : FALSE + var/beakerContents = list() + if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents += list(list("name" = R.name, "volume" = R.volume)) + data["beakerContents"] = beakerContents + return data + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params) + if(..()) + return + switch(action) + if("power") + if(on) + on = FALSE + else if(!state_open) + on = TRUE + . = TRUE + if("door") + if(state_open) + close_machine() + else + open_machine() + . = TRUE + if("autoeject") + autoeject = !autoeject + . = TRUE + if("ejectbeaker") + if(beaker) + beaker.forceMove(drop_location()) + if(Adjacent(usr) && !issilicon(usr)) + usr.put_in_hands(beaker) + beaker = null + . = TRUE + update_icon() + +/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user) + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open) + on = !on + update_icon() + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user) + . = ..() + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(state_open) + close_machine() + else + open_machine() + update_icon() + return TRUE + +/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) + return // we don't see the pipe network while inside cryo. + +/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) + +/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through() + return // can't ventcrawl in or out of cryo. + +/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes() + return 0 // you can't see the pipe network when inside a cryo cell. + +/obj/machinery/atmospherics/components/unary/cryo_cell/return_temperature() + var/datum/gas_mixture/G = airs[1] + + if(G.total_moles() > 10) + return G.temperature + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W) + . = ..() + if(.) + SetInitDirections() + var/obj/machinery/atmospherics/node = nodes[1] + if(node) + node.disconnect(src) + nodes[1] = null + nullifyPipenet(parents[1]) + atmosinit() + node = nodes[1] + if(node) + node.atmosinit() + node.addMember(src) + SSair.add_to_rebuild_queue(src) + +#undef CRYOMOBS diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 877826c1..237e1f17 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -116,7 +116,7 @@ if(node) node.atmosinit() node.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) return TRUE /obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user) diff --git a/code/modules/atmospherics/machinery/pipes/layermanifold.dm b/code/modules/atmospherics/machinery/pipes/layermanifold.dm index 33092f53..bc8fd677 100644 --- a/code/modules/atmospherics/machinery/pipes/layermanifold.dm +++ b/code/modules/atmospherics/machinery/pipes/layermanifold.dm @@ -31,7 +31,7 @@ nodes = list() for(var/obj/machinery/atmospherics/A in needs_nullifying) A.disconnect(src) - A.build_network() + SSair.add_to_rebuild_queue(A) /obj/machinery/atmospherics/pipe/layer_manifold/proc/get_all_connected_nodes() return front_nodes + back_nodes + nodes diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index 15615b8e..23fd2292 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -22,7 +22,7 @@ var/obj/machinery/atmospherics/oldN = nodes[i] ..() if(oldN) - oldN.build_network() + SSair.add_to_rebuild_queue(oldN) /obj/machinery/atmospherics/pipe/destroy_network() QDEL_NULL(parent) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index afd1e918..f307b59a 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -184,7 +184,6 @@ /obj/machinery/portable_atmospherics/canister/proto name = "prototype canister" - /obj/machinery/portable_atmospherics/canister/proto/default name = "prototype canister" desc = "The best way to fix an atmospheric emergency... or the best way to introduce one." @@ -197,7 +196,6 @@ can_min_release_pressure = (ONE_ATMOSPHERE / 30) prototype = TRUE - /obj/machinery/portable_atmospherics/canister/proto/default/oxygen name = "prototype canister" desc = "A prototype canister for a prototype bike, what could go wrong?" @@ -206,8 +204,6 @@ filled = 1 release_pressure = ONE_ATMOSPHERE*2 - - /obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture) ..() if(existing_mixture) @@ -217,7 +213,7 @@ pump = new(src, FALSE) pump.on = TRUE pump.stat = 0 - pump.build_network() + SSair.add_to_rebuild_queue(pump) update_icon() @@ -233,6 +229,7 @@ air_contents.gases[gas_type] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) if(starter_temp) air_contents.temperature = starter_temp + /obj/machinery/portable_atmospherics/canister/air/create_gas() air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index ddb907a2..fd2888fb 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -20,7 +20,7 @@ pump = new(src, FALSE) pump.on = TRUE pump.stat = 0 - pump.build_network() + SSair.add_to_rebuild_queue(pump) /obj/machinery/portable_atmospherics/pump/Destroy() var/turf/T = get_turf(src) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index beaaa51a..7780522d 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -249,7 +249,7 @@ All ShuttleMove procs go here A.atmosinit() if(A.returnPipenet()) A.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) else // atmosinit() calls update_icon(), so we don't need to call it update_icon()