mirror of
https://github.com/CHOMPstation/CHOMPstation.git
synced 2026-08-23 20:16:19 +01:00
Merge branch 'master' of https://github.com/VOREStation/VOREStation into Fluff_Items_worshop
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
overlays += I
|
||||
|
||||
/obj/machinery/atmospherics/binary/algae_farm/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
internal = null
|
||||
|
||||
/obj/machinery/atmospherics/binary/algae_farm/process()
|
||||
@@ -101,7 +101,7 @@
|
||||
if(inoperable() || !anchored)
|
||||
icon_state = "algae-off"
|
||||
else if(recent_moles_transferred >= moles_per_tick)
|
||||
icon_state = "aglae-full"
|
||||
icon_state = "algae-full"
|
||||
else if(recent_moles_transferred > 0)
|
||||
icon_state = "algae-full"
|
||||
else
|
||||
|
||||
@@ -42,7 +42,7 @@ obj/machinery/atmospherics/binary
|
||||
return null
|
||||
|
||||
Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
@@ -54,8 +54,6 @@ obj/machinery/atmospherics/binary
|
||||
node1 = null
|
||||
node2 = null
|
||||
|
||||
..()
|
||||
|
||||
initialize()
|
||||
if(node1 && node2) return
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
initialize_directions = NORTH|SOUTH
|
||||
|
||||
Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
@@ -48,8 +48,6 @@
|
||||
node1 = null
|
||||
node2 = null
|
||||
|
||||
..()
|
||||
|
||||
process()
|
||||
..()
|
||||
if(anchored && !(stat&BROKEN))
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
input = null
|
||||
output = null
|
||||
filters.Cut()
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/omni/filter/sort_ports()
|
||||
for(var/datum/omni_port/P in ports)
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
return null
|
||||
|
||||
/obj/machinery/atmospherics/portables_connector/Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(connected_device)
|
||||
connected_device.disconnect()
|
||||
@@ -72,8 +72,6 @@
|
||||
|
||||
node = null
|
||||
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/portables_connector/initialize()
|
||||
if(node) return
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ obj/machinery/atmospherics/trinary
|
||||
return null
|
||||
|
||||
Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
@@ -67,8 +67,6 @@ obj/machinery/atmospherics/trinary
|
||||
node2 = null
|
||||
node3 = null
|
||||
|
||||
..()
|
||||
|
||||
initialize()
|
||||
if(node1 && node2 && node3) return
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
return null
|
||||
|
||||
/obj/machinery/atmospherics/tvalve/Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
@@ -116,8 +116,6 @@
|
||||
node2 = null
|
||||
node3 = null
|
||||
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/tvalve/proc/go_to_side()
|
||||
|
||||
if(state) return 0
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
return null
|
||||
|
||||
Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node)
|
||||
node.disconnect(src)
|
||||
@@ -39,8 +39,6 @@
|
||||
|
||||
node = null
|
||||
|
||||
..()
|
||||
|
||||
initialize()
|
||||
if(node) return
|
||||
|
||||
|
||||
@@ -80,7 +80,10 @@
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_pump/Destroy()
|
||||
unregister_radio(src, frequency)
|
||||
..()
|
||||
if(initial_loc)
|
||||
initial_loc.air_vent_info -= id_tag
|
||||
initial_loc.air_vent_names -= id_tag
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_pump/high_volume
|
||||
name = "Large Air Vent"
|
||||
@@ -414,13 +417,6 @@
|
||||
new /obj/item/pipe(loc, make_from=src)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_pump/Destroy()
|
||||
if(initial_loc)
|
||||
initial_loc.air_vent_info -= id_tag
|
||||
initial_loc.air_vent_names -= id_tag
|
||||
..()
|
||||
return
|
||||
|
||||
#undef DEFAULT_PRESSURE_DELTA
|
||||
|
||||
#undef EXTERNAL_PRESSURE_BOUND
|
||||
|
||||
@@ -44,8 +44,10 @@
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
|
||||
unregister_radio(src, frequency)
|
||||
..()
|
||||
|
||||
if(initial_loc)
|
||||
initial_loc.air_scrub_info -= id_tag
|
||||
initial_loc.air_scrub_names -= id_tag
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0)
|
||||
if(!check_icon_cache())
|
||||
@@ -285,10 +287,3 @@
|
||||
user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
|
||||
else
|
||||
user << "You are too far away to read the gauge."
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
|
||||
if(initial_loc)
|
||||
initial_loc.air_scrub_info -= id_tag
|
||||
initial_loc.air_scrub_names -= id_tag
|
||||
..()
|
||||
return
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
return null
|
||||
|
||||
/obj/machinery/atmospherics/valve/Destroy()
|
||||
loc = null
|
||||
. = ..()
|
||||
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
@@ -84,8 +84,6 @@
|
||||
node1 = null
|
||||
node2 = null
|
||||
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/valve/proc/open()
|
||||
if(open) return 0
|
||||
|
||||
|
||||
@@ -16,6 +16,15 @@ datum/pipe_network
|
||||
|
||||
..()
|
||||
|
||||
Destroy()
|
||||
pipe_networks -= src
|
||||
for(var/datum/pipeline/line_member in line_members)
|
||||
line_member.network = null
|
||||
for(var/obj/machinery/atmospherics/normal_member in normal_members)
|
||||
normal_member.reassign_network(src, null)
|
||||
gases.Cut() // Do not qdel the gases, we don't own them
|
||||
return ..()
|
||||
|
||||
proc/process()
|
||||
//Equalize gases amongst pipe if called for
|
||||
if(update)
|
||||
|
||||
@@ -10,14 +10,14 @@ datum/pipeline
|
||||
var/alert_pressure = 0
|
||||
|
||||
Destroy()
|
||||
if(network)
|
||||
qdel(network)
|
||||
qdel_null(network)
|
||||
|
||||
if(air && air.volume)
|
||||
temporarily_store_air()
|
||||
qdel(air)
|
||||
for(var/obj/machinery/atmospherics/pipe/P in members)
|
||||
P.parent = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
proc/process()//This use to be called called from the pipe networks
|
||||
|
||||
|
||||
@@ -63,11 +63,11 @@
|
||||
return parent.return_network(reference)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/Destroy()
|
||||
qdel(parent)
|
||||
qdel_null(parent)
|
||||
if(air_temporary)
|
||||
loc.assume_air(air_temporary)
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
|
||||
if (istype(src, /obj/machinery/atmospherics/pipe/tank))
|
||||
@@ -227,10 +227,12 @@
|
||||
/obj/machinery/atmospherics/pipe/simple/Destroy()
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
node1 = null
|
||||
if(node2)
|
||||
node2.disconnect(src)
|
||||
node1 = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
|
||||
return list(node1, node2)
|
||||
@@ -461,12 +463,15 @@
|
||||
/obj/machinery/atmospherics/pipe/manifold/Destroy()
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
node1 = null
|
||||
if(node2)
|
||||
node2.disconnect(src)
|
||||
node2 = null
|
||||
if(node3)
|
||||
node3.disconnect(src)
|
||||
node3 = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference)
|
||||
if(reference == node1)
|
||||
@@ -705,14 +710,18 @@
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/Destroy()
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
node1 = null
|
||||
if(node2)
|
||||
node2.disconnect(src)
|
||||
node2 = null
|
||||
if(node3)
|
||||
node3.disconnect(src)
|
||||
node3 = null
|
||||
if(node4)
|
||||
node4.disconnect(src)
|
||||
node4 = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference)
|
||||
if(reference == node1)
|
||||
@@ -967,8 +976,9 @@
|
||||
/obj/machinery/atmospherics/pipe/cap/Destroy()
|
||||
if(node)
|
||||
node.disconnect(src)
|
||||
node = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference)
|
||||
if(reference == node)
|
||||
@@ -1060,7 +1070,7 @@
|
||||
desc = "A large vessel containing pressurized gas."
|
||||
|
||||
volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet
|
||||
var/start_pressure = 25*ONE_ATMOSPHERE
|
||||
var/start_pressure = 75*ONE_ATMOSPHERE //Vorestation edit
|
||||
|
||||
level = 1
|
||||
dir = SOUTH
|
||||
@@ -1081,8 +1091,9 @@
|
||||
/obj/machinery/atmospherics/pipe/tank/Destroy()
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
node1 = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
|
||||
return list(node1)
|
||||
@@ -1251,8 +1262,9 @@
|
||||
/obj/machinery/atmospherics/pipe/vent/Destroy()
|
||||
if(node1)
|
||||
node1.disconnect(src)
|
||||
node1 = null
|
||||
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
|
||||
return list(node1)
|
||||
|
||||
@@ -117,6 +117,10 @@ obj/var/phoronproof = 0
|
||||
if(burn_eyes && head && (head.body_parts_covered & EYES) && (head.item_flags & AIRTIGHT))
|
||||
burn_eyes = 0
|
||||
|
||||
//VOREStation Edit - NIF Support
|
||||
if(nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION))
|
||||
burn_eyes = 0
|
||||
|
||||
//If we still need to, burn their eyes
|
||||
if(burn_eyes)
|
||||
burn_eyes()
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
/turf/var/datum/gas_mixture/air
|
||||
|
||||
/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
|
||||
if(graphic_add && graphic_add.len)
|
||||
if(LAZYLEN(graphic_add))
|
||||
overlays += graphic_add
|
||||
if(graphic_remove && graphic_remove.len)
|
||||
if(LAZYLEN(graphic_remove))
|
||||
overlays -= graphic_remove
|
||||
|
||||
/turf/proc/update_air_properties()
|
||||
|
||||
+10
-4
@@ -77,7 +77,8 @@ Class Procs:
|
||||
fire_tiles.Add(T)
|
||||
air_master.active_fire_zones |= src
|
||||
if(fuel) fuel_objs += fuel
|
||||
T.update_graphic(air.graphic)
|
||||
if(air.graphic)
|
||||
T.update_graphic(air.graphic)
|
||||
|
||||
/zone/proc/remove(turf/simulated/T)
|
||||
#ifdef ZASDBG
|
||||
@@ -92,7 +93,8 @@ Class Procs:
|
||||
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T
|
||||
fuel_objs -= fuel
|
||||
T.zone = null
|
||||
T.update_graphic(graphic_remove = air.graphic)
|
||||
if(air.graphic)
|
||||
T.update_graphic(graphic_remove = air.graphic)
|
||||
if(contents.len)
|
||||
air.group_multiplier = contents.len
|
||||
else
|
||||
@@ -106,9 +108,11 @@ Class Procs:
|
||||
ASSERT(!into.invalid)
|
||||
#endif
|
||||
c_invalidate()
|
||||
var/list/air_graphic = air.graphic // Cache for sanic speed
|
||||
for(var/turf/simulated/T in contents)
|
||||
into.add(T)
|
||||
T.update_graphic(graphic_remove = air.graphic)
|
||||
if(air_graphic)
|
||||
T.update_graphic(graphic_remove = air_graphic)
|
||||
#ifdef ZASDBG
|
||||
T.dbg(merged)
|
||||
#endif
|
||||
@@ -131,8 +135,10 @@ Class Procs:
|
||||
/zone/proc/rebuild()
|
||||
if(invalid) return //Short circuit for explosions where rebuild is called many times over.
|
||||
c_invalidate()
|
||||
var/list/air_graphic = air.graphic // Cache for sanic speed
|
||||
for(var/turf/simulated/T in contents)
|
||||
T.update_graphic(graphic_remove = air.graphic) //we need to remove the overlays so they're not doubled when the zone is rebuilt
|
||||
if(air_graphic)
|
||||
T.update_graphic(graphic_remove = air_graphic) //we need to remove the overlays so they're not doubled when the zone is rebuilt
|
||||
//T.dbg(invalid_zone)
|
||||
T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
|
||||
air_master.mark_for_update(T)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#define MC_TICK_CHECK ( ( world.tick_usage > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
|
||||
|
||||
// Used for splitting up your remaining time into phases, if you want to evenly divide it.
|
||||
#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count
|
||||
#define MC_SPLIT_TICK \
|
||||
if(split_tick_phases > 1){\
|
||||
Master.current_ticklimit = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
|
||||
--split_tick_phases;\
|
||||
} else {\
|
||||
Master.current_ticklimit = original_tick_limit;\
|
||||
}
|
||||
|
||||
// Used to smooth out costs to try and avoid oscillation.
|
||||
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
|
||||
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
|
||||
#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
|
||||
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
|
||||
|
||||
#define START_PROCESSING(Processor, Datum) if (!Datum.isprocessing) {Datum.isprocessing = 1;Processor.processing += Datum}
|
||||
#define STOP_PROCESSING(Processor, Datum) Datum.isprocessing = 0;Processor.processing -= Datum
|
||||
|
||||
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
|
||||
|
||||
//subsystem does not initialize.
|
||||
#define SS_NO_INIT 1
|
||||
|
||||
//subsystem does not fire.
|
||||
// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
|
||||
// (Requires a MC restart to change)
|
||||
#define SS_NO_FIRE 2
|
||||
|
||||
//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
|
||||
// SS_BACKGROUND has its own priority bracket
|
||||
#define SS_BACKGROUND 4
|
||||
|
||||
//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
|
||||
#define SS_NO_TICK_CHECK 8
|
||||
|
||||
//Treat wait as a tick count, not DS, run every wait ticks.
|
||||
// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
|
||||
// (implies all runlevels because of how it works)
|
||||
// (overrides SS_BACKGROUND)
|
||||
// This is designed for basically anything that works as a mini-mc (like SStimer)
|
||||
#define SS_TICKER 16
|
||||
|
||||
//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
|
||||
// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
|
||||
#define SS_KEEP_TIMING 32
|
||||
|
||||
//Calculate its next fire after its fired.
|
||||
// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
|
||||
// This flag overrides SS_KEEP_TIMING
|
||||
#define SS_POST_FIRE_TIMING 64
|
||||
|
||||
//SUBSYSTEM STATES
|
||||
#define SS_IDLE 0 //aint doing shit.
|
||||
#define SS_QUEUED 1 //queued to run
|
||||
#define SS_RUNNING 2 //actively running
|
||||
#define SS_PAUSED 3 //paused by mc_tick_check
|
||||
#define SS_SLEEPING 4 //fire() slept.
|
||||
#define SS_PAUSING 5 //in the middle of pausing
|
||||
|
||||
// Standard way to define a global subsystem, keep boilerplate organized here!
|
||||
#define SUBSYSTEM_DEF(X) var/datum/controller/subsystem/##X/SS##X;\
|
||||
/datum/controller/subsystem/##X/New(){\
|
||||
NEW_SS_GLOBAL(SS##X);\
|
||||
PreInit();\
|
||||
}\
|
||||
/datum/controller/subsystem/##X
|
||||
@@ -1,6 +1,7 @@
|
||||
// Belly Mode Constants
|
||||
#define DM_HOLD "Hold"
|
||||
#define DM_DIGEST "Digest"
|
||||
#define DM_DIGEST_NUMB "Digest (Numbing)"
|
||||
#define DM_HEAL "Heal"
|
||||
#define DM_ABSORB "Absorb"
|
||||
#define DM_TRANSFORM_HAIR_AND_EYES "Transform (Hair and eyes)"
|
||||
@@ -18,7 +19,6 @@
|
||||
#define DM_DRAIN "Drain"
|
||||
#define DM_UNABSORB "Unabsorb"
|
||||
|
||||
|
||||
// Stance for hostile mobs to be in while devouring someone.
|
||||
#define HOSTILE_STANCE_EATING 99
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
|
||||
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
|
||||
//collapsed to percent_of_tick_used * tick_lag
|
||||
#define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag)
|
||||
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(world.tick_usage-starting_tickusage))
|
||||
|
||||
//time of day but automatically adjusts to the server going into the next day within the same round.
|
||||
//for when you need a reliable time number that doesn't depend on byond time.
|
||||
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
|
||||
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
|
||||
@@ -201,3 +201,5 @@
|
||||
#define TSC_MORPH "Morpheus"
|
||||
#define TSC_XION "Xion" // Not really needed but consistancy I guess.
|
||||
#define TSC_GIL "Gilthari"
|
||||
|
||||
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
|
||||
|
||||
@@ -15,3 +15,12 @@
|
||||
#define VANTAG_KILL "vantag_kill"
|
||||
|
||||
#define ANNOUNCER_NAME "Facility PA"
|
||||
|
||||
//For custom species
|
||||
#define STARTING_SPECIES_POINTS 1
|
||||
#define MAX_SPECIES_TRAITS 5
|
||||
|
||||
// Resleeving Mind Record Status
|
||||
#define MR_NORMAL 0
|
||||
#define MR_UNSURE 1
|
||||
#define MR_DEAD 2
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// List indexes for software datum references on mobs
|
||||
// This also controls the order they are displayed in the NIF stat panel
|
||||
//AR Overlays
|
||||
#define NIF_CIVILIAN_AR 1
|
||||
#define NIF_MEDICAL_AR 2
|
||||
#define NIF_SECURITY_AR 3
|
||||
#define NIF_ENGINE_AR 4
|
||||
#define NIF_SCIENCE_AR 5
|
||||
#define NIF_OMNI_AR 6
|
||||
//Misc Vision
|
||||
#define NIF_CORRECTIVE_GLASS 7
|
||||
#define NIF_MESONS 8
|
||||
#define NIF_MATERIAL 9
|
||||
#define NIF_THERMALS 10
|
||||
#define NIF_NIGHTVIS 11
|
||||
#define NIF_UVFILTER 12
|
||||
#define NIF_FLASHPROT 13
|
||||
//Health-related
|
||||
#define NIF_CREWMONITOR 14
|
||||
#define NIF_ORGANIC_HEAL 15
|
||||
#define NIF_SYNTH_HEAL 16
|
||||
#define NIF_AUTOSTASIS 17 //These two are just part of
|
||||
#define NIF_MED_ALARM 18 //medichines right now
|
||||
#define NIF_TOXHEAL 19 //And this, for organics
|
||||
#define NIF_SPAREBREATH 20
|
||||
//Combat Related
|
||||
#define NIF_BRUTEARMOR 21
|
||||
#define NIF_BURNARMOR 22
|
||||
#define NIF_PAINKILLERS 23
|
||||
#define NIF_HARDCLAWS 24
|
||||
#define NIF_HIDDENLASER 25
|
||||
//Other
|
||||
#define NIF_COMMLINK 26
|
||||
#define NIF_APCCHARGE 27
|
||||
#define NIF_PRESSURE 28
|
||||
#define NIF_HEATSINK 29
|
||||
#define NIF_COMPLIANCE 30
|
||||
#define NIF_SIZECHANGE 31
|
||||
#define NIF_SOULCATCHER 32
|
||||
|
||||
// Must be equal to the highest number above
|
||||
#define TOTAL_NIF_SOFTWARE 32
|
||||
|
||||
//////////////////////
|
||||
// NIF flag list hints
|
||||
#define NIF_FLAGS_VISION 1
|
||||
#define NIF_FLAGS_HEALTH 2
|
||||
#define NIF_FLAGS_COMBAT 3
|
||||
#define NIF_FLAGS_OTHER 4
|
||||
|
||||
// NIF flags
|
||||
//Vision
|
||||
#define NIF_V_AR_CIVILIAN 0x1
|
||||
#define NIF_V_AR_MEDICAL 0x2
|
||||
#define NIF_V_AR_SECURITY 0x4
|
||||
#define NIF_V_AR_ENGINE 0x8
|
||||
#define NIF_V_AR_SCIENCE 0x10
|
||||
#define NIF_V_AR_OMNI 0x20
|
||||
#define NIF_V_CORRECTIVE 0x40
|
||||
#define NIF_V_MESONS 0x80
|
||||
#define NIF_V_MATERIAL 0x100
|
||||
#define NIF_V_THERMALS 0x200
|
||||
#define NIF_V_NIGHTVIS 0x400
|
||||
#define NIF_V_UVFILTER 0x800
|
||||
#define NIF_V_FLASHPROT 0x1000
|
||||
|
||||
//Health
|
||||
#define NIF_H_ORGREPAIR 0x1
|
||||
#define NIF_H_SYNTHREPAIR 0x2
|
||||
#define NIF_H_AUTOSTASIS 0x4 //These two are just part of
|
||||
#define NIF_H_ALERTMED 0x8 //medichines right now
|
||||
#define NIF_H_TOXREGEN 0x10
|
||||
#define NIF_H_SPAREBREATH 0x20
|
||||
|
||||
//Combat
|
||||
#define NIF_C_BRUTEARMOR 0x1
|
||||
#define NIF_C_BURNARMOR 0x2
|
||||
#define NIF_C_PAINKILLERS 0x4
|
||||
#define NIF_C_HARDCLAWS 0x8
|
||||
#define NIF_C_HIDELASER 0x10
|
||||
|
||||
//Other
|
||||
#define NIF_O_COMMLINK 0x1
|
||||
#define NIF_O_APCCHARGE 0x2
|
||||
#define NIF_O_PRESSURESEAL 0x4
|
||||
#define NIF_O_HEATSINKS 0x8
|
||||
#define NIF_O_SCMYSELF 0x10 //Soulcatcher stuff
|
||||
#define NIF_O_SCOTHERS 0x20
|
||||
|
||||
///////////////////
|
||||
// applies_to flags
|
||||
#define NIF_ORGANIC 0x1
|
||||
#define NIF_SYNTHETIC 0x2
|
||||
|
||||
/////////////
|
||||
// stat flags
|
||||
#define NIF_WORKING 0
|
||||
#define NIF_POWFAIL 1
|
||||
#define NIF_TEMPFAIL 2
|
||||
#define NIF_INSTALLING 3
|
||||
#define NIF_PREINSTALL 4
|
||||
|
||||
///////////////////
|
||||
// tick_flags flags
|
||||
#define NIF_NEVERTICK 0
|
||||
#define NIF_ALWAYSTICK 1
|
||||
#define NIF_ACTIVETICK 2
|
||||
@@ -0,0 +1,17 @@
|
||||
//defines that give qdel hints. these can be given as a return in destory() or by calling
|
||||
|
||||
#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion.
|
||||
#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory.
|
||||
#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it.
|
||||
#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference.
|
||||
#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste.
|
||||
#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm.
|
||||
//if TESTING is enabled, qdel will call this object's find_references() verb.
|
||||
//defines for the gc_destroyed var
|
||||
|
||||
#define GC_QUEUED_FOR_QUEUING -1
|
||||
#define GC_QUEUED_FOR_HARD_DEL -2
|
||||
#define GC_CURRENTLY_BEING_QDELETED -3
|
||||
|
||||
#define QDELETED(X) (!X || X.gc_destroyed)
|
||||
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
// SS runlevels
|
||||
|
||||
#define RUNLEVEL_INIT 0 // "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set)
|
||||
#define RUNLEVEL_LOBBY 1 // Initial runlevel before setup. Returns to here if setup fails.
|
||||
#define RUNLEVEL_SETUP 2 // While the gamemode setup is running. I.E gameticker.setup()
|
||||
#define RUNLEVEL_GAME 4 // After successful game ticker setup, while the round is running.
|
||||
#define RUNLEVEL_POSTGAME 8 // When round completes but before reboot
|
||||
|
||||
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
|
||||
|
||||
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
|
||||
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
|
||||
|
||||
#define INIT_ORDER_LIGHTING 0
|
||||
@@ -0,0 +1,7 @@
|
||||
#define TICK_LIMIT_RUNNING 80
|
||||
#define TICK_LIMIT_TO_RUN 78
|
||||
#define TICK_LIMIT_MC 70
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT 98
|
||||
|
||||
#define TICK_CHECK ( world.tick_usage > Master.current_ticklimit )
|
||||
#define CHECK_TICK if TICK_CHECK stoplag()
|
||||
+19
-14
@@ -271,20 +271,25 @@
|
||||
var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM)
|
||||
var/list/hearturfs = list()
|
||||
|
||||
for(var/atom/movable/AM in hear)
|
||||
if(ismob(AM))
|
||||
mobs += AM
|
||||
hearturfs += AM.locs[1]
|
||||
else if(isobj(AM))
|
||||
objs += AM
|
||||
hearturfs += AM.locs[1]
|
||||
for(var/thing in hear)
|
||||
if(istype(thing,/obj))
|
||||
objs += thing
|
||||
hearturfs += get_turf(thing)
|
||||
else if(istype(thing,/mob))
|
||||
mobs += thing
|
||||
hearturfs += get_turf(thing)
|
||||
|
||||
//A list of every mob with a client
|
||||
for(var/mob/M in player_list)
|
||||
if(M.loc && M.locs[1] in hearturfs)
|
||||
mobs |= M
|
||||
for(var/mob in player_list)
|
||||
if(isnull(mob)) //VOREStation Add - Fix for a weird bug with nulls in this list
|
||||
player_list -= mob //VOREStation Add
|
||||
continue //VOREStation Add
|
||||
if(get_turf(mob) in hearturfs)
|
||||
mobs |= mob
|
||||
continue
|
||||
|
||||
else if(M.stat == DEAD && !M.forbid_seeing_deadchat)
|
||||
var/mob/M = mob
|
||||
if(M.stat == DEAD && !M.forbid_seeing_deadchat)
|
||||
switch(type)
|
||||
if(1) //Audio messages use ghost_ears
|
||||
if(M.is_preference_enabled(/datum/client_preference/ghost_ears))
|
||||
@@ -294,9 +299,9 @@
|
||||
mobs |= M
|
||||
|
||||
//For objects below the top level who still want to hear
|
||||
for(var/obj/O in listening_objects)
|
||||
if(O.loc && O.locs[1] in hearturfs)
|
||||
objs |= O
|
||||
for(var/obj in listening_objects)
|
||||
if(get_turf(obj) in hearturfs)
|
||||
objs |= obj
|
||||
|
||||
return list("mobs" = mobs, "objs" = objs)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ var/global/list/all_species[0]
|
||||
var/global/list/all_languages[0]
|
||||
var/global/list/language_keys[0] // Table of say codes for all languages
|
||||
var/global/list/whitelisted_species = list("Human") // Species that require a whitelist check.
|
||||
var/global/list/playable_species = list("Human") // A list of ALL playable species, whitelisted, latejoin or otherwise.
|
||||
var/global/list/playable_species = list("Custom Species","Human") // A list of ALL playable species, whitelisted, latejoin or otherwise. //VOREStation Edit - Making sure custom species is obvious.
|
||||
|
||||
var/list/mannequins_
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type
|
||||
var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type
|
||||
var/global/list/negative_traits = list() // Negative custom species traits, indexed by path
|
||||
var/global/list/neutral_traits = list() // Neutral custom species traits, indexed by path
|
||||
var/global/list/positive_traits = list() // Positive custom species traits, indexed by path
|
||||
var/global/list/traits_costs = list() // Just path = cost list, saves time in char setup
|
||||
var/global/list/all_traits = list() // All of 'em at once (same instances)
|
||||
|
||||
//stores numeric player size options indexed by name
|
||||
var/global/list/player_sizes_list = list(
|
||||
@@ -120,4 +125,20 @@ var/global/list/tf_egg_types = list(
|
||||
for(var/path in paths)
|
||||
var/datum/sprite_accessory/tail/instance = new path()
|
||||
tail_styles_list[path] = instance
|
||||
return 1 // Hooks must return 1
|
||||
|
||||
// Custom species traits
|
||||
paths = typesof(/datum/trait) - /datum/trait
|
||||
for(var/path in paths)
|
||||
var/datum/trait/instance = new path()
|
||||
var/cost = instance.cost
|
||||
traits_costs[path] = cost
|
||||
all_traits[path] = instance
|
||||
switch(cost)
|
||||
if(-INFINITY to -0.1)
|
||||
negative_traits[path] = instance
|
||||
if(0)
|
||||
neutral_traits[path] = instance
|
||||
if(0.1 to INFINITY)
|
||||
positive_traits[path] = instance
|
||||
|
||||
return 1 // Hooks must return 1
|
||||
@@ -873,3 +873,22 @@ proc/sort_atoms_by_layer(var/list/atoms)
|
||||
result.Swap(i, gap + i)
|
||||
swapped = 1
|
||||
return result
|
||||
|
||||
// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
|
||||
// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
|
||||
// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
|
||||
|
||||
// Mutable appearances are children of images, just so you know.
|
||||
|
||||
/mutable_appearance/New()
|
||||
..()
|
||||
plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE
|
||||
// And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
|
||||
|
||||
// Helper similar to image()
|
||||
/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
|
||||
var/mutable_appearance/MA = new()
|
||||
MA.icon = icon
|
||||
MA.icon_state = icon_state
|
||||
MA.layer = layer
|
||||
return MA
|
||||
|
||||
@@ -599,3 +599,66 @@ proc/dd_sortedTextList(list/incoming)
|
||||
for(var/path in subtypesof(prototype))
|
||||
L += new path()
|
||||
return L
|
||||
|
||||
//creates every subtype of prototype (excluding prototype) and adds it to list L as a type/instance pair.
|
||||
//if no list/L is provided, one is created.
|
||||
/proc/init_subtypes_assoc(prototype, list/L)
|
||||
if(!istype(L)) L = list()
|
||||
for(var/path in subtypesof(prototype))
|
||||
L[path] = new path()
|
||||
return L
|
||||
|
||||
//Move a single element from position fromIndex within a list, to position toIndex
|
||||
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
|
||||
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
|
||||
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
|
||||
//fromIndex and toIndex must be in the range [1,L.len+1]
|
||||
//This will preserve associations ~Carnie
|
||||
/proc/moveElement(list/L, fromIndex, toIndex)
|
||||
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
|
||||
return
|
||||
if(fromIndex > toIndex)
|
||||
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
|
||||
|
||||
L.Insert(toIndex, null)
|
||||
L.Swap(fromIndex, toIndex)
|
||||
L.Cut(fromIndex, fromIndex+1)
|
||||
|
||||
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
|
||||
//Same as moveElement but for ranges of elements
|
||||
//This will preserve associations ~Carnie
|
||||
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
|
||||
var/distance = abs(toIndex - fromIndex)
|
||||
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
|
||||
if(fromIndex <= toIndex)
|
||||
return //no need to move
|
||||
fromIndex += len //we want to shift left instead of right
|
||||
|
||||
for(var/i=0, i<distance, ++i)
|
||||
L.Insert(fromIndex, null)
|
||||
L.Swap(fromIndex, toIndex)
|
||||
L.Cut(toIndex, toIndex+1)
|
||||
else
|
||||
if(fromIndex > toIndex)
|
||||
fromIndex += len
|
||||
|
||||
for(var/i=0, i<len, ++i)
|
||||
L.Insert(toIndex, null)
|
||||
L.Swap(fromIndex, toIndex)
|
||||
L.Cut(fromIndex, fromIndex+1)
|
||||
|
||||
//replaces reverseList ~Carnie
|
||||
/proc/reverseRange(list/L, start=1, end=0)
|
||||
if(L.len)
|
||||
start = start % L.len
|
||||
end = end % (L.len+1)
|
||||
if(start <= 0)
|
||||
start += L.len
|
||||
if(end <= 0)
|
||||
end += L.len + 1
|
||||
|
||||
--end
|
||||
while(start < end)
|
||||
L.Swap(start++,end--)
|
||||
|
||||
return L
|
||||
|
||||
@@ -180,6 +180,8 @@
|
||||
return "[..()] ([ckey])"
|
||||
|
||||
/proc/log_info_line(var/datum/d)
|
||||
if(!d)
|
||||
return "*null*"
|
||||
if(!istype(d))
|
||||
return
|
||||
return json_encode(d)
|
||||
return d.log_info_line()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/proc/log_nsay(text,inside)
|
||||
if (config.log_say)
|
||||
diary << "\[[time_stamp()]]NSAY (NIF:[inside]): [text][log_end]"
|
||||
|
||||
/proc/log_nme(text,inside)
|
||||
if (config.log_emote)
|
||||
diary << "\[[time_stamp()]]NME (NIF:[inside]): [text][log_end]"
|
||||
|
||||
/proc/log_subtle(text)
|
||||
if (config.log_emote)
|
||||
diary << "\[[time_stamp()]]SUBTLE: [text][log_end]"
|
||||
@@ -228,7 +228,7 @@ Proc for attack log creation, because really why not
|
||||
. = 0
|
||||
break
|
||||
|
||||
if(target_loc && (!target || target_loc != target.loc))
|
||||
if(target_loc && (QDELETED(target) || target_loc != target.loc))
|
||||
. = 0
|
||||
break
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//TimSort interface
|
||||
/proc/sortTim(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
|
||||
if(L && L.len >= 2)
|
||||
fromIndex = fromIndex % L.len
|
||||
toIndex = toIndex % (L.len+1)
|
||||
if(fromIndex <= 0)
|
||||
fromIndex += L.len
|
||||
if(toIndex <= 0)
|
||||
toIndex += L.len + 1
|
||||
|
||||
sortInstance.L = L
|
||||
sortInstance.cmp = cmp
|
||||
sortInstance.associative = associative
|
||||
|
||||
sortInstance.timSort(fromIndex, toIndex)
|
||||
|
||||
return L
|
||||
@@ -0,0 +1,656 @@
|
||||
//These are macros used to reduce on proc calls
|
||||
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
|
||||
|
||||
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
|
||||
//Should be a power of 2
|
||||
#define MIN_MERGE 32
|
||||
|
||||
//When we get into galloping mode, we stay there until both runs win less often than MIN_GALLOP consecutive times.
|
||||
#define MIN_GALLOP 7
|
||||
|
||||
//This is a global instance to allow much of this code to be reused. The interfaces are kept separately
|
||||
var/datum/sortInstance/sortInstance = new()
|
||||
/datum/sortInstance
|
||||
//The array being sorted.
|
||||
var/list/L
|
||||
|
||||
//The comparator proc-reference
|
||||
var/cmp = /proc/cmp_numeric_asc
|
||||
|
||||
//whether we are sorting list keys (0: L[i]) or associated values (1: L[L[i]])
|
||||
var/associative = 0
|
||||
|
||||
//This controls when we get *into* galloping mode. It is initialized to MIN_GALLOP.
|
||||
//The mergeLo and mergeHi methods nudge it higher for random data, and lower for highly structured data.
|
||||
var/minGallop = MIN_GALLOP
|
||||
|
||||
//Stores information regarding runs yet to be merged.
|
||||
//Run i starts at runBase[i] and extends for runLen[i] elements.
|
||||
//runBase[i] + runLen[i] == runBase[i+1]
|
||||
//var/stackSize
|
||||
var/list/runBases = list()
|
||||
var/list/runLens = list()
|
||||
|
||||
|
||||
/datum/sortInstance/proc/timSort(start, end)
|
||||
runBases.Cut()
|
||||
runLens.Cut()
|
||||
|
||||
var/remaining = end - start
|
||||
|
||||
//If array is small, do a 'mini-TimSort' with no merges
|
||||
if(remaining < MIN_MERGE)
|
||||
var/initRunLen = countRunAndMakeAscending(start, end)
|
||||
binarySort(start, end, start+initRunLen)
|
||||
return
|
||||
|
||||
//March over the array finding natural runs
|
||||
//Extend any short natural runs to runs of length minRun
|
||||
var/minRun = minRunLength(remaining)
|
||||
|
||||
do
|
||||
//identify next run
|
||||
var/runLen = countRunAndMakeAscending(start, end)
|
||||
|
||||
//if run is short, extend to min(minRun, remaining)
|
||||
if(runLen < minRun)
|
||||
var/force = (remaining <= minRun) ? remaining : minRun
|
||||
|
||||
binarySort(start, start+force, start+runLen)
|
||||
runLen = force
|
||||
|
||||
//add data about run to queue
|
||||
runBases.Add(start)
|
||||
runLens.Add(runLen)
|
||||
|
||||
//maybe merge
|
||||
mergeCollapse()
|
||||
|
||||
//Advance to find next run
|
||||
start += runLen
|
||||
remaining -= runLen
|
||||
|
||||
while(remaining > 0)
|
||||
|
||||
|
||||
//Merge all remaining runs to complete sort
|
||||
//ASSERT(start == end)
|
||||
mergeForceCollapse();
|
||||
//ASSERT(runBases.len == 1)
|
||||
|
||||
//reset minGallop, for successive calls
|
||||
minGallop = MIN_GALLOP
|
||||
|
||||
return L
|
||||
|
||||
/*
|
||||
Sorts the specified portion of the specified array using a binary
|
||||
insertion sort. This is the best method for sorting small numbers
|
||||
of elements. It requires O(n log n) compares, but O(n^2) data
|
||||
movement (worst case).
|
||||
|
||||
If the initial part of the specified range is already sorted,
|
||||
this method can take advantage of it: the method assumes that the
|
||||
elements in range [lo,start) are already sorted
|
||||
|
||||
lo the index of the first element in the range to be sorted
|
||||
hi the index after the last element in the range to be sorted
|
||||
start the index of the first element in the range that is not already known to be sorted
|
||||
*/
|
||||
/datum/sortInstance/proc/binarySort(lo, hi, start)
|
||||
//ASSERT(lo <= start && start <= hi)
|
||||
if(start <= lo)
|
||||
start = lo + 1
|
||||
|
||||
for(,start < hi, ++start)
|
||||
var/pivot = fetchElement(L,start)
|
||||
|
||||
//set left and right to the index where pivot belongs
|
||||
var/left = lo
|
||||
var/right = start
|
||||
//ASSERT(left <= right)
|
||||
|
||||
//[lo, left) elements <= pivot < [right, start) elements
|
||||
//in other words, find where the pivot element should go using bisection search
|
||||
while(left < right)
|
||||
var/mid = (left + right) >> 1 //round((left+right)/2)
|
||||
if(call(cmp)(fetchElement(L,mid), pivot) > 0)
|
||||
right = mid
|
||||
else
|
||||
left = mid+1
|
||||
|
||||
//ASSERT(left == right)
|
||||
moveElement(L, start, left) //move pivot element to correct location in the sorted range
|
||||
|
||||
/*
|
||||
Returns the length of the run beginning at the specified position and reverses the run if it is back-to-front
|
||||
|
||||
A run is the longest ascending sequence with:
|
||||
a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
or the longest descending sequence with:
|
||||
a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
|
||||
For its intended use in a stable mergesort, the strictness of the
|
||||
definition of "descending" is needed so that the call can safely
|
||||
reverse a descending sequence without violating stability.
|
||||
*/
|
||||
/datum/sortInstance/proc/countRunAndMakeAscending(lo, hi)
|
||||
//ASSERT(lo < hi)
|
||||
|
||||
var/runHi = lo + 1
|
||||
if(runHi >= hi)
|
||||
return 1
|
||||
|
||||
var/last = fetchElement(L,lo)
|
||||
var/current = fetchElement(L,runHi++)
|
||||
|
||||
if(call(cmp)(current, last) < 0)
|
||||
while(runHi < hi)
|
||||
last = current
|
||||
current = fetchElement(L,runHi)
|
||||
if(call(cmp)(current, last) >= 0)
|
||||
break
|
||||
++runHi
|
||||
reverseRange(L, lo, runHi)
|
||||
else
|
||||
while(runHi < hi)
|
||||
last = current
|
||||
current = fetchElement(L,runHi)
|
||||
if(call(cmp)(current, last) < 0)
|
||||
break
|
||||
++runHi
|
||||
|
||||
return runHi - lo
|
||||
|
||||
//Returns the minimum acceptable run length for an array of the specified length.
|
||||
//Natural runs shorter than this will be extended with binarySort
|
||||
/datum/sortInstance/proc/minRunLength(n)
|
||||
//ASSERT(n >= 0)
|
||||
var/r = 0 //becomes 1 if any bits are shifted off
|
||||
while(n >= MIN_MERGE)
|
||||
r |= (n & 1)
|
||||
n >>= 1
|
||||
return n + r
|
||||
|
||||
//Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
|
||||
// runLen[i-3] > runLen[i-2] + runLen[i-1]
|
||||
// runLen[i-2] > runLen[i-1]
|
||||
//This method is called each time a new run is pushed onto the stack.
|
||||
//So the invariants are guaranteed to hold for i<stackSize upon entry to the method
|
||||
/datum/sortInstance/proc/mergeCollapse()
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
|
||||
if(runLens[n-1] < runLens[n+1])
|
||||
--n
|
||||
mergeAt(n)
|
||||
else if(runLens[n] <= runLens[n+1])
|
||||
mergeAt(n)
|
||||
else
|
||||
break //Invariant is established
|
||||
|
||||
|
||||
//Merges all runs on the stack until only one remains.
|
||||
//Called only once, to finalise the sort
|
||||
/datum/sortInstance/proc/mergeForceCollapse()
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] < runLens[n+1])
|
||||
--n
|
||||
mergeAt(n)
|
||||
|
||||
|
||||
//Merges the two consecutive runs at stack indices i and i+1
|
||||
//Run i must be the penultimate or antepenultimate run on the stack
|
||||
//In other words, i must be equal to stackSize-2 or stackSize-3
|
||||
/datum/sortInstance/proc/mergeAt(i)
|
||||
//ASSERT(runBases.len >= 2)
|
||||
//ASSERT(i >= 1)
|
||||
//ASSERT(i == runBases.len - 1 || i == runBases.len - 2)
|
||||
|
||||
var/base1 = runBases[i]
|
||||
var/base2 = runBases[i+1]
|
||||
var/len1 = runLens[i]
|
||||
var/len2 = runLens[i+1]
|
||||
|
||||
//ASSERT(len1 > 0 && len2 > 0)
|
||||
//ASSERT(base1 + len1 == base2)
|
||||
|
||||
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
|
||||
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
runLens[i] += runLens[i+1]
|
||||
runLens.Cut(i+1, i+2)
|
||||
runBases.Cut(i+1, i+2)
|
||||
|
||||
|
||||
//Find where the first element of run2 goes in run1.
|
||||
//Prior elements in run1 can be ignored (because they're already in place)
|
||||
var/k = gallopRight(fetchElement(L,base2), base1, len1, 0)
|
||||
//ASSERT(k >= 0)
|
||||
base1 += k
|
||||
len1 -= k
|
||||
if(len1 == 0)
|
||||
return
|
||||
|
||||
//Find where the last element of run1 goes in run2.
|
||||
//Subsequent elements in run2 can be ignored (because they're already in place)
|
||||
len2 = gallopLeft(fetchElement(L,base1 + len1 - 1), base2, len2, len2-1)
|
||||
//ASSERT(len2 >= 0)
|
||||
if(len2 == 0)
|
||||
return
|
||||
|
||||
//Merge remaining runs, using tmp array with min(len1, len2) elements
|
||||
if(len1 <= len2)
|
||||
mergeLo(base1, len1, base2, len2)
|
||||
else
|
||||
mergeHi(base1, len1, base2, len2)
|
||||
|
||||
|
||||
/*
|
||||
Locates the position to insert key within the specified sorted range
|
||||
If the range contains elements equal to key, this will return the index of the LEFTMOST of those elements
|
||||
|
||||
key the element to be inserted into the sorted range
|
||||
base the index of the first element of the sorted range
|
||||
len the length of the sorted range, must be greater than 0
|
||||
hint the offset from base at which to begin the search, such that 0 <= hint < len; i.e. base <= hint < base+hint
|
||||
|
||||
Returns the index at which to insert element 'key'
|
||||
*/
|
||||
/datum/sortInstance/proc/gallopLeft(key, base, len, hint)
|
||||
//ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
|
||||
var/lastOffset = 0
|
||||
var/offset = 1
|
||||
if(call(cmp)(key, fetchElement(L,base+hint)) > 0)
|
||||
var/maxOffset = len - hint
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) > 0)
|
||||
lastOffset = offset
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
|
||||
lastOffset += hint
|
||||
offset += hint
|
||||
|
||||
else
|
||||
var/maxOffset = hint + 1
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) <= 0)
|
||||
lastOffset = offset
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
|
||||
var/temp = lastOffset
|
||||
lastOffset = hint - offset
|
||||
offset = hint - temp
|
||||
|
||||
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
|
||||
|
||||
//Now L[base+lastOffset] < key <= L[base+offset], so key belongs somewhere to the right of lastOffset but no farther than
|
||||
//offset. Do a binary search with invariant L[base+lastOffset-1] < key <= L[base+offset]
|
||||
++lastOffset
|
||||
while(lastOffset < offset)
|
||||
var/m = lastOffset + ((offset - lastOffset) >> 1)
|
||||
|
||||
if(call(cmp)(key, fetchElement(L,base+m)) > 0)
|
||||
lastOffset = m + 1
|
||||
else
|
||||
offset = m
|
||||
|
||||
//ASSERT(lastOffset == offset)
|
||||
return offset
|
||||
|
||||
/**
|
||||
* Like gallopLeft, except that if the range contains an element equal to
|
||||
* key, gallopRight returns the index after the rightmost equal element.
|
||||
*
|
||||
* @param key the key whose insertion point to search for
|
||||
* @param a the array in which to search
|
||||
* @param base the index of the first element in the range
|
||||
* @param len the length of the range; must be > 0
|
||||
* @param hint the index at which to begin the search, 0 <= hint < n.
|
||||
* The closer hint is to the result, the faster this method will run.
|
||||
* @param c the comparator used to order the range, and to search
|
||||
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
|
||||
*/
|
||||
/datum/sortInstance/proc/gallopRight(key, base, len, hint)
|
||||
//ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
|
||||
var/offset = 1
|
||||
var/lastOffset = 0
|
||||
if(call(cmp)(key, fetchElement(L,base+hint)) < 0) //key <= L[base+hint]
|
||||
var/maxOffset = hint + 1 //therefore we want to insert somewhere in the range [base,base+hint] = [base+,base+(hint+1))
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) < 0) //we are iterating backwards
|
||||
lastOffset = offset
|
||||
offset = (offset << 1) + 1 //1 3 7 15
|
||||
//if(offset <= 0) //int overflow, not an issue here since we are using floats
|
||||
// offset = maxOffset
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
|
||||
var/temp = lastOffset
|
||||
lastOffset = hint - offset
|
||||
offset = hint - temp
|
||||
|
||||
else //key > L[base+hint]
|
||||
var/maxOffset = len - hint //therefore we want to insert somewhere in the range (base+hint,base+len) = [base+hint+1, base+hint+(len-hint))
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) >= 0)
|
||||
lastOffset = offset
|
||||
offset = (offset << 1) + 1
|
||||
//if(offset <= 0) //int overflow, not an issue here since we are using floats
|
||||
// offset = maxOffset
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
|
||||
lastOffset += hint
|
||||
offset += hint
|
||||
|
||||
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
|
||||
|
||||
++lastOffset
|
||||
while(lastOffset < offset)
|
||||
var/m = lastOffset + ((offset - lastOffset) >> 1)
|
||||
|
||||
if(call(cmp)(key, fetchElement(L,base+m)) < 0) //key <= L[base+m]
|
||||
offset = m
|
||||
else //key > L[base+m]
|
||||
lastOffset = m + 1
|
||||
|
||||
//ASSERT(lastOffset == offset)
|
||||
|
||||
return offset
|
||||
|
||||
|
||||
//Merges two adjacent runs in-place in a stable fashion.
|
||||
//For performance this method should only be called when len1 <= len2!
|
||||
/datum/sortInstance/proc/mergeLo(base1, len1, base2, len2)
|
||||
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
|
||||
var/cursor1 = base1
|
||||
var/cursor2 = base2
|
||||
|
||||
//degenerate cases
|
||||
if(len2 == 1)
|
||||
moveElement(L, cursor2, cursor1)
|
||||
return
|
||||
|
||||
if(len1 == 1)
|
||||
moveElement(L, cursor1, cursor2+len2)
|
||||
return
|
||||
|
||||
|
||||
//Move first element of second run
|
||||
moveElement(L, cursor2++, cursor1++)
|
||||
--len2
|
||||
|
||||
outer:
|
||||
while(1)
|
||||
var/count1 = 0 //# of times in a row that first run won
|
||||
var/count2 = 0 // " " " " " " second run won
|
||||
|
||||
//do the straightfoward thin until one run starts winning consistently
|
||||
|
||||
do
|
||||
//ASSERT(len1 > 1 && len2 > 0)
|
||||
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
|
||||
moveElement(L, cursor2++, cursor1++)
|
||||
--len2
|
||||
|
||||
++count2
|
||||
count1 = 0
|
||||
|
||||
if(len2 == 0)
|
||||
break outer
|
||||
else
|
||||
++cursor1
|
||||
|
||||
++count1
|
||||
count2 = 0
|
||||
|
||||
if(--len1 == 1)
|
||||
break outer
|
||||
|
||||
while((count1 | count2) < minGallop)
|
||||
|
||||
|
||||
//one run is winning consistently so galloping may provide huge benifits
|
||||
//so try galloping, until such time as the run is no longer consistently winning
|
||||
do
|
||||
//ASSERT(len1 > 1 && len2 > 0)
|
||||
|
||||
count1 = gallopRight(fetchElement(L,cursor2), cursor1, len1, 0)
|
||||
if(count1)
|
||||
cursor1 += count1
|
||||
len1 -= count1
|
||||
|
||||
if(len1 <= 1)
|
||||
break outer
|
||||
|
||||
moveElement(L, cursor2, cursor1)
|
||||
++cursor2
|
||||
++cursor1
|
||||
if(--len2 == 0)
|
||||
break outer
|
||||
|
||||
count2 = gallopLeft(fetchElement(L,cursor1), cursor2, len2, 0)
|
||||
if(count2)
|
||||
moveRange(L, cursor2, cursor1, count2)
|
||||
|
||||
cursor2 += count2
|
||||
cursor1 += count2
|
||||
len2 -= count2
|
||||
|
||||
if(len2 == 0)
|
||||
break outer
|
||||
|
||||
++cursor1
|
||||
if(--len1 == 1)
|
||||
break outer
|
||||
|
||||
--minGallop
|
||||
|
||||
while((count1|count2) > MIN_GALLOP)
|
||||
|
||||
if(minGallop < 0)
|
||||
minGallop = 0
|
||||
minGallop += 2; // Penalize for leaving gallop mode
|
||||
|
||||
|
||||
if(len1 == 1)
|
||||
//ASSERT(len2 > 0)
|
||||
moveElement(L, cursor1, cursor2+len2)
|
||||
|
||||
//else
|
||||
//ASSERT(len2 == 0)
|
||||
//ASSERT(len1 > 1)
|
||||
|
||||
|
||||
/datum/sortInstance/proc/mergeHi(base1, len1, base2, len2)
|
||||
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
|
||||
var/cursor1 = base1 + len1 - 1 //start at end of sublists
|
||||
var/cursor2 = base2 + len2 - 1
|
||||
|
||||
//degenerate cases
|
||||
if(len2 == 1)
|
||||
moveElement(L, base2, base1)
|
||||
return
|
||||
|
||||
if(len1 == 1)
|
||||
moveElement(L, base1, cursor2+1)
|
||||
return
|
||||
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
outer:
|
||||
while(1)
|
||||
var/count1 = 0 //# of times in a row that first run won
|
||||
var/count2 = 0 // " " " " " " second run won
|
||||
|
||||
//do the straightfoward thing until one run starts winning consistently
|
||||
do
|
||||
//ASSERT(len1 > 0 && len2 > 1)
|
||||
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
++count1
|
||||
count2 = 0
|
||||
|
||||
if(len1 == 0)
|
||||
break outer
|
||||
else
|
||||
--cursor2
|
||||
--len2
|
||||
|
||||
++count2
|
||||
count1 = 0
|
||||
|
||||
if(len2 == 1)
|
||||
break outer
|
||||
while((count1 | count2) < minGallop)
|
||||
|
||||
//one run is winning consistently so galloping may provide huge benifits
|
||||
//so try galloping, until such time as the run is no longer consistently winning
|
||||
do
|
||||
//ASSERT(len1 > 0 && len2 > 1)
|
||||
|
||||
count1 = len1 - gallopRight(fetchElement(L,cursor2), base1, len1, len1-1) //should cursor1 be base1?
|
||||
if(count1)
|
||||
cursor1 -= count1
|
||||
|
||||
moveRange(L, cursor1+1, cursor2+1, count1) //cursor1+1 == cursor2 by definition
|
||||
|
||||
cursor2 -= count1
|
||||
len1 -= count1
|
||||
|
||||
if(len1 == 0)
|
||||
break outer
|
||||
|
||||
--cursor2
|
||||
|
||||
if(--len2 == 1)
|
||||
break outer
|
||||
|
||||
count2 = len2 - gallopLeft(fetchElement(L,cursor1), cursor1+1, len2, len2-1)
|
||||
if(count2)
|
||||
cursor2 -= count2
|
||||
len2 -= count2
|
||||
|
||||
if(len2 <= 1)
|
||||
break outer
|
||||
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
if(len1 == 0)
|
||||
break outer
|
||||
|
||||
--minGallop
|
||||
while((count1|count2) > MIN_GALLOP)
|
||||
|
||||
if(minGallop < 0)
|
||||
minGallop = 0
|
||||
minGallop += 2 // Penalize for leaving gallop mode
|
||||
|
||||
if(len2 == 1)
|
||||
//ASSERT(len1 > 0)
|
||||
|
||||
cursor1 -= len1
|
||||
moveRange(L, cursor1+1, cursor2+1, len1)
|
||||
|
||||
//else
|
||||
//ASSERT(len1 == 0)
|
||||
//ASSERT(len2 > 0)
|
||||
|
||||
|
||||
/datum/sortInstance/proc/mergeSort(start, end)
|
||||
var/remaining = end - start
|
||||
|
||||
//If array is small, do an insertion sort
|
||||
if(remaining < MIN_MERGE)
|
||||
//var/initRunLen = countRunAndMakeAscending(start, end)
|
||||
binarySort(start, end, start/*+initRunLen*/)
|
||||
return
|
||||
|
||||
var/minRun = minRunLength(remaining)
|
||||
|
||||
do
|
||||
var/runLen = (remaining <= minRun) ? remaining : minRun
|
||||
|
||||
binarySort(start, start+runLen, start)
|
||||
|
||||
//add data about run to queue
|
||||
runBases.Add(start)
|
||||
runLens.Add(runLen)
|
||||
|
||||
//Advance to find next run
|
||||
start += runLen
|
||||
remaining -= runLen
|
||||
|
||||
while(remaining > 0)
|
||||
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
|
||||
if(runLens[n-1] < runLens[n+1])
|
||||
--n
|
||||
mergeAt2(n)
|
||||
else if(runLens[n] <= runLens[n+1])
|
||||
mergeAt2(n)
|
||||
else
|
||||
break //Invariant is established
|
||||
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] < runLens[n+1])
|
||||
--n
|
||||
mergeAt2(n)
|
||||
|
||||
return L
|
||||
|
||||
/datum/sortInstance/proc/mergeAt2(i)
|
||||
var/cursor1 = runBases[i]
|
||||
var/cursor2 = runBases[i+1]
|
||||
|
||||
var/end1 = cursor1+runLens[i]
|
||||
var/end2 = cursor2+runLens[i+1]
|
||||
|
||||
var/val1 = fetchElement(L,cursor1)
|
||||
var/val2 = fetchElement(L,cursor2)
|
||||
|
||||
while(1)
|
||||
if(call(cmp)(val1,val2) < 0)
|
||||
if(++cursor1 >= end1)
|
||||
break
|
||||
val1 = fetchElement(L,cursor1)
|
||||
else
|
||||
moveElement(L,cursor2,cursor1)
|
||||
|
||||
++cursor2
|
||||
if(++cursor2 >= end2)
|
||||
break
|
||||
++end1
|
||||
++cursor1
|
||||
//if(++cursor1 >= end1)
|
||||
// break
|
||||
|
||||
val2 = fetchElement(L,cursor2)
|
||||
|
||||
|
||||
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
|
||||
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
runLens[i] += runLens[i+1]
|
||||
runLens.Cut(i+1, i+2)
|
||||
runBases.Cut(i+1, i+2)
|
||||
|
||||
#undef MIN_GALLOP
|
||||
#undef MIN_MERGE
|
||||
|
||||
#undef fetchElement
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Comparators for use with /datum/sortInstance (or wherever you want)
|
||||
// They should return negative, zero, or positive numbers for a < b, a == b, and a > b respectively.
|
||||
//
|
||||
|
||||
// Sorts numeric ascending
|
||||
/proc/cmp_numeric_asc(a,b)
|
||||
return a - b
|
||||
|
||||
// Sorts subsystems alphabetically
|
||||
/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return sorttext(b.name, a.name)
|
||||
|
||||
// Sorts subsystems by init_order
|
||||
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return b.init_order - a.init_order
|
||||
|
||||
// Sorts subsystems by priority
|
||||
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return a.priority - b.priority
|
||||
+21
-1
@@ -100,4 +100,24 @@ var/round_start_time = 0
|
||||
|
||||
/hook/startup/proc/set_roundstart_hour()
|
||||
roundstart_hour = pick(2,7,12,17)
|
||||
return 1
|
||||
return 1
|
||||
|
||||
/var/midnight_rollovers = 0
|
||||
/var/rollovercheck_last_timeofday = 0
|
||||
/proc/update_midnight_rollover()
|
||||
if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
|
||||
return midnight_rollovers++
|
||||
return midnight_rollovers
|
||||
|
||||
//Increases delay as the server gets more overloaded,
|
||||
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
|
||||
#define DELTA_CALC max(((max(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta,1)), 1)
|
||||
|
||||
/proc/stoplag()
|
||||
. = 0
|
||||
var/i = 1
|
||||
do
|
||||
. += round(i*DELTA_CALC)
|
||||
sleep(i*world.tick_lag*DELTA_CALC)
|
||||
i *= 2
|
||||
while (world.tick_usage > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
|
||||
|
||||
@@ -1157,12 +1157,22 @@ proc/is_hot(obj/item/W as obj)
|
||||
istype(W, /obj/item/weapon/surgical/bonesetter)
|
||||
)
|
||||
|
||||
//check if mob is lying down on something we can operate him on.
|
||||
// check if mob is lying down on something we can operate him on.
|
||||
// The RNG with table/rollerbeds comes into play in do_surgery() so that fail_step() can be used instead.
|
||||
/proc/can_operate(mob/living/carbon/M)
|
||||
return (M.lying && \
|
||||
locate(/obj/machinery/optable, M.loc) || \
|
||||
(locate(/obj/structure/bed/roller, M.loc) && prob(75)) || \
|
||||
(locate(/obj/structure/table/, M.loc) && prob(66)))
|
||||
return M.lying
|
||||
|
||||
// Returns an instance of a valid surgery surface.
|
||||
/mob/living/proc/get_surgery_surface()
|
||||
if(!lying)
|
||||
return null // Not lying down means no surface.
|
||||
var/obj/surface = null
|
||||
for(var/obj/O in loc) // Looks for the best surface.
|
||||
if(O.surgery_odds)
|
||||
if(!surface || surface.surgery_odds < O)
|
||||
surface = O
|
||||
if(surface)
|
||||
return surface
|
||||
|
||||
/proc/reverse_direction(var/dir)
|
||||
switch(dir)
|
||||
@@ -1254,6 +1264,10 @@ var/mob/dview/dview_mob = new
|
||||
if(!center)
|
||||
return
|
||||
|
||||
if(!dview_mob) //VOREStation Add - Emergency Backup
|
||||
dview_mob = new()
|
||||
WARNING("dview mob was lost, and had to be recreated!")
|
||||
|
||||
dview_mob.loc = center
|
||||
|
||||
dview_mob.see_invisible = invis_flags
|
||||
@@ -1284,6 +1298,13 @@ var/mob/dview/dview_mob = new
|
||||
else
|
||||
living_mob_list -= src
|
||||
|
||||
/mob/dview/Destroy(var/force)
|
||||
crash_with("Attempt to delete the dview_mob: [log_info_line(src)]")
|
||||
if (!force)
|
||||
return QDEL_HINT_LETMELIVE
|
||||
global.dview_mob = new
|
||||
return ..()
|
||||
|
||||
// call to generate a stack trace and print to runtime logs
|
||||
/proc/crash_with(msg)
|
||||
CRASH(msg)
|
||||
|
||||
@@ -49,5 +49,38 @@
|
||||
#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
|
||||
|
||||
#define to_chat(target, message) target << message
|
||||
#define to_world(message) world << message
|
||||
#define to_world_log(message) world.log << message
|
||||
// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later.
|
||||
#define log_world(message) world.log << message
|
||||
|
||||
|
||||
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
|
||||
|
||||
#define qdel_null_list(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
|
||||
|
||||
#define qdel_null(x) if(x) { qdel(x) ; x = null }
|
||||
|
||||
#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") }
|
||||
|
||||
// Helper macros to aid in optimizing lazy instantiation of lists.
|
||||
// All of these are null-safe, you can use them without knowing if the list var is initialized yet
|
||||
|
||||
//Picks from the list, with some safeties, and returns the "default" arg if it fails
|
||||
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
|
||||
// Ensures L is initailized after this point
|
||||
#define LAZYINITLIST(L) if (!L) L = list()
|
||||
// Sets a L back to null iff it is empty
|
||||
#define UNSETEMPTY(L) if (L && !L.len) L = null
|
||||
// Removes I from list L, and sets I to null if it is now empty
|
||||
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
|
||||
// Adds I to L, initalizing I if necessary
|
||||
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
|
||||
// Reads I from L safely - Works with both associative and traditional lists.
|
||||
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
|
||||
// Reads the length of L, returning 0 if null
|
||||
#define LAZYLEN(L) length(L)
|
||||
// Null-safe L.Cut()
|
||||
#define LAZYCLEARLIST(L) if(L) L.Cut()
|
||||
// Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression.
|
||||
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
recieving object instead, so that's the default action. This allows you to drag
|
||||
almost anything into a trash can.
|
||||
*/
|
||||
|
||||
/atom/proc/CanMouseDrop(atom/over, var/mob/user = usr)
|
||||
if(!user || !over)
|
||||
return FALSE
|
||||
if(user.incapacitated())
|
||||
return FALSE
|
||||
if(!src.Adjacent(user) || !over.Adjacent(user))
|
||||
return FALSE // should stop you from dragging through windows
|
||||
return TRUE
|
||||
|
||||
/atom/MouseDrop(atom/over)
|
||||
if(!usr || !over) return
|
||||
if(!Adjacent(usr) || !over.Adjacent(usr)) return // should stop you from dragging through windows
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
message_admins("ERROR: ability_master's New() was not given an owner argument. This is a bug.")
|
||||
|
||||
/obj/screen/movable/ability_master/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
//Get rid of the ability objects.
|
||||
remove_all_abilities()
|
||||
ability_objects.Cut()
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
/datum/action/Destroy()
|
||||
if(owner)
|
||||
Remove(owner)
|
||||
target = null
|
||||
qdel_null(button)
|
||||
return ..()
|
||||
|
||||
/datum/action/proc/Grant(mob/living/T)
|
||||
if(owner)
|
||||
@@ -45,7 +48,7 @@
|
||||
if(button)
|
||||
if(T.client)
|
||||
T.client.screen -= button
|
||||
del(button)
|
||||
qdel_null(button)
|
||||
T.actions.Remove(src)
|
||||
T.update_action_buttons()
|
||||
owner = null
|
||||
|
||||
@@ -169,7 +169,7 @@ datum/hud/New(mob/owner)
|
||||
..()
|
||||
|
||||
/datum/hud/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
grab_intent = null
|
||||
hurt_intent = null
|
||||
disarm_intent = null
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
var/obj/item/owner
|
||||
|
||||
/obj/screen/item_action/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
owner = null
|
||||
|
||||
/obj/screen/item_action/Click()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var/mob/spell_holder
|
||||
|
||||
/obj/screen/movable/spell_master/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
for(var/obj/screen/spell/spells in spell_objects)
|
||||
spells.spellmaster = null
|
||||
spell_objects.Cut()
|
||||
@@ -156,7 +156,7 @@
|
||||
var/icon/last_charged_icon
|
||||
|
||||
/obj/screen/spell/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
spell = null
|
||||
last_charged_icon = null
|
||||
if(spellmaster)
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
* Config vars
|
||||
*/
|
||||
// Process name
|
||||
var/name
|
||||
|
||||
// Process schedule interval
|
||||
// This controls how often the process would run under ideal conditions.
|
||||
@@ -388,6 +387,6 @@
|
||||
exceptions[eid] = 0
|
||||
|
||||
/datum/controller/process/proc/catchBadType(var/datum/caught)
|
||||
if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed))
|
||||
if(isnull(caught) || !istype(caught) || QDELETED(caught))
|
||||
return // Only bother with types we can identify and that don't belong
|
||||
catchException("Type [caught.type] does not belong in process' queue")
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
// The time a datum was destroyed by the GC, or null if it hasn't been
|
||||
/datum/var/gcDestroyed
|
||||
|
||||
#define GC_COLLECTIONS_PER_RUN 300
|
||||
#define GC_COLLECTION_TIMEOUT (30 SECONDS)
|
||||
#define GC_FORCE_DEL_PER_RUN 30
|
||||
|
||||
var/datum/controller/process/garbage_collector/garbage_collector
|
||||
var/list/delayed_garbage = list()
|
||||
|
||||
/datum/controller/process/garbage_collector
|
||||
var/garbage_collect = 1 // Whether or not to actually do work
|
||||
var/total_dels = 0 // number of total del()'s
|
||||
var/tick_dels = 0 // number of del()'s we've done this tick
|
||||
var/soft_dels = 0
|
||||
var/hard_dels = 0 // number of hard dels in total
|
||||
var/list/destroyed = list() // list of refID's of things that should be garbage collected
|
||||
// refID's are associated with the time at which they time out and need to be manually del()
|
||||
// we do this so we aren't constantly locating them and preventing them from being gc'd
|
||||
|
||||
var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
|
||||
/datum/controller/process/garbage_collector/setup()
|
||||
name = "garbage"
|
||||
schedule_interval = 5 SECONDS
|
||||
start_delay = 3
|
||||
|
||||
if(!garbage_collector)
|
||||
garbage_collector = src
|
||||
|
||||
for(var/garbage in delayed_garbage)
|
||||
qdel(garbage)
|
||||
delayed_garbage.Cut()
|
||||
delayed_garbage = null
|
||||
|
||||
#ifdef GC_FINDREF
|
||||
world/loop_checks = 0
|
||||
#endif
|
||||
|
||||
/datum/controller/process/garbage_collector/doWork()
|
||||
if(!garbage_collect)
|
||||
return
|
||||
|
||||
tick_dels = 0
|
||||
var/time_to_kill = world.time - GC_COLLECTION_TIMEOUT
|
||||
var/checkRemain = GC_COLLECTIONS_PER_RUN
|
||||
var/remaining_force_dels = GC_FORCE_DEL_PER_RUN
|
||||
|
||||
|
||||
while(destroyed.len && --checkRemain >= 0)
|
||||
if(remaining_force_dels <= 0)
|
||||
#ifdef GC_DEBUG
|
||||
testing("GC: Reached max force dels per tick [dels] vs [maxDels]")
|
||||
#endif
|
||||
break // Server's already pretty pounded, everything else can wait 2 seconds
|
||||
var/refID = destroyed[1]
|
||||
var/GCd_at_time = destroyed[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
#ifdef GC_DEBUG
|
||||
testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + collection_timeout]")
|
||||
#endif
|
||||
break // Everything else is newer, skip them
|
||||
var/datum/A = locate(refID)
|
||||
#ifdef GC_DEBUG
|
||||
testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
|
||||
#endif
|
||||
if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
// Something's still referring to the qdel'd object. Kill it.
|
||||
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
|
||||
logging["[A.type]"]++
|
||||
del(A)
|
||||
|
||||
hard_dels++
|
||||
remaining_force_dels--
|
||||
else
|
||||
#ifdef GC_DEBUG
|
||||
testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
|
||||
#endif
|
||||
soft_dels++
|
||||
tick_dels++
|
||||
total_dels++
|
||||
destroyed.Cut(1, 2)
|
||||
SCHECK
|
||||
|
||||
#undef GC_FORCE_DEL_PER_TICK
|
||||
#undef GC_COLLECTION_TIMEOUT
|
||||
#undef GC_COLLECTIONS_PER_TICK
|
||||
|
||||
#ifdef GC_FINDREF
|
||||
/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ)
|
||||
. = 0
|
||||
for(var/V in D.vars)
|
||||
if(V == "contents")
|
||||
continue
|
||||
if(istype(D.vars[V], /atom))
|
||||
var/atom/A = D.vars[V]
|
||||
if(A in targ)
|
||||
testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]")
|
||||
. += 1
|
||||
else if(islist(D.vars[V]))
|
||||
. += LookForListRefs(D.vars[V], targ, D, V)
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/list/targ, var/datum/D, var/V)
|
||||
. = 0
|
||||
for(var/F in L)
|
||||
if(istype(F, /atom))
|
||||
var/atom/A = F
|
||||
if(A in targ)
|
||||
testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]")
|
||||
. += 1
|
||||
if(islist(F))
|
||||
. += LookForListRefs(F, targ, D, "[F] in list [V]")
|
||||
#endif
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/AddTrash(datum/A)
|
||||
if(!istype(A) || !isnull(A.gcDestroyed))
|
||||
return
|
||||
#ifdef GC_DEBUG
|
||||
testing("GC: AddTrash(\ref[A] - [A.type])")
|
||||
#endif
|
||||
A.gcDestroyed = world.time
|
||||
destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
|
||||
destroyed["\ref[A]"] = world.time
|
||||
|
||||
/datum/controller/process/garbage_collector/statProcess()
|
||||
..()
|
||||
stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued")
|
||||
stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run")
|
||||
|
||||
|
||||
// Tests if an atom has been deleted.
|
||||
/proc/deleted(atom/A)
|
||||
return !A || !isnull(A.gcDestroyed)
|
||||
|
||||
// Should be treated as a replacement for the 'del' keyword.
|
||||
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(var/datum/A)
|
||||
if(!A)
|
||||
return
|
||||
if(!istype(A))
|
||||
warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
|
||||
crash_with("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
|
||||
del(A)
|
||||
if(garbage_collector)
|
||||
garbage_collector.total_dels++
|
||||
garbage_collector.hard_dels++
|
||||
else if(isnull(A.gcDestroyed))
|
||||
// Let our friend know they're about to get collected
|
||||
. = !A.Destroy()
|
||||
if(. && A)
|
||||
A.finalize_qdel()
|
||||
|
||||
/datum/proc/finalize_qdel()
|
||||
del(src)
|
||||
|
||||
/atom/finalize_qdel()
|
||||
if(garbage_collector)
|
||||
garbage_collector.AddTrash(src)
|
||||
else
|
||||
delayed_garbage |= src
|
||||
|
||||
/icon/finalize_qdel()
|
||||
del(src)
|
||||
|
||||
/image/finalize_qdel()
|
||||
del(src)
|
||||
|
||||
/mob/finalize_qdel()
|
||||
del(src)
|
||||
|
||||
/turf/finalize_qdel()
|
||||
del(src)
|
||||
|
||||
// Default implementation of clean-up code.
|
||||
// This should be overridden to remove all references pointing to the object being destroyed.
|
||||
// Return true if the the GC controller should allow the object to continue existing.
|
||||
/datum/proc/Destroy()
|
||||
nanomanager.close_uis(src)
|
||||
tag = null
|
||||
return
|
||||
|
||||
#ifdef TESTING
|
||||
/client/var/running_find_references
|
||||
|
||||
/mob/verb/create_thing()
|
||||
set category = "Debug"
|
||||
set name = "Create Thing"
|
||||
|
||||
var/path = input("Enter path")
|
||||
var/atom/thing = new path(loc)
|
||||
thing.find_references()
|
||||
|
||||
/atom/verb/find_references()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
return
|
||||
|
||||
if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
|
||||
return
|
||||
|
||||
// Remove this object from the list of things to be auto-deleted.
|
||||
if(garbage_collector)
|
||||
garbage_collector.destroyed -= "\ref[src]"
|
||||
|
||||
usr.client.running_find_references = type
|
||||
testing("Beginning search for references to a [type].")
|
||||
var/list/things = list()
|
||||
for(var/client/thing)
|
||||
things += thing
|
||||
for(var/datum/thing)
|
||||
things += thing
|
||||
for(var/atom/thing)
|
||||
things += thing
|
||||
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
|
||||
for(var/datum/thing in things)
|
||||
if(!usr.client.running_find_references) return
|
||||
for(var/varname in thing.vars)
|
||||
var/variable = thing.vars[varname]
|
||||
if(variable == src)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
|
||||
else if(islist(variable))
|
||||
if(src in variable)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
|
||||
testing("Completed search for references to a [type].")
|
||||
usr.client.running_find_references = null
|
||||
|
||||
/client/verb/purge_all_destroyed_objects()
|
||||
set category = "Debug"
|
||||
if(garbage_collector)
|
||||
while(garbage_collector.destroyed.len)
|
||||
var/datum/o = locate(garbage_collector.destroyed[1])
|
||||
if(istype(o) && o.gcDestroyed)
|
||||
del(o)
|
||||
garbage_collector.dels++
|
||||
garbage_collector.destroyed.Cut(1, 2)
|
||||
#endif
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
#undef GC_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef GC_FINDREF
|
||||
#undef GC_FINDREF
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
/var/lighting_overlays_initialised = FALSE
|
||||
|
||||
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
|
||||
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
|
||||
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
|
||||
|
||||
/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
|
||||
/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
|
||||
/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
|
||||
|
||||
|
||||
/datum/controller/process/lighting
|
||||
// Queues of update counts, waiting to be rolled into stats lists
|
||||
var/list/stats_queues = list(
|
||||
"Source" = list(), "Corner" = list(), "Overlay" = list())
|
||||
// Stats lists
|
||||
var/list/stats_lists = list(
|
||||
"Source" = list(), "Corner" = list(), "Overlay" = list())
|
||||
var/update_stats_every = (1 SECONDS)
|
||||
var/next_stats_update = 0
|
||||
var/stat_updates_to_keep = 5
|
||||
|
||||
/datum/controller/process/lighting/setup()
|
||||
name = "lighting"
|
||||
|
||||
schedule_interval = 0 // run as fast as you possibly can
|
||||
sleep_interval = 10 // Yield every 10% of a tick
|
||||
defer_usage = 80 // Defer at 80% of a tick
|
||||
create_all_lighting_overlays()
|
||||
lighting_overlays_initialised = TRUE
|
||||
|
||||
// Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
|
||||
spawn(300)
|
||||
doWork(1)
|
||||
|
||||
/datum/controller/process/lighting/doWork(roundstart)
|
||||
|
||||
lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
|
||||
lighting_update_lights = list()
|
||||
for(var/datum/light_source/L in lighting_update_lights_old)
|
||||
|
||||
if(L.check() || L.destroyed || L.force_update)
|
||||
L.remove_lum()
|
||||
if(!L.destroyed)
|
||||
L.apply_lum()
|
||||
|
||||
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
|
||||
L.smart_vis_update()
|
||||
|
||||
L.vis_update = FALSE
|
||||
L.force_update = FALSE
|
||||
L.needs_update = FALSE
|
||||
|
||||
SCHECK
|
||||
|
||||
lighting_update_corners_old = lighting_update_corners //Same as above.
|
||||
lighting_update_corners = list()
|
||||
for(var/A in lighting_update_corners_old)
|
||||
var/datum/lighting_corner/C = A
|
||||
|
||||
C.update_overlays()
|
||||
|
||||
C.needs_update = FALSE
|
||||
|
||||
SCHECK
|
||||
|
||||
lighting_update_overlays_old = lighting_update_overlays //Same as above.
|
||||
lighting_update_overlays = list()
|
||||
|
||||
for(var/A in lighting_update_overlays_old)
|
||||
var/atom/movable/lighting_overlay/O = A
|
||||
O.update_overlay()
|
||||
O.needs_update = 0
|
||||
SCHECK
|
||||
|
||||
stats_queues["Source"] += lighting_update_lights_old.len
|
||||
stats_queues["Corner"] += lighting_update_corners_old.len
|
||||
stats_queues["Overlay"] += lighting_update_overlays_old.len
|
||||
|
||||
if(next_stats_update <= world.time)
|
||||
next_stats_update = world.time + update_stats_every
|
||||
for(var/stat_name in stats_queues)
|
||||
var/stat_sum = 0
|
||||
var/list/stats_queue = stats_queues[stat_name]
|
||||
for(var/count in stats_queue)
|
||||
stat_sum += count
|
||||
stats_queue.Cut()
|
||||
|
||||
var/list/stats_list = stats_lists[stat_name]
|
||||
stats_list.Insert(1, stat_sum)
|
||||
if(stats_list.len > stat_updates_to_keep)
|
||||
stats_list.Cut(stats_list.len)
|
||||
|
||||
/datum/controller/process/lighting/statProcess()
|
||||
..()
|
||||
stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
|
||||
for(var/stat_type in stats_lists)
|
||||
stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
|
||||
@@ -20,7 +20,7 @@
|
||||
/datum/controller/process/machinery/proc/internal_process_machinery()
|
||||
for(last_object in machines)
|
||||
var/obj/machinery/M = last_object
|
||||
if(M && !M.gcDestroyed)
|
||||
if(M && !QDELETED(M))
|
||||
if(M.process() == PROCESS_KILL)
|
||||
//M.inMachineList = 0 We don't use this debugging function
|
||||
machines.Remove(M)
|
||||
@@ -34,7 +34,7 @@
|
||||
/datum/controller/process/machinery/proc/internal_process_power()
|
||||
for(last_object in powernets)
|
||||
var/datum/powernet/powerNetwork = last_object
|
||||
if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed))
|
||||
if(istype(powerNetwork) && !QDELETED(powerNetwork))
|
||||
powerNetwork.reset()
|
||||
SCHECK
|
||||
continue
|
||||
@@ -52,7 +52,7 @@
|
||||
/datum/controller/process/machinery/proc/internal_process_pipenets()
|
||||
for(last_object in pipe_networks)
|
||||
var/datum/pipe_network/pipeNetwork = last_object
|
||||
if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
|
||||
if(istype(pipeNetwork) && !QDELETED(pipeNetwork))
|
||||
pipeNetwork.process()
|
||||
SCHECK
|
||||
continue
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/datum/controller/process/mob
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
|
||||
/datum/controller/process/mob/setup()
|
||||
name = "mob"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
start_delay = 16
|
||||
|
||||
/datum/controller/process/mob/started()
|
||||
..()
|
||||
if(!mob_list)
|
||||
mob_list = list()
|
||||
|
||||
/datum/controller/process/mob/doWork()
|
||||
for(last_object in mob_list)
|
||||
var/mob/M = last_object
|
||||
if(M && isnull(M.gcDestroyed))
|
||||
try
|
||||
M.Life()
|
||||
catch(var/exception/e)
|
||||
catchException(e, M)
|
||||
SCHECK
|
||||
else
|
||||
catchBadType(M)
|
||||
mob_list -= M
|
||||
|
||||
/datum/controller/process/mob/statProcess()
|
||||
..()
|
||||
stat(null, "[mob_list.len] mobs")
|
||||
@@ -9,7 +9,7 @@
|
||||
/datum/controller/process/nanoui/doWork()
|
||||
for(last_object in nanomanager.processing_uis)
|
||||
var/datum/nanoui/NUI = last_object
|
||||
if(istype(NUI) && isnull(NUI.gcDestroyed))
|
||||
if(istype(NUI) && !QDELETED(NUI))
|
||||
try
|
||||
NUI.process()
|
||||
catch(var/exception/e)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/datum/controller/process/obj/doWork()
|
||||
for(last_object in processing_objects)
|
||||
var/datum/O = last_object
|
||||
if(O && isnull(O.gcDestroyed))
|
||||
if(!QDELETED(O))
|
||||
try
|
||||
O:process()
|
||||
catch(var/exception/e)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/datum/controller/process/radiation
|
||||
var/repository/radiation/linked = null
|
||||
|
||||
/datum/controller/process/radiation/setup()
|
||||
name = "radiation controller"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
linked = radiation_repository
|
||||
|
||||
/datum/controller/process/radiation/doWork()
|
||||
sources_decay()
|
||||
cache_expires()
|
||||
irradiate_targets()
|
||||
|
||||
// Step 1 - Sources Decay
|
||||
/datum/controller/process/radiation/proc/sources_decay()
|
||||
var/list/sources = linked.sources
|
||||
for(var/thing in sources)
|
||||
var/datum/radiation_source/S = thing
|
||||
if(QDELETED(S))
|
||||
sources.Remove(S)
|
||||
continue
|
||||
if(S.decay)
|
||||
S.update_rad_power(S.rad_power - config.radiation_decay_rate)
|
||||
if(S.rad_power <= config.radiation_lower_limit)
|
||||
sources.Remove(S)
|
||||
SCHECK // This scheck probably just wastes resources, but better safe than sorry in this case.
|
||||
|
||||
// Step 2 - Cache Expires
|
||||
/datum/controller/process/radiation/proc/cache_expires()
|
||||
var/list/resistance_cache = linked.resistance_cache
|
||||
for(var/thing in resistance_cache)
|
||||
var/turf/T = thing
|
||||
if(QDELETED(T))
|
||||
resistance_cache.Remove(T)
|
||||
continue
|
||||
if((length(T.contents) + 1) != resistance_cache[T])
|
||||
resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed.
|
||||
SCHECK
|
||||
|
||||
// Step 3 - Registered irradiatable things are checked for radiation
|
||||
/datum/controller/process/radiation/proc/irradiate_targets()
|
||||
var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway.
|
||||
if(length(linked.sources) > 0)
|
||||
for(var/thing in registered_listeners)
|
||||
var/atom/A = thing
|
||||
if(QDELETED(A))
|
||||
continue
|
||||
var/turf/T = get_turf(thing)
|
||||
var/rads = linked.get_rads_at_turf(T)
|
||||
if(rads)
|
||||
A.rad_act(rads)
|
||||
SCHECK
|
||||
|
||||
/datum/controller/process/radiation/statProcess()
|
||||
..()
|
||||
stat(null, "[linked.sources.len] sources, [linked.resistance_cache.len] cached turfs")
|
||||
@@ -30,8 +30,8 @@
|
||||
// Note: We won't be informed about tasks being destroyed, but this is the best we can do.
|
||||
/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target)
|
||||
scheduled_tasks = list()
|
||||
for(var/st in target.scheduled_tasks)
|
||||
if(!deleted(st) && istype(st, /datum/scheduled_task))
|
||||
for(var/datum/scheduled_task/st in target.scheduled_tasks)
|
||||
if(!QDELETED(st) && istype(st))
|
||||
schedule(st)
|
||||
scheduler = src
|
||||
|
||||
@@ -46,12 +46,9 @@
|
||||
|
||||
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
|
||||
scheduled_tasks += st
|
||||
destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
|
||||
|
||||
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
|
||||
if(st in scheduled_tasks)
|
||||
scheduled_tasks -= st
|
||||
destroyed_event.unregister(st, src)
|
||||
scheduled_tasks -= st
|
||||
|
||||
/**********
|
||||
* Helpers *
|
||||
@@ -102,6 +99,7 @@
|
||||
task_after_process_args += src
|
||||
|
||||
/datum/scheduled_task/Destroy()
|
||||
scheduler.unschedule(src)
|
||||
procedure = null
|
||||
arguments.Cut()
|
||||
task_after_process = null
|
||||
|
||||
@@ -33,3 +33,10 @@ var/global/datum/controller/process/ticker/tickerProcess
|
||||
|
||||
/datum/controller/process/ticker/proc/getLastTickerTimeDuration()
|
||||
return lastTickerTimeDuration
|
||||
|
||||
// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
|
||||
/datum/controller/process/ticker/proc/HasRoundStarted()
|
||||
return (ticker && ticker.current_state >= GAME_STATE_PLAYING)
|
||||
|
||||
/datum/controller/process/ticker/proc/IsRoundInProgress()
|
||||
return (ticker && ticker.current_state == GAME_STATE_PLAYING)
|
||||
|
||||
@@ -314,7 +314,6 @@ var/global/datum/controller/radio/radio_controller
|
||||
devices_line -= null
|
||||
if (devices_line.len==0)
|
||||
devices -= devices_filter
|
||||
del(devices_line)
|
||||
|
||||
/datum/signal
|
||||
var/obj/source
|
||||
|
||||
@@ -46,7 +46,8 @@ var/list/gamemode_cache = list()
|
||||
var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke.
|
||||
var/allow_Metadata = 0 // Metadata is supported.
|
||||
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
|
||||
var/Ticklag = 0.9
|
||||
var/fps = 20
|
||||
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
|
||||
var/Tickcomp = 0
|
||||
var/socket_talk = 0 // use socket_talk to communicate with other processes
|
||||
var/list/resource_urls = null
|
||||
@@ -220,6 +221,10 @@ var/list/gamemode_cache = list()
|
||||
|
||||
var/show_human_death_message = 1
|
||||
|
||||
var/radiation_decay_rate = 1 //How much radiation is reduced by each tick
|
||||
var/radiation_resistance_multiplier = 8.5 //VOREstation edit
|
||||
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
|
||||
|
||||
/datum/configuration/New()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
for (var/T in L)
|
||||
@@ -568,7 +573,12 @@ var/list/gamemode_cache = list()
|
||||
irc_bot_export = 1
|
||||
|
||||
if("ticklag")
|
||||
Ticklag = text2num(value)
|
||||
var/ticklag = text2num(value)
|
||||
if(ticklag > 0)
|
||||
fps = 10 / ticklag
|
||||
|
||||
if("tick_limit_mc_init")
|
||||
tick_limit_mc_init = text2num(value)
|
||||
|
||||
if("allow_antag_hud")
|
||||
config.antag_hud_allowed = 1
|
||||
@@ -720,6 +730,9 @@ var/list/gamemode_cache = list()
|
||||
if(values.len > 0)
|
||||
language_prefixes = values
|
||||
|
||||
if("radiation_lower_limit")
|
||||
radiation_lower_limit = text2num(value)
|
||||
|
||||
else
|
||||
log_misc("Unknown setting in configuration: '[name]'")
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/controller
|
||||
var/name
|
||||
// The object used for the clickable stat() button.
|
||||
var/obj/effect/statclick/statclick
|
||||
|
||||
/datum/controller/proc/Initialize()
|
||||
|
||||
//cleanup actions
|
||||
/datum/controller/proc/Shutdown()
|
||||
|
||||
//when we enter dmm_suite.load_map
|
||||
/datum/controller/proc/StartLoadingMap()
|
||||
|
||||
//when we exit dmm_suite.load_map
|
||||
/datum/controller/proc/StopLoadingMap()
|
||||
|
||||
/datum/controller/proc/Recover()
|
||||
|
||||
/datum/controller/proc/stat_entry()
|
||||
@@ -22,6 +22,10 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
|
||||
var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
|
||||
var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
|
||||
|
||||
/datum/emergency_shuttle_controller/New()
|
||||
escape_pods = list()
|
||||
..()
|
||||
|
||||
/datum/emergency_shuttle_controller/proc/process()
|
||||
if (wait_for_launch)
|
||||
if (evac && auto_recall && world.time >= auto_recall_time)
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Failsafe
|
||||
*
|
||||
* Pretty much pokes the MC to make sure it's still alive.
|
||||
**/
|
||||
|
||||
var/datum/controller/failsafe/Failsafe
|
||||
|
||||
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
|
||||
name = "Failsafe"
|
||||
|
||||
// The length of time to check on the MC (in deciseconds).
|
||||
// Set to 0 to disable.
|
||||
var/processing_interval = 20
|
||||
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
|
||||
var/defcon = 5
|
||||
//the world.time of the last check, so the mc can restart US if we hang.
|
||||
// (Real friends look out for *eachother*)
|
||||
var/lasttick = 0
|
||||
|
||||
// Track the MC iteration to make sure its still on track.
|
||||
var/master_iteration = 0
|
||||
var/running = TRUE
|
||||
|
||||
/datum/controller/failsafe/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
if(Failsafe != src)
|
||||
if(istype(Failsafe))
|
||||
qdel(Failsafe)
|
||||
Failsafe = src
|
||||
Initialize()
|
||||
|
||||
/datum/controller/failsafe/Initialize()
|
||||
set waitfor = 0
|
||||
Failsafe.Loop()
|
||||
if(!QDELETED(src))
|
||||
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
|
||||
|
||||
/datum/controller/failsafe/Destroy()
|
||||
running = FALSE
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/controller/failsafe/proc/Loop()
|
||||
while(running)
|
||||
lasttick = world.time
|
||||
if(!Master)
|
||||
// Replace the missing Master! This should never, ever happen.
|
||||
new /datum/controller/master()
|
||||
// Only poke it if overrides are not in effect.
|
||||
if(processing_interval > 0)
|
||||
if(Master.processing && Master.iteration)
|
||||
// Check if processing is done yet.
|
||||
if(Master.iteration == master_iteration)
|
||||
switch(defcon)
|
||||
if(4,5)
|
||||
--defcon
|
||||
if(3)
|
||||
to_chat(admins, "<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(2)
|
||||
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(1)
|
||||
|
||||
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
|
||||
--defcon
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else if(rtn < 0)
|
||||
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
|
||||
to_chat(admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
|
||||
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
|
||||
//no need to handle that specially when defcon 0 can handle it
|
||||
if(0) //DEFCON 0! (mc failed to restart)
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else
|
||||
defcon = min(defcon + 1,5)
|
||||
master_iteration = Master.iteration
|
||||
if (defcon <= 1)
|
||||
sleep(processing_interval*2)
|
||||
else
|
||||
sleep(processing_interval)
|
||||
else
|
||||
defcon = 5
|
||||
sleep(initial(processing_interval))
|
||||
|
||||
/datum/controller/failsafe/proc/defcon_pretty()
|
||||
return defcon
|
||||
|
||||
/datum/controller/failsafe/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
|
||||
@@ -0,0 +1,556 @@
|
||||
/**
|
||||
* StonedMC
|
||||
*
|
||||
* Designed to properly split up a given tick among subsystems
|
||||
* Note: if you read parts of this code and think "why is it doing it that way"
|
||||
* Odds are, there is a reason
|
||||
*
|
||||
**/
|
||||
var/datum/controller/master/Master = new()
|
||||
|
||||
/datum/controller/master
|
||||
name = "Master"
|
||||
|
||||
// Are we processing (higher values increase the processing delay by n ticks)
|
||||
var/processing = 1
|
||||
// How many times have we ran
|
||||
var/iteration = 0
|
||||
|
||||
// world.time of last fire, for tracking lag outside of the mc
|
||||
var/last_run
|
||||
|
||||
// List of subsystems to process().
|
||||
var/list/subsystems
|
||||
|
||||
// Vars for keeping track of tick drift.
|
||||
var/init_timeofday
|
||||
var/init_time
|
||||
var/tickdrift = 0
|
||||
|
||||
var/sleep_delta
|
||||
|
||||
var/make_runtime = 0
|
||||
|
||||
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
|
||||
|
||||
// The type of the last subsystem to be process()'d.
|
||||
var/last_type_processed
|
||||
|
||||
var/datum/controller/subsystem/queue_head //Start of queue linked list
|
||||
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
|
||||
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
|
||||
var/queue_priority_count_bg = 0 //Same, but for background subsystems
|
||||
var/map_loading = FALSE //Are we loading in a new map?
|
||||
|
||||
var/current_runlevel //for scheduling different subsystems for different stages of the round
|
||||
|
||||
var/static/restart_clear = 0
|
||||
var/static/restart_timeout = 0
|
||||
var/static/restart_count = 0
|
||||
|
||||
//current tick limit, assigned before running a subsystem.
|
||||
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
|
||||
var/static/current_ticklimit = TICK_LIMIT_RUNNING
|
||||
|
||||
/datum/controller/master/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
subsystems = list()
|
||||
if (Master != src)
|
||||
if (istype(Master))
|
||||
Recover()
|
||||
qdel(Master)
|
||||
else
|
||||
init_subtypes(/datum/controller/subsystem, subsystems)
|
||||
Master = src
|
||||
|
||||
/datum/controller/master/Destroy()
|
||||
..()
|
||||
// Tell qdel() to Del() this object.
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/controller/master/Shutdown()
|
||||
processing = FALSE
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
reverseRange(subsystems)
|
||||
for(var/datum/controller/subsystem/ss in subsystems)
|
||||
ss.Shutdown()
|
||||
|
||||
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
/proc/Recreate_MC()
|
||||
. = -1 //so if we runtime, things know we failed
|
||||
if (world.time < Master.restart_timeout)
|
||||
return 0
|
||||
if (world.time < Master.restart_clear)
|
||||
Master.restart_count *= 0.5
|
||||
|
||||
var/delay = 50 * ++Master.restart_count
|
||||
Master.restart_timeout = world.time + delay
|
||||
Master.restart_clear = world.time + (delay * 2)
|
||||
Master.processing = 0 //stop ticking this one
|
||||
try
|
||||
new/datum/controller/master()
|
||||
catch
|
||||
return -1
|
||||
return 1
|
||||
|
||||
|
||||
/datum/controller/master/Recover()
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
|
||||
for (var/varname in Master.vars)
|
||||
switch (varname)
|
||||
if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk.
|
||||
continue
|
||||
else
|
||||
var/varval = Master.vars[varname]
|
||||
if (istype(varval, /datum)) // Check if it has a type var.
|
||||
var/datum/D = varval
|
||||
msg += "\t [varname] = [D]([D.type])\n"
|
||||
else
|
||||
msg += "\t [varname] = [varval]\n"
|
||||
log_world(msg)
|
||||
|
||||
var/datum/controller/subsystem/BadBoy = Master.last_type_processed
|
||||
var/FireHim = FALSE
|
||||
if(istype(BadBoy))
|
||||
msg = null
|
||||
switch(++BadBoy.failure_strikes)
|
||||
if(2)
|
||||
msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again."
|
||||
FireHim = TRUE
|
||||
if(3)
|
||||
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
|
||||
BadBoy.flags |= SS_NO_FIRE
|
||||
if(msg)
|
||||
log_game(msg)
|
||||
message_admins("<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
|
||||
if (istype(Master.subsystems))
|
||||
if(FireHim)
|
||||
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
||||
subsystems = Master.subsystems
|
||||
current_runlevel = Master.current_runlevel
|
||||
StartProcessing(10)
|
||||
else
|
||||
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
|
||||
Initialize(20, TRUE)
|
||||
|
||||
|
||||
// Please don't stuff random bullshit here,
|
||||
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
||||
/datum/controller/master/Initialize(delay, init_sss)
|
||||
set waitfor = 0
|
||||
|
||||
if(delay)
|
||||
sleep(delay)
|
||||
|
||||
if(init_sss)
|
||||
init_subtypes(/datum/controller/subsystem, subsystems)
|
||||
|
||||
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
|
||||
|
||||
// Sort subsystems by init_order, so they initialize in the correct order.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
|
||||
var/start_timeofday = REALTIMEOFDAY
|
||||
// Initialize subsystems.
|
||||
current_ticklimit = config.tick_limit_mc_init
|
||||
for (var/datum/controller/subsystem/SS in subsystems)
|
||||
if (SS.flags & SS_NO_INIT)
|
||||
continue
|
||||
SS.Initialize(REALTIMEOFDAY)
|
||||
CHECK_TICK
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
|
||||
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
|
||||
if (!current_runlevel)
|
||||
SetRunLevel(RUNLEVEL_LOBBY)
|
||||
|
||||
// Sort subsystems by display setting for easy access.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_display)
|
||||
// Set world options.
|
||||
#ifdef UNIT_TEST
|
||||
world.sleep_offline = 0
|
||||
#else
|
||||
world.sleep_offline = 1
|
||||
#endif
|
||||
world.fps = config.fps
|
||||
var/initialized_tod = REALTIMEOFDAY
|
||||
sleep(1)
|
||||
initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
|
||||
// Loop.
|
||||
Master.StartProcessing(0)
|
||||
|
||||
/datum/controller/master/proc/SetRunLevel(new_runlevel)
|
||||
var/old_runlevel = isnull(current_runlevel) ? "NULL" : runlevel_flags[current_runlevel]
|
||||
testing("MC: Runlevel changed from [old_runlevel] to [new_runlevel]")
|
||||
current_runlevel = RUNLEVEL_FLAG_TO_INDEX(new_runlevel)
|
||||
if(current_runlevel < 1)
|
||||
CRASH("Attempted to set invalid runlevel: [new_runlevel]")
|
||||
|
||||
// Starts the mc, and sticks around to restart it if the loop ever ends.
|
||||
/datum/controller/master/proc/StartProcessing(delay)
|
||||
set waitfor = 0
|
||||
if(delay)
|
||||
sleep(delay)
|
||||
var/rtn = Loop()
|
||||
if (rtn > 0 || processing < 0)
|
||||
return //this was suppose to happen.
|
||||
//loop ended, restart the mc
|
||||
log_game("MC crashed or runtimed, restarting")
|
||||
message_admins("MC crashed or runtimed, restarting")
|
||||
var/rtn2 = Recreate_MC()
|
||||
if (rtn2 <= 0)
|
||||
log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
|
||||
message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
|
||||
Failsafe.defcon = 2
|
||||
|
||||
// Main loop.
|
||||
/datum/controller/master/proc/Loop()
|
||||
. = -1
|
||||
//Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
|
||||
// local vars rock
|
||||
|
||||
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
|
||||
var/list/tickersubsystems = list()
|
||||
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
|
||||
var/timer = world.time
|
||||
for (var/thing in subsystems)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
if (SS.flags & SS_NO_FIRE)
|
||||
continue
|
||||
SS.queued_time = 0
|
||||
SS.queue_next = null
|
||||
SS.queue_prev = null
|
||||
SS.state = SS_IDLE
|
||||
if (SS.flags & SS_TICKER)
|
||||
tickersubsystems += SS
|
||||
timer += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = timer
|
||||
continue
|
||||
|
||||
var/ss_runlevels = SS.runlevels
|
||||
var/added_to_any = FALSE
|
||||
for(var/I in 1 to global.runlevel_flags.len)
|
||||
if(ss_runlevels & global.runlevel_flags[I])
|
||||
while(runlevel_sorted_subsystems.len < I)
|
||||
runlevel_sorted_subsystems += list(list())
|
||||
runlevel_sorted_subsystems[I] += SS
|
||||
added_to_any = TRUE
|
||||
if(!added_to_any)
|
||||
WARNING("[SS.name] subsystem is not SS_NO_FIRE but also does not have any runlevels set!")
|
||||
|
||||
queue_head = null
|
||||
queue_tail = null
|
||||
//these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
|
||||
//(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
|
||||
sortTim(tickersubsystems, /proc/cmp_subsystem_priority)
|
||||
for(var/I in runlevel_sorted_subsystems)
|
||||
sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
|
||||
I += tickersubsystems
|
||||
|
||||
var/cached_runlevel = current_runlevel
|
||||
var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
|
||||
|
||||
init_timeofday = REALTIMEOFDAY
|
||||
init_time = world.time
|
||||
|
||||
iteration = 1
|
||||
var/error_level = 0
|
||||
var/sleep_delta = 0
|
||||
var/list/subsystems_to_check
|
||||
//the actual loop.
|
||||
while (1)
|
||||
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
|
||||
if (processing <= 0)
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later
|
||||
// because sleeps are processed in the order received, so longer sleeps are more likely to run first
|
||||
if (world.tick_usage > TICK_LIMIT_MC)
|
||||
sleep_delta += 2
|
||||
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
continue
|
||||
|
||||
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0)
|
||||
if (last_run + (world.tick_lag * processing) > world.time)
|
||||
sleep_delta += 1
|
||||
if (world.tick_usage > (TICK_LIMIT_MC*0.5))
|
||||
sleep_delta += 1
|
||||
|
||||
if (make_runtime)
|
||||
var/datum/controller/subsystem/SS
|
||||
SS.can_fire = 0
|
||||
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
|
||||
new/datum/controller/failsafe() // (re)Start the failsafe.
|
||||
if (!queue_head || !(iteration % 3))
|
||||
var/checking_runlevel = current_runlevel
|
||||
if(cached_runlevel != checking_runlevel)
|
||||
//resechedule subsystems
|
||||
cached_runlevel = checking_runlevel
|
||||
current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
|
||||
var/stagger = world.time
|
||||
for(var/I in current_runlevel_subsystems)
|
||||
var/datum/controller/subsystem/SS = I
|
||||
if(SS.next_fire <= world.time)
|
||||
stagger += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = stagger
|
||||
|
||||
subsystems_to_check = current_runlevel_subsystems
|
||||
else
|
||||
subsystems_to_check = tickersubsystems
|
||||
if (CheckQueue(subsystems_to_check) <= 0)
|
||||
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
|
||||
log_world("MC: SoftReset() failed, crashing")
|
||||
return
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
if (queue_head)
|
||||
if (RunQueue() <= 0)
|
||||
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
|
||||
log_world("MC: SoftReset() failed, crashing")
|
||||
return
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
error_level--
|
||||
if (!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync
|
||||
queue_priority_count = 0
|
||||
queue_priority_count_bg = 0
|
||||
|
||||
iteration++
|
||||
last_run = world.time
|
||||
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
|
||||
current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
|
||||
|
||||
|
||||
|
||||
// This is what decides if something should run.
|
||||
/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
|
||||
. = 0 //so the mc knows if we runtimed
|
||||
|
||||
//we create our variables outside of the loops to save on overhead
|
||||
var/datum/controller/subsystem/SS
|
||||
var/SS_flags
|
||||
|
||||
for (var/thing in subsystemstocheck)
|
||||
if (!thing)
|
||||
subsystemstocheck -= thing
|
||||
SS = thing
|
||||
if (SS.state != SS_IDLE)
|
||||
continue
|
||||
if (SS.can_fire <= 0)
|
||||
continue
|
||||
if (SS.next_fire > world.time)
|
||||
continue
|
||||
SS_flags = SS.flags
|
||||
if (SS_flags & SS_NO_FIRE)
|
||||
subsystemstocheck -= SS
|
||||
continue
|
||||
if (!(SS_flags & SS_TICKER) && (SS_flags & SS_KEEP_TIMING) && SS.last_fire + (SS.wait * 0.75) > world.time)
|
||||
continue
|
||||
SS.enqueue()
|
||||
. = 1
|
||||
|
||||
|
||||
// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
|
||||
/datum/controller/master/proc/RunQueue()
|
||||
. = 0
|
||||
var/datum/controller/subsystem/queue_node
|
||||
var/queue_node_flags
|
||||
var/queue_node_priority
|
||||
var/queue_node_paused
|
||||
|
||||
var/current_tick_budget
|
||||
var/tick_precentage
|
||||
var/tick_remaining
|
||||
var/ran = TRUE //this is right
|
||||
var/ran_non_ticker = FALSE
|
||||
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
|
||||
var/tick_usage
|
||||
|
||||
//keep running while we have stuff to run and we haven't gone over a tick
|
||||
// this is so subsystems paused eariler can use tick time that later subsystems never used
|
||||
while (ran && queue_head && world.tick_usage < TICK_LIMIT_MC)
|
||||
ran = FALSE
|
||||
bg_calc = FALSE
|
||||
current_tick_budget = queue_priority_count
|
||||
queue_node = queue_head
|
||||
while (queue_node)
|
||||
if (ran && world.tick_usage > TICK_LIMIT_RUNNING)
|
||||
break
|
||||
|
||||
queue_node_flags = queue_node.flags
|
||||
queue_node_priority = queue_node.queued_priority
|
||||
|
||||
//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
|
||||
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
|
||||
// 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 - world.tick_usage && ran_non_ticker)
|
||||
queue_node.queued_priority += queue_priority_count * 0.10
|
||||
queue_priority_count -= queue_node_priority
|
||||
queue_priority_count += queue_node.queued_priority
|
||||
current_tick_budget -= queue_node_priority
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
|
||||
if ((queue_node_flags & SS_BACKGROUND) && !bg_calc)
|
||||
current_tick_budget = queue_priority_count_bg
|
||||
bg_calc = TRUE
|
||||
|
||||
tick_remaining = TICK_LIMIT_RUNNING - world.tick_usage
|
||||
|
||||
if (current_tick_budget > 0 && queue_node_priority > 0)
|
||||
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
|
||||
else
|
||||
tick_precentage = tick_remaining
|
||||
|
||||
current_ticklimit = world.tick_usage + tick_precentage
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER))
|
||||
ran_non_ticker = TRUE
|
||||
ran = TRUE
|
||||
tick_usage = world.tick_usage
|
||||
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
|
||||
last_type_processed = queue_node
|
||||
|
||||
queue_node.state = SS_RUNNING
|
||||
|
||||
var/state = queue_node.ignite(queue_node_paused)
|
||||
if (state == SS_RUNNING)
|
||||
state = SS_IDLE
|
||||
current_tick_budget -= queue_node_priority
|
||||
tick_usage = world.tick_usage - tick_usage
|
||||
|
||||
if (tick_usage < 0)
|
||||
tick_usage = 0
|
||||
|
||||
queue_node.state = state
|
||||
|
||||
if (state == SS_PAUSED)
|
||||
queue_node.paused_ticks++
|
||||
queue_node.paused_tick_usage += tick_usage
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
|
||||
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
|
||||
tick_usage += queue_node.paused_tick_usage
|
||||
|
||||
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
|
||||
|
||||
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
|
||||
queue_node.paused_ticks = 0
|
||||
queue_node.paused_tick_usage = 0
|
||||
|
||||
if (queue_node_flags & SS_BACKGROUND) //update our running total
|
||||
queue_priority_count_bg -= queue_node_priority
|
||||
else
|
||||
queue_priority_count -= queue_node_priority
|
||||
|
||||
queue_node.last_fire = world.time
|
||||
queue_node.times_fired++
|
||||
|
||||
if (queue_node_flags & SS_TICKER)
|
||||
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
|
||||
else if (queue_node_flags & SS_POST_FIRE_TIMING)
|
||||
queue_node.next_fire = world.time + queue_node.wait
|
||||
else if (queue_node_flags & SS_KEEP_TIMING)
|
||||
queue_node.next_fire += queue_node.wait
|
||||
else
|
||||
queue_node.next_fire = queue_node.queued_time + queue_node.wait
|
||||
|
||||
queue_node.queued_time = 0
|
||||
|
||||
//remove from queue
|
||||
queue_node.dequeue()
|
||||
|
||||
queue_node = queue_node.queue_next
|
||||
|
||||
. = 1
|
||||
|
||||
//resets the queue, and all subsystems, while filtering out the subsystem lists
|
||||
// called if any mc's queue procs runtime or exit improperly.
|
||||
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
|
||||
. = 0
|
||||
log_world("MC: SoftReset called, resetting MC queue state.")
|
||||
if (!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
|
||||
log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
|
||||
return
|
||||
var/subsystemstocheck = subsystems + ticker_SS
|
||||
for(var/I in runlevel_SS)
|
||||
subsystemstocheck |= I
|
||||
|
||||
for (var/thing in subsystemstocheck)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
if (!SS || !istype(SS))
|
||||
//list(SS) is so if a list makes it in the subsystem list, we remove the list, not the contents
|
||||
subsystems -= list(SS)
|
||||
ticker_SS -= list(SS)
|
||||
for(var/I in runlevel_SS)
|
||||
I -= list(SS)
|
||||
log_world("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
|
||||
continue
|
||||
if (SS.queue_next && !istype(SS.queue_next))
|
||||
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
|
||||
SS.queue_next = null
|
||||
if (SS.queue_prev && !istype(SS.queue_prev))
|
||||
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
|
||||
SS.queue_prev = null
|
||||
SS.queued_priority = 0
|
||||
SS.queued_time = 0
|
||||
SS.state = SS_IDLE
|
||||
if (queue_head && !istype(queue_head))
|
||||
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
|
||||
queue_head = null
|
||||
if (queue_tail && !istype(queue_tail))
|
||||
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
|
||||
queue_tail = null
|
||||
queue_priority_count = 0
|
||||
queue_priority_count_bg = 0
|
||||
log_world("MC: SoftReset: Finished.")
|
||||
. = 1
|
||||
|
||||
|
||||
|
||||
/datum/controller/master/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
|
||||
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
|
||||
|
||||
/datum/controller/master/StartLoadingMap()
|
||||
//disallow more than one map to load at once, multithreading it will just cause race conditions
|
||||
while(map_loading)
|
||||
stoplag()
|
||||
for(var/S in subsystems)
|
||||
var/datum/controller/subsystem/SS = S
|
||||
SS.StartLoadingMap()
|
||||
map_loading = TRUE
|
||||
|
||||
/datum/controller/master/StopLoadingMap(bounds = null)
|
||||
map_loading = FALSE
|
||||
for(var/S in subsystems)
|
||||
var/datum/controller/subsystem/SS = S
|
||||
SS.StopLoadingMap()
|
||||
@@ -2,6 +2,10 @@
|
||||
//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs)
|
||||
//WIP, needs lots of work still
|
||||
|
||||
//
|
||||
// TODO - This will be completely replaced by master.dm in time.
|
||||
//
|
||||
|
||||
var/global/datum/controller/game_controller/master_controller //Set in world.New()
|
||||
|
||||
var/global/controller_iteration = 0
|
||||
@@ -31,7 +35,6 @@ datum/controller/game_controller/New()
|
||||
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
|
||||
|
||||
datum/controller/game_controller/proc/setup()
|
||||
world.tick_lag = config.Ticklag
|
||||
|
||||
setup_objects()
|
||||
setupgenetics()
|
||||
@@ -71,8 +74,8 @@ datum/controller/game_controller/proc/setup_objects()
|
||||
sleep(1)
|
||||
|
||||
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
|
||||
for(var/obj/object in world)
|
||||
if(isnull(object.gcDestroyed))
|
||||
for(var/atom/movable/object in world)
|
||||
if(!QDELETED(object))
|
||||
object.initialize()
|
||||
CHECK_SLEEP_MASTER
|
||||
sleep(1)
|
||||
@@ -100,7 +103,7 @@ datum/controller/game_controller/proc/setup_objects()
|
||||
|
||||
admin_notice("<span class='danger'>Initializing turbolifts</span>", R_DEBUG)
|
||||
for(var/thing in turbolifts)
|
||||
if(!deleted(thing))
|
||||
var/obj/turbolift_map_holder/lift = thing
|
||||
var/obj/turbolift_map_holder/lift = thing
|
||||
if(!QDELETED(lift))
|
||||
lift.initialize()
|
||||
CHECK_SLEEP_MASTER
|
||||
|
||||
@@ -15,8 +15,13 @@ var/global/datum/shuttle_controller/shuttle_controller
|
||||
|
||||
//This is called by gameticker after all the machines and radio frequencies have been properly initialized
|
||||
/datum/shuttle_controller/proc/setup_shuttle_docks()
|
||||
for(var/shuttle_tag in shuttles)
|
||||
var/datum/shuttle/shuttle = shuttles[shuttle_tag]
|
||||
// for(var/shuttle_tag in shuttles)
|
||||
// var/datum/shuttle/shuttle = shuttles[shuttle_tag]
|
||||
for(var/shuttle_type in subtypesof(/datum/shuttle))
|
||||
var/datum/shuttle/shuttle = shuttle_type
|
||||
if(initial(shuttle.category) == shuttle_type)
|
||||
continue
|
||||
shuttle = new shuttle()
|
||||
shuttle.init_docking_controllers()
|
||||
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
|
||||
|
||||
@@ -28,389 +33,3 @@ var/global/datum/shuttle_controller/shuttle_controller
|
||||
shuttles = list()
|
||||
process_shuttles = list()
|
||||
|
||||
var/datum/shuttle/ferry/shuttle
|
||||
|
||||
// Escape shuttle and pods
|
||||
shuttle = new/datum/shuttle/ferry/emergency()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/escape/station)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape/transit)
|
||||
shuttle.docking_controller_tag = "escape_shuttle"
|
||||
shuttle.dock_target_station = "escape_dock"
|
||||
shuttle.dock_target_offsite = "centcom_dock"
|
||||
shuttle.transit_direction = NORTH
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN
|
||||
//shuttle.docking_controller_tag = "supply_shuttle"
|
||||
//shuttle.dock_target_station = "cargo_bay"
|
||||
shuttles["Escape"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod1/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod1/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod1/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_1"
|
||||
shuttle.dock_target_station = "escape_pod_1_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_1_recovery"
|
||||
shuttle.transit_direction = NORTH
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 1"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod2/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod2/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod2/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_2"
|
||||
shuttle.dock_target_station = "escape_pod_2_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_2_recovery"
|
||||
shuttle.transit_direction = NORTH
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 2"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod3/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod3/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod3/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_3"
|
||||
shuttle.dock_target_station = "escape_pod_3_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_3_recovery"
|
||||
shuttle.transit_direction = NORTH
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 3"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod4/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod4/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod4/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_4"
|
||||
shuttle.dock_target_station = "escape_pod_4_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_4_recovery"
|
||||
shuttle.transit_direction = NORTH //should this be SOUTH? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 4"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod5/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod5/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod5/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_5"
|
||||
shuttle.dock_target_station = "escape_pod_5_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_5_recovery"
|
||||
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 5"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/escape_pod6/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape_pod6/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape_pod6/transit)
|
||||
shuttle.docking_controller_tag = "escape_pod_6"
|
||||
shuttle.dock_target_station = "escape_pod_6_berth"
|
||||
shuttle.dock_target_offsite = "escape_pod_6_recovery"
|
||||
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Escape Pod 6"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/cryo/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/cryo/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/cryo/transit)
|
||||
shuttle.docking_controller_tag = "cryostorage_shuttle"
|
||||
shuttle.dock_target_station = "cryostorage_shuttle_berth"
|
||||
shuttle.dock_target_offsite = "cryostorage_shuttle_recovery"
|
||||
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Cryostorage Shuttle"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/large_escape_pod1/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod1/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/large_escape_pod1/transit)
|
||||
shuttle.docking_controller_tag = "large_escape_pod_1"
|
||||
shuttle.dock_target_station = "large_escape_pod_1_berth"
|
||||
shuttle.dock_target_offsite = "large_escape_pod_1_recovery"
|
||||
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Large Escape Pod 1"] = shuttle
|
||||
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/large_escape_pod2/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod2/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/large_escape_pod2/transit)
|
||||
shuttle.docking_controller_tag = "large_escape_pod_2"
|
||||
shuttle.dock_target_station = "large_escape_pod_2_berth"
|
||||
shuttle.dock_target_offsite = "large_escape_pod_2_recovery"
|
||||
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Large Escape Pod 2"] = shuttle
|
||||
|
||||
//give the emergency shuttle controller it's shuttles
|
||||
emergency_shuttle.shuttle = shuttles["Escape"]
|
||||
emergency_shuttle.escape_pods = list(
|
||||
shuttles["Escape Pod 1"],
|
||||
shuttles["Escape Pod 2"],
|
||||
shuttles["Escape Pod 3"],
|
||||
shuttles["Escape Pod 4"],
|
||||
shuttles["Escape Pod 5"],
|
||||
shuttles["Escape Pod 6"],
|
||||
shuttles["Cryostorage Shuttle"],
|
||||
shuttles["Large Escape Pod 1"],
|
||||
shuttles["Large Escape Pod 2"],
|
||||
)
|
||||
|
||||
// Supply shuttle
|
||||
shuttle = new/datum/shuttle/ferry/supply()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/supply/dock)
|
||||
shuttle.area_station = locate(/area/supply/station)
|
||||
shuttle.docking_controller_tag = "supply_shuttle"
|
||||
shuttle.dock_target_station = "cargo_bay"
|
||||
shuttles["Supply"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
supply_controller.shuttle = shuttle
|
||||
|
||||
// Admin shuttles.
|
||||
shuttle = new()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/transport1/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/transport1/station)
|
||||
shuttle.docking_controller_tag = "centcom_shuttle"
|
||||
shuttle.dock_target_station = "centcom_shuttle_dock_airlock"
|
||||
shuttle.dock_target_offsite = "centcom_shuttle_bay"
|
||||
shuttles["CentCom"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
/* VOREStation Removal - This is a multishuttle now and is down there with them.
|
||||
shuttle = new()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
|
||||
shuttle.area_offsite = locate(/area/shuttle/administration/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/administration/station)
|
||||
shuttle.docking_controller_tag = "admin_shuttle"
|
||||
shuttle.dock_target_station = "admin_shuttle_dock_airlock"
|
||||
shuttle.dock_target_offsite = "admin_shuttle_bay"
|
||||
shuttles["Administration"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
VOREStation Removal End */
|
||||
shuttle = new()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
|
||||
shuttle.area_offsite = locate(/area/shuttle/trade/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/trade/station)
|
||||
shuttle.docking_controller_tag = "trade_shuttle"
|
||||
shuttle.dock_target_station = "trade_shuttle_dock_airlock"
|
||||
shuttle.dock_target_offsite = "trade_shuttle_bay"
|
||||
shuttles["Trade"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
shuttle = new()
|
||||
shuttle.area_offsite = locate(/area/shuttle/alien/base)
|
||||
shuttle.area_station = locate(/area/shuttle/alien/mine)
|
||||
shuttles["Alien"] = shuttle
|
||||
//process_shuttles += shuttle //don't need to process this. It can only be moved using admin magic anyways.
|
||||
|
||||
// Public shuttles
|
||||
shuttle = new()
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/constructionsite/site)
|
||||
shuttle.area_station = locate(/area/shuttle/constructionsite/station)
|
||||
shuttle.docking_controller_tag = "engineering_shuttle"
|
||||
shuttle.dock_target_station = "engineering_dock_airlock"
|
||||
shuttle.dock_target_offsite = "edock_airlock"
|
||||
shuttles["Engineering"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
shuttle = new()
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/mining/outpost)
|
||||
shuttle.area_station = locate(/area/shuttle/mining/station)
|
||||
shuttle.docking_controller_tag = "mining_shuttle"
|
||||
shuttle.dock_target_station = "mining_dock_airlock"
|
||||
shuttle.dock_target_offsite = "mining_outpost_airlock"
|
||||
shuttles["Mining"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
shuttle = new()
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/research/outpost)
|
||||
shuttle.area_station = locate(/area/shuttle/research/station)
|
||||
shuttle.docking_controller_tag = "research_shuttle"
|
||||
shuttle.dock_target_station = "research_dock_airlock"
|
||||
shuttle.dock_target_offsite = "research_outpost_dock"
|
||||
shuttles["Research"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
// ERT Shuttle
|
||||
var/datum/shuttle/ferry/multidock/specops/ERT = new()
|
||||
ERT.location = 0
|
||||
ERT.warmup_time = 10
|
||||
ERT.area_offsite = locate(/area/shuttle/specops/station) //centcom is the home station, the player station is offsite
|
||||
ERT.area_station = locate(/area/shuttle/specops/centcom)
|
||||
ERT.docking_controller_tag = "specops_shuttle_port"
|
||||
ERT.docking_controller_tag_station = "specops_shuttle_port"
|
||||
ERT.docking_controller_tag_offsite = "specops_shuttle_fore"
|
||||
ERT.dock_target_station = "specops_centcom_dock"
|
||||
ERT.dock_target_offsite = "specops_dock_airlock"
|
||||
shuttles["Special Operations"] = ERT
|
||||
process_shuttles += ERT
|
||||
|
||||
//Skipjack.
|
||||
var/datum/shuttle/multi_shuttle/VS = new/datum/shuttle/multi_shuttle()
|
||||
VS.origin = locate(/area/skipjack_station/start)
|
||||
|
||||
VS.destinations = list(
|
||||
"Fore Starboard Solars" = locate(/area/skipjack_station/northeast_solars),
|
||||
"Fore Port Solars" = locate(/area/skipjack_station/northwest_solars),
|
||||
"Aft Starboard Solars" = locate(/area/skipjack_station/southeast_solars),
|
||||
"Aft Port Solars" = locate(/area/skipjack_station/southwest_solars), // Vorestation edit
|
||||
"Station Arrivals Dock" = locate(/area/skipjack_station/southwest_solars),
|
||||
"Mining Station" = locate(/area/skipjack_station/mining)
|
||||
)
|
||||
|
||||
VS.announcer = "Colony Automated Radar Array"
|
||||
VS.arrival_message = "Attention. Unidentified object approaching the colony."
|
||||
VS.departure_message = "Attention. Unidentified object exiting local space. Unidentified object expected to escape Virgo-3's gravity well with current velocity." // Vorestation edit
|
||||
VS.interim = locate(/area/skipjack_station/transit)
|
||||
|
||||
VS.warmup_time = 0
|
||||
shuttles["Skipjack"] = VS
|
||||
|
||||
//Nuke Ops shuttle.
|
||||
var/datum/shuttle/multi_shuttle/MS = new/datum/shuttle/multi_shuttle()
|
||||
MS.origin = locate(/area/syndicate_station/start)
|
||||
MS.start_location = "Mercenary Base"
|
||||
|
||||
MS.destinations = list(
|
||||
"Northwest of the station" = locate(/area/syndicate_station/northwest),
|
||||
"North of the station" = locate(/area/syndicate_station/north),
|
||||
"Northeast of the station" = locate(/area/syndicate_station/northeast),
|
||||
"Southwest of the station" = locate(/area/syndicate_station/southwest),
|
||||
"South of the station" = locate(/area/syndicate_station/south),
|
||||
"Southeast of the station" = locate(/area/syndicate_station/southeast),
|
||||
"Telecomms Satellite" = locate(/area/syndicate_station/commssat),
|
||||
"Mining Station" = locate(/area/syndicate_station/mining),
|
||||
"Arrivals dock" = locate(/area/syndicate_station/arrivals_dock),
|
||||
)
|
||||
|
||||
MS.docking_controller_tag = "merc_shuttle"
|
||||
MS.destination_dock_targets = list(
|
||||
"Mercenary Base" = "merc_base",
|
||||
"Arrivals dock" = "nuke_shuttle_dock_airlock",
|
||||
)
|
||||
|
||||
MS.announcer = "Automated Traffic Control"
|
||||
MS.arrival_message = "Attention. A vessel is approaching the colony."
|
||||
MS.departure_message = "Attention. A vessel is now leaving from the colony."
|
||||
MS.interim = locate(/area/syndicate_station/transit)
|
||||
|
||||
MS.warmup_time = 0
|
||||
shuttles["Mercenary"] = MS
|
||||
|
||||
// VOREStation Add - The admin shuttle is a multishuttle now.
|
||||
//Centcom admin shuttle.
|
||||
var/datum/shuttle/multi_shuttle/CC = new/datum/shuttle/multi_shuttle()
|
||||
CC.legit = 1
|
||||
CC.origin = locate(/area/shuttle/administration/centcom)
|
||||
CC.start_location = "Central Command (AS)"
|
||||
|
||||
CC.destinations = list(
|
||||
"Deep Space (AS)" = locate(/area/shuttle/administration/transit),
|
||||
"NSS Adephagia (AS)" = locate(/area/shuttle/administration/station)
|
||||
)
|
||||
|
||||
CC.docking_controller_tag = "admin_shuttle"
|
||||
CC.destination_dock_targets = list(
|
||||
"Central Command (AS)" = "admin_shuttle_bay",
|
||||
"NSS Adephagia (AS)" = "admin_shuttle_dock_airlock"
|
||||
)
|
||||
|
||||
var/area/ccaway_dest = locate(/area/shuttle/administration/away_mission)
|
||||
if(ccaway_dest.contents.len) //Otherwise this is an empty imaginary area
|
||||
CC.destinations["Unknown Location [rand(1000,9999)]"] = ccaway_dest
|
||||
|
||||
CC.announcer = "Automated Traffic Control"
|
||||
//These seem backwards because they are written from the perspective of the merc and vox ships
|
||||
CC.departure_message = "Attention. A Centcom vessel is approaching the colony."
|
||||
CC.arrival_message = "Attention. A Centcom vessel is now leaving from the colony."
|
||||
|
||||
CC.move_time = 0
|
||||
CC.warmup_time = 0
|
||||
shuttles["Administration"] = CC
|
||||
//VOREStation Add End - Admin shuttle.
|
||||
//////////////////////////////////////////////////////////////
|
||||
//VOREStation Add - Away-mission shuttle
|
||||
var/datum/shuttle/multi_shuttle/AM = new/datum/shuttle/multi_shuttle()
|
||||
AM.legit = 1
|
||||
AM.origin = locate(/area/shuttle/awaymission/home)
|
||||
AM.start_location = "NSS Adephagia (AM)"
|
||||
|
||||
AM.destinations = list(
|
||||
"Old Engineering Base (AM)" = locate(/area/shuttle/awaymission/oldengbase)
|
||||
)
|
||||
|
||||
AM.docking_controller_tag = "awaymission_shuttle"
|
||||
AM.destination_dock_targets = list(
|
||||
"NSS Adephagia (AM)" = "d1a2_dock_airlock"
|
||||
)
|
||||
|
||||
var/area/awaym_dest = locate(/area/shuttle/awaymission/away)
|
||||
if(awaym_dest.contents.len) //Otherwise this is an empty imaginary area
|
||||
AM.destinations["Unknown Location [rand(1000,9999)]"] = awaym_dest
|
||||
|
||||
AM.announcer = "Automated Traffic Control"
|
||||
//These seem backwards because they are written from the perspective of the merc and vox ships
|
||||
AM.departure_message = "Attention. The away mission vessel is approaching the colony."
|
||||
AM.arrival_message = "Attention. The away mission vessel is now leaving from the colony."
|
||||
AM.interim = locate(/area/shuttle/awaymission/warp)
|
||||
|
||||
AM.move_time = 60
|
||||
AM.warmup_time = 8
|
||||
shuttles["AwayMission"] = AM
|
||||
//VOREStation Add End - Away-mission shuttle
|
||||
///////////////////////////////////////////////
|
||||
//VOREStation Add - Belter Shuttle
|
||||
shuttle = new/datum/shuttle/ferry()
|
||||
shuttle.location = 0
|
||||
shuttle.warmup_time = 6
|
||||
shuttle.area_station = locate(/area/shuttle/belter/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/belter/belt/zone1)
|
||||
shuttle.area_transition = locate(/area/shuttle/belter/transit)
|
||||
shuttle.docking_controller_tag = "belter_docking"
|
||||
shuttle.dock_target_station = "belter_nodocking" //Fake tags to prevent the shuttle from opening doors.
|
||||
shuttle.dock_target_offsite = "belter_nodocking"
|
||||
shuttle.transit_direction = EAST
|
||||
shuttle.move_time = 60 + rand(10,40)
|
||||
process_shuttles += shuttle
|
||||
shuttles["Belter"] = shuttle
|
||||
//VOREStation Add End - Belter Shuttle
|
||||
@@ -1,210 +0,0 @@
|
||||
|
||||
var/global/datum/shuttle_controller/shuttle_controller
|
||||
|
||||
|
||||
/datum/shuttle_controller
|
||||
var/list/shuttles //maps shuttle tags to shuttle datums, so that they can be looked up.
|
||||
var/list/process_shuttles //simple list of shuttles, for processing
|
||||
|
||||
/datum/shuttle_controller/proc/process()
|
||||
//process ferry shuttles
|
||||
for (var/datum/shuttle/ferry/shuttle in process_shuttles)
|
||||
if (shuttle.process_state)
|
||||
shuttle.process()
|
||||
|
||||
|
||||
//This is called by gameticker after all the machines and radio frequencies have been properly initialized
|
||||
/datum/shuttle_controller/proc/setup_shuttle_docks()
|
||||
for(var/shuttle_tag in shuttles)
|
||||
var/datum/shuttle/shuttle = shuttles[shuttle_tag]
|
||||
shuttle.init_docking_controllers()
|
||||
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
|
||||
|
||||
for(var/obj/machinery/embedded_controller/C in machines)
|
||||
if(istype(C.program, /datum/computer/file/embedded_program/docking))
|
||||
C.program.tag = null //clear the tags, 'cause we don't need 'em anymore
|
||||
|
||||
/datum/shuttle_controller/New()
|
||||
shuttles = list()
|
||||
process_shuttles = list()
|
||||
|
||||
var/datum/shuttle/ferry/shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Escape shuttle and pods
|
||||
shuttle = new/datum/shuttle/ferry/emergency()
|
||||
shuttle.location = 1 // At offsite
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/shuttle/escape/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/escape/station)
|
||||
shuttle.area_transition = locate(/area/shuttle/escape/transit)
|
||||
shuttle.docking_controller_tag = "escape_shuttle"
|
||||
shuttle.dock_target_station = "escape_dock"
|
||||
shuttle.dock_target_offsite = "centcom_dock"
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN
|
||||
shuttles["Escape"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0 // At station
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/large_escape_pod1/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod1/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/large_escape_pod1/transit)
|
||||
shuttle.docking_controller_tag = "large_escape_pod_1"
|
||||
shuttle.dock_target_station = "large_escape_pod_1_berth"
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Large Escape Pod 1"] = shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
shuttle = new/datum/shuttle/ferry/escape_pod()
|
||||
shuttle.location = 0 // At station
|
||||
shuttle.warmup_time = 0
|
||||
shuttle.area_station = locate(/area/shuttle/large_escape_pod2/station)
|
||||
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod2/centcom)
|
||||
shuttle.area_transition = locate(/area/shuttle/large_escape_pod2/transit)
|
||||
shuttle.docking_controller_tag = "large_escape_pod_2"
|
||||
shuttle.dock_target_station = "large_escape_pod_2_berth"
|
||||
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
|
||||
process_shuttles += shuttle
|
||||
shuttles["Large Escape Pod 2"] = shuttle
|
||||
|
||||
//give the emergency shuttle controller it's shuttles
|
||||
emergency_shuttle.shuttle = shuttles["Escape"]
|
||||
emergency_shuttle.escape_pods = list(
|
||||
shuttles["Large Escape Pod 1"],
|
||||
shuttles["Large Escape Pod 2"],
|
||||
)
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Supply shuttle
|
||||
shuttle = new/datum/shuttle/ferry/supply()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10
|
||||
shuttle.area_offsite = locate(/area/supply/dock)
|
||||
shuttle.area_station = locate(/area/supply/station)
|
||||
shuttle.docking_controller_tag = "supply_shuttle"
|
||||
shuttle.dock_target_station = "cargo_bay"
|
||||
shuttles["Supply"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
supply_controller.shuttle = shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Trade Ship
|
||||
shuttle = new()
|
||||
shuttle.location = 1
|
||||
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
|
||||
shuttle.area_offsite = locate(/area/shuttle/trade/centcom)
|
||||
shuttle.area_station = locate(/area/shuttle/trade/station)
|
||||
shuttle.docking_controller_tag = "trade_shuttle"
|
||||
shuttle.dock_target_station = "trade_shuttle_dock_airlock"
|
||||
shuttle.dock_target_offsite = "trade_shuttle_bay"
|
||||
shuttles["Trade"] = shuttle
|
||||
process_shuttles += shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Away Mission Shuttle
|
||||
var/datum/shuttle/multi_shuttle/AM = new/datum/shuttle/multi_shuttle()
|
||||
AM.legit = 1
|
||||
AM.origin = locate(/area/shuttle/awaymission/home)
|
||||
AM.start_location = "NSS Adephagia (AM)"
|
||||
|
||||
AM.destinations = list(
|
||||
"Old Engineering Base (AM)" = locate(/area/shuttle/awaymission/oldengbase)
|
||||
)
|
||||
|
||||
AM.docking_controller_tag = "awaymission_shuttle"
|
||||
AM.destination_dock_targets = list(
|
||||
"NSS Adephagia (AM)" = "d1a2_dock_airlock"
|
||||
)
|
||||
|
||||
var/area/awaym_dest = locate(/area/shuttle/awaymission/away)
|
||||
if(awaym_dest.contents.len) //Otherwise this is an empty imaginary area
|
||||
AM.destinations["Unknown Location [rand(1000,9999)]"] = awaym_dest
|
||||
|
||||
AM.announcer = "Automated Traffic Control"
|
||||
//These seem backwards because they are written from the perspective of the merc and vox ships
|
||||
AM.departure_message = "Attention. The away mission vessel is approaching the colony."
|
||||
AM.arrival_message = "Attention. The away mission vessel is now leaving from the colony."
|
||||
AM.interim = locate(/area/shuttle/awaymission/warp)
|
||||
|
||||
AM.move_time = 60
|
||||
AM.warmup_time = 8
|
||||
shuttles["AwayMission"] = AM
|
||||
|
||||
|
||||
// TODO - Not implemented yet on new map
|
||||
///////////////////////////////////////////////
|
||||
//VOREStation Add - Belter Shuttle
|
||||
// shuttle = new/datum/shuttle/ferry()
|
||||
// shuttle.location = 0
|
||||
// shuttle.warmup_time = 6
|
||||
// shuttle.area_station = locate(/area/shuttle/belter/station)
|
||||
// shuttle.area_offsite = locate(/area/shuttle/belter/belt/zone1)
|
||||
// shuttle.area_transition = locate(/area/shuttle/belter/transit)
|
||||
// shuttle.docking_controller_tag = "belter_docking"
|
||||
// shuttle.dock_target_station = "belter_nodocking" //Fake tags to prevent the shuttle from opening doors.
|
||||
// shuttle.dock_target_offsite = "belter_nodocking"
|
||||
// shuttle.transit_direction = EAST
|
||||
// shuttle.move_time = 60 + rand(10,40)
|
||||
// process_shuttles += shuttle
|
||||
// shuttles["Belter"] = shuttle
|
||||
//VOREStation Add End - Belter Shuttle
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Tether Shuttle
|
||||
var/datum/shuttle/ferry/tether_backup/TB = new()
|
||||
TB.location = 1 // At offsite
|
||||
TB.warmup_time = 5
|
||||
TB.move_time = 45
|
||||
TB.area_offsite = locate(/area/shuttle/tether/surface)
|
||||
TB.area_station = locate(/area/shuttle/tether/station)
|
||||
TB.area_transition = locate(/area/shuttle/tether/transit)
|
||||
TB.crash_areas = list(locate(/area/shuttle/tether/crash1), locate(/area/shuttle/tether/crash2))
|
||||
TB.docking_controller_tag = "tether_shuttle"
|
||||
TB.dock_target_station = "tether_dock_airlock"
|
||||
TB.dock_target_offsite = "tether_pad_airlock"
|
||||
shuttles["Tether Backup"] = TB
|
||||
process_shuttles += TB
|
||||
|
||||
for(var/obj/structure/shuttle/engine/propulsion/E in TB.area_offsite)
|
||||
TB.engines += E
|
||||
for(var/obj/machinery/computer/shuttle_control/tether_backup/comp in TB.area_offsite)
|
||||
TB.computer = comp
|
||||
break
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Antag Space "Proto Shuttle" Shuttle
|
||||
AM = new/datum/shuttle/multi_shuttle()
|
||||
AM.docking_controller_tag = "antag_space_shuttle"
|
||||
AM.start_location = "Home Base"
|
||||
AM.origin = locate(/area/shuttle/antag_space/base)
|
||||
AM.interim = locate(/area/shuttle/antag_space/transit)
|
||||
AM.destinations = list(
|
||||
"Nearby" = locate(/area/shuttle/antag_space/north),
|
||||
"Docks" = locate(/area/shuttle/antag_space/docks)
|
||||
)
|
||||
AM.destination_dock_targets = list("Home Base" = "antag_space_dock")
|
||||
AM.move_time = 60
|
||||
AM.warmup_time = 8
|
||||
shuttles["Proto"] = AM
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Antag Surface "Land Crawler" Shuttle
|
||||
AM = new/datum/shuttle/multi_shuttle()
|
||||
AM.docking_controller_tag = "antag_ground_shuttle"
|
||||
AM.start_location = "Home Base"
|
||||
AM.origin = locate(/area/shuttle/antag_ground/base)
|
||||
AM.interim = locate(/area/shuttle/antag_ground/transit)
|
||||
AM.destinations = list(
|
||||
"Solar Array" = locate(/area/shuttle/antag_ground/solars),
|
||||
"Mining Outpost" = locate(/area/shuttle/antag_ground/mining)
|
||||
)
|
||||
AM.destination_dock_targets = list("Home Base" = "antag_ground_dock")
|
||||
AM.move_time = 60
|
||||
AM.warmup_time = 8
|
||||
shuttles["Land Crawler"] = AM
|
||||
@@ -0,0 +1,202 @@
|
||||
/datum/controller/subsystem
|
||||
// Metadata; you should define these.
|
||||
name = "fire coderbus" //name of the subsystem
|
||||
var/init_order = 0 //order of initialization. Higher numbers are initialized first, lower numbers later. Can be decimal and negative values.
|
||||
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
|
||||
var/priority = 50 //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
|
||||
|
||||
var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
|
||||
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
|
||||
|
||||
//set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
|
||||
// use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list
|
||||
var/can_fire = TRUE
|
||||
|
||||
// Bookkeeping variables; probably shouldn't mess with these.
|
||||
var/last_fire = 0 //last world.time we called fire()
|
||||
var/next_fire = 0 //scheduled world.time for next fire()
|
||||
var/cost = 0 //average time to execute
|
||||
var/tick_usage = 0 //average tick usage
|
||||
var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
|
||||
var/paused_ticks = 0 //ticks this ss is taking to run right now.
|
||||
var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
|
||||
var/ticks = 1 //how many ticks does this ss take to run on avg.
|
||||
var/times_fired = 0 //number of times we have called fire()
|
||||
var/queued_time = 0 //time we entered the queue, (for timing and priority reasons)
|
||||
var/queued_priority //we keep a running total to make the math easier, if priority changes mid-fire that would break our running total, so we store it here
|
||||
//linked list stuff for the queue
|
||||
var/datum/controller/subsystem/queue_next
|
||||
var/datum/controller/subsystem/queue_prev
|
||||
|
||||
var/static/failure_strikes = 0 //How many times we suspect this subsystem has crashed the MC, 3 strikes and you're out!
|
||||
|
||||
//Do not override
|
||||
/datum/controller/subsystem/New()
|
||||
return
|
||||
|
||||
// Used to initialize the subsystem BEFORE the map has loaded
|
||||
// Called AFTER Recover if that is called
|
||||
// Prefer to use Initialize if possible
|
||||
/datum/controller/subsystem/proc/PreInit()
|
||||
return
|
||||
|
||||
//This is used so the mc knows when the subsystem sleeps. do not override.
|
||||
/datum/controller/subsystem/proc/ignite(resumed = 0)
|
||||
set waitfor = 0
|
||||
. = SS_SLEEPING
|
||||
fire(resumed)
|
||||
. = state
|
||||
if (state == SS_SLEEPING)
|
||||
state = SS_IDLE
|
||||
if (state == SS_PAUSING)
|
||||
var/QT = queued_time
|
||||
enqueue()
|
||||
state = SS_PAUSED
|
||||
queued_time = QT
|
||||
|
||||
//previously, this would have been named 'process()' but that name is used everywhere for different things!
|
||||
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
|
||||
//Sleeping in here prevents future fires until returned.
|
||||
/datum/controller/subsystem/proc/fire(resumed = 0)
|
||||
flags |= SS_NO_FIRE
|
||||
throw EXCEPTION("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
|
||||
|
||||
/datum/controller/subsystem/Destroy()
|
||||
dequeue()
|
||||
can_fire = 0
|
||||
flags |= SS_NO_FIRE
|
||||
Master.subsystems -= src
|
||||
|
||||
|
||||
//Queue it to run.
|
||||
// (we loop thru a linked list until we get to the end or find the right point)
|
||||
// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
|
||||
/datum/controller/subsystem/proc/enqueue()
|
||||
var/SS_priority = priority
|
||||
var/SS_flags = flags
|
||||
var/datum/controller/subsystem/queue_node
|
||||
var/queue_node_priority
|
||||
var/queue_node_flags
|
||||
|
||||
for (queue_node = Master.queue_head; queue_node; queue_node = queue_node.queue_next)
|
||||
queue_node_priority = queue_node.queued_priority
|
||||
queue_node_flags = queue_node.flags
|
||||
|
||||
if (queue_node_flags & SS_TICKER)
|
||||
if (!(SS_flags & SS_TICKER))
|
||||
continue
|
||||
if (queue_node_priority < SS_priority)
|
||||
break
|
||||
|
||||
else if (queue_node_flags & SS_BACKGROUND)
|
||||
if (!(SS_flags & SS_BACKGROUND))
|
||||
break
|
||||
if (queue_node_priority < SS_priority)
|
||||
break
|
||||
|
||||
else
|
||||
if (SS_flags & SS_BACKGROUND)
|
||||
continue
|
||||
if (SS_flags & SS_TICKER)
|
||||
break
|
||||
if (queue_node_priority < SS_priority)
|
||||
break
|
||||
|
||||
queued_time = world.time
|
||||
queued_priority = SS_priority
|
||||
state = SS_QUEUED
|
||||
if (SS_flags & SS_BACKGROUND) //update our running total
|
||||
Master.queue_priority_count_bg += SS_priority
|
||||
else
|
||||
Master.queue_priority_count += SS_priority
|
||||
|
||||
queue_next = queue_node
|
||||
if (!queue_node)//we stopped at the end, add to tail
|
||||
queue_prev = Master.queue_tail
|
||||
if (Master.queue_tail)
|
||||
Master.queue_tail.queue_next = src
|
||||
else //empty queue, we also need to set the head
|
||||
Master.queue_head = src
|
||||
Master.queue_tail = src
|
||||
|
||||
else if (queue_node == Master.queue_head)//insert at start of list
|
||||
Master.queue_head.queue_prev = src
|
||||
Master.queue_head = src
|
||||
queue_prev = null
|
||||
else
|
||||
queue_node.queue_prev.queue_next = src
|
||||
queue_prev = queue_node.queue_prev
|
||||
queue_node.queue_prev = src
|
||||
|
||||
|
||||
/datum/controller/subsystem/proc/dequeue()
|
||||
if (queue_next)
|
||||
queue_next.queue_prev = queue_prev
|
||||
if (queue_prev)
|
||||
queue_prev.queue_next = queue_next
|
||||
if (src == Master.queue_tail)
|
||||
Master.queue_tail = queue_prev
|
||||
if (src == Master.queue_head)
|
||||
Master.queue_head = queue_next
|
||||
queued_time = 0
|
||||
if (state == SS_QUEUED)
|
||||
state = SS_IDLE
|
||||
|
||||
|
||||
/datum/controller/subsystem/proc/pause()
|
||||
. = 1
|
||||
switch(state)
|
||||
if(SS_RUNNING)
|
||||
state = SS_PAUSED
|
||||
if(SS_SLEEPING)
|
||||
state = SS_PAUSING
|
||||
|
||||
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/controller/subsystem/Initialize(start_timeofday)
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
return time
|
||||
|
||||
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
|
||||
/datum/controller/subsystem/stat_entry(msg)
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
|
||||
|
||||
if(can_fire && !(SS_NO_FIRE in flags))
|
||||
msg = "[round(cost,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
|
||||
else
|
||||
msg = "OFFLINE\t[msg]"
|
||||
|
||||
var/title = name
|
||||
if (can_fire)
|
||||
title = "\[[state_letter()]][title]"
|
||||
|
||||
stat(title, statclick.update(msg))
|
||||
|
||||
/datum/controller/subsystem/proc/state_letter()
|
||||
switch (state)
|
||||
if (SS_RUNNING)
|
||||
. = "R"
|
||||
if (SS_QUEUED)
|
||||
. = "Q"
|
||||
if (SS_PAUSED, SS_PAUSING)
|
||||
. = "P"
|
||||
if (SS_SLEEPING)
|
||||
. = "S"
|
||||
if (SS_IDLE)
|
||||
. = " "
|
||||
|
||||
//could be used to postpone a costly subsystem for (default one) var/cycles, cycles
|
||||
//for instance, during cpu intensive operations like explosions
|
||||
/datum/controller/subsystem/proc/postpone(cycles = 1)
|
||||
if(next_fire - world.time < wait)
|
||||
next_fire += (wait*cycles)
|
||||
|
||||
//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
|
||||
//should attempt to salvage what it can from the old instance of subsystem
|
||||
/datum/controller/subsystem/Recover()
|
||||
@@ -1,46 +0,0 @@
|
||||
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
|
||||
|
||||
/datum/subsystem
|
||||
//things you will want to define
|
||||
var/name //name of the subsystem
|
||||
var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values.
|
||||
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
|
||||
|
||||
//things you will probably want to leave alone
|
||||
var/can_fire = 0 //prevent fire() calls
|
||||
var/last_fire = 0 //last world.time we called fire()
|
||||
var/next_fire = 0 //scheduled world.time for next fire()
|
||||
var/cpu = 0 //cpu-usage stats (somewhat vague)
|
||||
var/cost = 0 //average time to execute
|
||||
var/times_fired = 0 //number of times we have called fire()
|
||||
|
||||
//used to initialize the subsystem BEFORE the map has loaded
|
||||
/datum/subsystem/New()
|
||||
|
||||
//previously, this would have been named 'process()' but that name is used everywhere for different things!
|
||||
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
|
||||
//fire(), and the procs it calls, SHOULD NOT HAVE ANY SLEEP OPERATIONS in them!
|
||||
//YE BE WARNED!
|
||||
/datum/subsystem/proc/fire()
|
||||
can_fire = 0
|
||||
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/subsystem/proc/Initialize(start_timeofday)
|
||||
var/time = (world.timeofday - start_timeofday) / 10
|
||||
var/msg = "Initialized [name] SubSystem within [time] seconds"
|
||||
world << "<span class='userdanger'>[msg]</span>"
|
||||
world.log << msg
|
||||
|
||||
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
|
||||
/datum/subsystem/proc/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)")
|
||||
|
||||
//could be used to postpone a costly subsystem for one cycle
|
||||
//for instance, during cpu intensive operations like explosions
|
||||
/datum/subsystem/proc/postpone()
|
||||
if(next_fire - world.time < wait)
|
||||
next_fire += wait
|
||||
|
||||
//usually called via datum/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
|
||||
//should attempt to salvage what it can from the old instance of subsystem
|
||||
/datum/subsystem/proc/Recover()
|
||||
@@ -0,0 +1,378 @@
|
||||
//
|
||||
// Garbage Collector Subsystem - Implements qdel() and the GC queue
|
||||
//
|
||||
SUBSYSTEM_DEF(garbage)
|
||||
name = "Garbage"
|
||||
priority = 15
|
||||
wait = 5
|
||||
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
|
||||
var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
var/delslasttick = 0 // number of del()'s we've done this tick
|
||||
var/gcedlasttick = 0 // number of things that gc'ed last tick
|
||||
var/totaldels = 0
|
||||
var/totalgcs = 0
|
||||
|
||||
var/highest_del_time = 0
|
||||
var/highest_del_tickusage = 0
|
||||
|
||||
var/list/queue = list() // list of refID's of things that should be garbage collected
|
||||
// refID's are associated with the time at which they time out and need to be manually del()
|
||||
// we do this so we aren't constantly locating them and preventing them from being gc'd
|
||||
|
||||
var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks
|
||||
|
||||
var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
|
||||
|
||||
var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
|
||||
// all types that did not respect qdel(A, force=TRUE) and returned one
|
||||
// of the immortality qdel hints
|
||||
var/list/noforcerespect = list()
|
||||
|
||||
#ifdef TESTING
|
||||
var/list/qdel_list = list() // list of all types that have been qdel()eted
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/garbage/stat_entry(msg)
|
||||
msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
|
||||
msg += "GR:"
|
||||
if (!(delslasttick+gcedlasttick))
|
||||
msg += "n/a|"
|
||||
else
|
||||
msg += "[round((gcedlasttick/(delslasttick+gcedlasttick))*100, 0.01)]%|"
|
||||
|
||||
msg += "TD:[totaldels]|TG:[totalgcs]|"
|
||||
if (!(totaldels+totalgcs))
|
||||
msg += "n/a|"
|
||||
else
|
||||
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
|
||||
..(msg)
|
||||
|
||||
/datum/controller/subsystem/garbage/Shutdown()
|
||||
//Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
|
||||
if(didntgc.len || sleptDestroy.len)
|
||||
var/list/dellog = list()
|
||||
for(var/path in didntgc)
|
||||
dellog += "Path : [path] \n"
|
||||
dellog += "Failures : [didntgc[path]] \n"
|
||||
if(path in sleptDestroy)
|
||||
dellog += "Sleeps : [sleptDestroy[path]] \n"
|
||||
sleptDestroy -= path
|
||||
for(var/path in sleptDestroy)
|
||||
dellog += "Path : [path] \n"
|
||||
dellog += "Sleeps : [sleptDestroy[path]] \n"
|
||||
log_misc(dellog.Join())
|
||||
|
||||
/datum/controller/subsystem/garbage/fire()
|
||||
HandleToBeQueued()
|
||||
if(state == SS_RUNNING)
|
||||
HandleQueue()
|
||||
|
||||
if (state == SS_PAUSED) //make us wait again before the next run.
|
||||
state = SS_RUNNING
|
||||
|
||||
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
|
||||
//Don't attempt to optimize, not worth the effort.
|
||||
/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
|
||||
var/list/tobequeued = src.tobequeued
|
||||
var/starttime = world.time
|
||||
var/starttimeofday = world.timeofday
|
||||
while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
|
||||
if (MC_TICK_CHECK)
|
||||
break
|
||||
var/ref = tobequeued[1]
|
||||
Queue(ref)
|
||||
tobequeued.Cut(1, 2)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HandleQueue()
|
||||
delslasttick = 0
|
||||
gcedlasttick = 0
|
||||
var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
|
||||
var/list/queue = src.queue
|
||||
var/starttime = world.time
|
||||
var/starttimeofday = world.timeofday
|
||||
while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
|
||||
if (MC_TICK_CHECK)
|
||||
break
|
||||
var/refID = queue[1]
|
||||
if (!refID)
|
||||
queue.Cut(1, 2)
|
||||
continue
|
||||
|
||||
var/GCd_at_time = queue[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
break // Everything else is newer, skip them
|
||||
queue.Cut(1, 2)
|
||||
var/datum/A
|
||||
A = locate(refID)
|
||||
if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
A.find_references()
|
||||
#endif
|
||||
|
||||
// Something's still referring to the qdel'd object. Kill it.
|
||||
var/type = A.type
|
||||
testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
|
||||
didntgc["[type]"]++
|
||||
|
||||
HardDelete(A)
|
||||
|
||||
++delslasttick
|
||||
++totaldels
|
||||
else
|
||||
++gcedlasttick
|
||||
++totalgcs
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
|
||||
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
tobequeued += A
|
||||
A.gc_destroyed = GC_QUEUED_FOR_QUEUING
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/Queue(datum/A)
|
||||
if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
|
||||
return
|
||||
if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
|
||||
HardDelete(A)
|
||||
return
|
||||
var/gctime = world.time
|
||||
var/refid = "\ref[A]"
|
||||
|
||||
A.gc_destroyed = gctime
|
||||
|
||||
if (queue[refid])
|
||||
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
|
||||
|
||||
queue[refid] = gctime
|
||||
|
||||
//this is purely to seperate things profile wise.
|
||||
/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
|
||||
var/time = world.timeofday
|
||||
var/tick = world.tick_usage
|
||||
var/ticktime = world.time
|
||||
|
||||
var/type = A.type
|
||||
var/refID = "\ref[A]"
|
||||
|
||||
del(A)
|
||||
|
||||
tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100))
|
||||
if (tick > highest_del_tickusage)
|
||||
highest_del_tickusage = tick
|
||||
time = world.timeofday - time
|
||||
if (!time && TICK_DELTA_TO_MS(tick) > 1)
|
||||
time = TICK_DELTA_TO_MS(tick)/100
|
||||
if (time > highest_del_time)
|
||||
highest_del_time = time
|
||||
if (time > 10)
|
||||
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
|
||||
postpone(time/5)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
|
||||
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
tobequeued += A
|
||||
A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
|
||||
|
||||
/datum/controller/subsystem/garbage/Recover()
|
||||
if (istype(SSgarbage.queue))
|
||||
queue |= SSgarbage.queue
|
||||
if (istype(SSgarbage.tobequeued))
|
||||
tobequeued |= SSgarbage.tobequeued
|
||||
|
||||
// Should be treated as a replacement for the 'del' keyword.
|
||||
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(datum/D, force=FALSE)
|
||||
if(!istype(D))
|
||||
del(D)
|
||||
return
|
||||
#ifdef TESTING
|
||||
SSgarbage.qdel_list += D.type
|
||||
#endif
|
||||
if(isnull(D.gc_destroyed))
|
||||
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
|
||||
var/start_time = world.time
|
||||
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
|
||||
if(world.time != start_time)
|
||||
SSgarbage.sleptDestroy[D.type]++
|
||||
if(!D)
|
||||
return
|
||||
switch(hint)
|
||||
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
if (QDEL_HINT_IWILLGC)
|
||||
D.gc_destroyed = world.time
|
||||
return
|
||||
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
D.gc_destroyed = null //clear the gc variable (important!)
|
||||
return
|
||||
// Returning LETMELIVE after being told to force destroy
|
||||
// indicates the objects Destroy() does not respect force
|
||||
if(!SSgarbage.noforcerespect[D.type])
|
||||
SSgarbage.noforcerespect[D.type] = D.type
|
||||
crash_with("[D.type] has been force deleted, but is \
|
||||
returning an immortal QDEL_HINT, indicating it does \
|
||||
not respect the force flag for qdel(). It has been \
|
||||
placed in the queue, further instances of this type \
|
||||
will also be queued.")
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
|
||||
SSgarbage.HardQueue(D)
|
||||
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
SSgarbage.HardDelete(D)
|
||||
if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
#ifdef TESTING
|
||||
D.find_references()
|
||||
#endif
|
||||
else
|
||||
if(!SSgarbage.noqdelhint[D.type])
|
||||
SSgarbage.noqdelhint[D.type] = D.type
|
||||
crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
|
||||
|
||||
// Default implementation of clean-up code.
|
||||
// This should be overridden to remove all references pointing to the object being destroyed.
|
||||
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
|
||||
/datum/proc/Destroy(force=FALSE)
|
||||
tag = null
|
||||
nanomanager.close_uis(src)
|
||||
return QDEL_HINT_QUEUE
|
||||
|
||||
/datum/var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
#ifdef TESTING
|
||||
/datum/var/running_find_references
|
||||
/datum/var/last_find_references = 0
|
||||
|
||||
/datum/verb/find_refs()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
find_references(FALSE)
|
||||
|
||||
/datum/proc/find_references(skip_alert)
|
||||
running_find_references = type
|
||||
if(usr && usr.client)
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = 1
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
return
|
||||
|
||||
if(!skip_alert)
|
||||
if(alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
//this keeps the garbage collector from failing to collect objects being searched for in here
|
||||
SSgarbage.can_fire = 0
|
||||
|
||||
if(usr && usr.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
testing("Beginning search for references to a [type].")
|
||||
last_find_references = world.time
|
||||
find_references_in_globals()
|
||||
for(var/datum/thing in world)
|
||||
DoSearchVar(thing, "WorldRef: [thing]")
|
||||
testing("Completed search for references to a [type].")
|
||||
if(usr && usr.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = 1
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
|
||||
/client/verb/purge_all_destroyed_objects()
|
||||
set category = "Debug"
|
||||
if(SSgarbage)
|
||||
while(SSgarbage.queue.len)
|
||||
var/datum/o = locate(SSgarbage.queue[1])
|
||||
if(istype(o) && o.gc_destroyed)
|
||||
del(o)
|
||||
SSgarbage.totaldels++
|
||||
SSgarbage.queue.Cut(1, 2)
|
||||
|
||||
/datum/verb/qdel_then_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
qdel(src)
|
||||
if(!running_find_references)
|
||||
find_references(TRUE)
|
||||
|
||||
/client/verb/show_qdeleted()
|
||||
set category = "Debug"
|
||||
set name = "Show qdel() Log"
|
||||
set desc = "Render the qdel() log and display it"
|
||||
|
||||
var/dat = "<B>List of things that have been qdel()eted this round</B><BR><BR>"
|
||||
|
||||
var/tmplist = list()
|
||||
for(var/elem in SSgarbage.qdel_list)
|
||||
if(!(elem in tmplist))
|
||||
tmplist[elem] = 0
|
||||
tmplist[elem]++
|
||||
|
||||
for(var/path in tmplist)
|
||||
dat += "[path] - [tmplist[path]] times<BR>"
|
||||
|
||||
usr << browse(dat, "window=qdeletedlog")
|
||||
|
||||
/datum/proc/DoSearchVar(X, Xname)
|
||||
if(usr && usr.client && !usr.client.running_find_references) return
|
||||
if(istype(X, /datum))
|
||||
var/datum/D = X
|
||||
if(D.last_find_references == last_find_references)
|
||||
return
|
||||
D.last_find_references = last_find_references
|
||||
for(var/V in D.vars)
|
||||
for(var/varname in D.vars)
|
||||
var/variable = D.vars[varname]
|
||||
if(variable == src)
|
||||
testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]")
|
||||
else if(islist(variable))
|
||||
if(src in variable)
|
||||
testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]")
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
for(var/I in variable)
|
||||
DoSearchVar(I, TRUE)
|
||||
else
|
||||
DoSearchVar(variable, "[Xname]: [varname]")
|
||||
#endif
|
||||
else if(islist(X))
|
||||
if(src in X)
|
||||
testing("Found [src.type] \ref[src] in list [Xname].")
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
for(var/I in X)
|
||||
DoSearchVar(I, Xname + ": list")
|
||||
#else
|
||||
CHECK_TICK
|
||||
#endif
|
||||
|
||||
//if find_references isn't working for some datum
|
||||
//update this list using tools/GenerateGlobalVarAccess
|
||||
/datum/proc/find_references_in_globals()
|
||||
// TODO - Impement Global Variable Access
|
||||
// for(var/global_var in _all_globals)
|
||||
// DoSearchVar(readglobal(global_var), "Global: [global_var]")
|
||||
#endif
|
||||
|
||||
|
||||
/image/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
** Lighting Subsystem - Process the lighting! Do it!
|
||||
*/
|
||||
|
||||
#define SSLIGHTING_STAGE_LIGHTS 1
|
||||
#define SSLIGHTING_STAGE_CORNERS 2
|
||||
#define SSLIGHTING_STAGE_OVERLAYS 3
|
||||
#define SSLIGHTING_STAGE_DONE 4
|
||||
// This subsystem's fire() method also gets called once during Master.Initialize().
|
||||
// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return.
|
||||
// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it.
|
||||
#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; }
|
||||
|
||||
// Globals
|
||||
/var/lighting_overlays_initialised = FALSE
|
||||
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
|
||||
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
|
||||
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
|
||||
|
||||
SUBSYSTEM_DEF(lighting)
|
||||
name = "Lighting"
|
||||
wait = 2 // Ticks, not deciseconds
|
||||
init_order = INIT_ORDER_LIGHTING
|
||||
flags = SS_TICKER
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/stage = null
|
||||
|
||||
var/cost_lights = 0
|
||||
var/cost_corners = 0
|
||||
var/cost_overlays = 0
|
||||
|
||||
/datum/controller/subsystem/lighting/Initialize(timeofday)
|
||||
if(!lighting_overlays_initialised)
|
||||
// TODO - TG initializes starlight here.
|
||||
create_all_lighting_overlays()
|
||||
lighting_overlays_initialised = TRUE
|
||||
|
||||
// Pre-process lighting once before the round starts.
|
||||
internal_process_lights(FALSE, TRUE)
|
||||
internal_process_corners(FALSE, TRUE)
|
||||
internal_process_overlays(FALSE, TRUE)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/lighting/fire(resumed = FALSE)
|
||||
var/timer
|
||||
if(!resumed)
|
||||
ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle
|
||||
ASSERT(stage == null) // Or somehow didn't finish all the steps from last cycle
|
||||
stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
|
||||
|
||||
if(stage == SSLIGHTING_STAGE_LIGHTS)
|
||||
timer = world.tick_usage
|
||||
internal_process_lights(resumed)
|
||||
cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer))
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
stage = SSLIGHTING_STAGE_CORNERS
|
||||
|
||||
if(stage == SSLIGHTING_STAGE_CORNERS)
|
||||
timer = world.tick_usage
|
||||
internal_process_corners(resumed)
|
||||
cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer))
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
stage = SSLIGHTING_STAGE_OVERLAYS
|
||||
|
||||
if(stage == SSLIGHTING_STAGE_OVERLAYS)
|
||||
timer = world.tick_usage
|
||||
internal_process_overlays(resumed)
|
||||
cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer))
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
stage = SSLIGHTING_STAGE_DONE
|
||||
|
||||
// Okay, we're done! Woo! Got thru a whole air_master cycle!
|
||||
ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left
|
||||
ASSERT(stage == SSLIGHTING_STAGE_DONE) // And that we didn't somehow skip past the last step
|
||||
currentrun = null
|
||||
stage = null
|
||||
|
||||
/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE)
|
||||
if (!resumed)
|
||||
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
|
||||
src.currentrun = global.lighting_update_lights
|
||||
global.lighting_update_lights = list()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
while(currentrun.len)
|
||||
var/datum/light_source/L = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
|
||||
if(!L) continue
|
||||
if(L.check() || L.destroyed || L.force_update)
|
||||
L.remove_lum()
|
||||
if(!L.destroyed)
|
||||
L.apply_lum()
|
||||
|
||||
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
|
||||
L.smart_vis_update()
|
||||
|
||||
L.vis_update = FALSE
|
||||
L.force_update = FALSE
|
||||
L.needs_update = FALSE
|
||||
|
||||
DUAL_TICK_CHECK
|
||||
|
||||
/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE)
|
||||
if (!resumed)
|
||||
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
|
||||
src.currentrun = global.lighting_update_corners
|
||||
global.lighting_update_corners = list()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
while(currentrun.len)
|
||||
var/datum/lighting_corner/C = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
|
||||
if(!C) continue
|
||||
C.update_overlays()
|
||||
C.needs_update = FALSE
|
||||
|
||||
DUAL_TICK_CHECK
|
||||
|
||||
/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE)
|
||||
if (!resumed)
|
||||
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
|
||||
src.currentrun = global.lighting_update_overlays
|
||||
global.lighting_update_overlays = list()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
while(currentrun.len)
|
||||
var/atom/movable/lighting_overlay/O = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
|
||||
if(!O) continue
|
||||
O.update_overlay()
|
||||
O.needs_update = FALSE
|
||||
|
||||
DUAL_TICK_CHECK
|
||||
|
||||
/datum/controller/subsystem/lighting/stat_entry(msg_prefix)
|
||||
var/list/msg = list(msg_prefix)
|
||||
msg += "T:{"
|
||||
msg += "S [total_lighting_sources] | "
|
||||
msg += "C [total_lighting_corners] | "
|
||||
msg += "O [total_lighting_overlays]"
|
||||
msg += "}"
|
||||
msg += "C:{"
|
||||
msg += "S [round(cost_lights, 1)] | "
|
||||
msg += "C [round(cost_corners, 1)] | "
|
||||
msg += "O [round(cost_overlays, 1)]"
|
||||
msg += "}"
|
||||
..(msg.Join())
|
||||
|
||||
#undef DUAL_TICK_CHECK
|
||||
#undef SSLIGHTING_STAGE_LIGHTS
|
||||
#undef SSLIGHTING_STAGE_CORNERS
|
||||
#undef SSLIGHTING_STAGE_OVERLAYS
|
||||
#undef SSLIGHTING_STAGE_STATS
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Mobs Subsystem - Process mob.Life()
|
||||
//
|
||||
SUBSYSTEM_DEF(mobs)
|
||||
name = "Mobs"
|
||||
priority = 100
|
||||
wait = 2 SECONDS
|
||||
flags = SS_KEEP_TIMING|SS_NO_INIT
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/mobs/stat_entry()
|
||||
..("P: [global.mob_list.len]")
|
||||
|
||||
/datum/controller/subsystem/mobs/fire(resumed = 0)
|
||||
if (!resumed)
|
||||
src.currentrun = mob_list.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
var/times_fired = src.times_fired
|
||||
while(currentrun.len)
|
||||
var/mob/M = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
|
||||
if(QDELETED(M))
|
||||
mob_list -= M
|
||||
else
|
||||
// Right now mob.Life() is unstable enough I think we need to use a try catch.
|
||||
// Obviously we should try and get rid of this for performance reasons when we can.
|
||||
try
|
||||
M.Life(times_fired)
|
||||
catch(var/exception/e)
|
||||
log_runtime(e, M, "Caught by [name] subsystem")
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
@@ -0,0 +1,133 @@
|
||||
////////////////////////////////
|
||||
//// Mind/body data storage system
|
||||
//// for the resleeving tech
|
||||
////////////////////////////////
|
||||
|
||||
SUBSYSTEM_DEF(transcore)
|
||||
name = "Transcore"
|
||||
priority = 20
|
||||
wait = 1 MINUTE
|
||||
flags = SS_BACKGROUND|SS_NO_TICK_CHECK|SS_NO_INIT
|
||||
runlevels = RUNLEVEL_GAME
|
||||
|
||||
// THINGS
|
||||
var/overdue_time = 15 MINUTES
|
||||
var/core_dumped = FALSE // Core has been dumped! Also set can_fire = 0 when you set this.
|
||||
|
||||
var/datum/transhuman/mind_record/list/backed_up = list() // All known mind records, indexed by MR.mindname/mind.name
|
||||
var/datum/transhuman/mind_record/list/has_left = list() // Why do we even have this?
|
||||
var/datum/transhuman/body_record/list/body_scans = list() // All known body records, indexed by BR.mydna.name
|
||||
|
||||
/datum/controller/subsystem/transcore/fire()
|
||||
for(var/N in backed_up)
|
||||
var/datum/transhuman/mind_record/curr_MR = backed_up[N]
|
||||
if(!curr_MR)
|
||||
log_debug("Tried to process [N] in transcore w/o a record!")
|
||||
continue
|
||||
if(curr_MR.one_time)
|
||||
continue
|
||||
var/since_backup = world.time - curr_MR.last_update
|
||||
if(since_backup < overdue_time)
|
||||
curr_MR.dead_state = MR_NORMAL
|
||||
else
|
||||
if(curr_MR.dead_state != MR_DEAD) //First time switching to dead
|
||||
notify(N)
|
||||
curr_MR.dead_state = MR_DEAD
|
||||
|
||||
/datum/controller/subsystem/transcore/stat_entry(msg)
|
||||
if(core_dumped)
|
||||
msg += "CORE DUMPED | "
|
||||
msg += "MR: [backed_up.len] | BR: [body_scans.len]"
|
||||
..(msg)
|
||||
|
||||
/datum/controller/subsystem/transcore/Recover()
|
||||
if (istype(SStranscore.body_scans))
|
||||
for(var/N in SStranscore.body_scans[N])
|
||||
if(N) body_scans[N] = SStranscore.body_scans[N]
|
||||
if(SStranscore.core_dumped)
|
||||
core_dumped = TRUE
|
||||
can_fire = FALSE
|
||||
else if (istype(SStranscore.backed_up))
|
||||
for(var/N in SStranscore.backed_up[N])
|
||||
if(N) backed_up[N] = SStranscore.backed_up[N]
|
||||
|
||||
/datum/controller/subsystem/transcore/proc/m_backup(var/datum/mind/mind, var/obj/item/device/nif/nif, var/one_time = FALSE)
|
||||
ASSERT(mind)
|
||||
if(!mind.name || core_dumped)
|
||||
return 0
|
||||
|
||||
var/datum/transhuman/mind_record/MR
|
||||
|
||||
if(mind.name in backed_up)
|
||||
MR = backed_up[mind.name]
|
||||
MR.last_update = world.time
|
||||
MR.one_time = one_time
|
||||
|
||||
//Pass a 0 to not change NIF status (because the elseif is checking for null)
|
||||
if(nif)
|
||||
MR.nif_path = nif.type
|
||||
MR.nif_durability = nif.durability
|
||||
var/list/nifsofts = list()
|
||||
for(var/N in nif.nifsofts)
|
||||
if(N)
|
||||
var/datum/nifsoft/nifsoft = N
|
||||
nifsofts += nifsoft.type
|
||||
MR.nif_software = nifsofts
|
||||
else if(isnull(nif)) //Didn't pass anything, so no NIF
|
||||
MR.nif_path = null
|
||||
MR.nif_durability = null
|
||||
MR.nif_software = null
|
||||
|
||||
else
|
||||
MR = new(mind, mind.current, add_to_db = TRUE, one_time = one_time)
|
||||
|
||||
return 1
|
||||
|
||||
// Send a past-due notification to the medical radio channel.
|
||||
/datum/controller/subsystem/transcore/proc/notify(var/name)
|
||||
ASSERT(name)
|
||||
var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null)
|
||||
a.autosay("[name] is past-due for a mind backup. This will be the only notification.", "TransCore Oversight", "Medical")
|
||||
qdel(a)
|
||||
|
||||
// Called from mind_record to add itself to the transcore.
|
||||
/datum/controller/subsystem/transcore/proc/add_backup(var/datum/transhuman/mind_record/MR)
|
||||
ASSERT(MR)
|
||||
backed_up[MR.mindname] = MR
|
||||
backed_up = sortAssoc(backed_up)
|
||||
log_debug("Added [MR.mindname] to transcore DB.")
|
||||
|
||||
// Remove a mind_record from the backup-checking list. Keeps track of it in has_left // Why do we do that? ~Leshana
|
||||
/datum/controller/subsystem/transcore/proc/stop_backup(var/datum/transhuman/mind_record/MR)
|
||||
ASSERT(MR)
|
||||
has_left[MR.mindname] = MR
|
||||
backed_up.Remove("[MR.mindname]")
|
||||
MR.cryo_at = world.time
|
||||
log_debug("Put [MR.mindname] in transcore suspended DB.")
|
||||
|
||||
// Called from body_record to add itself to the transcore.
|
||||
/datum/controller/subsystem/transcore/proc/add_body(var/datum/transhuman/body_record/BR)
|
||||
ASSERT(BR)
|
||||
body_scans[BR.mydna.name] = BR
|
||||
body_scans = sortAssoc(body_scans)
|
||||
log_debug("Added [BR.mydna.name] to transcore body DB.")
|
||||
|
||||
// Remove a body record from the database (Usually done when someone cryos) // Why? ~Leshana
|
||||
/datum/controller/subsystem/transcore/proc/remove_body(var/datum/transhuman/body_record/BR)
|
||||
ASSERT(BR)
|
||||
body_scans.Remove("[BR.mydna.name]")
|
||||
log_debug("Removed [BR.mydna.name] from transcore body DB.")
|
||||
|
||||
// Moves all mind records from the databaes into the disk and shuts down all backup canary processing.
|
||||
/datum/controller/subsystem/transcore/proc/core_dump(var/obj/item/weapon/disk/transcore/disk)
|
||||
ASSERT(disk)
|
||||
var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null)
|
||||
a.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Command")
|
||||
a.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Medical")
|
||||
qdel(a)
|
||||
|
||||
disk.stored += backed_up
|
||||
backed_up.Cut()
|
||||
core_dumped = TRUE
|
||||
can_fire = FALSE
|
||||
return disk.stored.len
|
||||
@@ -1,19 +1,57 @@
|
||||
//TODO: rewrite and standardise all controller datums to the datum/controller type
|
||||
//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done
|
||||
/client/proc/restart_controller(controller in list("Supply"))
|
||||
|
||||
// Clickable stat() button.
|
||||
/obj/effect/statclick
|
||||
name = "Initializing..."
|
||||
var/target
|
||||
|
||||
/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical
|
||||
..()
|
||||
name = text
|
||||
src.target = target
|
||||
|
||||
/obj/effect/statclick/proc/update(text)
|
||||
name = text
|
||||
return src
|
||||
|
||||
/obj/effect/statclick/debug
|
||||
var/class
|
||||
|
||||
/obj/effect/statclick/debug/Click()
|
||||
if(!usr.client.holder || !target)
|
||||
return
|
||||
if(!class)
|
||||
if(istype(target, /datum/controller/subsystem))
|
||||
class = "subsystem"
|
||||
else if(istype(target, /datum/controller))
|
||||
class = "controller"
|
||||
else if(istype(target, /datum))
|
||||
class = "datum"
|
||||
else
|
||||
class = "unknown"
|
||||
|
||||
usr.client.debug_variables(target)
|
||||
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
|
||||
|
||||
|
||||
// Debug verbs.
|
||||
/client/proc/restart_controller(controller in list("Master", "Failsafe"))
|
||||
set category = "Debug"
|
||||
set name = "Restart Controller"
|
||||
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
|
||||
|
||||
if(!holder) return
|
||||
usr = null
|
||||
src = null
|
||||
if(!holder)
|
||||
return
|
||||
switch(controller)
|
||||
if("Supply")
|
||||
supply_controller.process()
|
||||
feedback_add_details("admin_verb","RSupply")
|
||||
if("Master")
|
||||
Recreate_MC()
|
||||
feedback_add_details("admin_verb","RMC")
|
||||
if("Failsafe")
|
||||
new /datum/controller/failsafe()
|
||||
feedback_add_details("admin_verb","RFailsafe")
|
||||
|
||||
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
|
||||
return
|
||||
|
||||
/client/proc/debug_antagonist_template(antag_type in all_antag_types)
|
||||
set category = "Debug"
|
||||
|
||||
@@ -23,9 +23,15 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
|
||||
/datum/controller/vote/New()
|
||||
if(vote != src)
|
||||
if(istype(vote))
|
||||
del(vote)
|
||||
Recover()
|
||||
qdel(vote)
|
||||
vote = src
|
||||
|
||||
/datum/controller/vote/Destroy()
|
||||
..()
|
||||
// Tell qdel() to Del() this object.
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/controller/vote/proc/process() //called by master_controller
|
||||
if(mode)
|
||||
// No more change mode votes after the game has started.
|
||||
|
||||
@@ -8,29 +8,29 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_blanks
|
||||
name = "ammunition (12g, blank)"
|
||||
path =/obj/item/ammo_casing/shotgun/blank
|
||||
path =/obj/item/ammo_casing/a12g/blank
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_beanbag
|
||||
name = "ammunition (12g, beanbag)"
|
||||
path =/obj/item/ammo_casing/shotgun/beanbag
|
||||
path =/obj/item/ammo_casing/a12g/beanbag
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_flash
|
||||
name = "ammunition (12g, flash)"
|
||||
path =/obj/item/ammo_casing/shotgun/flash
|
||||
path =/obj/item/ammo_casing/a12g/flash
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun
|
||||
name = "ammunition (12g, slug)"
|
||||
path =/obj/item/ammo_casing/shotgun
|
||||
path =/obj/item/ammo_casing/a12g
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_pellet
|
||||
name = "ammunition (12g, pellet)"
|
||||
path =/obj/item/ammo_casing/shotgun/pellet
|
||||
path =/obj/item/ammo_casing/a12g/pellet
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/stunshell
|
||||
name = "ammunition (stun cartridge, shotgun)"
|
||||
path =/obj/item/ammo_casing/shotgun/stunshell
|
||||
path =/obj/item/ammo_casing/a12g/stunshell
|
||||
hidden = 1
|
||||
|
||||
//////////////////
|
||||
@@ -49,119 +49,119 @@
|
||||
/////// .45
|
||||
/datum/category_item/autolathe/arms/pistol_45
|
||||
name = "pistol magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/c45m
|
||||
path =/obj/item/ammo_magazine/m45
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45p
|
||||
name = "pistol magazine (.45 practice)"
|
||||
path =/obj/item/ammo_magazine/c45m/practice
|
||||
path =/obj/item/ammo_magazine/m45/practice
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45r
|
||||
name = "pistol magazine (.45 rubber)"
|
||||
path =/obj/item/ammo_magazine/c45m/rubber
|
||||
path =/obj/item/ammo_magazine/m45/rubber
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45f
|
||||
name = "pistol magazine (.45 flash)"
|
||||
path =/obj/item/ammo_magazine/c45m/flash
|
||||
path =/obj/item/ammo_magazine/m45/flash
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45uzi
|
||||
name = "uzi magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/c45uzi
|
||||
path =/obj/item/ammo_magazine/m45uzi
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/tommymag
|
||||
name = "Tommygun magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/tommymag
|
||||
path =/obj/item/ammo_magazine/m45tommy
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/tommydrum
|
||||
name = "Tommygun drum magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/tommydrum
|
||||
path =/obj/item/ammo_magazine/m45tommydrum
|
||||
hidden = 1
|
||||
|
||||
/////// 9mm
|
||||
|
||||
/obj/item/ammo_magazine/mc9mm/flash
|
||||
ammo_type =/obj/item/ammo_casing/c9mmf
|
||||
/obj/item/ammo_magazine/m9mm/flash
|
||||
ammo_type =/obj/item/ammo_casing/a9mmf
|
||||
|
||||
/obj/item/ammo_magazine/mc9mm/rubber
|
||||
/obj/item/ammo_magazine/m9mm/rubber
|
||||
name = "magazine (9mm rubber)"
|
||||
ammo_type =/obj/item/ammo_casing/c9mmr
|
||||
ammo_type =/obj/item/ammo_casing/a9mmr
|
||||
|
||||
/obj/item/ammo_magazine/mc9mm/practice
|
||||
/obj/item/ammo_magazine/m9mm/practice
|
||||
name = "magazine (9mm practice)"
|
||||
ammo_type =/obj/item/ammo_casing/c9mmp
|
||||
ammo_type =/obj/item/ammo_casing/a9mmp
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_9mm
|
||||
name = "pistol magazine (9mm)"
|
||||
path =/obj/item/ammo_magazine/mc9mm
|
||||
path =/obj/item/ammo_magazine/m9mm
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_9mmr
|
||||
name = "pistol magazine (9mm rubber)"
|
||||
path =/obj/item/ammo_magazine/mc9mm/rubber
|
||||
path =/obj/item/ammo_magazine/m9mm/rubber
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_9mmp
|
||||
name = "pistol magazine (9mm practice)"
|
||||
path =/obj/item/ammo_magazine/mc9mm/practice
|
||||
path =/obj/item/ammo_magazine/m9mm/practice
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_9mmf
|
||||
name = "pistol magazine (9mm flash)"
|
||||
path =/obj/item/ammo_magazine/mc9mm/flash
|
||||
path =/obj/item/ammo_magazine/m9mm/flash
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_9mm
|
||||
name = "top-mounted SMG magazine (9mm)"
|
||||
path =/obj/item/ammo_magazine/mc9mmt
|
||||
path =/obj/item/ammo_magazine/m9mmt
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_9mmr
|
||||
name = "top-mounted SMG magazine (9mm rubber)"
|
||||
path =/obj/item/ammo_magazine/mc9mmt/rubber
|
||||
path =/obj/item/ammo_magazine/m9mmt/rubber
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_9mmp
|
||||
name = "top-mounted SMG magazine (9mm practice)"
|
||||
path =/obj/item/ammo_magazine/mc9mmt/practice
|
||||
path =/obj/item/ammo_magazine/m9mmt/practice
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_9mmf
|
||||
name = "top-mounted SMG magazine (9mm flash)"
|
||||
path =/obj/item/ammo_magazine/mc9mmt/flash
|
||||
path =/obj/item/ammo_magazine/m9mmt/flash
|
||||
|
||||
/////// 10mm
|
||||
/datum/category_item/autolathe/arms/smg_10mm
|
||||
name = "SMG magazine (10mm)"
|
||||
path =/obj/item/ammo_magazine/a10mm
|
||||
path =/obj/item/ammo_magazine/m10mm
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_50
|
||||
name = "pistol magazine (.50AE)"
|
||||
path =/obj/item/ammo_magazine/a50
|
||||
path =/obj/item/ammo_magazine/m50
|
||||
hidden = 1
|
||||
|
||||
/////// 5.56mm
|
||||
/datum/category_item/autolathe/arms/rifle_556
|
||||
name = "rifle magazine (5.56mm)"
|
||||
path =/obj/item/ammo_magazine/c556
|
||||
path =/obj/item/ammo_magazine/m556
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_556p
|
||||
name = "rifle magazine (5.56mm practice)"
|
||||
path =/obj/item/ammo_magazine/c556/practice
|
||||
path =/obj/item/ammo_magazine/m556/practice
|
||||
|
||||
/datum/category_item/autolathe/arms/machinegun_556
|
||||
name = "machinegun box magazine (5.56)"
|
||||
path =/obj/item/ammo_magazine/a556
|
||||
path =/obj/item/ammo_magazine/m556saw
|
||||
hidden = 1
|
||||
/////// 7.62
|
||||
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_762
|
||||
name = "rifle magazine (7.62mm)"
|
||||
path =/obj/item/ammo_magazine/c762
|
||||
path =/obj/item/ammo_magazine/m762
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_magazine
|
||||
name = "24rnd shotgun magazine (12g)"
|
||||
path =/obj/item/ammo_magazine/g12
|
||||
path =/obj/item/ammo_magazine/m12gdrum
|
||||
hidden = 1
|
||||
|
||||
/*
|
||||
@@ -188,57 +188,57 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45
|
||||
name = "pistol magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/c45m/empty
|
||||
path =/obj/item/ammo_magazine/m45/empty
|
||||
category = "Arms and Ammunition"
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_45uzi
|
||||
name = "uzi magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/c45uzi/empty
|
||||
path =/obj/item/ammo_magazine/m45uzi/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/tommymag
|
||||
name = "Tommygun magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/tommymag/empty
|
||||
path =/obj/item/ammo_magazine/m45tommy/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/tommydrum
|
||||
name = "Tommygun drum magazine (.45)"
|
||||
path =/obj/item/ammo_magazine/tommydrum/empty
|
||||
path =/obj/item/ammo_magazine/m45tommydrum/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_9mm
|
||||
name = "pistol magazine (9mm)"
|
||||
path =/obj/item/ammo_magazine/mc9mm/empty
|
||||
path =/obj/item/ammo_magazine/m9mm/empty
|
||||
category = "Arms and Ammunition"
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_9mm
|
||||
name = "top-mounted SMG magazine (9mm)"
|
||||
path =/obj/item/ammo_magazine/mc9mmt/empty
|
||||
path =/obj/item/ammo_magazine/m9mmt/empty
|
||||
category = "Arms and Ammunition"
|
||||
|
||||
/datum/category_item/autolathe/arms/smg_10mm
|
||||
name = "SMG magazine (10mm)"
|
||||
path =/obj/item/ammo_magazine/a10mm/empty
|
||||
path =/obj/item/ammo_magazine/m10mm/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_50
|
||||
name = "pistol magazine (.50AE)"
|
||||
path =/obj/item/ammo_magazine/a50/empty
|
||||
path =/obj/item/ammo_magazine/m50/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_556
|
||||
name = "10rnd rifle magazine (5.56mm)"
|
||||
path =/obj/item/ammo_magazine/a556/empty
|
||||
path =/obj/item/ammo_magazine/m556saw/empty
|
||||
category = "Arms and Ammunition"
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_556m
|
||||
name = "20rnd rifle magazine (5.56mm)"
|
||||
path =/obj/item/ammo_magazine/a556m/empty
|
||||
path =/obj/item/ammo_magazine/m556sawm/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_762
|
||||
name = "20rnd rifle magazine (7.62mm)"
|
||||
path =/obj/item/ammo_magazine/c762/empty
|
||||
path =/obj/item/ammo_magazine/m762/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/shotgun_magazine
|
||||
name = "24rnd shotgun magazine (12g)"
|
||||
path =/obj/item/ammo_magazine/g12/empty
|
||||
path =/obj/item/ammo_magazine/m12gdrum/empty
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1*/
|
||||
|
||||
@@ -272,17 +272,17 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_357
|
||||
name = "speedloader (.357)"
|
||||
path =/obj/item/ammo_magazine/a357
|
||||
path =/obj/item/ammo_magazine/s357
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_38
|
||||
name = "speedloader (.38)"
|
||||
path =/obj/item/ammo_magazine/c38
|
||||
path =/obj/item/ammo_magazine/s38
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_38r
|
||||
name = "speedloader (.38 rubber)"
|
||||
path =/obj/item/ammo_magazine/c38/rubber
|
||||
path =/obj/item/ammo_magazine/s38/rubber
|
||||
|
||||
// Commented out until metal exploits with autolathe is fixed.
|
||||
/*/datum/category_item/autolathe/arms/pistol_clip_45
|
||||
@@ -335,35 +335,35 @@
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_clip_10mm
|
||||
name = "ammo clip (10mm)"
|
||||
path =/obj/item/ammo_magazine/clip/a10mm
|
||||
path =/obj/item/ammo_magazine/clip/c10mm
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/pistol_clip_50
|
||||
name = "ammo clip (.50AE)"
|
||||
path =/obj/item/ammo_magazine/clip/a50
|
||||
path =/obj/item/ammo_magazine/clip/c50
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
*/
|
||||
/datum/category_item/autolathe/arms/rifle_clip_556
|
||||
name = "ammo clip (5.56mm)"
|
||||
path =/obj/item/ammo_magazine/clip/a556
|
||||
path =/obj/item/ammo_magazine/clip/c556
|
||||
category = "Arms and Ammunition"
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_clip_556_practice
|
||||
name = "ammo clip (5.56mm practice)"
|
||||
path =/obj/item/ammo_magazine/clip/a556/practice
|
||||
path =/obj/item/ammo_magazine/clip/c556/practice
|
||||
category = "Arms and Ammunition"
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_clip_762
|
||||
name = "ammo clip (7.62mm)"
|
||||
path =/obj/item/ammo_magazine/clip/a762
|
||||
path =/obj/item/ammo_magazine/clip/c762
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/rifle_clip_762_practice
|
||||
name = "ammo clip (7.62mm practice)"
|
||||
path =/obj/item/ammo_magazine/clip/a762/practice
|
||||
path =/obj/item/ammo_magazine/clip/c762/practice
|
||||
|
||||
/datum/category_item/autolathe/arms/knuckledusters
|
||||
name = "knuckle dusters"
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
/datum/category_item/autolathe/arms/speedloader_357_flash
|
||||
name = "speedloader (.357 flash)"
|
||||
path =/obj/item/ammo_magazine/a357/flash
|
||||
path =/obj/item/ammo_magazine/m357/flash
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_357_stun
|
||||
name = "speedloader (.357 stun)"
|
||||
path =/obj/item/ammo_magazine/a357/stun
|
||||
path =/obj/item/ammo_magazine/m357/stun
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_357_rubber
|
||||
name = "speedloader (.357 rubber)"
|
||||
path =/obj/item/ammo_magazine/a357/rubber
|
||||
path =/obj/item/ammo_magazine/m357/rubber
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_44
|
||||
name = "speedloader (.44)"
|
||||
path =/obj/item/ammo_magazine/a44sl
|
||||
path =/obj/item/ammo_magazine/m44sl
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/speedloader_44_rubber
|
||||
name = "speedloader (.44 rubber)"
|
||||
path =/obj/item/ammo_magazine/a44sl/rubber
|
||||
path =/obj/item/ammo_magazine/m44sl/rubber
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/mag_44
|
||||
name = "magazine (.44)"
|
||||
path =/obj/item/ammo_magazine/a44
|
||||
path =/obj/item/ammo_magazine/m44
|
||||
hidden = 1
|
||||
|
||||
/datum/category_item/autolathe/arms/mag_44_rubber
|
||||
name = "magazine (.44 rubber)"
|
||||
path =/obj/item/ammo_magazine/a44/rubber
|
||||
path =/obj/item/ammo_magazine/m44/rubber
|
||||
hidden = 1
|
||||
|
||||
@@ -155,4 +155,5 @@ Data storage vars:
|
||||
tag = null
|
||||
arg_list.Cut()
|
||||
stop()
|
||||
return QDEL_HINT_LETMELIVE
|
||||
//Do not call ..()
|
||||
|
||||
+47
-34
@@ -1,34 +1,47 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/datum/data
|
||||
var/name = "data"
|
||||
var/size = 1.0
|
||||
|
||||
|
||||
/datum/data/function
|
||||
name = "function"
|
||||
size = 2.0
|
||||
|
||||
|
||||
/datum/data/function/data_control
|
||||
name = "data control"
|
||||
|
||||
|
||||
/datum/data/function/id_changer
|
||||
name = "id changer"
|
||||
|
||||
|
||||
/datum/data/record
|
||||
name = "record"
|
||||
size = 5.0
|
||||
var/list/fields = list( )
|
||||
|
||||
|
||||
/datum/data/text
|
||||
name = "text"
|
||||
var/data = null
|
||||
|
||||
|
||||
|
||||
/datum/debug
|
||||
var/list/debuglist
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/datum/data
|
||||
var/name = "data"
|
||||
var/size = 1.0
|
||||
|
||||
|
||||
/datum/data/function
|
||||
name = "function"
|
||||
size = 2.0
|
||||
|
||||
|
||||
/datum/data/function/data_control
|
||||
name = "data control"
|
||||
|
||||
|
||||
/datum/data/function/id_changer
|
||||
name = "id changer"
|
||||
|
||||
|
||||
/datum/data/record
|
||||
name = "record"
|
||||
size = 5.0
|
||||
var/list/fields = list( )
|
||||
|
||||
// Mostly used for data_core records, but unfortuantely used some other places too. But mostly here, so lets make a good effort.
|
||||
// TODO - Some machines/computers might be holding references to us. Lets look into that, but at least for now lets make sure that the manifest is cleaned up.
|
||||
/datum/data/record/Destroy(var/force)
|
||||
if(data_core.locked.Find(src))
|
||||
if(!force)
|
||||
crash_with("Someone tried to qdel a record that was in data_core.locked [log_info_line(src)]")
|
||||
return QDEL_HINT_LETMELIVE
|
||||
data_core.locked -= src
|
||||
data_core.medical -= src
|
||||
data_core.general -= src
|
||||
data_core.security -= src
|
||||
..()
|
||||
return QDEL_HINT_FINDREFERENCE // If we must, we can resort to QDEL_HINT_HARDDEL. But lets see how this works in practice ~Leshana
|
||||
|
||||
/datum/data/text
|
||||
name = "text"
|
||||
var/data = null
|
||||
|
||||
|
||||
|
||||
/datum/debug
|
||||
var/list/debuglist
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
|
||||
/decls/Destroy()
|
||||
crash_with("Prevented attempt to delete a decl instance: [log_info_line(src)]")
|
||||
return 1 // Prevents Decl destruction
|
||||
return QDEL_HINT_LETMELIVE // Prevents Decl destruction
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
var/global/repository/radiation/radiation_repository = new()
|
||||
|
||||
/repository/radiation
|
||||
var/list/sources = list() // all radiation source datums
|
||||
var/list/sources_assoc = list() // Sources indexed by turf for de-duplication.
|
||||
var/list/resistance_cache = list() // Cache of turf's radiation resistance.
|
||||
|
||||
// Describes a point source of radiation. Created either in response to a pulse of radiation, or over an irradiated atom.
|
||||
// Sources will decay over time, unless something is renewing their power!
|
||||
/datum/radiation_source
|
||||
var/turf/source_turf // Location of the radiation source.
|
||||
var/rad_power // Strength of the radiation being emitted.
|
||||
var/decay = TRUE // True for automatic decay. False if owner promises to handle it (i.e. supermatter)
|
||||
var/respect_maint = FALSE // True for not affecting RAD_SHIELDED areas.
|
||||
var/flat = FALSE // True for power falloff with distance.
|
||||
var/range // Cached maximum range, used for quick checks against mobs.
|
||||
|
||||
/datum/radiation_source/Destroy()
|
||||
radiation_repository.sources -= src
|
||||
if(radiation_repository.sources_assoc[src.source_turf] == src)
|
||||
radiation_repository.sources -= src.source_turf
|
||||
src.source_turf = null
|
||||
. = ..()
|
||||
|
||||
/datum/radiation_source/proc/update_rad_power(var/new_power = null)
|
||||
if(new_power == null || new_power == rad_power)
|
||||
return // No change
|
||||
else if(new_power <= 0)
|
||||
qdel(src) // Decayed to nothing
|
||||
else
|
||||
rad_power = new_power
|
||||
if(!flat)
|
||||
range = min(round(sqrt(rad_power / config.radiation_lower_limit)), 31) // R = rad_power / dist**2 - Solve for dist
|
||||
|
||||
// Ray trace from all active radiation sources to T and return the strongest effect.
|
||||
/repository/radiation/proc/get_rads_at_turf(var/turf/T)
|
||||
if(!istype(T)) return 0
|
||||
|
||||
. = 0
|
||||
for(var/value in sources)
|
||||
var/datum/radiation_source/source = value
|
||||
if(source.rad_power < .)
|
||||
continue // Already being affected by a stronger source
|
||||
if(source.source_turf.z != T.z)
|
||||
continue // Radiation is not multi-z
|
||||
var/dist = get_dist(source.source_turf, T)
|
||||
if(dist > source.range)
|
||||
continue // Too far to possibly affect
|
||||
if(source.respect_maint)
|
||||
var/atom/A = T.loc
|
||||
if(A.flags & RAD_SHIELDED)
|
||||
continue // In shielded area
|
||||
if(source.flat)
|
||||
. = max(., source.rad_power)
|
||||
continue // No need to ray trace for flat field
|
||||
|
||||
// Okay, now ray trace to find resistence!
|
||||
var/turf/origin = source.source_turf
|
||||
var/working = source.rad_power
|
||||
while(origin != T)
|
||||
origin = get_step_towards(origin, T) //Raytracing
|
||||
if(!(origin in resistance_cache)) //Only get the resistance if we don't already know it.
|
||||
origin.calc_rad_resistance()
|
||||
working = max((working - (origin.cached_rad_resistance * config.radiation_resistance_multiplier)), 0)
|
||||
if(working <= .)
|
||||
break // Already affected by a stronger source (or its zero...)
|
||||
. = max((working * (1 / (dist ** 2))), .) //Butchered version of the inverse square law. Works for this purpose
|
||||
|
||||
// Add a radiation source instance to the repository. It will override any existing source on the same turf.
|
||||
/repository/radiation/proc/add_source(var/datum/radiation_source/S)
|
||||
if(!isturf(S.source_turf))
|
||||
return
|
||||
var/datum/radiation_source/existing = sources_assoc[S.source_turf]
|
||||
if(existing)
|
||||
qdel(existing)
|
||||
sources += S
|
||||
sources_assoc[S.source_turf] = S
|
||||
|
||||
// Creates a temporary radiation source that will decay
|
||||
/repository/radiation/proc/radiate(source, power) //Sends out a radiation pulse, taking walls into account
|
||||
if(!(source && power)) //Sanity checking
|
||||
return
|
||||
var/datum/radiation_source/S = new()
|
||||
S.source_turf = get_turf(source)
|
||||
S.update_rad_power(power)
|
||||
add_source(S)
|
||||
|
||||
// Sets the radiation in a range to a constant value.
|
||||
/repository/radiation/proc/flat_radiate(source, power, range, var/respect_maint = FALSE)
|
||||
if(!(source && power && range))
|
||||
return
|
||||
var/datum/radiation_source/S = new()
|
||||
S.flat = TRUE
|
||||
S.range = range
|
||||
S.respect_maint = respect_maint
|
||||
S.source_turf = get_turf(source)
|
||||
S.update_rad_power(power)
|
||||
add_source(S)
|
||||
|
||||
// Irradiates a full Z-level. Hacky way of doing it, but not too expensive.
|
||||
/repository/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint = FALSE)
|
||||
if(!(power && source))
|
||||
return
|
||||
var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), source.z)
|
||||
flat_radiate(epicentre, power, world.maxx, respect_maint)
|
||||
|
||||
/turf
|
||||
var/cached_rad_resistance = 0
|
||||
|
||||
/turf/proc/calc_rad_resistance()
|
||||
cached_rad_resistance = 0
|
||||
for(var/obj/O in src.contents)
|
||||
if(O.rad_resistance) //Override
|
||||
cached_rad_resistance += O.rad_resistance
|
||||
|
||||
else if(O.density) //So open doors don't get counted
|
||||
var/material/M = O.get_material()
|
||||
if(!M) continue
|
||||
cached_rad_resistance += M.weight
|
||||
// Looks like storing the contents length is meant to be a basic check if the cache is stale due to items enter/exiting. Better than nothing so I'm leaving it as is. ~Leshana
|
||||
radiation_repository.resistance_cache[src] = (length(contents) + 1)
|
||||
|
||||
/turf/simulated/wall/calc_rad_resistance()
|
||||
radiation_repository.resistance_cache[src] = (length(contents) + 1)
|
||||
cached_rad_resistance = (density ? material.weight : 0)
|
||||
|
||||
/obj
|
||||
var/rad_resistance = 0 // Allow overriding rad resistance
|
||||
|
||||
// If people expand the system, this may be useful. Here as a placeholder until then
|
||||
/atom/proc/rad_act(var/severity)
|
||||
return 1
|
||||
|
||||
/mob/living/rad_act(var/severity)
|
||||
if(severity)
|
||||
src.apply_effect(severity, IRRADIATE, src.getarmor(null, "rad"))
|
||||
for(var/atom/I in src)
|
||||
I.rad_act(severity)
|
||||
@@ -47,7 +47,7 @@
|
||||
name = "Surplus militia rifles"
|
||||
contains = list(
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin = 3,
|
||||
/obj/item/ammo_magazine/clip/a762 = 6
|
||||
/obj/item/ammo_magazine/clip/c762 = 6
|
||||
)
|
||||
cost = 50
|
||||
contraband = 1
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
name = "Flare guns crate"
|
||||
contains = list(
|
||||
/obj/item/weapon/gun/projectile/sec/flash,
|
||||
/obj/item/ammo_magazine/c45m/flash,
|
||||
/obj/item/ammo_magazine/m45/flash,
|
||||
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/flare,
|
||||
/obj/item/weapon/storage/box/flashshells
|
||||
)
|
||||
@@ -73,7 +73,7 @@
|
||||
containertype = /obj/structure/closet/crate/secure
|
||||
containername = "Energy marksman crate"
|
||||
access = access_armory
|
||||
|
||||
/* VOREStation edit -- This is a bad idea.
|
||||
/datum/supply_packs/munitions/burstlaser
|
||||
name = "Burst laser crate"
|
||||
contains = list(/obj/item/weapon/gun/energy/gun/burst = 2)
|
||||
@@ -81,7 +81,7 @@
|
||||
containertype = /obj/structure/closet/crate/secure
|
||||
containername = "Burst laser crate"
|
||||
access = access_armory
|
||||
|
||||
*/
|
||||
/datum/supply_packs/munitions/ionweapons
|
||||
name = "Electromagnetic weapons crate"
|
||||
contains = list(
|
||||
@@ -129,7 +129,7 @@
|
||||
contains = list(
|
||||
/obj/item/device/assembly/timer,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/rifle/practice = 2,
|
||||
/obj/item/ammo_magazine/clip/a762/practice = 4,
|
||||
/obj/item/ammo_magazine/clip/c762/practice = 4,
|
||||
/obj/item/target = 2,
|
||||
/obj/item/target/alien = 2,
|
||||
/obj/item/target/syndicate = 2
|
||||
@@ -154,9 +154,9 @@
|
||||
name = "Automatic weapon ammunition crate"
|
||||
num_contained = 6
|
||||
contains = list(
|
||||
/obj/item/ammo_magazine/mc9mmt,
|
||||
/obj/item/ammo_magazine/mc9mmt/rubber,
|
||||
/obj/item/ammo_magazine/a556
|
||||
/obj/item/ammo_magazine/m9mmt,
|
||||
/obj/item/ammo_magazine/m9mmt/rubber,
|
||||
/obj/item/ammo_magazine/m556saw
|
||||
)
|
||||
cost = 25
|
||||
containertype = /obj/structure/closet/crate/secure
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
containername = "Dangerous Predator crate"
|
||||
access = access_xenobiology
|
||||
|
||||
/datum/supply_packs/sci/pred
|
||||
/datum/supply_packs/sci/pred_doom
|
||||
name = "EXTREMELY Dangerous Predator crate"
|
||||
cost = 200
|
||||
containertype = /obj/structure/largecrate/animal/dangerous
|
||||
|
||||
@@ -8,36 +8,36 @@
|
||||
|
||||
/datum/uplink_item/item/ammo/a357
|
||||
name = ".357 Speedloader"
|
||||
path = /obj/item/ammo_magazine/a357
|
||||
path = /obj/item/ammo_magazine/s357
|
||||
|
||||
/datum/uplink_item/item/ammo/mc9mm
|
||||
name = "Pistol Magazine (9mm)"
|
||||
path = /obj/item/ammo_magazine/mc9mm
|
||||
path = /obj/item/ammo_magazine/m9mm
|
||||
|
||||
/datum/uplink_item/item/ammo/c45m
|
||||
name = "Pistol Magazine (.45)"
|
||||
path = /obj/item/ammo_magazine/c45m
|
||||
path = /obj/item/ammo_magazine/m45
|
||||
|
||||
/datum/uplink_item/item/ammo/c45map
|
||||
name = "Pistol Magazine (.45 AP)"
|
||||
path = /obj/item/ammo_magazine/c45m/ap
|
||||
path = /obj/item/ammo_magazine/m45/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/tommymag
|
||||
name = "Tommygun Magazine (.45)"
|
||||
path = /obj/item/ammo_magazine/tommymag
|
||||
path = /obj/item/ammo_magazine/m45tommy
|
||||
|
||||
/datum/uplink_item/item/ammo/tommymagap
|
||||
name = "Tommygun Magazine (.45 AP)"
|
||||
path = /obj/item/ammo_magazine/tommymag/ap
|
||||
path = /obj/item/ammo_magazine/m45tommy/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/tommydrum
|
||||
name = "Tommygun Drum Magazine (.45)"
|
||||
path = /obj/item/ammo_magazine/tommydrum
|
||||
path = /obj/item/ammo_magazine/m45tommydrum
|
||||
item_cost = 40
|
||||
|
||||
/datum/uplink_item/item/ammo/tommydrumap
|
||||
name = "Tommygun Drum Magazine (.45 AP)"
|
||||
path = /obj/item/ammo_magazine/tommydrum/ap
|
||||
path = /obj/item/ammo_magazine/m45tommydrum/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/darts
|
||||
name = "Darts"
|
||||
@@ -50,39 +50,39 @@
|
||||
|
||||
/datum/uplink_item/item/ammo/c556
|
||||
name = "Rifle Magazine (5.56mm)"
|
||||
path = /obj/item/ammo_magazine/c556
|
||||
path = /obj/item/ammo_magazine/m556
|
||||
|
||||
/datum/uplink_item/item/ammo/c556/ext
|
||||
name = "Rifle Magazine (5.56mm Extended)"
|
||||
path = /obj/item/ammo_magazine/c556/ext
|
||||
path = /obj/item/ammo_magazine/m556/ext
|
||||
|
||||
/datum/uplink_item/item/ammo/c556/ap
|
||||
name = "Rifle Magazine (5.56mm AP)"
|
||||
path = /obj/item/ammo_magazine/c556/ap
|
||||
path = /obj/item/ammo_magazine/m556/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/c556/ap/ext
|
||||
name = "Rifle Magazine (5.56mm AP Extended)"
|
||||
path = /obj/item/ammo_magazine/c556/ap/ext
|
||||
path = /obj/item/ammo_magazine/m556/ap/ext
|
||||
|
||||
/datum/uplink_item/item/ammo/c762
|
||||
name = "Rifle Magazine (7.62mm)"
|
||||
path = /obj/item/ammo_magazine/c762
|
||||
path = /obj/item/ammo_magazine/m762
|
||||
|
||||
/datum/uplink_item/item/ammo/c762/ap
|
||||
name = "Rifle Magazine (7.62mm AP)"
|
||||
path = /obj/item/ammo_magazine/c762/ap
|
||||
path = /obj/item/ammo_magazine/m762/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/a10mm
|
||||
name = "SMG Magazine (10mm)"
|
||||
path = /obj/item/ammo_magazine/a10mm
|
||||
path = /obj/item/ammo_magazine/m10mm
|
||||
|
||||
/datum/uplink_item/item/ammo/a556
|
||||
name = "Machinegun Magazine (5.56mm)"
|
||||
path = /obj/item/ammo_magazine/a556
|
||||
path = /obj/item/ammo_magazine/m556saw
|
||||
|
||||
/datum/uplink_item/item/ammo/a556/ap
|
||||
name = "Machinegun Magazine (5.56mm AP)"
|
||||
path = /obj/item/ammo_magazine/a556/ap
|
||||
path = /obj/item/ammo_magazine/m556saw/ap
|
||||
|
||||
/datum/uplink_item/item/ammo/g12
|
||||
name = "12g Shotgun Ammo Box (Slug)"
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
//obtain a weak reference to a datum
|
||||
/proc/weakref(datum/D)
|
||||
if(D.gcDestroyed)
|
||||
if(!istype(D))
|
||||
return
|
||||
if(QDELETED(D))
|
||||
return
|
||||
if(!D.weakref)
|
||||
D.weakref = new/weakref(D)
|
||||
@@ -22,7 +24,7 @@
|
||||
/weakref/Destroy()
|
||||
// A weakref datum should not be manually destroyed as it is a shared resource,
|
||||
// rather it should be automatically collected by the BYOND GC when all references are gone.
|
||||
return 0
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
/weakref/proc/resolve()
|
||||
var/datum/D = locate(ref)
|
||||
|
||||
@@ -48,6 +48,7 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown"
|
||||
|
||||
/datum/wires/Destroy()
|
||||
holder = null
|
||||
signallers.Cut()
|
||||
return ..()
|
||||
|
||||
/datum/wires/proc/GenerateWires()
|
||||
|
||||
@@ -48,7 +48,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
var/obj/machinery/power/apc/apc = null
|
||||
var/no_air = null
|
||||
// var/list/lights // list of all lights on this area
|
||||
var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area
|
||||
var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area
|
||||
var/firedoors_closed = 0
|
||||
var/list/ambience = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
|
||||
var/list/forced_ambience = null
|
||||
|
||||
@@ -87,6 +87,8 @@
|
||||
/area/proc/firedoors_close()
|
||||
if(!firedoors_closed)
|
||||
firedoors_closed = TRUE
|
||||
if(!all_doors)
|
||||
return
|
||||
for(var/obj/machinery/door/firedoor/E in all_doors)
|
||||
if(!E.blocked)
|
||||
if(E.operating)
|
||||
@@ -99,6 +101,8 @@
|
||||
/area/proc/firedoors_open()
|
||||
if(firedoors_closed)
|
||||
firedoors_closed = FALSE
|
||||
if(!all_doors)
|
||||
return
|
||||
for(var/obj/machinery/door/firedoor/E in all_doors)
|
||||
if(!E.blocked)
|
||||
if(E.operating)
|
||||
|
||||
@@ -23,16 +23,6 @@
|
||||
if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING)
|
||||
initialize()
|
||||
|
||||
/atom/movable/Del()
|
||||
if(isnull(gcDestroyed) && loc)
|
||||
testing("GC: -- [type] was deleted via del() rather than qdel() --")
|
||||
crash_with("GC: -- [type] was deleted via del() rather than qdel() --") // stick a stack trace in the runtime logs
|
||||
// else if(isnull(gcDestroyed))
|
||||
// testing("GC: [type] was deleted via GC without qdel()") //Not really a huge issue but from now on, please qdel()
|
||||
// else
|
||||
// testing("GC: [type] was deleted via GC with qdel()")
|
||||
..()
|
||||
|
||||
/atom/movable/Destroy()
|
||||
. = ..()
|
||||
if(reagents)
|
||||
@@ -53,7 +43,7 @@
|
||||
pulledby = null
|
||||
|
||||
/atom/movable/proc/initialize()
|
||||
if(!isnull(gcDestroyed))
|
||||
if(QDELETED(src))
|
||||
crash_with("GC: -- [type] had initialize() called after qdel() --")
|
||||
|
||||
/atom/movable/Bump(var/atom/A, yes)
|
||||
|
||||
@@ -90,6 +90,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
|
||||
|
||||
// VOREStation
|
||||
var/custom_species
|
||||
var/base_species = "Human"
|
||||
var/list/species_traits = list()
|
||||
var/blood_color = "#A10808"
|
||||
// VOREStation
|
||||
|
||||
// New stuff
|
||||
@@ -105,6 +108,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
|
||||
new_dna.real_name=real_name
|
||||
new_dna.species=species
|
||||
new_dna.body_markings=body_markings.Copy()
|
||||
new_dna.base_species=base_species //VOREStation Edit
|
||||
new_dna.species_traits=species_traits.Copy() //VOREStation Edit
|
||||
new_dna.blood_color=blood_color //VOREStation Edit
|
||||
for(var/b=1;b<=DNA_SE_LENGTH;b++)
|
||||
new_dna.SE[b]=SE[b]
|
||||
if(b<=DNA_UI_LENGTH)
|
||||
@@ -163,6 +169,11 @@ var/global/list/datum/dna/gene/dna_genes[0]
|
||||
|
||||
// Technically custom_species is not part of the UI, but this place avoids merge problems.
|
||||
src.custom_species = character.custom_species
|
||||
if(istype(character.species,/datum/species/custom))
|
||||
var/datum/species/custom/CS = character.species
|
||||
src.species_traits = CS.traits.Copy()
|
||||
src.base_species = CS.base_species
|
||||
src.blood_color = CS.blood_color
|
||||
|
||||
// +1 to account for the none-of-the-above possibility
|
||||
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
|
||||
|
||||
@@ -201,11 +201,16 @@
|
||||
|
||||
// Technically custom_species is not part of the UI, but this place avoids merge problems.
|
||||
H.custom_species = dna.custom_species
|
||||
if(istype(H.species,/datum/species/custom))
|
||||
var/datum/species/custom/CS = H.species
|
||||
var/datum/species/custom/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
|
||||
new_CS.blood_color = dna.blood_color
|
||||
|
||||
// VOREStation Edit End
|
||||
|
||||
H.force_update_organs() //VOREStation Add - Gotta do this too
|
||||
H.force_update_limbs()
|
||||
H.update_body(0)
|
||||
//H.update_body(0) //VOREStation Edit - Done in force_update_limbs already
|
||||
H.update_eyes()
|
||||
H.update_hair()
|
||||
|
||||
@@ -213,6 +218,13 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
//VOREStation Add
|
||||
/mob/living/carbon/human/proc/force_update_organs()
|
||||
for(var/organ in organs + internal_organs)
|
||||
var/obj/item/organ/O = organ
|
||||
O.species = species
|
||||
//VOREStation Add End
|
||||
|
||||
// Used below, simple injection modifier.
|
||||
/proc/probinj(var/pr, var/inj)
|
||||
return prob(pr+inj*pr)
|
||||
|
||||
@@ -37,7 +37,6 @@ var/global/universe_has_ended = 0
|
||||
// Apply changes when entering state
|
||||
/datum/universal_state/supermatter_cascade/OnEnter()
|
||||
set background = 1
|
||||
garbage_collector.garbage_collect = 0
|
||||
world << "<span class='sinister' style='font-size:22pt'>You are blinded by a brilliant flash of energy.</span>"
|
||||
|
||||
world << sound('sound/effects/cascade.ogg')
|
||||
|
||||
@@ -41,6 +41,10 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
|
||||
if(clong && prob(25))
|
||||
src.loc = clong.loc
|
||||
|
||||
Destroy()
|
||||
walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever.
|
||||
return ..()
|
||||
|
||||
/proc/immovablerod()
|
||||
var/startx = 0
|
||||
var/starty = 0
|
||||
|
||||
@@ -90,6 +90,10 @@ The "dust" will damage the hull of the station causin minor hull breaches.
|
||||
walk_towards(src, goal, 1)
|
||||
return
|
||||
|
||||
Destroy()
|
||||
walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever.
|
||||
return ..()
|
||||
|
||||
touch_map_edge()
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
vote.process()
|
||||
if(pregame_timeleft <= 0)
|
||||
current_state = GAME_STATE_SETTING_UP
|
||||
Master.SetRunLevel(RUNLEVEL_SETUP)
|
||||
while (!setup())
|
||||
|
||||
|
||||
@@ -63,6 +64,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
if((master_mode=="random") || (master_mode=="secret"))
|
||||
if(!runnable_modes.len)
|
||||
current_state = GAME_STATE_PREGAME
|
||||
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
||||
world << "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby."
|
||||
return 0
|
||||
if(secret_force_mode != "secret")
|
||||
@@ -77,6 +79,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
|
||||
if(!src.mode)
|
||||
current_state = GAME_STATE_PREGAME
|
||||
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
||||
world << "<span class='danger'>Serious error in mode setup!</span> Reverting to pre-game lobby."
|
||||
return 0
|
||||
|
||||
@@ -88,6 +91,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
if(!src.mode.can_start())
|
||||
world << "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby."
|
||||
current_state = GAME_STATE_PREGAME
|
||||
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
||||
mode.fail_setup()
|
||||
mode = null
|
||||
job_master.ResetOccupations()
|
||||
@@ -116,6 +120,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
|
||||
shuttle_controller.setup_shuttle_docks()
|
||||
|
||||
// TODO - Leshana - Dear God Fix This. Fix all of this. Not just this line, this entire proc. This entire file!
|
||||
spawn(0)//Forking here so we dont have to wait for this to finish
|
||||
mode.post_setup()
|
||||
//Cleanup some stuff
|
||||
@@ -144,6 +149,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
*/
|
||||
|
||||
processScheduler.start()
|
||||
Master.SetRunLevel(RUNLEVEL_GAME)
|
||||
|
||||
if(config.sql_enabled)
|
||||
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
|
||||
@@ -308,6 +314,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
|
||||
if(!mode.explosion_in_progress && game_finished && (mode_finished || post_game))
|
||||
current_state = GAME_STATE_FINISHED
|
||||
Master.SetRunLevel(RUNLEVEL_POSTGAME)
|
||||
|
||||
spawn
|
||||
declare_completion()
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
|
||||
/obj/effect/meteor/Destroy()
|
||||
walk(src,0) //this cancels the walk_towards() proc
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/effect/meteor/New()
|
||||
..()
|
||||
@@ -271,8 +271,7 @@
|
||||
if(explode)
|
||||
explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0)
|
||||
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
|
||||
for(var/mob/living/L in view(10, src))
|
||||
L.apply_effect(40, IRRADIATE)
|
||||
radiation_repository.radiate(src, 50)
|
||||
|
||||
// This meteor fries toasters.
|
||||
/obj/effect/meteor/emp
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
economic_modifier = 1
|
||||
access = list() //See /datum/job/assistant/get_access()
|
||||
minimal_access = list() //See /datum/job/assistant/get_access()
|
||||
alt_titles = list("Technical Assistant","Medical Intern","Research Assistant","Visitor", "Resident")
|
||||
alt_titles = list("Technical Assistant","Test Subject","Medical Intern","Research Assistant","Visitor", "Resident") // Test Subject is a VOREStation edit.
|
||||
|
||||
/datum/job/assistant/equip(var/mob/living/carbon/human/H, var/alt_title)
|
||||
if(!H) return 0
|
||||
|
||||
@@ -231,8 +231,8 @@
|
||||
department = "Civilian"
|
||||
department_flag = CIVILIAN
|
||||
faction = "Station"
|
||||
total_positions = 1
|
||||
spawn_positions = 1
|
||||
total_positions = 2 // VOREStation Edit. Original number is 1.
|
||||
spawn_positions = 2 // VOREStation Edit. Original number is 1.
|
||||
supervisors = "the head of personnel"
|
||||
selection_color = "#515151"
|
||||
idtype = /obj/item/weapon/card/id/civilian
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
if(COMPANY_OPPOSED) loyalty = 0.70
|
||||
|
||||
//give them an account in the station database
|
||||
var/money_amount = (rand(5,50) + rand(5, 50)) * loyalty * economic_modifier * ECO_MODIFIER
|
||||
var/money_amount = (rand(15,40) + rand(15,40)) * loyalty * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks.
|
||||
var/datum/money_account/M = create_account(H.real_name, money_amount, null)
|
||||
if(H.mind)
|
||||
var/remembered_info = ""
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
visible_message("A red light flashes on \the [src].")
|
||||
return
|
||||
cable.use(amount)
|
||||
if(deleted(cable))
|
||||
if(QDELETED(cable))
|
||||
cable = null
|
||||
return 1
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
use_power = 1
|
||||
idle_power_usage = 1
|
||||
active_power_usage = 5
|
||||
surgery_odds = 100
|
||||
var/mob/living/carbon/human/victim = null
|
||||
var/strapped = 0.0
|
||||
var/obj/machinery/computer/operating/computer = null
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user