Merge branch 'master' of https://github.com/VOREStation/VOREStation into Fluff_Items_worshop

This commit is contained in:
JOAN-RIG\Joan Risu
2017-07-01 01:46:18 -07:00
486 changed files with 13703 additions and 4253 deletions
@@ -46,7 +46,7 @@
overlays += I
/obj/machinery/atmospherics/binary/algae_farm/Destroy()
..()
. = ..()
internal = null
/obj/machinery/atmospherics/binary/algae_farm/process()
@@ -101,7 +101,7 @@
if(inoperable() || !anchored)
icon_state = "algae-off"
else if(recent_moles_transferred >= moles_per_tick)
icon_state = "aglae-full"
icon_state = "algae-full"
else if(recent_moles_transferred > 0)
icon_state = "algae-full"
else
@@ -42,7 +42,7 @@ obj/machinery/atmospherics/binary
return null
Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -54,8 +54,6 @@ obj/machinery/atmospherics/binary
node1 = null
node2 = null
..()
initialize()
if(node1 && node2) return
@@ -36,7 +36,7 @@
initialize_directions = NORTH|SOUTH
Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -48,8 +48,6 @@
node1 = null
node2 = null
..()
process()
..()
if(anchored && !(stat&BROKEN))
@@ -28,7 +28,7 @@
input = null
output = null
filters.Cut()
..()
return ..()
/obj/machinery/atmospherics/omni/filter/sort_ports()
for(var/datum/omni_port/P in ports)
@@ -61,7 +61,7 @@
return null
/obj/machinery/atmospherics/portables_connector/Destroy()
loc = null
. = ..()
if(connected_device)
connected_device.disconnect()
@@ -72,8 +72,6 @@
node = null
..()
/obj/machinery/atmospherics/portables_connector/initialize()
if(node) return
@@ -51,7 +51,7 @@ obj/machinery/atmospherics/trinary
return null
Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -67,8 +67,6 @@ obj/machinery/atmospherics/trinary
node2 = null
node3 = null
..()
initialize()
if(node1 && node2 && node3) return
+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
+21 -9
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)
@@ -1060,7 +1070,7 @@
desc = "A large vessel containing pressurized gas."
volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet
var/start_pressure = 25*ONE_ATMOSPHERE
var/start_pressure = 75*ONE_ATMOSPHERE //Vorestation edit
level = 1
dir = SOUTH
@@ -1081,8 +1091,9 @@
/obj/machinery/atmospherics/pipe/tank/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
return list(node1)
@@ -1251,8 +1262,9 @@
/obj/machinery/atmospherics/pipe/vent/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
return list(node1)
+4
View File
@@ -117,6 +117,10 @@ obj/var/phoronproof = 0
if(burn_eyes && head && (head.body_parts_covered & EYES) && (head.item_flags & AIRTIGHT))
burn_eyes = 0
//VOREStation Edit - NIF Support
if(nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION))
burn_eyes = 0
//If we still need to, burn their eyes
if(burn_eyes)
burn_eyes()
+2 -2
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
+1 -1
View File
@@ -1,6 +1,7 @@
// Belly Mode Constants
#define DM_HOLD "Hold"
#define DM_DIGEST "Digest"
#define DM_DIGEST_NUMB "Digest (Numbing)"
#define DM_HEAL "Heal"
#define DM_ABSORB "Absorb"
#define DM_TRANSFORM_HAIR_AND_EYES "Transform (Hair and eyes)"
@@ -18,7 +19,6 @@
#define DM_DRAIN "Drain"
#define DM_UNABSORB "Unabsorb"
// Stance for hostile mobs to be in while devouring someone.
#define HOSTILE_STANCE_EATING 99
+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
@@ -201,3 +201,5 @@
#define TSC_MORPH "Morpheus"
#define TSC_XION "Xion" // Not really needed but consistancy I guess.
#define TSC_GIL "Gilthari"
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
+9
View File
@@ -15,3 +15,12 @@
#define VANTAG_KILL "vantag_kill"
#define ANNOUNCER_NAME "Facility PA"
//For custom species
#define STARTING_SPECIES_POINTS 1
#define MAX_SPECIES_TRAITS 5
// Resleeving Mind Record Status
#define MR_NORMAL 0
#define MR_UNSURE 1
#define MR_DEAD 2
+107
View File
@@ -0,0 +1,107 @@
// List indexes for software datum references on mobs
// This also controls the order they are displayed in the NIF stat panel
//AR Overlays
#define NIF_CIVILIAN_AR 1
#define NIF_MEDICAL_AR 2
#define NIF_SECURITY_AR 3
#define NIF_ENGINE_AR 4
#define NIF_SCIENCE_AR 5
#define NIF_OMNI_AR 6
//Misc Vision
#define NIF_CORRECTIVE_GLASS 7
#define NIF_MESONS 8
#define NIF_MATERIAL 9
#define NIF_THERMALS 10
#define NIF_NIGHTVIS 11
#define NIF_UVFILTER 12
#define NIF_FLASHPROT 13
//Health-related
#define NIF_CREWMONITOR 14
#define NIF_ORGANIC_HEAL 15
#define NIF_SYNTH_HEAL 16
#define NIF_AUTOSTASIS 17 //These two are just part of
#define NIF_MED_ALARM 18 //medichines right now
#define NIF_TOXHEAL 19 //And this, for organics
#define NIF_SPAREBREATH 20
//Combat Related
#define NIF_BRUTEARMOR 21
#define NIF_BURNARMOR 22
#define NIF_PAINKILLERS 23
#define NIF_HARDCLAWS 24
#define NIF_HIDDENLASER 25
//Other
#define NIF_COMMLINK 26
#define NIF_APCCHARGE 27
#define NIF_PRESSURE 28
#define NIF_HEATSINK 29
#define NIF_COMPLIANCE 30
#define NIF_SIZECHANGE 31
#define NIF_SOULCATCHER 32
// Must be equal to the highest number above
#define TOTAL_NIF_SOFTWARE 32
//////////////////////
// NIF flag list hints
#define NIF_FLAGS_VISION 1
#define NIF_FLAGS_HEALTH 2
#define NIF_FLAGS_COMBAT 3
#define NIF_FLAGS_OTHER 4
// NIF flags
//Vision
#define NIF_V_AR_CIVILIAN 0x1
#define NIF_V_AR_MEDICAL 0x2
#define NIF_V_AR_SECURITY 0x4
#define NIF_V_AR_ENGINE 0x8
#define NIF_V_AR_SCIENCE 0x10
#define NIF_V_AR_OMNI 0x20
#define NIF_V_CORRECTIVE 0x40
#define NIF_V_MESONS 0x80
#define NIF_V_MATERIAL 0x100
#define NIF_V_THERMALS 0x200
#define NIF_V_NIGHTVIS 0x400
#define NIF_V_UVFILTER 0x800
#define NIF_V_FLASHPROT 0x1000
//Health
#define NIF_H_ORGREPAIR 0x1
#define NIF_H_SYNTHREPAIR 0x2
#define NIF_H_AUTOSTASIS 0x4 //These two are just part of
#define NIF_H_ALERTMED 0x8 //medichines right now
#define NIF_H_TOXREGEN 0x10
#define NIF_H_SPAREBREATH 0x20
//Combat
#define NIF_C_BRUTEARMOR 0x1
#define NIF_C_BURNARMOR 0x2
#define NIF_C_PAINKILLERS 0x4
#define NIF_C_HARDCLAWS 0x8
#define NIF_C_HIDELASER 0x10
//Other
#define NIF_O_COMMLINK 0x1
#define NIF_O_APCCHARGE 0x2
#define NIF_O_PRESSURESEAL 0x4
#define NIF_O_HEATSINKS 0x8
#define NIF_O_SCMYSELF 0x10 //Soulcatcher stuff
#define NIF_O_SCOTHERS 0x20
///////////////////
// applies_to flags
#define NIF_ORGANIC 0x1
#define NIF_SYNTHETIC 0x2
/////////////
// stat flags
#define NIF_WORKING 0
#define NIF_POWFAIL 1
#define NIF_TEMPFAIL 2
#define NIF_INSTALLING 3
#define NIF_PREINSTALL 4
///////////////////
// tick_flags flags
#define NIF_NEVERTICK 0
#define NIF_ALWAYSTICK 1
#define NIF_ACTIVETICK 2
+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)
+15
View File
@@ -0,0 +1,15 @@
// SS runlevels
#define RUNLEVEL_INIT 0 // "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set)
#define RUNLEVEL_LOBBY 1 // Initial runlevel before setup. Returns to here if setup fails.
#define RUNLEVEL_SETUP 2 // While the gamemode setup is running. I.E gameticker.setup()
#define RUNLEVEL_GAME 4 // After successful game ticker setup, while the round is running.
#define RUNLEVEL_POSTGAME 8 // When round completes but before reboot
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
#define INIT_ORDER_LIGHTING 0
+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()
+19 -14
View File
@@ -271,20 +271,25 @@
var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM)
var/list/hearturfs = list()
for(var/atom/movable/AM in hear)
if(ismob(AM))
mobs += AM
hearturfs += AM.locs[1]
else if(isobj(AM))
objs += AM
hearturfs += AM.locs[1]
for(var/thing in hear)
if(istype(thing,/obj))
objs += thing
hearturfs += get_turf(thing)
else if(istype(thing,/mob))
mobs += thing
hearturfs += get_turf(thing)
//A list of every mob with a client
for(var/mob/M in player_list)
if(M.loc && M.locs[1] in hearturfs)
mobs |= M
for(var/mob in player_list)
if(isnull(mob)) //VOREStation Add - Fix for a weird bug with nulls in this list
player_list -= mob //VOREStation Add
continue //VOREStation Add
if(get_turf(mob) in hearturfs)
mobs |= mob
continue
else if(M.stat == DEAD && !M.forbid_seeing_deadchat)
var/mob/M = mob
if(M.stat == DEAD && !M.forbid_seeing_deadchat)
switch(type)
if(1) //Audio messages use ghost_ears
if(M.is_preference_enabled(/datum/client_preference/ghost_ears))
@@ -294,9 +299,9 @@
mobs |= M
//For objects below the top level who still want to hear
for(var/obj/O in listening_objects)
if(O.loc && O.locs[1] in hearturfs)
objs |= O
for(var/obj in listening_objects)
if(get_turf(obj) in hearturfs)
objs |= obj
return list("mobs" = mobs, "objs" = objs)
+1 -1
View File
@@ -32,7 +32,7 @@ var/global/list/all_species[0]
var/global/list/all_languages[0]
var/global/list/language_keys[0] // Table of say codes for all languages
var/global/list/whitelisted_species = list("Human") // Species that require a whitelist check.
var/global/list/playable_species = list("Human") // A list of ALL playable species, whitelisted, latejoin or otherwise.
var/global/list/playable_species = list("Custom Species","Human") // A list of ALL playable species, whitelisted, latejoin or otherwise. //VOREStation Edit - Making sure custom species is obvious.
var/list/mannequins_
+22 -1
View File
@@ -4,6 +4,11 @@
var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type
var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type
var/global/list/negative_traits = list() // Negative custom species traits, indexed by path
var/global/list/neutral_traits = list() // Neutral custom species traits, indexed by path
var/global/list/positive_traits = list() // Positive custom species traits, indexed by path
var/global/list/traits_costs = list() // Just path = cost list, saves time in char setup
var/global/list/all_traits = list() // All of 'em at once (same instances)
//stores numeric player size options indexed by name
var/global/list/player_sizes_list = list(
@@ -120,4 +125,20 @@ var/global/list/tf_egg_types = list(
for(var/path in paths)
var/datum/sprite_accessory/tail/instance = new path()
tail_styles_list[path] = instance
return 1 // Hooks must return 1
// Custom species traits
paths = typesof(/datum/trait) - /datum/trait
for(var/path in paths)
var/datum/trait/instance = new path()
var/cost = instance.cost
traits_costs[path] = cost
all_traits[path] = instance
switch(cost)
if(-INFINITY to -0.1)
negative_traits[path] = instance
if(0)
neutral_traits[path] = instance
if(0.1 to INFINITY)
positive_traits[path] = instance
return 1 // Hooks must return 1
+19
View File
@@ -873,3 +873,22 @@ proc/sort_atoms_by_layer(var/list/atoms)
result.Swap(i, gap + i)
swapped = 1
return result
// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
// Mutable appearances are children of images, just so you know.
/mutable_appearance/New()
..()
plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE
// And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
// Helper similar to image()
/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
var/mutable_appearance/MA = new()
MA.icon = icon
MA.icon_state = icon_state
MA.layer = layer
return MA
+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
+3 -1
View File
@@ -180,6 +180,8 @@
return "[..()] ([ckey])"
/proc/log_info_line(var/datum/d)
if(!d)
return "*null*"
if(!istype(d))
return
return json_encode(d)
return d.log_info_line()
+11
View File
@@ -0,0 +1,11 @@
/proc/log_nsay(text,inside)
if (config.log_say)
diary << "\[[time_stamp()]]NSAY (NIF:[inside]): [text][log_end]"
/proc/log_nme(text,inside)
if (config.log_emote)
diary << "\[[time_stamp()]]NME (NIF:[inside]): [text][log_end]"
/proc/log_subtle(text)
if (config.log_emote)
diary << "\[[time_stamp()]]SUBTLE: [text][log_end]"
+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))
+26 -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)
@@ -1254,6 +1264,10 @@ var/mob/dview/dview_mob = new
if(!center)
return
if(!dview_mob) //VOREStation Add - Emergency Backup
dview_mob = new()
WARNING("dview mob was lost, and had to be recreated!")
dview_mob.loc = center
dview_mob.see_invisible = invis_flags
@@ -1284,6 +1298,13 @@ var/mob/dview/dview_mob = new
else
living_mob_list -= src
/mob/dview/Destroy(var/force)
crash_with("Attempt to delete the dview_mob: [log_info_line(src)]")
if (!force)
return QDEL_HINT_LETMELIVE
global.dview_mob = new
return ..()
// call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
+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
@@ -169,7 +169,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
-98
View File
@@ -1,98 +0,0 @@
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
/datum/controller/process/lighting
// Queues of update counts, waiting to be rolled into stats lists
var/list/stats_queues = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
// Stats lists
var/list/stats_lists = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
var/update_stats_every = (1 SECONDS)
var/next_stats_update = 0
var/stat_updates_to_keep = 5
/datum/controller/process/lighting/setup()
name = "lighting"
schedule_interval = 0 // run as fast as you possibly can
sleep_interval = 10 // Yield every 10% of a tick
defer_usage = 80 // Defer at 80% of a tick
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
spawn(300)
doWork(1)
/datum/controller/process/lighting/doWork(roundstart)
lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
lighting_update_lights = list()
for(var/datum/light_source/L in lighting_update_lights_old)
if(L.check() || L.destroyed || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
SCHECK
lighting_update_corners_old = lighting_update_corners //Same as above.
lighting_update_corners = list()
for(var/A in lighting_update_corners_old)
var/datum/lighting_corner/C = A
C.update_overlays()
C.needs_update = FALSE
SCHECK
lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = list()
for(var/A in lighting_update_overlays_old)
var/atom/movable/lighting_overlay/O = A
O.update_overlay()
O.needs_update = 0
SCHECK
stats_queues["Source"] += lighting_update_lights_old.len
stats_queues["Corner"] += lighting_update_corners_old.len
stats_queues["Overlay"] += lighting_update_overlays_old.len
if(next_stats_update <= world.time)
next_stats_update = world.time + update_stats_every
for(var/stat_name in stats_queues)
var/stat_sum = 0
var/list/stats_queue = stats_queues[stat_name]
for(var/count in stats_queue)
stat_sum += count
stats_queue.Cut()
var/list/stats_list = stats_lists[stat_name]
stats_list.Insert(1, stat_sum)
if(stats_list.len > stat_updates_to_keep)
stats_list.Cut(stats_list.len)
/datum/controller/process/lighting/statProcess()
..()
stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
for(var/stat_type in stats_lists)
stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
+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
-29
View File
@@ -1,29 +0,0 @@
/datum/controller/process/mob
var/tmp/datum/updateQueue/updateQueueInstance
/datum/controller/process/mob/setup()
name = "mob"
schedule_interval = 20 // every 2 seconds
start_delay = 16
/datum/controller/process/mob/started()
..()
if(!mob_list)
mob_list = list()
/datum/controller/process/mob/doWork()
for(last_object in mob_list)
var/mob/M = last_object
if(M && isnull(M.gcDestroyed))
try
M.Life()
catch(var/exception/e)
catchException(e, M)
SCHECK
else
catchBadType(M)
mob_list -= M
/datum/controller/process/mob/statProcess()
..()
stat(null, "[mob_list.len] mobs")
+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
@@ -220,6 +221,10 @@ var/list/gamemode_cache = list()
var/show_human_death_message = 1
var/radiation_decay_rate = 1 //How much radiation is reduced by each tick
var/radiation_resistance_multiplier = 8.5 //VOREstation edit
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
for (var/T in L)
@@ -568,7 +573,12 @@ var/list/gamemode_cache = list()
irc_bot_export = 1
if("ticklag")
Ticklag = text2num(value)
var/ticklag = text2num(value)
if(ticklag > 0)
fps = 10 / ticklag
if("tick_limit_mc_init")
tick_limit_mc_init = text2num(value)
if("allow_antag_hud")
config.antag_hud_allowed = 1
@@ -720,6 +730,9 @@ var/list/gamemode_cache = list()
if(values.len > 0)
language_prefixes = values
if("radiation_lower_limit")
radiation_lower_limit = text2num(value)
else
log_misc("Unknown setting in configuration: '[name]'")
+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()
+8 -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,7 +35,6 @@ datum/controller/game_controller/New()
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
datum/controller/game_controller/proc/setup()
world.tick_lag = config.Ticklag
setup_objects()
setupgenetics()
@@ -71,8 +74,8 @@ datum/controller/game_controller/proc/setup_objects()
sleep(1)
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
for(var/obj/object in world)
if(isnull(object.gcDestroyed))
for(var/atom/movable/object in world)
if(!QDELETED(object))
object.initialize()
CHECK_SLEEP_MASTER
sleep(1)
@@ -100,7 +103,7 @@ datum/controller/game_controller/proc/setup_objects()
admin_notice("<span class='danger'>Initializing turbolifts</span>", R_DEBUG)
for(var/thing in turbolifts)
if(!deleted(thing))
var/obj/turbolift_map_holder/lift = thing
var/obj/turbolift_map_holder/lift = thing
if(!QDELETED(lift))
lift.initialize()
CHECK_SLEEP_MASTER
+7 -388
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,389 +33,3 @@ var/global/datum/shuttle_controller/shuttle_controller
shuttles = list()
process_shuttles = list()
var/datum/shuttle/ferry/shuttle
// Escape shuttle and pods
shuttle = new/datum/shuttle/ferry/emergency()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/escape/centcom)
shuttle.area_station = locate(/area/shuttle/escape/station)
shuttle.area_transition = locate(/area/shuttle/escape/transit)
shuttle.docking_controller_tag = "escape_shuttle"
shuttle.dock_target_station = "escape_dock"
shuttle.dock_target_offsite = "centcom_dock"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN
//shuttle.docking_controller_tag = "supply_shuttle"
//shuttle.dock_target_station = "cargo_bay"
shuttles["Escape"] = shuttle
process_shuttles += shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod1/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod1/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod1/transit)
shuttle.docking_controller_tag = "escape_pod_1"
shuttle.dock_target_station = "escape_pod_1_berth"
shuttle.dock_target_offsite = "escape_pod_1_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 1"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod2/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod2/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod2/transit)
shuttle.docking_controller_tag = "escape_pod_2"
shuttle.dock_target_station = "escape_pod_2_berth"
shuttle.dock_target_offsite = "escape_pod_2_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 2"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod3/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod3/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod3/transit)
shuttle.docking_controller_tag = "escape_pod_3"
shuttle.dock_target_station = "escape_pod_3_berth"
shuttle.dock_target_offsite = "escape_pod_3_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 3"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod4/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod4/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod4/transit)
shuttle.docking_controller_tag = "escape_pod_4"
shuttle.dock_target_station = "escape_pod_4_berth"
shuttle.dock_target_offsite = "escape_pod_4_recovery"
shuttle.transit_direction = NORTH //should this be SOUTH? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 4"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod5/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod5/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod5/transit)
shuttle.docking_controller_tag = "escape_pod_5"
shuttle.dock_target_station = "escape_pod_5_berth"
shuttle.dock_target_offsite = "escape_pod_5_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 5"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod6/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod6/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod6/transit)
shuttle.docking_controller_tag = "escape_pod_6"
shuttle.dock_target_station = "escape_pod_6_berth"
shuttle.dock_target_offsite = "escape_pod_6_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 6"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/cryo/station)
shuttle.area_offsite = locate(/area/shuttle/cryo/centcom)
shuttle.area_transition = locate(/area/shuttle/cryo/transit)
shuttle.docking_controller_tag = "cryostorage_shuttle"
shuttle.dock_target_station = "cryostorage_shuttle_berth"
shuttle.dock_target_offsite = "cryostorage_shuttle_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Cryostorage Shuttle"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod1/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod1/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod1/transit)
shuttle.docking_controller_tag = "large_escape_pod_1"
shuttle.dock_target_station = "large_escape_pod_1_berth"
shuttle.dock_target_offsite = "large_escape_pod_1_recovery"
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 1"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod2/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod2/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod2/transit)
shuttle.docking_controller_tag = "large_escape_pod_2"
shuttle.dock_target_station = "large_escape_pod_2_berth"
shuttle.dock_target_offsite = "large_escape_pod_2_recovery"
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 2"] = shuttle
//give the emergency shuttle controller it's shuttles
emergency_shuttle.shuttle = shuttles["Escape"]
emergency_shuttle.escape_pods = list(
shuttles["Escape Pod 1"],
shuttles["Escape Pod 2"],
shuttles["Escape Pod 3"],
shuttles["Escape Pod 4"],
shuttles["Escape Pod 5"],
shuttles["Escape Pod 6"],
shuttles["Cryostorage Shuttle"],
shuttles["Large Escape Pod 1"],
shuttles["Large Escape Pod 2"],
)
// Supply shuttle
shuttle = new/datum/shuttle/ferry/supply()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/supply/dock)
shuttle.area_station = locate(/area/supply/station)
shuttle.docking_controller_tag = "supply_shuttle"
shuttle.dock_target_station = "cargo_bay"
shuttles["Supply"] = shuttle
process_shuttles += shuttle
supply_controller.shuttle = shuttle
// Admin shuttles.
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/transport1/centcom)
shuttle.area_station = locate(/area/shuttle/transport1/station)
shuttle.docking_controller_tag = "centcom_shuttle"
shuttle.dock_target_station = "centcom_shuttle_dock_airlock"
shuttle.dock_target_offsite = "centcom_shuttle_bay"
shuttles["CentCom"] = shuttle
process_shuttles += shuttle
/* VOREStation Removal - This is a multishuttle now and is down there with them.
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
shuttle.area_offsite = locate(/area/shuttle/administration/centcom)
shuttle.area_station = locate(/area/shuttle/administration/station)
shuttle.docking_controller_tag = "admin_shuttle"
shuttle.dock_target_station = "admin_shuttle_dock_airlock"
shuttle.dock_target_offsite = "admin_shuttle_bay"
shuttles["Administration"] = shuttle
process_shuttles += shuttle
VOREStation Removal End */
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
shuttle.area_offsite = locate(/area/shuttle/trade/centcom)
shuttle.area_station = locate(/area/shuttle/trade/station)
shuttle.docking_controller_tag = "trade_shuttle"
shuttle.dock_target_station = "trade_shuttle_dock_airlock"
shuttle.dock_target_offsite = "trade_shuttle_bay"
shuttles["Trade"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.area_offsite = locate(/area/shuttle/alien/base)
shuttle.area_station = locate(/area/shuttle/alien/mine)
shuttles["Alien"] = shuttle
//process_shuttles += shuttle //don't need to process this. It can only be moved using admin magic anyways.
// Public shuttles
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/constructionsite/site)
shuttle.area_station = locate(/area/shuttle/constructionsite/station)
shuttle.docking_controller_tag = "engineering_shuttle"
shuttle.dock_target_station = "engineering_dock_airlock"
shuttle.dock_target_offsite = "edock_airlock"
shuttles["Engineering"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/mining/outpost)
shuttle.area_station = locate(/area/shuttle/mining/station)
shuttle.docking_controller_tag = "mining_shuttle"
shuttle.dock_target_station = "mining_dock_airlock"
shuttle.dock_target_offsite = "mining_outpost_airlock"
shuttles["Mining"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/research/outpost)
shuttle.area_station = locate(/area/shuttle/research/station)
shuttle.docking_controller_tag = "research_shuttle"
shuttle.dock_target_station = "research_dock_airlock"
shuttle.dock_target_offsite = "research_outpost_dock"
shuttles["Research"] = shuttle
process_shuttles += shuttle
// ERT Shuttle
var/datum/shuttle/ferry/multidock/specops/ERT = new()
ERT.location = 0
ERT.warmup_time = 10
ERT.area_offsite = locate(/area/shuttle/specops/station) //centcom is the home station, the player station is offsite
ERT.area_station = locate(/area/shuttle/specops/centcom)
ERT.docking_controller_tag = "specops_shuttle_port"
ERT.docking_controller_tag_station = "specops_shuttle_port"
ERT.docking_controller_tag_offsite = "specops_shuttle_fore"
ERT.dock_target_station = "specops_centcom_dock"
ERT.dock_target_offsite = "specops_dock_airlock"
shuttles["Special Operations"] = ERT
process_shuttles += ERT
//Skipjack.
var/datum/shuttle/multi_shuttle/VS = new/datum/shuttle/multi_shuttle()
VS.origin = locate(/area/skipjack_station/start)
VS.destinations = list(
"Fore Starboard Solars" = locate(/area/skipjack_station/northeast_solars),
"Fore Port Solars" = locate(/area/skipjack_station/northwest_solars),
"Aft Starboard Solars" = locate(/area/skipjack_station/southeast_solars),
"Aft Port Solars" = locate(/area/skipjack_station/southwest_solars), // Vorestation edit
"Station Arrivals Dock" = locate(/area/skipjack_station/southwest_solars),
"Mining Station" = locate(/area/skipjack_station/mining)
)
VS.announcer = "Colony Automated Radar Array"
VS.arrival_message = "Attention. Unidentified object approaching the colony."
VS.departure_message = "Attention. Unidentified object exiting local space. Unidentified object expected to escape Virgo-3's gravity well with current velocity." // Vorestation edit
VS.interim = locate(/area/skipjack_station/transit)
VS.warmup_time = 0
shuttles["Skipjack"] = VS
//Nuke Ops shuttle.
var/datum/shuttle/multi_shuttle/MS = new/datum/shuttle/multi_shuttle()
MS.origin = locate(/area/syndicate_station/start)
MS.start_location = "Mercenary Base"
MS.destinations = list(
"Northwest of the station" = locate(/area/syndicate_station/northwest),
"North of the station" = locate(/area/syndicate_station/north),
"Northeast of the station" = locate(/area/syndicate_station/northeast),
"Southwest of the station" = locate(/area/syndicate_station/southwest),
"South of the station" = locate(/area/syndicate_station/south),
"Southeast of the station" = locate(/area/syndicate_station/southeast),
"Telecomms Satellite" = locate(/area/syndicate_station/commssat),
"Mining Station" = locate(/area/syndicate_station/mining),
"Arrivals dock" = locate(/area/syndicate_station/arrivals_dock),
)
MS.docking_controller_tag = "merc_shuttle"
MS.destination_dock_targets = list(
"Mercenary Base" = "merc_base",
"Arrivals dock" = "nuke_shuttle_dock_airlock",
)
MS.announcer = "Automated Traffic Control"
MS.arrival_message = "Attention. A vessel is approaching the colony."
MS.departure_message = "Attention. A vessel is now leaving from the colony."
MS.interim = locate(/area/syndicate_station/transit)
MS.warmup_time = 0
shuttles["Mercenary"] = MS
// VOREStation Add - The admin shuttle is a multishuttle now.
//Centcom admin shuttle.
var/datum/shuttle/multi_shuttle/CC = new/datum/shuttle/multi_shuttle()
CC.legit = 1
CC.origin = locate(/area/shuttle/administration/centcom)
CC.start_location = "Central Command (AS)"
CC.destinations = list(
"Deep Space (AS)" = locate(/area/shuttle/administration/transit),
"NSS Adephagia (AS)" = locate(/area/shuttle/administration/station)
)
CC.docking_controller_tag = "admin_shuttle"
CC.destination_dock_targets = list(
"Central Command (AS)" = "admin_shuttle_bay",
"NSS Adephagia (AS)" = "admin_shuttle_dock_airlock"
)
var/area/ccaway_dest = locate(/area/shuttle/administration/away_mission)
if(ccaway_dest.contents.len) //Otherwise this is an empty imaginary area
CC.destinations["Unknown Location [rand(1000,9999)]"] = ccaway_dest
CC.announcer = "Automated Traffic Control"
//These seem backwards because they are written from the perspective of the merc and vox ships
CC.departure_message = "Attention. A Centcom vessel is approaching the colony."
CC.arrival_message = "Attention. A Centcom vessel is now leaving from the colony."
CC.move_time = 0
CC.warmup_time = 0
shuttles["Administration"] = CC
//VOREStation Add End - Admin shuttle.
//////////////////////////////////////////////////////////////
//VOREStation Add - Away-mission shuttle
var/datum/shuttle/multi_shuttle/AM = new/datum/shuttle/multi_shuttle()
AM.legit = 1
AM.origin = locate(/area/shuttle/awaymission/home)
AM.start_location = "NSS Adephagia (AM)"
AM.destinations = list(
"Old Engineering Base (AM)" = locate(/area/shuttle/awaymission/oldengbase)
)
AM.docking_controller_tag = "awaymission_shuttle"
AM.destination_dock_targets = list(
"NSS Adephagia (AM)" = "d1a2_dock_airlock"
)
var/area/awaym_dest = locate(/area/shuttle/awaymission/away)
if(awaym_dest.contents.len) //Otherwise this is an empty imaginary area
AM.destinations["Unknown Location [rand(1000,9999)]"] = awaym_dest
AM.announcer = "Automated Traffic Control"
//These seem backwards because they are written from the perspective of the merc and vox ships
AM.departure_message = "Attention. The away mission vessel is approaching the colony."
AM.arrival_message = "Attention. The away mission vessel is now leaving from the colony."
AM.interim = locate(/area/shuttle/awaymission/warp)
AM.move_time = 60
AM.warmup_time = 8
shuttles["AwayMission"] = AM
//VOREStation Add End - Away-mission shuttle
///////////////////////////////////////////////
//VOREStation Add - Belter Shuttle
shuttle = new/datum/shuttle/ferry()
shuttle.location = 0
shuttle.warmup_time = 6
shuttle.area_station = locate(/area/shuttle/belter/station)
shuttle.area_offsite = locate(/area/shuttle/belter/belt/zone1)
shuttle.area_transition = locate(/area/shuttle/belter/transit)
shuttle.docking_controller_tag = "belter_docking"
shuttle.dock_target_station = "belter_nodocking" //Fake tags to prevent the shuttle from opening doors.
shuttle.dock_target_offsite = "belter_nodocking"
shuttle.transit_direction = EAST
shuttle.move_time = 60 + rand(10,40)
process_shuttles += shuttle
shuttles["Belter"] = shuttle
//VOREStation Add End - Belter Shuttle
-210
View File
@@ -1,210 +0,0 @@
var/global/datum/shuttle_controller/shuttle_controller
/datum/shuttle_controller
var/list/shuttles //maps shuttle tags to shuttle datums, so that they can be looked up.
var/list/process_shuttles //simple list of shuttles, for processing
/datum/shuttle_controller/proc/process()
//process ferry shuttles
for (var/datum/shuttle/ferry/shuttle in process_shuttles)
if (shuttle.process_state)
shuttle.process()
//This is called by gameticker after all the machines and radio frequencies have been properly initialized
/datum/shuttle_controller/proc/setup_shuttle_docks()
for(var/shuttle_tag in shuttles)
var/datum/shuttle/shuttle = shuttles[shuttle_tag]
shuttle.init_docking_controllers()
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
for(var/obj/machinery/embedded_controller/C in machines)
if(istype(C.program, /datum/computer/file/embedded_program/docking))
C.program.tag = null //clear the tags, 'cause we don't need 'em anymore
/datum/shuttle_controller/New()
shuttles = list()
process_shuttles = list()
var/datum/shuttle/ferry/shuttle
//////////////////////////////////////////////////////////////
// Escape shuttle and pods
shuttle = new/datum/shuttle/ferry/emergency()
shuttle.location = 1 // At offsite
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/escape/centcom)
shuttle.area_station = locate(/area/shuttle/escape/station)
shuttle.area_transition = locate(/area/shuttle/escape/transit)
shuttle.docking_controller_tag = "escape_shuttle"
shuttle.dock_target_station = "escape_dock"
shuttle.dock_target_offsite = "centcom_dock"
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN
shuttles["Escape"] = shuttle
process_shuttles += shuttle
//////////////////////////////////////////////////////////////
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0 // At station
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod1/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod1/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod1/transit)
shuttle.docking_controller_tag = "large_escape_pod_1"
shuttle.dock_target_station = "large_escape_pod_1_berth"
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 1"] = shuttle
//////////////////////////////////////////////////////////////
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0 // At station
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod2/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod2/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod2/transit)
shuttle.docking_controller_tag = "large_escape_pod_2"
shuttle.dock_target_station = "large_escape_pod_2_berth"
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 2"] = shuttle
//give the emergency shuttle controller it's shuttles
emergency_shuttle.shuttle = shuttles["Escape"]
emergency_shuttle.escape_pods = list(
shuttles["Large Escape Pod 1"],
shuttles["Large Escape Pod 2"],
)
//////////////////////////////////////////////////////////////
// Supply shuttle
shuttle = new/datum/shuttle/ferry/supply()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/supply/dock)
shuttle.area_station = locate(/area/supply/station)
shuttle.docking_controller_tag = "supply_shuttle"
shuttle.dock_target_station = "cargo_bay"
shuttles["Supply"] = shuttle
process_shuttles += shuttle
supply_controller.shuttle = shuttle
//////////////////////////////////////////////////////////////
// Trade Ship
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
shuttle.area_offsite = locate(/area/shuttle/trade/centcom)
shuttle.area_station = locate(/area/shuttle/trade/station)
shuttle.docking_controller_tag = "trade_shuttle"
shuttle.dock_target_station = "trade_shuttle_dock_airlock"
shuttle.dock_target_offsite = "trade_shuttle_bay"
shuttles["Trade"] = shuttle
process_shuttles += shuttle
//////////////////////////////////////////////////////////////
// Away Mission Shuttle
var/datum/shuttle/multi_shuttle/AM = new/datum/shuttle/multi_shuttle()
AM.legit = 1
AM.origin = locate(/area/shuttle/awaymission/home)
AM.start_location = "NSS Adephagia (AM)"
AM.destinations = list(
"Old Engineering Base (AM)" = locate(/area/shuttle/awaymission/oldengbase)
)
AM.docking_controller_tag = "awaymission_shuttle"
AM.destination_dock_targets = list(
"NSS Adephagia (AM)" = "d1a2_dock_airlock"
)
var/area/awaym_dest = locate(/area/shuttle/awaymission/away)
if(awaym_dest.contents.len) //Otherwise this is an empty imaginary area
AM.destinations["Unknown Location [rand(1000,9999)]"] = awaym_dest
AM.announcer = "Automated Traffic Control"
//These seem backwards because they are written from the perspective of the merc and vox ships
AM.departure_message = "Attention. The away mission vessel is approaching the colony."
AM.arrival_message = "Attention. The away mission vessel is now leaving from the colony."
AM.interim = locate(/area/shuttle/awaymission/warp)
AM.move_time = 60
AM.warmup_time = 8
shuttles["AwayMission"] = AM
// TODO - Not implemented yet on new map
///////////////////////////////////////////////
//VOREStation Add - Belter Shuttle
// shuttle = new/datum/shuttle/ferry()
// shuttle.location = 0
// shuttle.warmup_time = 6
// shuttle.area_station = locate(/area/shuttle/belter/station)
// shuttle.area_offsite = locate(/area/shuttle/belter/belt/zone1)
// shuttle.area_transition = locate(/area/shuttle/belter/transit)
// shuttle.docking_controller_tag = "belter_docking"
// shuttle.dock_target_station = "belter_nodocking" //Fake tags to prevent the shuttle from opening doors.
// shuttle.dock_target_offsite = "belter_nodocking"
// shuttle.transit_direction = EAST
// shuttle.move_time = 60 + rand(10,40)
// process_shuttles += shuttle
// shuttles["Belter"] = shuttle
//VOREStation Add End - Belter Shuttle
//////////////////////////////////////////////////////////////
// Tether Shuttle
var/datum/shuttle/ferry/tether_backup/TB = new()
TB.location = 1 // At offsite
TB.warmup_time = 5
TB.move_time = 45
TB.area_offsite = locate(/area/shuttle/tether/surface)
TB.area_station = locate(/area/shuttle/tether/station)
TB.area_transition = locate(/area/shuttle/tether/transit)
TB.crash_areas = list(locate(/area/shuttle/tether/crash1), locate(/area/shuttle/tether/crash2))
TB.docking_controller_tag = "tether_shuttle"
TB.dock_target_station = "tether_dock_airlock"
TB.dock_target_offsite = "tether_pad_airlock"
shuttles["Tether Backup"] = TB
process_shuttles += TB
for(var/obj/structure/shuttle/engine/propulsion/E in TB.area_offsite)
TB.engines += E
for(var/obj/machinery/computer/shuttle_control/tether_backup/comp in TB.area_offsite)
TB.computer = comp
break
//////////////////////////////////////////////////////////////
// Antag Space "Proto Shuttle" Shuttle
AM = new/datum/shuttle/multi_shuttle()
AM.docking_controller_tag = "antag_space_shuttle"
AM.start_location = "Home Base"
AM.origin = locate(/area/shuttle/antag_space/base)
AM.interim = locate(/area/shuttle/antag_space/transit)
AM.destinations = list(
"Nearby" = locate(/area/shuttle/antag_space/north),
"Docks" = locate(/area/shuttle/antag_space/docks)
)
AM.destination_dock_targets = list("Home Base" = "antag_space_dock")
AM.move_time = 60
AM.warmup_time = 8
shuttles["Proto"] = AM
//////////////////////////////////////////////////////////////
// Antag Surface "Land Crawler" Shuttle
AM = new/datum/shuttle/multi_shuttle()
AM.docking_controller_tag = "antag_ground_shuttle"
AM.start_location = "Home Base"
AM.origin = locate(/area/shuttle/antag_ground/base)
AM.interim = locate(/area/shuttle/antag_ground/transit)
AM.destinations = list(
"Solar Array" = locate(/area/shuttle/antag_ground/solars),
"Mining Outpost" = locate(/area/shuttle/antag_ground/mining)
)
AM.destination_dock_targets = list("Home Base" = "antag_ground_dock")
AM.move_time = 60
AM.warmup_time = 8
shuttles["Land Crawler"] = AM
+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
+166
View File
@@ -0,0 +1,166 @@
/*
** Lighting Subsystem - Process the lighting! Do it!
*/
#define SSLIGHTING_STAGE_LIGHTS 1
#define SSLIGHTING_STAGE_CORNERS 2
#define SSLIGHTING_STAGE_OVERLAYS 3
#define SSLIGHTING_STAGE_DONE 4
// This subsystem's fire() method also gets called once during Master.Initialize().
// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return.
// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it.
#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; }
// Globals
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
SUBSYSTEM_DEF(lighting)
name = "Lighting"
wait = 2 // Ticks, not deciseconds
init_order = INIT_ORDER_LIGHTING
flags = SS_TICKER
var/list/currentrun = list()
var/stage = null
var/cost_lights = 0
var/cost_corners = 0
var/cost_overlays = 0
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!lighting_overlays_initialised)
// TODO - TG initializes starlight here.
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts.
internal_process_lights(FALSE, TRUE)
internal_process_corners(FALSE, TRUE)
internal_process_overlays(FALSE, TRUE)
return ..()
/datum/controller/subsystem/lighting/fire(resumed = FALSE)
var/timer
if(!resumed)
ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle
ASSERT(stage == null) // Or somehow didn't finish all the steps from last cycle
stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
if(stage == SSLIGHTING_STAGE_LIGHTS)
timer = world.tick_usage
internal_process_lights(resumed)
cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_CORNERS
if(stage == SSLIGHTING_STAGE_CORNERS)
timer = world.tick_usage
internal_process_corners(resumed)
cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_OVERLAYS
if(stage == SSLIGHTING_STAGE_OVERLAYS)
timer = world.tick_usage
internal_process_overlays(resumed)
cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_DONE
// Okay, we're done! Woo! Got thru a whole air_master cycle!
ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left
ASSERT(stage == SSLIGHTING_STAGE_DONE) // And that we didn't somehow skip past the last step
currentrun = null
stage = null
/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_lights
global.lighting_update_lights = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/light_source/L = currentrun[currentrun.len]
currentrun.len--
if(!L) continue
if(L.check() || L.destroyed || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_corners
global.lighting_update_corners = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/lighting_corner/C = currentrun[currentrun.len]
currentrun.len--
if(!C) continue
C.update_overlays()
C.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_overlays
global.lighting_update_overlays = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/atom/movable/lighting_overlay/O = currentrun[currentrun.len]
currentrun.len--
if(!O) continue
O.update_overlay()
O.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/stat_entry(msg_prefix)
var/list/msg = list(msg_prefix)
msg += "T:{"
msg += "S [total_lighting_sources] | "
msg += "C [total_lighting_corners] | "
msg += "O [total_lighting_overlays]"
msg += "}"
msg += "C:{"
msg += "S [round(cost_lights, 1)] | "
msg += "C [round(cost_corners, 1)] | "
msg += "O [round(cost_overlays, 1)]"
msg += "}"
..(msg.Join())
#undef DUAL_TICK_CHECK
#undef SSLIGHTING_STAGE_LIGHTS
#undef SSLIGHTING_STAGE_CORNERS
#undef SSLIGHTING_STAGE_OVERLAYS
#undef SSLIGHTING_STAGE_STATS
+38
View File
@@ -0,0 +1,38 @@
//
// Mobs Subsystem - Process mob.Life()
//
SUBSYSTEM_DEF(mobs)
name = "Mobs"
priority = 100
wait = 2 SECONDS
flags = SS_KEEP_TIMING|SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
/datum/controller/subsystem/mobs/stat_entry()
..("P: [global.mob_list.len]")
/datum/controller/subsystem/mobs/fire(resumed = 0)
if (!resumed)
src.currentrun = mob_list.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
var/times_fired = src.times_fired
while(currentrun.len)
var/mob/M = currentrun[currentrun.len]
currentrun.len--
if(QDELETED(M))
mob_list -= M
else
// Right now mob.Life() is unstable enough I think we need to use a try catch.
// Obviously we should try and get rid of this for performance reasons when we can.
try
M.Life(times_fired)
catch(var/exception/e)
log_runtime(e, M, "Caught by [name] subsystem")
if (MC_TICK_CHECK)
return
+133
View File
@@ -0,0 +1,133 @@
////////////////////////////////
//// Mind/body data storage system
//// for the resleeving tech
////////////////////////////////
SUBSYSTEM_DEF(transcore)
name = "Transcore"
priority = 20
wait = 1 MINUTE
flags = SS_BACKGROUND|SS_NO_TICK_CHECK|SS_NO_INIT
runlevels = RUNLEVEL_GAME
// THINGS
var/overdue_time = 15 MINUTES
var/core_dumped = FALSE // Core has been dumped! Also set can_fire = 0 when you set this.
var/datum/transhuman/mind_record/list/backed_up = list() // All known mind records, indexed by MR.mindname/mind.name
var/datum/transhuman/mind_record/list/has_left = list() // Why do we even have this?
var/datum/transhuman/body_record/list/body_scans = list() // All known body records, indexed by BR.mydna.name
/datum/controller/subsystem/transcore/fire()
for(var/N in backed_up)
var/datum/transhuman/mind_record/curr_MR = backed_up[N]
if(!curr_MR)
log_debug("Tried to process [N] in transcore w/o a record!")
continue
if(curr_MR.one_time)
continue
var/since_backup = world.time - curr_MR.last_update
if(since_backup < overdue_time)
curr_MR.dead_state = MR_NORMAL
else
if(curr_MR.dead_state != MR_DEAD) //First time switching to dead
notify(N)
curr_MR.dead_state = MR_DEAD
/datum/controller/subsystem/transcore/stat_entry(msg)
if(core_dumped)
msg += "CORE DUMPED | "
msg += "MR: [backed_up.len] | BR: [body_scans.len]"
..(msg)
/datum/controller/subsystem/transcore/Recover()
if (istype(SStranscore.body_scans))
for(var/N in SStranscore.body_scans[N])
if(N) body_scans[N] = SStranscore.body_scans[N]
if(SStranscore.core_dumped)
core_dumped = TRUE
can_fire = FALSE
else if (istype(SStranscore.backed_up))
for(var/N in SStranscore.backed_up[N])
if(N) backed_up[N] = SStranscore.backed_up[N]
/datum/controller/subsystem/transcore/proc/m_backup(var/datum/mind/mind, var/obj/item/device/nif/nif, var/one_time = FALSE)
ASSERT(mind)
if(!mind.name || core_dumped)
return 0
var/datum/transhuman/mind_record/MR
if(mind.name in backed_up)
MR = backed_up[mind.name]
MR.last_update = world.time
MR.one_time = one_time
//Pass a 0 to not change NIF status (because the elseif is checking for null)
if(nif)
MR.nif_path = nif.type
MR.nif_durability = nif.durability
var/list/nifsofts = list()
for(var/N in nif.nifsofts)
if(N)
var/datum/nifsoft/nifsoft = N
nifsofts += nifsoft.type
MR.nif_software = nifsofts
else if(isnull(nif)) //Didn't pass anything, so no NIF
MR.nif_path = null
MR.nif_durability = null
MR.nif_software = null
else
MR = new(mind, mind.current, add_to_db = TRUE, one_time = one_time)
return 1
// Send a past-due notification to the medical radio channel.
/datum/controller/subsystem/transcore/proc/notify(var/name)
ASSERT(name)
var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null)
a.autosay("[name] is past-due for a mind backup. This will be the only notification.", "TransCore Oversight", "Medical")
qdel(a)
// Called from mind_record to add itself to the transcore.
/datum/controller/subsystem/transcore/proc/add_backup(var/datum/transhuman/mind_record/MR)
ASSERT(MR)
backed_up[MR.mindname] = MR
backed_up = sortAssoc(backed_up)
log_debug("Added [MR.mindname] to transcore DB.")
// Remove a mind_record from the backup-checking list. Keeps track of it in has_left // Why do we do that? ~Leshana
/datum/controller/subsystem/transcore/proc/stop_backup(var/datum/transhuman/mind_record/MR)
ASSERT(MR)
has_left[MR.mindname] = MR
backed_up.Remove("[MR.mindname]")
MR.cryo_at = world.time
log_debug("Put [MR.mindname] in transcore suspended DB.")
// Called from body_record to add itself to the transcore.
/datum/controller/subsystem/transcore/proc/add_body(var/datum/transhuman/body_record/BR)
ASSERT(BR)
body_scans[BR.mydna.name] = BR
body_scans = sortAssoc(body_scans)
log_debug("Added [BR.mydna.name] to transcore body DB.")
// Remove a body record from the database (Usually done when someone cryos) // Why? ~Leshana
/datum/controller/subsystem/transcore/proc/remove_body(var/datum/transhuman/body_record/BR)
ASSERT(BR)
body_scans.Remove("[BR.mydna.name]")
log_debug("Removed [BR.mydna.name] from transcore body DB.")
// Moves all mind records from the databaes into the disk and shuts down all backup canary processing.
/datum/controller/subsystem/transcore/proc/core_dump(var/obj/item/weapon/disk/transcore/disk)
ASSERT(disk)
var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null)
a.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Command")
a.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Medical")
qdel(a)
disk.stored += backed_up
backed_up.Cut()
core_dumped = TRUE
can_fire = FALSE
return disk.stored.len
+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"
+7 -7
View File
@@ -1,34 +1,34 @@
/datum/category_item/autolathe/arms/speedloader_357_flash
name = "speedloader (.357 flash)"
path =/obj/item/ammo_magazine/a357/flash
path =/obj/item/ammo_magazine/m357/flash
hidden = 1
/datum/category_item/autolathe/arms/speedloader_357_stun
name = "speedloader (.357 stun)"
path =/obj/item/ammo_magazine/a357/stun
path =/obj/item/ammo_magazine/m357/stun
hidden = 1
/datum/category_item/autolathe/arms/speedloader_357_rubber
name = "speedloader (.357 rubber)"
path =/obj/item/ammo_magazine/a357/rubber
path =/obj/item/ammo_magazine/m357/rubber
hidden = 1
/datum/category_item/autolathe/arms/speedloader_44
name = "speedloader (.44)"
path =/obj/item/ammo_magazine/a44sl
path =/obj/item/ammo_magazine/m44sl
hidden = 1
/datum/category_item/autolathe/arms/speedloader_44_rubber
name = "speedloader (.44 rubber)"
path =/obj/item/ammo_magazine/a44sl/rubber
path =/obj/item/ammo_magazine/m44sl/rubber
hidden = 1
/datum/category_item/autolathe/arms/mag_44
name = "magazine (.44)"
path =/obj/item/ammo_magazine/a44
path =/obj/item/ammo_magazine/m44
hidden = 1
/datum/category_item/autolathe/arms/mag_44_rubber
name = "magazine (.44 rubber)"
path =/obj/item/ammo_magazine/a44/rubber
path =/obj/item/ammo_magazine/m44/rubber
hidden = 1
@@ -155,4 +155,5 @@ Data storage vars:
tag = null
arg_list.Cut()
stop()
return QDEL_HINT_LETMELIVE
//Do not call ..()
+47 -34
View File
@@ -1,34 +1,47 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/datum/data
var/name = "data"
var/size = 1.0
/datum/data/function
name = "function"
size = 2.0
/datum/data/function/data_control
name = "data control"
/datum/data/function/id_changer
name = "id changer"
/datum/data/record
name = "record"
size = 5.0
var/list/fields = list( )
/datum/data/text
name = "text"
var/data = null
/datum/debug
var/list/debuglist
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/datum/data
var/name = "data"
var/size = 1.0
/datum/data/function
name = "function"
size = 2.0
/datum/data/function/data_control
name = "data control"
/datum/data/function/id_changer
name = "id changer"
/datum/data/record
name = "record"
size = 5.0
var/list/fields = list( )
// Mostly used for data_core records, but unfortuantely used some other places too. But mostly here, so lets make a good effort.
// TODO - Some machines/computers might be holding references to us. Lets look into that, but at least for now lets make sure that the manifest is cleaned up.
/datum/data/record/Destroy(var/force)
if(data_core.locked.Find(src))
if(!force)
crash_with("Someone tried to qdel a record that was in data_core.locked [log_info_line(src)]")
return QDEL_HINT_LETMELIVE
data_core.locked -= src
data_core.medical -= src
data_core.general -= src
data_core.security -= src
..()
return QDEL_HINT_FINDREFERENCE // If we must, we can resort to QDEL_HINT_HARDDEL. But lets see how this works in practice ~Leshana
/datum/data/text
name = "text"
var/data = null
/datum/debug
var/list/debuglist
+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
+7 -7
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
)
@@ -73,7 +73,7 @@
containertype = /obj/structure/closet/crate/secure
containername = "Energy marksman crate"
access = access_armory
/* VOREStation edit -- This is a bad idea.
/datum/supply_packs/munitions/burstlaser
name = "Burst laser crate"
contains = list(/obj/item/weapon/gun/energy/gun/burst = 2)
@@ -81,7 +81,7 @@
containertype = /obj/structure/closet/crate/secure
containername = "Burst laser crate"
access = access_armory
*/
/datum/supply_packs/munitions/ionweapons
name = "Electromagnetic weapons crate"
contains = list(
@@ -129,7 +129,7 @@
contains = list(
/obj/item/device/assembly/timer,
/obj/item/weapon/gun/projectile/shotgun/pump/rifle/practice = 2,
/obj/item/ammo_magazine/clip/a762/practice = 4,
/obj/item/ammo_magazine/clip/c762/practice = 4,
/obj/item/target = 2,
/obj/item/target/alien = 2,
/obj/item/target/syndicate = 2
@@ -154,9 +154,9 @@
name = "Automatic weapon ammunition crate"
num_contained = 6
contains = list(
/obj/item/ammo_magazine/mc9mmt,
/obj/item/ammo_magazine/mc9mmt/rubber,
/obj/item/ammo_magazine/a556
/obj/item/ammo_magazine/m9mmt,
/obj/item/ammo_magazine/m9mmt/rubber,
/obj/item/ammo_magazine/m556saw
)
cost = 25
containertype = /obj/structure/closet/crate/secure
+1 -1
View File
@@ -49,7 +49,7 @@
containername = "Dangerous Predator crate"
access = access_xenobiology
/datum/supply_packs/sci/pred
/datum/supply_packs/sci/pred_doom
name = "EXTREMELY Dangerous Predator crate"
cost = 200
containertype = /obj/structure/largecrate/animal/dangerous
+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
@@ -87,6 +87,8 @@
/area/proc/firedoors_close()
if(!firedoors_closed)
firedoors_closed = TRUE
if(!all_doors)
return
for(var/obj/machinery/door/firedoor/E in all_doors)
if(!E.blocked)
if(E.operating)
@@ -99,6 +101,8 @@
/area/proc/firedoors_open()
if(firedoors_closed)
firedoors_closed = FALSE
if(!all_doors)
return
for(var/obj/machinery/door/firedoor/E in all_doors)
if(!E.blocked)
if(E.operating)
+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)
+11
View File
@@ -90,6 +90,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
// VOREStation
var/custom_species
var/base_species = "Human"
var/list/species_traits = list()
var/blood_color = "#A10808"
// VOREStation
// New stuff
@@ -105,6 +108,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
new_dna.real_name=real_name
new_dna.species=species
new_dna.body_markings=body_markings.Copy()
new_dna.base_species=base_species //VOREStation Edit
new_dna.species_traits=species_traits.Copy() //VOREStation Edit
new_dna.blood_color=blood_color //VOREStation Edit
for(var/b=1;b<=DNA_SE_LENGTH;b++)
new_dna.SE[b]=SE[b]
if(b<=DNA_UI_LENGTH)
@@ -163,6 +169,11 @@ var/global/list/datum/dna/gene/dna_genes[0]
// Technically custom_species is not part of the UI, but this place avoids merge problems.
src.custom_species = character.custom_species
if(istype(character.species,/datum/species/custom))
var/datum/species/custom/CS = character.species
src.species_traits = CS.traits.Copy()
src.base_species = CS.base_species
src.blood_color = CS.blood_color
// +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
+13 -1
View File
@@ -201,11 +201,16 @@
// Technically custom_species is not part of the UI, but this place avoids merge problems.
H.custom_species = dna.custom_species
if(istype(H.species,/datum/species/custom))
var/datum/species/custom/CS = H.species
var/datum/species/custom/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
new_CS.blood_color = dna.blood_color
// VOREStation Edit End
H.force_update_organs() //VOREStation Add - Gotta do this too
H.force_update_limbs()
H.update_body(0)
//H.update_body(0) //VOREStation Edit - Done in force_update_limbs already
H.update_eyes()
H.update_hair()
@@ -213,6 +218,13 @@
else
return 0
//VOREStation Add
/mob/living/carbon/human/proc/force_update_organs()
for(var/organ in organs + internal_organs)
var/obj/item/organ/O = organ
O.species = species
//VOREStation Add End
// Used below, simple injection modifier.
/proc/probinj(var/pr, var/inj)
return prob(pr+inj*pr)
@@ -37,7 +37,6 @@ var/global/universe_has_ended = 0
// Apply changes when entering state
/datum/universal_state/supermatter_cascade/OnEnter()
set background = 1
garbage_collector.garbage_collect = 0
world << "<span class='sinister' style='font-size:22pt'>You are blinded by a brilliant flash of energy.</span>"
world << sound('sound/effects/cascade.ogg')
+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
@@ -51,6 +51,7 @@ var/global/datum/controller/gameticker/ticker
vote.process()
if(pregame_timeleft <= 0)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
while (!setup())
@@ -63,6 +64,7 @@ var/global/datum/controller/gameticker/ticker
if((master_mode=="random") || (master_mode=="secret"))
if(!runnable_modes.len)
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
world << "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby."
return 0
if(secret_force_mode != "secret")
@@ -77,6 +79,7 @@ var/global/datum/controller/gameticker/ticker
if(!src.mode)
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
world << "<span class='danger'>Serious error in mode setup!</span> Reverting to pre-game lobby."
return 0
@@ -88,6 +91,7 @@ var/global/datum/controller/gameticker/ticker
if(!src.mode.can_start())
world << "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby."
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
mode.fail_setup()
mode = null
job_master.ResetOccupations()
@@ -116,6 +120,7 @@ var/global/datum/controller/gameticker/ticker
shuttle_controller.setup_shuttle_docks()
// TODO - Leshana - Dear God Fix This. Fix all of this. Not just this line, this entire proc. This entire file!
spawn(0)//Forking here so we dont have to wait for this to finish
mode.post_setup()
//Cleanup some stuff
@@ -144,6 +149,7 @@ var/global/datum/controller/gameticker/ticker
*/
processScheduler.start()
Master.SetRunLevel(RUNLEVEL_GAME)
if(config.sql_enabled)
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
@@ -308,6 +314,7 @@ var/global/datum/controller/gameticker/ticker
if(!mode.explosion_in_progress && game_finished && (mode_finished || post_game))
current_state = GAME_STATE_FINISHED
Master.SetRunLevel(RUNLEVEL_POSTGAME)
spawn
declare_completion()
+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
@@ -11,7 +11,7 @@
economic_modifier = 1
access = list() //See /datum/job/assistant/get_access()
minimal_access = list() //See /datum/job/assistant/get_access()
alt_titles = list("Technical Assistant","Medical Intern","Research Assistant","Visitor", "Resident")
alt_titles = list("Technical Assistant","Test Subject","Medical Intern","Research Assistant","Visitor", "Resident") // Test Subject is a VOREStation edit.
/datum/job/assistant/equip(var/mob/living/carbon/human/H, var/alt_title)
if(!H) return 0
+2 -2
View File
@@ -231,8 +231,8 @@
department = "Civilian"
department_flag = CIVILIAN
faction = "Station"
total_positions = 1
spawn_positions = 1
total_positions = 2 // VOREStation Edit. Original number is 1.
spawn_positions = 2 // VOREStation Edit. Original number is 1.
supervisors = "the head of personnel"
selection_color = "#515151"
idtype = /obj/item/weapon/card/id/civilian
+1 -1
View File
@@ -54,7 +54,7 @@
if(COMPANY_OPPOSED) loyalty = 0.70
//give them an account in the station database
var/money_amount = (rand(5,50) + rand(5, 50)) * loyalty * economic_modifier * ECO_MODIFIER
var/money_amount = (rand(15,40) + rand(15,40)) * loyalty * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks.
var/datum/money_account/M = create_account(H.real_name, money_amount, null)
if(H.mind)
var/remembered_info = ""
+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

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