Merge remote-tracking branch 'refs/remotes/PolarisSS13/master' into AcesFixesThingses

This commit is contained in:
Spades
2017-06-19 23:11:34 -04:00
285 changed files with 4949 additions and 2249 deletions
@@ -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
+1 -3
View File
@@ -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
+1 -3
View File
@@ -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
+9
View File
@@ -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)
+4 -4
View File
@@ -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
+20 -8
View File
@@ -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)
@@ -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)
+17 -13
View File
@@ -103,19 +103,23 @@ obj/var/phoronproof = 0
//Burn eyes if exposed.
if(vsc.plc.EYE_BURNS && (species.breath_type != "phoron"))
if(!head)
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
else
if(!(head.body_parts_covered & EYES))
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
var/burn_eyes = 1
//Check for protective glasses
if(glasses && (glasses.body_parts_covered & EYES) && (glasses.item_flags & AIRTIGHT))
burn_eyes = 0
//Check for protective maskwear
if(burn_eyes && wear_mask && (wear_mask.body_parts_covered & EYES) && (wear_mask.item_flags & AIRTIGHT))
burn_eyes = 0
//Check for protective helmets
if(burn_eyes && head && (head.body_parts_covered & EYES) && (head.item_flags & AIRTIGHT))
burn_eyes = 0
//If we still need to, burn their eyes
if(burn_eyes)
burn_eyes()
//Genetic Corruption
if(vsc.plc.GENETIC_CORRUPTION && (species.breath_type != "phoron"))
+2 -2
View File
@@ -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
View File
@@ -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)
+69
View File
@@ -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
+10
View File
@@ -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 )
+2
View File
@@ -196,3 +196,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
+17
View File
@@ -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)
+13
View File
@@ -0,0 +1,13 @@
// 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
+7
View File
@@ -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()
+16 -14
View File
@@ -271,20 +271,22 @@
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(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 +296,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)
+63
View File
@@ -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
+1 -1
View File
@@ -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
+17
View File
@@ -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
+656
View File
@@ -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
+20
View File
@@ -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
View File
@@ -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))
+22 -5
View File
@@ -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)
@@ -1284,6 +1294,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)
+33
View File
@@ -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() )
+10
View File
@@ -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
+1 -1
View File
@@ -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()
+4 -1
View File
@@ -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
+1 -1
View File
@@ -155,7 +155,7 @@ datum/hud/New(mob/owner)
..()
/datum/hud/Destroy()
..()
. = ..()
grab_intent = null
hurt_intent = null
disarm_intent = null
+1 -1
View File
@@ -46,7 +46,7 @@
var/obj/item/owner
/obj/screen/item_action/Destroy()
..()
. = ..()
owner = null
/obj/screen/item_action/Click()
+2 -2
View File
@@ -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")
-254
View File
@@ -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
+3 -3
View File
@@ -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 -1
View File
@@ -14,7 +14,7 @@
/datum/controller/process/mob/doWork()
for(last_object in mob_list)
var/mob/M = last_object
if(M && isnull(M.gcDestroyed))
if(M && !QDELETED(M))
try
M.Life()
catch(var/exception/e)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+56
View File
@@ -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")
+4 -6
View File
@@ -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
+7
View File
@@ -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)
-1
View File
@@ -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
+15 -2
View File
@@ -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
@@ -219,6 +220,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 = 6.5
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)
@@ -564,7 +569,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
@@ -716,6 +726,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]'")
+19
View File
@@ -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)
+102
View File
@@ -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])"))
+556
View File
@@ -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()
+7 -5
View File
@@ -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,8 +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
spawn(20)
createRandomZlevel()
@@ -47,7 +49,7 @@ datum/controller/game_controller/proc/setup_objects()
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
sleep(-1)
for(var/atom/movable/object in world)
if(isnull(object.gcDestroyed))
if(!QDELETED(object))
object.initialize()
admin_notice("<span class='danger'>Initializing areas</span>", R_DEBUG)
@@ -78,8 +80,8 @@ 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()
sleep(-1)
+7 -310
View File
@@ -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,311 +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
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
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),
"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 Kara gravity well with current velocity."
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
+202
View File
@@ -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()
-46
View File
@@ -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()
+378
View File
@@ -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
+46 -8
View File
@@ -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"
+7 -1
View File
@@ -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.
+55 -55
View File
@@ -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"
@@ -155,4 +155,5 @@ Data storage vars:
tag = null
arg_list.Cut()
stop()
return QDEL_HINT_LETMELIVE
//Do not call ..()
+1 -1
View File
@@ -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
+138
View File
@@ -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)
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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
)
@@ -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
+17 -17
View File
@@ -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)"
+4 -2
View File
@@ -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)
+1
View File
@@ -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()
+1 -1
View File
@@ -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
+4
View File
@@ -75,6 +75,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)
@@ -87,6 +89,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)
+1 -11
View File
@@ -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)
@@ -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')
+4
View File
@@ -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
+4
View File
@@ -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)
+7
View File
@@ -62,6 +62,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())
@@ -74,6 +75,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")
@@ -88,6 +90,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
@@ -99,6 +102,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()
@@ -127,6 +131,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
@@ -155,6 +160,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
@@ -319,6 +325,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()
+2 -3
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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
+4 -2
View File
@@ -287,8 +287,9 @@
var/bloodData[0]
if(H.vessel)
var/blood_volume = round(H.vessel.get_reagent_amount("blood"))
var/blood_max = H.species.blood_volume
bloodData["volume"] = blood_volume
bloodData["percent"] = round(((blood_volume / 560)*100))
bloodData["percent"] = round(((blood_volume / blood_max)*100))
occupantData["blood"] = bloodData
@@ -459,7 +460,8 @@
if(occupant.vessel)
var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
var/blood_percent = blood_volume / 560
var/blood_max = occupant.species.blood_volume
var/blood_percent = blood_volume / blood_max
blood_percent *= 100
extra_font = "<font color=[blood_volume > 448 ? "blue" : "red"]>"
@@ -21,9 +21,9 @@
return 1
/obj/machinery/portable_atmospherics/Destroy()
qdel(air_contents)
qdel(holding)
..()
qdel_null(air_contents)
qdel_null(holding)
. = ..()
/obj/machinery/portable_atmospherics/initialize()
. = ..()
@@ -40,11 +40,6 @@
else
update_icon()
/obj/machinery/portable_atmospherics/Destroy()
qdel(air_contents)
..()
/obj/machinery/portable_atmospherics/proc/StandardAirMix()
return list(
"oxygen" = O2STANDARD * MolesForPressure(),
+2 -2
View File
@@ -144,7 +144,7 @@
if(is_robot_module(O))
return 0
if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/a357) || istype(O,/obj/item/ammo_magazine/c38)) // Prevents ammo recycling exploit with speedloaders.
if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/s357) || istype(O,/obj/item/ammo_magazine/s38)) // Prevents ammo recycling exploit with speedloaders.
user << "\The [O] is too hazardous to recycle with the autolathe!"
return
/* ToDo: Make this actually check for ammo and change the value of the magazine if it's empty. -Spades
@@ -262,7 +262,7 @@
stored_material[material] = max(0, stored_material[material] - round(making.resources[material] * mat_efficiency) * multiplier)
update_icon() // So lid closes
sleep(build_time)
busy = 0
+1 -1
View File
@@ -206,7 +206,7 @@
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if (W.force >= src.toughness)
user.do_attack_animation(src)
visible_message("<span class='warning'><b>[src] has been [pick(W.attack_verb)] with [W] by [user]!</b></span>")
visible_message("<span class='warning'><b>[src] has been [W.attack_verb.len? pick(W.attack_verb) : "attacked"] with [W] by [user]!</b></span>")
if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items?
var/obj/item/I = W
if (I.hitsound)
+1 -1
View File
@@ -181,7 +181,7 @@
use_power(7500) //This might need tweaking.
return
else if((occupant.health >= heal_level) && (!eject_wait))
else if((occupant.health >= heal_level || occupant.health == occupant.maxHealth) && (!eject_wait))
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
audible_message("\The [src] signals that the cloning process is complete.")
connected_message("Cloning Process Complete.")
+1 -1
View File
@@ -189,7 +189,7 @@
/obj/structure/AIcore/deactivated/Destroy()
if(src in empty_playable_ai_cores)
empty_playable_ai_cores -= src
..()
return ..()
/obj/structure/AIcore/deactivated/proc/load_ai(var/mob/living/silicon/ai/transfer, var/obj/item/device/aicard/card, var/mob/user)
+1 -1
View File
@@ -314,7 +314,7 @@
if(occupant)
occupant.forceMove(loc)
occupant.resting = 1
..()
return ..()
/obj/machinery/cryopod/initialize()
..()
+6 -7
View File
@@ -263,6 +263,7 @@
icon = 'icons/obj/doors/Dooruranium.dmi'
mineral = "uranium"
var/last_event = 0
var/rad_power = 7.5
/obj/machinery/door/airlock/process()
// Deliberate no call to parent.
@@ -280,15 +281,10 @@
/obj/machinery/door/airlock/uranium/process()
if(world.time > last_event+20)
if(prob(50))
radiate()
radiation_repository.radiate(src, rad_power)
last_event = world.time
..()
/obj/machinery/door/airlock/uranium/proc/radiate()
for(var/mob/living/L in range (3,src))
L.apply_effect(15,IRRADIATE,0)
return
/obj/machinery/door/airlock/phoron
name = "Phoron Airlock"
desc = "No way this can end badly."
@@ -757,6 +753,9 @@ About the new airlock wires panel:
update_icon()
return 1
/obj/machinery/door/airlock/proc/can_remove_electronics()
return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN))))
/obj/machinery/door/airlock/attackby(C as obj, mob/user as mob)
//world << text("airlock attackby src [] obj [] mob []", src, C, user)
if(!istype(usr, /mob/living/silicon))
@@ -801,7 +800,7 @@ About the new airlock wires panel:
var/obj/item/weapon/pai_cable/cable = C
cable.plugin(src, user)
else if(!repairing && istype(C, /obj/item/weapon/crowbar))
if(src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) )
if(can_remove_electronics())
playsound(src.loc, 'sound/items/Crowbar.ogg', 75, 1)
user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.")
if(do_after(user,40))
+3 -3
View File
@@ -145,7 +145,7 @@ obj/machinery/door/airlock/New()
obj/machinery/door/airlock/Destroy()
if(frequency && radio_controller)
radio_controller.remove_object(src,frequency)
..()
return ..()
obj/machinery/airlock_sensor
icon = 'icons/obj/airlock_machines.dmi'
@@ -220,7 +220,7 @@ obj/machinery/airlock_sensor/New()
obj/machinery/airlock_sensor/Destroy()
if(radio_controller)
radio_controller.remove_object(src,frequency)
..()
return ..()
obj/machinery/airlock_sensor/airlock_interior
command = "cycle_interior"
@@ -292,7 +292,7 @@ obj/machinery/access_button/New()
obj/machinery/access_button/Destroy()
if(radio_controller)
radio_controller.remove_object(src, frequency)
..()
return ..()
obj/machinery/access_button/airlock_interior
frequency = 1379
+9 -1
View File
@@ -13,7 +13,7 @@
icon = 'icons/obj/doors/rapid_pdoor.dmi'
icon_state = null
min_force = 20 //minimum amount of force needed to damage the door with a melee weapon
var/material/implicit_material
// Icon states for different shutter types. Simply change this instead of rewriting the update_icon proc.
var/icon_state_open = null
var/icon_state_opening = null
@@ -29,6 +29,13 @@
//turning this off prevents awkward zone geometry in places like medbay lobby, for example.
block_air_zones = 0
/obj/machinery/door/blast/initialize()
..()
implicit_material = get_material_by_name("plasteel")
/obj/machinery/door/blast/get_material()
return implicit_material
// Proc: Bumped()
// Parameters: 1 (AM - Atom that tried to walk through this object)
// Description: If we are open returns zero, otherwise returns result of parent function.
@@ -46,6 +53,7 @@
icon_state = icon_state_closed
else
icon_state = icon_state_open
radiation_repository.resistance_cache.Remove(get_turf(src))
return
// Proc: force_open()
+2 -2
View File
@@ -73,8 +73,7 @@
/obj/machinery/door/Destroy()
density = 0
update_nearby_tiles()
..()
return
. = ..()
/obj/machinery/door/process()
if(close_door_at && world.time >= close_door_at)
@@ -362,6 +361,7 @@
icon_state = "door1"
else
icon_state = "door0"
radiation_repository.resistance_cache.Remove(get_turf(src))
return
+3 -3
View File
@@ -60,18 +60,18 @@
var/area/A = get_area(src)
ASSERT(istype(A))
A.all_doors.Add(src)
LAZYADD(A.all_doors, src)
areas_added = list(A)
for(var/direction in cardinal)
A = get_area(get_step(src,direction))
if(istype(A) && !(A in areas_added))
A.all_doors.Add(src)
LAZYADD(A.all_doors, src)
areas_added += A
/obj/machinery/door/firedoor/Destroy()
for(var/area/A in areas_added)
A.all_doors.Remove(src)
LAZYREMOVE(A.all_doors, src)
. = ..()
/obj/machinery/door/firedoor/get_material()
+1 -1
View File
@@ -60,7 +60,7 @@
/obj/machinery/door/window/Destroy()
density = 0
update_nearby_tiles()
..()
return ..()
/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj)
if (!( ismob(AM) ))
+1 -1
View File
@@ -150,7 +150,7 @@ var/list/floor_light_cache = list()
var/area/A = get_area(src)
if(A)
on = 0
..()
. = ..()
/obj/machinery/floor_light/cultify()
default_light_colour = "#FF0000"
+1 -1
View File
@@ -202,7 +202,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
/obj/machinery/hologram/holopad/Destroy()
for (var/mob/living/silicon/ai/master in masters)
clear_holo(master)
..()
return ..()
/*
Holographic project of everything else.
+9 -9
View File
@@ -485,7 +485,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -504,7 +504,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -523,7 +523,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -542,7 +542,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -559,7 +559,7 @@ Buildable meters
P.initialize_directions = pipe_dir //this var it's used to know if the pipe is bent or not
P.initialize_directions_he = pipe_dir
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -594,7 +594,7 @@ Buildable meters
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
M.initialize()
if (deleted(M))
if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
@@ -663,7 +663,7 @@ Buildable meters
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
M.initialize()
if (deleted(M))
if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
@@ -740,7 +740,7 @@ Buildable meters
P.initialize_directions = src.get_pdir()
P.initialize_directions_he = src.get_hdir()
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext //"There's nothing to connect this pipe to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
P.build_network()
@@ -922,7 +922,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
if (deleted(P))
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
+2 -3
View File
@@ -23,9 +23,8 @@
update_icon()
/obj/machinery/pipelayer/Destroy()
qdel(W)
W = null
..()
qdel_null(W)
. = ..()
/obj/machinery/pipelayer/RefreshParts()
var/mb_rating = 0
+1 -1
View File
@@ -97,7 +97,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
req_console_supplies -= department
if(departmentType & RC_INFO)
req_console_information -= department
..()
return ..()
/obj/machinery/requests_console/attack_hand(user as mob)
if(..(user))
+1 -1
View File
@@ -1029,7 +1029,7 @@
icon_state = "engivend"
icon_deny = "engivend-deny"
req_access = list(access_engine_equip)
products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/cell/high = 10,
products = list(/obj/item/device/geiger = 4,/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/cell/high = 10,
/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/module/power_control = 10,
/obj/item/weapon/circuitboard/airalarm = 10,/obj/item/weapon/circuitboard/firealarm = 10,/obj/item/weapon/circuitboard/status_display = 2,
/obj/item/weapon/circuitboard/ai_status_display = 2,/obj/item/weapon/circuitboard/newscaster = 2,/obj/item/weapon/circuitboard/holopad = 2,
+1 -1
View File
@@ -115,7 +115,7 @@
user << "<span class='warning'>You can't fit \the [W] inside.</span>"
return
else if(istype(W, /obj/item/clothing))
else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet))
if(washing.len < 5)
if(state in list(1, 3))
user.drop_item()
+1 -5
View File
@@ -973,11 +973,7 @@
process(var/obj/item/mecha_parts/mecha_equipment/generator/nuclear/EG)
if(..())
for(var/mob/living/carbon/M in view(EG.chassis))
if(istype(M,/mob/living/carbon/human))
M.apply_effect((EG.rad_per_cycle*3),IRRADIATE,0)
else
M.apply_effect(EG.rad_per_cycle, IRRADIATE)
radiation_repository.radiate(EG, (EG.rad_per_cycle * 3))
return 1
+6 -10
View File
@@ -140,18 +140,14 @@
cell = null
internal_tank = null
qdel(pr_int_temp_processor)
qdel(pr_inertial_movement)
qdel(pr_give_air)
qdel(pr_internal_damage)
qdel(spark_system)
pr_int_temp_processor = null
pr_give_air = null
pr_internal_damage = null
spark_system = null
qdel_null(pr_int_temp_processor)
qdel_null(pr_inertial_movement)
qdel_null(pr_give_air)
qdel_null(pr_internal_damage)
qdel_null(spark_system)
mechas_list -= src //global mech list
..()
. = ..()
////////////////////////
////// Helpers /////////

Some files were not shown because too many files have changed in this diff Show More