mirror of
https://github.com/KabKebab/GS13.git
synced 2026-07-20 20:37:45 +01:00
Revert "Merge pull request #1094 from ArchieBeepBoop/maptick"
This reverts commit785d5e2fe6, reversing changes made toed5e33db04.
This commit is contained in:
+4
-19
@@ -1,28 +1,13 @@
|
||||
/// 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_RUNNING 80
|
||||
#define TICK_LIMIT_TO_RUN 70
|
||||
/// Tick limit for MC while running
|
||||
#define TICK_LIMIT_MC 70
|
||||
/// Tick limit while initializing
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE)
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT 98
|
||||
|
||||
/// 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
|
||||
#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
|
||||
|
||||
/// 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 )
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#define GLOBAL_PROC "some_magic_bullshit"
|
||||
/// A shorthand for the callback datum, [documented here](datum/callback.html)
|
||||
|
||||
#define CALLBACK new /datum/callback
|
||||
#define INVOKE_ASYNC world.ImmediateInvokeAsync
|
||||
#define CALLBACK_NEW(typepath, args) CALLBACK(GLOBAL_PROC, /proc/___callbacknew, typepath, args)
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
#define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails.
|
||||
//defines for the gc_destroyed var
|
||||
|
||||
#define GC_QUEUE_CHECK 1
|
||||
#define GC_QUEUE_HARDDELETE 2
|
||||
#define GC_QUEUE_COUNT 2 //increase this when adding more steps.
|
||||
#define GC_QUEUE_PREQUEUE 1
|
||||
#define GC_QUEUE_CHECK 2
|
||||
#define GC_QUEUE_HARDDELETE 3
|
||||
#define GC_QUEUE_COUNT 3 //increase this when adding more steps.
|
||||
|
||||
#define GC_QUEUED_FOR_QUEUING -1
|
||||
#define GC_CURRENTLY_BEING_QDELETED -2
|
||||
#define GC_QUEUED_FOR_HARD_DEL -2
|
||||
#define GC_CURRENTLY_BEING_QDELETED -3
|
||||
|
||||
#define QDELING(X) (X.gc_destroyed)
|
||||
#define QDELETED(X) (!X || QDELING(X))
|
||||
|
||||
@@ -131,15 +131,8 @@
|
||||
|
||||
#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) {\
|
||||
|
||||
@@ -1472,37 +1472,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
|
||||
else
|
||||
D.vars[var_name] = var_value
|
||||
|
||||
#define TRAIT_CALLBACK_ADD(target, trait, source) CALLBACK(GLOBAL_PROC, /proc/___TraitAdd, ##target, ##trait, ##source)
|
||||
#define TRAIT_CALLBACK_REMOVE(target, trait, source) CALLBACK(GLOBAL_PROC, /proc/___TraitRemove, ##target, ##trait, ##source)
|
||||
|
||||
///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
|
||||
/proc/___TraitAdd(target,trait,source)
|
||||
if(!target || !trait || !source)
|
||||
return
|
||||
if(islist(target))
|
||||
for(var/i in target)
|
||||
if(!isatom(i))
|
||||
continue
|
||||
var/atom/the_atom = i
|
||||
ADD_TRAIT(the_atom,trait,source)
|
||||
else if(isatom(target))
|
||||
var/atom/the_atom2 = target
|
||||
ADD_TRAIT(the_atom2,trait,source)
|
||||
|
||||
///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
|
||||
/proc/___TraitRemove(target,trait,source)
|
||||
if(!target || !trait || !source)
|
||||
return
|
||||
if(islist(target))
|
||||
for(var/i in target)
|
||||
if(!isatom(i))
|
||||
continue
|
||||
var/atom/the_atom = i
|
||||
REMOVE_TRAIT(the_atom,trait,source)
|
||||
else if(isatom(target))
|
||||
var/atom/the_atom2 = target
|
||||
REMOVE_TRAIT(the_atom2,trait,source)
|
||||
|
||||
/proc/get_random_food()
|
||||
var/list/blocked = list(/obj/item/reagent_containers/food/snacks,
|
||||
/obj/item/reagent_containers/food/snacks/store/bread,
|
||||
|
||||
@@ -27,5 +27,3 @@ 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)
|
||||
|
||||
+10
-23
@@ -8,7 +8,6 @@
|
||||
**/
|
||||
|
||||
//This is the ABSOLUTE ONLY THING that should init globally like this
|
||||
//2019 update: the failsafe,config and Global controllers also do it
|
||||
GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
//THIS IS THE INIT ORDER
|
||||
@@ -36,9 +35,6 @@ 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?
|
||||
@@ -338,7 +334,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
new/datum/controller/failsafe() // (re)Start the failsafe.
|
||||
|
||||
//now do the actual stuff
|
||||
if (!skip_ticks)
|
||||
if (!queue_head || !(iteration % 3))
|
||||
var/checking_runlevel = current_runlevel
|
||||
if(cached_runlevel != checking_runlevel)
|
||||
//resechedule subsystems
|
||||
@@ -384,8 +380,6 @@ 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)
|
||||
@@ -449,12 +443,10 @@ 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
|
||||
@@ -462,15 +454,14 @@ 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)
|
||||
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
|
||||
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 (!bg_calc && (queue_node_flags & SS_BACKGROUND))
|
||||
if ((queue_node_flags & SS_BACKGROUND) && !bg_calc)
|
||||
current_tick_budget = queue_priority_count_bg
|
||||
bg_calc = TRUE
|
||||
|
||||
@@ -523,7 +514,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
queue_node.paused_ticks = 0
|
||||
queue_node.paused_tick_usage = 0
|
||||
|
||||
if (bg_calc) //update our running total
|
||||
if (queue_node_flags & SS_BACKGROUND) //update our running total
|
||||
queue_priority_count_bg -= queue_node_priority
|
||||
else
|
||||
queue_priority_count -= queue_node_priority
|
||||
@@ -591,10 +582,6 @@ 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)
|
||||
@@ -624,4 +611,4 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
if (client_count < CONFIG_GET(number/mc_tick_rate/disable_high_pop_mc_mode_amount))
|
||||
processing = CONFIG_GET(number/mc_tick_rate/base_mc_tick_rate)
|
||||
else if (client_count > CONFIG_GET(number/mc_tick_rate/high_pop_mc_mode_amount))
|
||||
processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate)
|
||||
processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate)
|
||||
@@ -1,3 +1,11 @@
|
||||
#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
|
||||
@@ -12,7 +20,6 @@ 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()
|
||||
@@ -20,7 +27,6 @@ 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()
|
||||
|
||||
@@ -33,7 +39,7 @@ SUBSYSTEM_DEF(air)
|
||||
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/currentpart = SSAIR_REBUILD_PIPENETS
|
||||
var/currentpart = SSAIR_PIPENETS
|
||||
|
||||
var/map_loading = TRUE
|
||||
var/list/queued_for_activation
|
||||
@@ -46,7 +52,6 @@ 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]|"
|
||||
@@ -72,18 +77,6 @@ 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))
|
||||
@@ -144,7 +137,9 @@ SUBSYSTEM_DEF(air)
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
currentpart = SSAIR_REBUILD_PIPENETS
|
||||
currentpart = SSAIR_PIPENETS
|
||||
|
||||
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
|
||||
if (!resumed)
|
||||
@@ -161,9 +156,6 @@ 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
|
||||
|
||||
@@ -8,10 +8,6 @@ SUBSYSTEM_DEF(server_maint)
|
||||
init_order = INIT_ORDER_SERVER_MAINT
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
var/list/currentrun
|
||||
var/cleanup_ticker = 0
|
||||
|
||||
/datum/controller/subsystem/server_maint/PreInit()
|
||||
world.hub_password = "" //quickly! before the hubbies see us.
|
||||
|
||||
/datum/controller/subsystem/server_maint/Initialize(timeofday)
|
||||
if (CONFIG_GET(flag/hub))
|
||||
@@ -22,30 +18,10 @@ SUBSYSTEM_DEF(server_maint)
|
||||
if(!resumed)
|
||||
if(listclearnulls(GLOB.clients))
|
||||
log_world("Found a null in clients list!")
|
||||
if(listclearnulls(GLOB.player_list))
|
||||
log_world("Found a null in player list!")
|
||||
src.currentrun = GLOB.clients.Copy()
|
||||
|
||||
switch (cleanup_ticker) // do only one of these at a time, once per 5 fires
|
||||
if (0)
|
||||
if(listclearnulls(GLOB.player_list))
|
||||
log_world("Found a null in player_list!")
|
||||
cleanup_ticker++
|
||||
if (5)
|
||||
if(listclearnulls(GLOB.mob_list))
|
||||
log_world("Found a null in mob_list!")
|
||||
cleanup_ticker++
|
||||
if (10)
|
||||
if(listclearnulls(GLOB.alive_mob_list))
|
||||
log_world("Found a null in alive_mob_list!")
|
||||
cleanup_ticker++
|
||||
if (15)
|
||||
if(listclearnulls(GLOB.dead_mob_list))
|
||||
log_world("Found a null in dead_mob_list!")
|
||||
cleanup_ticker++
|
||||
if (20)
|
||||
cleanup_ticker = 0
|
||||
else
|
||||
cleanup_ticker++
|
||||
|
||||
var/list/currentrun = src.currentrun
|
||||
var/round_started = SSticker.HasRoundStarted()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
|
||||
#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
|
||||
#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN)||BUCKET_LEN)
|
||||
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
|
||||
#define TIMER_ID_MAX (2**24) //max float with integer precision
|
||||
|
||||
@@ -39,15 +39,15 @@ SUBSYSTEM_DEF(timer)
|
||||
|
||||
/datum/controller/subsystem/timer/fire(resumed = FALSE)
|
||||
var/lit = last_invoke_tick
|
||||
var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5)
|
||||
var/last_check = world.time - TIMER_NO_INVOKE_WARNING
|
||||
var/list/bucket_list = src.bucket_list
|
||||
|
||||
if(!bucket_count)
|
||||
last_invoke_tick = world.time
|
||||
|
||||
if(lit && lit < last_check && head_offset < last_check && last_invoke_warning < last_check)
|
||||
if(lit && lit < last_check && last_invoke_warning < last_check)
|
||||
last_invoke_warning = world.time
|
||||
var/msg = "No regular timers processed in the last [BUCKET_LEN*1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
|
||||
var/msg = "No regular timers processed in the last [TIMER_NO_INVOKE_WARNING] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
|
||||
message_admins(msg)
|
||||
WARNING(msg)
|
||||
if(bucket_auto_reset)
|
||||
@@ -122,7 +122,7 @@ SUBSYSTEM_DEF(timer)
|
||||
if (!resumed)
|
||||
timer = null
|
||||
|
||||
while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset-1)*world.tick_lag) <= world.time)
|
||||
while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time)
|
||||
var/datum/timedevent/head = bucket_list[practical_offset]
|
||||
if (!timer || !head || timer == head)
|
||||
head = bucket_list[practical_offset]
|
||||
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(timer)
|
||||
|
||||
if (timer.timeToRun < head_offset)
|
||||
bucket_resolution = null //force bucket recreation
|
||||
stack_trace("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
|
||||
if (timer.callBack && !timer.spent)
|
||||
timer.callBack.InvokeAsync()
|
||||
@@ -170,9 +170,9 @@ SUBSYSTEM_DEF(timer)
|
||||
qdel(timer)
|
||||
continue
|
||||
|
||||
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
|
||||
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset))
|
||||
bucket_resolution = null //force bucket recreation
|
||||
stack_trace("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
if (timer.callBack && !timer.spent)
|
||||
timer.callBack.InvokeAsync()
|
||||
spent += timer
|
||||
@@ -448,7 +448,6 @@ SUBSYSTEM_DEF(timer)
|
||||
next.prev = src
|
||||
prev.next = src
|
||||
|
||||
///Returns a string of the type of the callback for this timer
|
||||
/datum/timedevent/proc/getcallingtype()
|
||||
. = "ERROR"
|
||||
if (callBack.object == GLOBAL_PROC)
|
||||
@@ -456,14 +455,6 @@ SUBSYSTEM_DEF(timer)
|
||||
else
|
||||
. = "[callBack.object.type]"
|
||||
|
||||
/**
|
||||
* Create a new timer and insert it in the queue
|
||||
*
|
||||
* Arguments:
|
||||
* * callback the callback to call on timer finish
|
||||
* * wait deciseconds to run the timer for
|
||||
* * flags flags for this timer, see: code\__DEFINES\subsystems.dm
|
||||
*/
|
||||
/proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
|
||||
if (!callback)
|
||||
CRASH("addtimer called without a callback")
|
||||
@@ -508,12 +499,6 @@ SUBSYSTEM_DEF(timer)
|
||||
var/datum/timedevent/timer = new(callback, wait, flags, hash)
|
||||
return timer.id
|
||||
|
||||
/**
|
||||
* Delete a timer
|
||||
*
|
||||
* Arguments:
|
||||
* * id a timerid or a /datum/timedevent
|
||||
*/
|
||||
/proc/deltimer(id)
|
||||
if (!id)
|
||||
return FALSE
|
||||
@@ -530,26 +515,6 @@ SUBSYSTEM_DEF(timer)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Get the remaining deciseconds on a timer
|
||||
*
|
||||
* Arguments:
|
||||
* * id a timerid or a /datum/timedevent
|
||||
*/
|
||||
/proc/timeleft(id)
|
||||
if (!id)
|
||||
return null
|
||||
if (id == TIMER_ID_NULL)
|
||||
CRASH("Tried to get timeleft of a null timerid. Use TIMER_STOPPABLE flag")
|
||||
if (!istext(id))
|
||||
if (istype(id, /datum/timedevent))
|
||||
var/datum/timedevent/timer = id
|
||||
return timer.timeToRun - world.time
|
||||
//id is string
|
||||
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
|
||||
if (timer && !timer.spent)
|
||||
return timer.timeToRun - world.time
|
||||
return null
|
||||
|
||||
#undef BUCKET_LEN
|
||||
#undef BUCKET_POS
|
||||
|
||||
+58
-103
@@ -1,72 +1,55 @@
|
||||
/**
|
||||
*# Callback Datums
|
||||
*A datum that holds a proc to be called on another object, used to track proccalls to other objects
|
||||
*
|
||||
* ## USAGE
|
||||
*
|
||||
* ```
|
||||
* var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
|
||||
* var/timerid = addtimer(C, time, timertype)
|
||||
* you can also use the compiler define shorthand
|
||||
* var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
|
||||
* ```
|
||||
*
|
||||
* Note: proc strings can only be given for datum proc calls, global procs must be proc paths
|
||||
*
|
||||
* Also proc strings are strongly advised against because they don't compile error if the proc stops existing
|
||||
*
|
||||
* In some cases you can provide a shortform of the procname, see the proc typepath shortcuts documentation below
|
||||
*
|
||||
* ## INVOKING THE CALLBACK
|
||||
*`var/result = C.Invoke(args, to, add)` additional args are added after the ones given when the callback was created
|
||||
*
|
||||
* `var/result = C.InvokeAsync(args, to, add)` Asyncronous - returns . on the first sleep then continues on in the background
|
||||
* after the sleep/block ends, otherwise operates normally.
|
||||
*
|
||||
* ## PROC TYPEPATH SHORTCUTS
|
||||
* (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
|
||||
*
|
||||
* ### global proc while in another global proc:
|
||||
* .procname
|
||||
*
|
||||
* `CALLBACK(GLOBAL_PROC, .some_proc_here)`
|
||||
*
|
||||
* ### proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
|
||||
* .procname
|
||||
*
|
||||
* `CALLBACK(src, .some_proc_here)`
|
||||
*
|
||||
* ### when the above doesn't apply:
|
||||
*.proc/procname
|
||||
*
|
||||
* `CALLBACK(src, .proc/some_proc_here)`
|
||||
*
|
||||
*
|
||||
* proc defined on a parent of a some type
|
||||
*
|
||||
* `/some/type/.proc/some_proc_here`
|
||||
*
|
||||
* Otherwise you must always provide the full typepath of the proc (/type/of/thing/proc/procname)
|
||||
*/
|
||||
/datum/callback
|
||||
/*
|
||||
USAGE:
|
||||
|
||||
///The object we will be calling the proc on
|
||||
var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
|
||||
var/timerid = addtimer(C, time, timertype)
|
||||
OR
|
||||
var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
|
||||
|
||||
Note: proc strings can only be given for datum proc calls, global procs must be proc paths
|
||||
Also proc strings are strongly advised against because they don't compile error if the proc stops existing
|
||||
See the note on proc typepath shortcuts
|
||||
|
||||
INVOKING THE CALLBACK:
|
||||
var/result = C.Invoke(args, to, add) //additional args are added after the ones given when the callback was created
|
||||
OR
|
||||
var/result = C.InvokeAsync(args, to, add) //Sleeps will not block, returns . on the first sleep (then continues on in the "background" after the sleep/block ends), otherwise operates normally.
|
||||
OR
|
||||
INVOKE_ASYNC(<CALLBACK args>) to immediately create and call InvokeAsync
|
||||
|
||||
PROC TYPEPATH SHORTCUTS (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
|
||||
|
||||
global proc while in another global proc:
|
||||
.procname
|
||||
Example:
|
||||
CALLBACK(GLOBAL_PROC, .some_proc_here)
|
||||
|
||||
proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
|
||||
.procname
|
||||
Example:
|
||||
CALLBACK(src, .some_proc_here)
|
||||
|
||||
|
||||
when the above doesn't apply:
|
||||
.proc/procname
|
||||
Example:
|
||||
CALLBACK(src, .proc/some_proc_here)
|
||||
|
||||
proc defined on a parent of a some type:
|
||||
/some/type/.proc/some_proc_here
|
||||
|
||||
|
||||
|
||||
Other wise you will have to do the full typepath of the proc (/type/of/thing/proc/procname)
|
||||
|
||||
*/
|
||||
|
||||
/datum/callback
|
||||
var/datum/object = GLOBAL_PROC
|
||||
///The proc we will be calling on the object
|
||||
var/delegate
|
||||
///A list of arguments to pass into the proc
|
||||
var/list/arguments
|
||||
///A weak reference to the user who triggered this callback
|
||||
var/datum/weakref/user
|
||||
|
||||
/**
|
||||
* Create a new callback datum
|
||||
*
|
||||
* Arguments
|
||||
* * thingtocall the object to call the proc on
|
||||
* * proctocall the proc to call on the target object
|
||||
* * ... an optional list of extra arguments to pass to the proc
|
||||
*/
|
||||
/datum/callback/New(thingtocall, proctocall, ...)
|
||||
if (thingtocall)
|
||||
object = thingtocall
|
||||
@@ -75,14 +58,7 @@
|
||||
arguments = args.Copy(3)
|
||||
if(usr)
|
||||
user = WEAKREF(usr)
|
||||
/**
|
||||
* Immediately Invoke proctocall on thingtocall, with waitfor set to false
|
||||
*
|
||||
* Arguments:
|
||||
* * thingtocall Object to call on
|
||||
* * proctocall Proc to call on that object
|
||||
* * ... optional list of arguments to pass as arguments to the proc being called
|
||||
*/
|
||||
|
||||
/world/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...)
|
||||
set waitfor = FALSE
|
||||
|
||||
@@ -96,14 +72,6 @@
|
||||
else
|
||||
call(thingtocall, proctocall)(arglist(calling_arguments))
|
||||
|
||||
/**
|
||||
* Invoke this callback
|
||||
*
|
||||
* Calls the registered proc on the registered object, if the user ref
|
||||
* can be resolved it also inclues that as an arg
|
||||
*
|
||||
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
|
||||
*/
|
||||
/datum/callback/proc/Invoke(...)
|
||||
if(!usr)
|
||||
var/datum/weakref/W = user
|
||||
@@ -129,14 +97,7 @@
|
||||
return call(delegate)(arglist(calling_arguments))
|
||||
return call(object, delegate)(arglist(calling_arguments))
|
||||
|
||||
/**
|
||||
* Invoke this callback async (waitfor=false)
|
||||
*
|
||||
* Calls the registered proc on the registered object, if the user ref
|
||||
* can be resolved it also inclues that as an arg
|
||||
*
|
||||
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
|
||||
*/
|
||||
//copy and pasted because fuck proc overhead
|
||||
/datum/callback/proc/InvokeAsync(...)
|
||||
set waitfor = FALSE
|
||||
|
||||
@@ -164,9 +125,7 @@
|
||||
return call(delegate)(arglist(calling_arguments))
|
||||
return call(object, delegate)(arglist(calling_arguments))
|
||||
|
||||
/**
|
||||
Helper datum for the select callbacks proc
|
||||
*/
|
||||
|
||||
/datum/callback_select
|
||||
var/list/finished
|
||||
var/pendingcount
|
||||
@@ -191,17 +150,15 @@
|
||||
if (savereturn)
|
||||
finished[index] = rtn
|
||||
|
||||
/**
|
||||
* Runs a list of callbacks asyncronously, returning only when all have finished
|
||||
*
|
||||
* Callbacks can be repeated, to call it multiple times
|
||||
*
|
||||
* Arguments:
|
||||
* * list/callbacks the list of callbacks to be called
|
||||
* * list/callback_args the list of lists of arguments to pass into each callback
|
||||
* * savereturns Optionally save and return the list of returned values from each of the callbacks
|
||||
* * resolution The number of byond ticks between each time you check if all callbacks are complete
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//runs a list of callbacks asynchronously, returning once all of them return.
|
||||
//callbacks can be repeated.
|
||||
//callbacks-args is an optional list of argument lists, in the same order as the callbacks,
|
||||
// the inner lists will be sent to the callbacks when invoked() as additional args.
|
||||
//can optionly save and return a list of return values, in the same order as the original list of callbacks
|
||||
//resolution is the number of byond ticks between checks.
|
||||
/proc/callback_select(list/callbacks, list/callback_args, savereturns = TRUE, resolution = 1)
|
||||
if (!callbacks)
|
||||
return
|
||||
@@ -221,5 +178,3 @@
|
||||
sleep(resolution*world.tick_lag)
|
||||
return CS.finished
|
||||
|
||||
/proc/___callbacknew(typepath, arguments)
|
||||
new typepath(arglist(arguments))
|
||||
|
||||
+3
-2
@@ -7,8 +7,9 @@ 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()
|
||||
if(fexists("byond-extools.dll"))
|
||||
call("byond-extools.dll", "maptick_initialize")()
|
||||
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")()
|
||||
enable_debugger()
|
||||
|
||||
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
|
||||
|
||||
@@ -1,342 +1,341 @@
|
||||
// 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)
|
||||
. += "<span class='notice'>Alt-click to crawl through it.</span>"
|
||||
|
||||
/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, "<span class='warning'>You must remove the plating first!</span>")
|
||||
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, "<span class='notice'>You begin to unfasten \the [src]...</span>")
|
||||
|
||||
if (internal_pressure > 2*ONE_ATMOSPHERE)
|
||||
to_chat(user, "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>")
|
||||
unsafe_wrenching = TRUE //Oh dear oh dear
|
||||
|
||||
if(I.use_tool(src, user, 20, volume=50))
|
||||
user.visible_message( \
|
||||
"[user] unfastens \the [src].", \
|
||||
"<span class='notice'>You unfasten \the [src].</span>", \
|
||||
"<span class='italics'>You hear ratchet.</span>")
|
||||
investigate_log("was <span class='warning'>REMOVED</span> 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("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
|
||||
|
||||
// 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("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>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("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>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
|
||||
// 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)
|
||||
. += "<span class='notice'>Alt-click to crawl through it.</span>"
|
||||
|
||||
/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, "<span class='warning'>You must remove the plating first!</span>")
|
||||
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, "<span class='notice'>You begin to unfasten \the [src]...</span>")
|
||||
|
||||
if (internal_pressure > 2*ONE_ATMOSPHERE)
|
||||
to_chat(user, "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>")
|
||||
unsafe_wrenching = TRUE //Oh dear oh dear
|
||||
|
||||
if(I.use_tool(src, user, 20, volume=50))
|
||||
user.visible_message( \
|
||||
"[user] unfastens \the [src].", \
|
||||
"<span class='notice'>You unfasten \the [src].</span>", \
|
||||
"<span class='italics'>You hear ratchet.</span>")
|
||||
investigate_log("was <span class='warning'>REMOVED</span> 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("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
|
||||
|
||||
// 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("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>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("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>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
|
||||
@@ -119,7 +119,7 @@
|
||||
if(node2)
|
||||
node2.atmosinit()
|
||||
node2.addMember(src)
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
build_network()
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -144,8 +144,8 @@
|
||||
for(var/i in 1 to device_type)
|
||||
var/datum/pipeline/parent = parents[i]
|
||||
if(!parent)
|
||||
stack_trace("Component is missing a pipenet! Rebuilding...")
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
throw EXCEPTION("Component is missing a pipenet! Rebuilding...")
|
||||
build_network()
|
||||
parent.update = 1
|
||||
|
||||
/obj/machinery/atmospherics/components/returnPipenets()
|
||||
|
||||
@@ -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, "<span class='warning'>[src]'s door won't budge!</span>")
|
||||
|
||||
/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("<span class='notice'>You see [user] kicking against the glass of [src]!</span>", \
|
||||
"<span class='notice'>You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
|
||||
"<span class='italics'>You hear a thump from [src].</span>")
|
||||
if(do_after(user, breakout_time, target = src))
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != src )
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \
|
||||
"<span class='notice'>You successfully break out of [src]!</span>")
|
||||
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("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
|
||||
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, "<span class='warning'>A beaker is already loaded into [src]!</span>")
|
||||
return
|
||||
if(!user.transferItemToLoc(I, src))
|
||||
return
|
||||
beaker = I
|
||||
user.visible_message("[user] places [I] in [src].", \
|
||||
"<span class='notice'>You place [I] in [src].</span>")
|
||||
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, "<span class='notice'>You can't access the maintenance panel while the pod is " \
|
||||
+ (on ? "active" : (occupant ? "full" : "open")) + ".</span>")
|
||||
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
|
||||
/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, "<span class='warning'>[src]'s door won't budge!</span>")
|
||||
|
||||
/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("<span class='notice'>You see [user] kicking against the glass of [src]!</span>", \
|
||||
"<span class='notice'>You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
|
||||
"<span class='italics'>You hear a thump from [src].</span>")
|
||||
if(do_after(user, breakout_time, target = src))
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != src )
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \
|
||||
"<span class='notice'>You successfully break out of [src]!</span>")
|
||||
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("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
|
||||
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, "<span class='warning'>A beaker is already loaded into [src]!</span>")
|
||||
return
|
||||
if(!user.transferItemToLoc(I, src))
|
||||
return
|
||||
beaker = I
|
||||
user.visible_message("[user] places [I] in [src].", \
|
||||
"<span class='notice'>You place [I] in [src].</span>")
|
||||
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, "<span class='notice'>You can't access the maintenance panel while the pod is " \
|
||||
+ (on ? "active" : (occupant ? "full" : "open")) + ".</span>")
|
||||
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
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
if(node)
|
||||
node.atmosinit()
|
||||
node.addMember(src)
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
build_network()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
nodes = list()
|
||||
for(var/obj/machinery/atmospherics/A in needs_nullifying)
|
||||
A.disconnect(src)
|
||||
SSair.add_to_rebuild_queue(A)
|
||||
A.build_network()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/layer_manifold/proc/get_all_connected_nodes()
|
||||
return front_nodes + back_nodes + nodes
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
var/obj/machinery/atmospherics/oldN = nodes[i]
|
||||
..()
|
||||
if(oldN)
|
||||
SSair.add_to_rebuild_queue(oldN)
|
||||
oldN.build_network()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/destroy_network()
|
||||
QDEL_NULL(parent)
|
||||
|
||||
@@ -184,6 +184,7 @@
|
||||
/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."
|
||||
@@ -196,6 +197,7 @@
|
||||
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?"
|
||||
@@ -204,6 +206,8 @@
|
||||
filled = 1
|
||||
release_pressure = ONE_ATMOSPHERE*2
|
||||
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture)
|
||||
..()
|
||||
if(existing_mixture)
|
||||
@@ -213,7 +217,7 @@
|
||||
pump = new(src, FALSE)
|
||||
pump.on = TRUE
|
||||
pump.stat = 0
|
||||
SSair.add_to_rebuild_queue(pump)
|
||||
pump.build_network()
|
||||
|
||||
update_icon()
|
||||
|
||||
@@ -229,7 +233,6 @@
|
||||
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)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
pump = new(src, FALSE)
|
||||
pump.on = TRUE
|
||||
pump.stat = 0
|
||||
SSair.add_to_rebuild_queue(pump)
|
||||
pump.build_network()
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/Destroy()
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -249,7 +249,7 @@ All ShuttleMove procs go here
|
||||
A.atmosinit()
|
||||
if(A.returnPipenet())
|
||||
A.addMember(src)
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
build_network()
|
||||
else
|
||||
// atmosinit() calls update_icon(), so we don't need to call it
|
||||
update_icon()
|
||||
|
||||
Reference in New Issue
Block a user