Unit Test rework & Master/Ticker update (#17912)

* Unit Test rework & Master/Ticker update

* Fixes and working unit testing

* Fixes

* Test fixes and FA update

* Fixed runtimes

* Radio subsystem

* move that glob wherever later

* ident

* CIBUILDING compile option

* Fixed runtimes

* Some changes to the workflow

* CI Split

* More split

* Pathing

* Linters and Annotators

* ci dir fix

* Missing undef fixed

* Enable grep checks

* More test conversions

* More split

* Correct file

* Removes unneeded inputs

* oop

* More dependency changes

* More conversions

* Conversion fixes

* Fixes

* Some assert fixes

* Corrects start gate

* Converted some README.dms to README.mds

* Removes duplicate proc

* Removes unused defines

* Example configs

* fix dll access viol by double calling

* Post-rebase fixes

* Cleans up names global list

* Undef restart counter

* More code/game/ cleanup

* Statpanel update

* Skybox

* add

* Fix ticker

* Roundend fix

* Persistence dependency update

* Reordering

* Reordering

* Reordering

* Initstage fix

* .

* .

* Reorder

* Reorder

* Circle

* Mobs

* Air

* Test fix

* CI Script Fix

* Configs

* More ticker stuff

* This is now in 'reboot world'

* Restart world announcements

* no glob in PreInit

* to define

* Update

* Removed old include

* Make this file normal again

* moved

* test

* shared unit testing objects

* Updates batched_spritesheets and universal_icon

* .

* job data debug

* rm that

* init order

* show us

* .

* i wonder

* .

* .

* urg

* do we not have a job ID?

* .

* rm sleep for now

* updated rust-g linux binaries

* binaries update 2

* binaries update 3

* testing something

* change that

* test something

* .

* .

* .

* locavar

* test

* move that

* .

* debug

* don't run this test

* strack trace it

* cleaner

* .

* .

* cras again

* also comment this out

* return to official rust g

* Update robot_icons.dm

* monitor the generation

* .

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Selis
2025-08-10 01:37:23 +02:00
committed by GitHub
co-authored by Kashargul
parent 5e54e17ebd
commit f4bf017921
382 changed files with 10193 additions and 4421 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ GENERAL_PROTECT_DATUM(/datum/controller/subsystem/admin_verbs)
SUBSYSTEM_DEF(admin_verbs)
name = "Admin Verbs"
flags = SS_NO_FIRE
//init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_EARLY
/// A list of all admin verbs indexed by their type.
var/list/datum/admin_verb/admin_verbs_by_type = list()
/// A list of all admin verbs indexed by their visibility flag.
+5 -1
View File
@@ -1,11 +1,15 @@
SUBSYSTEM_DEF(ai)
name = "AI"
init_order = INIT_ORDER_AI
priority = FIRE_PRIORITY_AI
wait = 2 SECONDS
flags = SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
dependencies = list(
/datum/controller/subsystem/air,
/datum/controller/subsystem/mobs
)
var/list/processing = list()
var/list/currentrun = list()
+4 -1
View File
@@ -1,11 +1,14 @@
SUBSYSTEM_DEF(aifast)
name = "AI (Fast)"
init_order = INIT_ORDER_AI_FAST
priority = FIRE_PRIORITY_AI
wait = 0.25 SECONDS // Every quarter second
flags = SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
dependencies = list(
/datum/controller/subsystem/ai
)
var/list/processing = list()
var/list/currentrun = list()
+3 -1
View File
@@ -65,7 +65,9 @@ Class Procs:
SUBSYSTEM_DEF(air)
name = "Air"
init_order = INIT_ORDER_AIR
dependencies = list(
/datum/controller/subsystem/atoms
)
priority = FIRE_PRIORITY_AIR
wait = 2 SECONDS // seconds (We probably can speed this up actually)
flags = SS_BACKGROUND // TODO - Should this really be background? It might be important.
+1 -1
View File
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(atc)
priority = FIRE_PRIORITY_ATC
runlevels = RUNLEVEL_GAME
wait = 2 SECONDS
init_order = INIT_ORDER_ATC
init_stage = INITSTAGE_LAST
flags = SS_BACKGROUND
VAR_PRIVATE/next_tick = 0
+5
View File
@@ -16,6 +16,11 @@ SUBSYSTEM_DEF(airflow)
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
priority = FIRE_PRIORITY_AIRFLOW
dependencies = list(
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/air
)
var/list/processing = list()
var/list/currentrun = list()
+3 -1
View File
@@ -2,7 +2,9 @@ SUBSYSTEM_DEF(alarm)
name = "Alarm"
wait = 2 SECONDS
priority = FIRE_PRIORITY_ALARM
init_order = INIT_ORDER_ALARM
dependencies = list(
/datum/controller/subsystem/mapping
)
var/list/datum/alarm/all_handlers
var/tmp/list/currentrun = null
var/static/list/active_alarm_cache = list()
+3 -2
View File
@@ -13,10 +13,11 @@ SUBSYSTEM_DEF(asset_loading)
while(length(generate_queue))
var/datum/asset/to_load = generate_queue[generate_queue.len]
to_load.queued_generation()
if(MC_TICK_CHECK)
return
to_load.queued_generation()
last_queue_len = length(generate_queue)
generate_queue.len--
// We just emptied the queue
+7 -2
View File
@@ -1,6 +1,11 @@
SUBSYSTEM_DEF(assets)
name = "Assets"
init_order = INIT_ORDER_ASSETS
dependencies = list(
/datum/controller/subsystem/holomaps,
/datum/controller/subsystem/robot_sprites
///datum/controller/subsystem/persistent_paintings,
///datum/controller/subsystem/greyscale_previews,
)
flags = SS_NO_FIRE
var/list/datum/asset_cache_item/cache = list()
var/list/preload = list()
@@ -32,7 +37,7 @@ SUBSYSTEM_DEF(assets)
transport.Initialize(cache)
subsystem_initialized = TRUE
initialized = TRUE
return SS_INIT_SUCCESS
/datum/controller/subsystem/assets/Recover()
+21 -13
View File
@@ -1,6 +1,14 @@
SUBSYSTEM_DEF(atoms)
name = "Atoms"
init_order = INIT_ORDER_ATOMS
dependencies = list(
/datum/controller/subsystem/garbage,
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/planets,
/datum/controller/subsystem/transcore,
/datum/controller/subsystem/chemistry,
/datum/controller/subsystem/sounds,
/datum/controller/subsystem/job
)
flags = SS_NO_FIRE
/// A stack of list(source, desired initialized state)
@@ -8,7 +16,7 @@ SUBSYSTEM_DEF(atoms)
var/list/initialized_state = list()
var/base_initialized
var/initialized = INITIALIZATION_INSSATOMS
var/atom_initialized = INITIALIZATION_INSSATOMS
var/list/late_loaders = list()
var/list/BadInitializeCalls = list()
@@ -25,19 +33,19 @@ SUBSYSTEM_DEF(atoms)
var/list/mapload_init_times = list()
#endif
initialized = INITIALIZATION_INSSATOMS
atom_initialized = INITIALIZATION_INSSATOMS
/datum/controller/subsystem/atoms/Initialize()
init_start_time = world.time
initialized = INITIALIZATION_INNEW_MAPLOAD
atom_initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
initialized = INITIALIZATION_INNEW_REGULAR
atom_initialized = INITIALIZATION_INNEW_REGULAR
return SS_INIT_SUCCESS
/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms, list/atoms_to_return)
if(initialized == INITIALIZATION_INSSATOMS)
if(atom_initialized == INITIALIZATION_INSSATOMS)
return
// Generate a unique mapload source for this run of InitializeAtoms
@@ -142,9 +150,9 @@ SUBSYSTEM_DEF(atoms)
/// Accepts a state and a source, the most recent state is used, sources exist to prevent overriding old values accidentally
/datum/controller/subsystem/atoms/proc/set_tracked_initalized(state, source)
if(!length(initialized_state))
base_initialized = initialized
base_initialized = atom_initialized
initialized_state += list(list(source, state))
initialized = state
atom_initialized = state
/datum/controller/subsystem/atoms/proc/clear_tracked_initalize(source)
if(!length(initialized_state))
@@ -155,18 +163,18 @@ SUBSYSTEM_DEF(atoms)
break
if(!length(initialized_state))
initialized = base_initialized
atom_initialized = base_initialized
base_initialized = INITIALIZATION_INNEW_REGULAR
return
initialized = initialized_state[length(initialized_state)][2]
atom_initialized = initialized_state[length(initialized_state)][2]
/// Returns TRUE if anything is currently being initialized
/datum/controller/subsystem/atoms/proc/initializing_something()
return length(initialized_state) > 1
/datum/controller/subsystem/atoms/Recover()
initialized = SSatoms.initialized
if(initialized == INITIALIZATION_INNEW_MAPLOAD)
atom_initialized = SSatoms.atom_initialized
if(atom_initialized == INITIALIZATION_INNEW_MAPLOAD)
InitializeAtoms()
initialized_state = SSatoms.initialized_state
BadInitializeCalls = SSatoms.BadInitializeCalls
@@ -187,7 +195,7 @@ SUBSYSTEM_DEF(atoms)
/// Prepares an atom to be deleted once the atoms SS is initialized.
/datum/controller/subsystem/atoms/proc/prepare_deletion(atom/target)
if (initialized == INITIALIZATION_INNEW_REGULAR)
if (atom_initialized == INITIALIZATION_INNEW_REGULAR)
// Atoms SS has already completed, just kill it now.
qdel(target)
else
@@ -1,6 +1,5 @@
SUBSYSTEM_DEF(character_setup)
name = "Character Setup"
init_order = INIT_ORDER_DEFAULT
priority = FIRE_PRIORITY_CHARSETUP
flags = SS_BACKGROUND | SS_NO_INIT
wait = 1 SECOND
+1 -1
View File
@@ -8,7 +8,7 @@ SUBSYSTEM_DEF(chat)
flags = SS_TICKER|SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
init_stage = INITSTAGE_LAST
/// Assosciates a ckey with a list of messages to send to them.
var/list/list/datum/chat_payload/client_to_payloads = list()
+3 -1
View File
@@ -2,7 +2,9 @@ SUBSYSTEM_DEF(chemistry)
name = "Chemistry"
wait = 20
flags = SS_NO_FIRE
init_order = INIT_ORDER_CHEMISTRY
dependencies = list(
/datum/controller/subsystem/garbage
)
var/list/chemical_reactions = list()
var/list/chemical_reactions_by_product = list()
+5 -1
View File
@@ -4,8 +4,12 @@
//
SUBSYSTEM_DEF(circuit)
name = "Circuit"
init_order = INIT_ORDER_CIRCUIT
flags = SS_NO_FIRE
dependencies = list(
/datum/controller/subsystem/atoms
)
var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs
var/list/cached_components = list() // Associative list of [component_path]:[component] pairs
var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs
@@ -0,0 +1,207 @@
/*
HOW IT WORKS
The SSradio is a global object maintaining all radio transmissions, think about it as about "ether".
Note that walkie-talkie, intercoms and headsets handle transmission using nonstandard way.
procs:
add_object(obj/device as obj, var/new_frequency as num, var/radio_filter as text|null = null)
Adds listening object.
parameters:
device - device receiving signals, must have proc receive_signal (see description below).
one device may listen several frequencies, but not same frequency twice.
new_frequency - see possibly frequencies below;
radio_filter - thing for optimization. Optional, but recommended.
All filters should be consolidated in this file, see defines later.
Device without listening filter will receive all signals (on specified frequency).
Device with filter will receive any signals sent without filter.
Device with filter will not receive any signals sent with different filter.
returns:
Reference to frequency object.
remove_object (obj/device, old_frequency)
Obliviously, after calling this proc, device will not receive any signals on old_frequency.
Other frequencies will left unaffected.
return_frequency(var/frequency as num)
returns:
Reference to frequency object. Use it if you need to send and do not need to listen.
radio_frequency is a global object maintaining list of devices that listening specific frequency.
procs:
post_signal(obj/source as obj|null, datum/signal/signal, var/radio_filter as text|null = null, var/range as num|null = null)
Sends signal to all devices that wants such signal.
parameters:
source - object, emitted signal. Usually, devices will not receive their own signals.
signal - see description below.
radio_filter - described above.
range - radius of regular byond's square circle on that z-level. null means everywhere, on all z-levels.
obj/proc/receive_signal(datum/signal/signal, var/receive_method as num, var/receive_param)
Handler from received signals. By default does nothing. Define your own for your object.
Avoid of sending signals directly from this proc, use spawn(-1). DO NOT use sleep() here or call procs that sleep please. If you must, use spawn()
parameters:
signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
TRANSMISSION_WIRE is currently unused.
receive_param - for TRANSMISSION_RADIO here comes frequency.
datum/signal
vars:
source
an object that emitted signal. Used for debug and bearing.
data
list with transmitting data. Usual use pattern:
data["msg"] = "hello world"
encryption
Some number symbolizing "encryption key".
Note that game actually do not use any cryptography here.
If receiving object don't know right key, it must ignore encrypted signal in its receive_signal.
*/
SUBSYSTEM_DEF(radio)
name = "Radio"
flags = SS_NO_FIRE
dependencies = list(
/datum/controller/subsystem/machines
)
var/list/datum/radio_frequency/frequencies = list()
/datum/controller/subsystem/radio/Initialize()
GLOB.autospeaker = new (null, FALSE, null, null, TRUE) //Set up Global Announcer
return SS_INIT_SUCCESS
/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/radio_filter = null as text|null)
var/f_text = num2text(new_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(!frequency)
frequency = new
frequency.frequency = new_frequency
frequencies[f_text] = frequency
frequency.add_listener(device, radio_filter)
return frequency
/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency)
var/f_text = num2text(old_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(frequency)
frequency.remove_listener(device)
if(frequency.devices.len == 0)
qdel(frequency)
frequencies -= f_text
return 1
/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num)
var/f_text = num2text(new_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(!frequency)
frequency = new
frequency.frequency = new_frequency
frequencies[f_text] = frequency
return frequency
//Frequency channels
/datum/radio_frequency
var/frequency as num
var/list/list/obj/devices = list()
/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/radio_filter = null as text|null, var/range = null as num|null)
var/turf/start_point
if(range)
start_point = get_turf(source)
if(!start_point)
qdel(signal)
return 0
if (radio_filter)
send_to_filter(source, signal, radio_filter, start_point, range)
send_to_filter(source, signal, RADIO_DEFAULT, start_point, range)
else
//Broadcast the signal to everyone!
for (var/next_filter in devices)
send_to_filter(source, signal, next_filter, start_point, range)
//Sends a signal to all machines belonging to a given filter. Should be called by post_signal()
/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/radio_filter, var/turf/start_point = null, var/range = null)
if (range && !start_point)
return
for(var/obj/device in devices[radio_filter])
if(device == source)
continue
if(range)
var/turf/end_point = get_turf(device)
if(!end_point)
continue
if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
continue
device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
/datum/radio_frequency/proc/add_listener(obj/device as obj, var/radio_filter as text|null)
if (!radio_filter)
radio_filter = RADIO_DEFAULT
//log_admin("add_listener(device=[device],radio_filter=[radio_filter]) frequency=[frequency]")
var/list/obj/devices_line = devices[radio_filter]
if (!devices_line)
devices_line = new
devices[radio_filter] = devices_line
devices_line+=device
// var/list/obj/devices_line___ = devices[filter_str]
// var/l = devices_line___.len
//log_admin("DEBUG: devices_line.len=[devices_line.len]")
//log_admin("DEBUG: devices(filter_str).len=[l]")
/datum/radio_frequency/proc/remove_listener(obj/device)
for (var/devices_filter in devices)
var/list/devices_line = devices[devices_filter]
devices_line-=device
while (null in devices_line)
devices_line -= null
if (devices_line.len==0)
devices -= devices_filter
/datum/signal
var/obj/source
var/transmission_method = 0 //unused at the moment
//0 = wire
//1 = radio transmission
//2 = subspace transmission
var/list/data = list()
var/encryption
var/frequency = 0
/datum/signal/proc/copy_from(datum/signal/model)
source = model.source
transmission_method = model.transmission_method
data = model.data
encryption = model.encryption
frequency = model.frequency
/datum/signal/proc/debug_print()
if (source)
. = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
else
. = "signal = {source = '[source]' ()\n"
for (var/i in data)
. += "data\[\"[i]\"\] = \"[data[i]]\"\n"
if(islist(data[i]))
var/list/L = data[i]
for(var/t in L)
. += "data\[\"[i]\"\] list has: [t]"
//callback used by objects to react to incoming radio signals
/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
return null
+4 -1
View File
@@ -1,8 +1,10 @@
SUBSYSTEM_DEF(dbcore)
name = "Database"
flags = SS_TICKER
init_stage = INITSTAGE_EARLY
wait = 10 // Not seconds because we're running on SS_TICKER
init_order = INIT_ORDER_DBCORE
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
priority = FIRE_PRIORITY_DATABASE
var/failed_connection_timeout = 0
@@ -41,6 +43,7 @@ SUBSYSTEM_DEF(dbcore)
//var/db_daemon_started = FALSE
/datum/controller/subsystem/dbcore/Initialize()
Connect()
return SS_INIT_SUCCESS
/datum/controller/subsystem/dbcore/stat_entry(msg)
+1
View File
@@ -1,6 +1,7 @@
SUBSYSTEM_DEF(events)
name = "Events" // VOREStation Edit - This is still the main events subsystem for us.
wait = 2 SECONDS
init_stage = INITSTAGE_LAST
var/tmp/list/currentrun = null
+3
View File
@@ -5,6 +5,9 @@ SUBSYSTEM_DEF(event_ticker)
name = "Events (Ticker)"
wait = 2 SECONDS
runlevels = RUNLEVEL_GAME
dependencies = list(
/datum/controller/subsystem/events
)
// List of `/datum/event2/event`s that are currently active, and receiving process() ticks.
var/list/active_events = list()
+1 -1
View File
@@ -96,7 +96,7 @@ SUBSYSTEM_DEF(game_master)
// These are ran before committing to an event.
// Returns TRUE if the system is allowed to procede, otherwise returns FALSE.
/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE)
if(!ticker || ticker.current_state != GAME_STATE_PLAYING)
if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
if(!quiet)
log_game_master("Unable to start event: Ticker is nonexistent, or the game is not ongoing.")
return FALSE
+1 -2
View File
@@ -27,8 +27,7 @@ SUBSYSTEM_DEF(garbage)
wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_order = INIT_ORDER_GARBAGE
// init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_FIRST
var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_CHECK_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level
+3 -1
View File
@@ -4,8 +4,10 @@
//
SUBSYSTEM_DEF(holomaps)
name = "HoloMiniMaps"
init_order = INIT_ORDER_HOLOMAPS
flags = SS_NO_FIRE
dependencies = list(
/datum/controller/subsystem/atoms
)
var/static/holomaps_initialized = FALSE
var/static/list/holoMiniMaps = list()
var/static/list/extraMiniMaps = list()
+1 -1
View File
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(input)
name = "Input"
wait = 1 // SS_TICKER means this runs every tick
init_order = INIT_ORDER_INPUT
init_stage = INITSTAGE_EARLY
flags = SS_TICKER | SS_NO_INIT
priority = FIRE_PRIORITY_INPUT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
+6 -1
View File
@@ -6,7 +6,12 @@
SUBSYSTEM_DEF(internal_wiki)
name = "Wiki"
wait = 1
init_order = INIT_ORDER_WIKI
//dependencies = list(
// /datum/controller/subsystem/chemistry,
// /datum/controller/subsystem/plants,
// /datum/controller/subsystem/supply
//)
init_stage = INITSTAGE_LAST
flags = SS_NO_FIRE
VAR_PRIVATE/list/pages = list()
+3 -1
View File
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(job)
name = "Job"
init_order = INIT_ORDER_JOB
dependencies = list(
/datum/controller/subsystem/mapping,
)
flags = SS_NO_FIRE
var/list/occupations = list() //List of all jobs
+6 -4
View File
@@ -1,7 +1,9 @@
SUBSYSTEM_DEF(lighting)
name = "Lighting"
dependencies = list(
/datum/controller/subsystem/machines
)
wait = 1
init_order = INIT_ORDER_LIGHTING
flags = SS_TICKER
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY // Do some work during lobby waiting period. May as well.
var/sun_mult = 1.0
@@ -19,8 +21,8 @@ SUBSYSTEM_DEF(lighting)
/datum/controller/subsystem/lighting/Initialize()
if(!subsystem_initialized)
subsystem_initialized = TRUE
if(!initialized)
initialized = TRUE
create_all_lighting_objects()
for(var/datum/planet/planet in SSplanets.planets)
@@ -177,5 +179,5 @@ SUBSYSTEM_DEF(lighting)
/datum/controller/subsystem/lighting
/datum/controller/subsystem/lighting/Recover()
subsystem_initialized = SSlighting.subsystem_initialized
initialized = SSlighting.initialized
..()
+1 -2
View File
@@ -1,7 +1,6 @@
SUBSYSTEM_DEF(lobby_monitor)
name = "Lobby Art"
init_order = INIT_ORDER_LOBBY
// init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_EARLY
flags = SS_NO_INIT
wait = 1 SECOND
runlevels = ALL
+3 -1
View File
@@ -9,8 +9,10 @@
SUBSYSTEM_DEF(machines)
name = "Machines"
dependencies = list(
/datum/controller/subsystem/points_of_interest
)
priority = FIRE_PRIORITY_MACHINES
init_order = INIT_ORDER_MACHINES
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
+16 -2
View File
@@ -1,19 +1,32 @@
// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
//dependencies = list(
// /datum/controller/subsystem/job,
///datum/controller/subsystem/processing/station,
// /datum/controller/subsystem/chemistry
// ///datum/controller/subsystem/processing/reagents
//)
dependencies = list(
///datum/controller/subsystem/garbage,
/datum/controller/subsystem/vis_overlays,
/datum/controller/subsystem/chemistry
)
flags = SS_NO_FIRE
var/list/map_templates = list()
var/obj/effect/landmark/engine_loader/engine_loader
var/list/shelter_templates = list()
// TODO: Implement Later
var/datum/map/current_map
/datum/controller/subsystem/mapping/Recover()
flags |= SS_NO_INIT // Make extra sure we don't initialize twice.
shelter_templates = SSmapping.shelter_templates
/datum/controller/subsystem/mapping/Initialize()
if(subsystem_initialized)
if(initialized)
return
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
load_map_templates()
@@ -24,6 +37,7 @@ SUBSYSTEM_DEF(mapping)
// TODO - Other stuff related to maps and areas could be moved here too. Look at /tg
// Lateload Code related to Expedition areas.
if(using_map) // VOREStation Edit: Re-enable this.
current_map = using_map
loadLateMaps()
if(CONFIG_GET(flag/generate_map)) // VOREStation Edit: Re-order this.
+1 -1
View File
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(media_tracks)
name = "Media Tracks"
flags = SS_NO_FIRE
init_order = INIT_ORDER_MEDIA_TRACKS
init_stage = INITSTAGE_EARLY
/// Every track, including secret
var/list/all_tracks = list()
+8 -10
View File
@@ -12,6 +12,12 @@ SUBSYSTEM_DEF(mobs)
flags = SS_KEEP_TIMING|SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
dependencies = list(
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/points_of_interest,
/datum/controller/subsystem/shuttles
)
var/list/currentrun = list()
var/log_extensively = FALSE
var/list/timelog = list()
@@ -96,22 +102,14 @@ SUBSYSTEM_DEF(mobs)
log_game(msg)
log_world(msg)
/datum/controller/subsystem/mobs/fail()
..()
log_recent()
/datum/controller/subsystem/mobs/critfail()
..()
log_recent()
/datum/controller/subsystem/mobs/proc/report_death(var/mob/living/L)
if(!L)
return
if(!L.key || !L.mind)
return
if(!ticker || !ticker.mode)
if(!SSticker || !SSticker.mode)
return
ticker.mode.check_win()
SSticker.mode.check_win()
// Don't bother with the rest if we've not got a DB to do anything with
if(!CONFIG_GET(flag/enable_stat_tracking) || !CONFIG_GET(flag/sql_enabled))
+3 -1
View File
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(nightshift)
name = "Night Shift"
init_order = INIT_ORDER_NIGHTSHIFT
dependencies = list(
/datum/controller/subsystem/lighting
)
priority = FIRE_PRIORITY_NIGHTSHIFT
wait = 60 SECONDS
flags = SS_NO_TICK_CHECK
+3 -1
View File
@@ -3,7 +3,9 @@ SUBSYSTEM_DEF(overlays)
flags = SS_TICKER
wait = 1 // SS_TICKER - Ticks
priority = FIRE_PRIORITY_OVERLAYS
init_order = INIT_ORDER_OVERLAY
dependencies = list(
/datum/controller/subsystem/atoms
)
/// The queue of atoms that need overlay updates.
var/static/tmp/list/queue = list()
@@ -4,8 +4,12 @@ Readme at code\modules\awaymissions\overmap_renamer\readme.md
SUBSYSTEM_DEF(overmap_renamer)
name = "Overmap Renamer"
init_order = INIT_ORDER_MAPRENAME //Loaded very late in initializations. Must come before mapping and objs. Uses both as inputs.
runlevels = RUNLEVEL_INIT
//Loaded very late in initializations. Must come before mapping and objs. Uses both as inputs.
init_stage = INITSTAGE_LAST
dependencies = list(
/datum/controller/subsystem/skybox
)
runlevels = RUNLEVEL_SETUP
flags = SS_NO_FIRE
+6 -1
View File
@@ -1,7 +1,12 @@
SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
dependencies = list(
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/points_of_interest
)
flags = SS_NO_FIRE
var/list/tracking_values = list()
var/list/persistence_datums = list()
+1 -1
View File
@@ -6,7 +6,7 @@
SUBSYSTEM_DEF(ping)
name = "Ping"
priority = FIRE_PRIORITY_PING
// init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_EARLY
wait = 4 SECONDS
flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+4 -2
View File
@@ -1,10 +1,12 @@
SUBSYSTEM_DEF(planets)
name = "Planets"
init_order = INIT_ORDER_PLANETS
priority = FIRE_PRIORITY_PLANETS
wait = 2 SECONDS
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
dependencies = list(
/datum/controller/subsystem/plants
)
var/static/list/planets = list()
var/static/list/z_to_planet = list()
@@ -75,7 +77,7 @@ SUBSYSTEM_DEF(planets)
if(MC_TICK_CHECK)
return
#ifndef UNIT_TEST // Don't be updating temperatures and such during unit tests
#ifndef UNIT_TESTS // Don't be updating temperatures and such during unit tests
var/list/needs_temp_update = src.needs_temp_update
while(needs_temp_update.len)
var/datum/planet/P = needs_temp_update[needs_temp_update.len]
+3 -1
View File
@@ -2,7 +2,9 @@
SUBSYSTEM_DEF(plants)
name = "Plants"
init_order = INIT_ORDER_PLANTS
dependencies = list(
/datum/controller/subsystem/mapping
)
priority = FIRE_PRIORITY_PLANTS
wait = PLANT_TICK_TIME
+3 -1
View File
@@ -3,8 +3,10 @@ SUBSYSTEM_DEF(points_of_interest)
name = "Points of Interest"
wait = 1 SECONDS
priority = FIRE_PRIORITY_POIS
init_order = INIT_ORDER_POIS
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT //POIs can be loaded mid-round.
dependencies = list(
/datum/controller/subsystem/holomaps
)
var/list/obj/effect/landmark/poi_loader/poi_queue = list()
/datum/controller/subsystem/points_of_interest/Initialize()
@@ -1,7 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(instruments)
name = "Instruments"
wait = 0.5
init_order = INIT_ORDER_INSTRUMENTS
flags = SS_KEEP_TIMING
priority = FIRE_PRIORITY_INSTRUMENTS
/// List of all instrument data, associative id = datum
@@ -96,14 +96,6 @@ SUBSYSTEM_DEF(processing)
log_game(msg)
log_world(msg)
/datum/controller/subsystem/processing/fail()
..()
log_recent()
/datum/controller/subsystem/processing/critfail()
..()
log_recent()
/datum/proc/DebugSubsystemProcess(var/wait, var/times_fired, var/datum/controller/subsystem/processing/subsystem)
subsystem.debug_last_thing = src
var/start_tick = world.time
+69
View File
@@ -0,0 +1,69 @@
SUBSYSTEM_DEF(profiler)
name = "Profiler"
init_stage = INITSTAGE_FIRST
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
wait = 300 SECONDS
var/fetch_cost = 0
var/write_cost = 0
/datum/controller/subsystem/profiler/stat_entry(msg)
msg += "F:[round(fetch_cost,1)]ms"
msg += "|W:[round(write_cost,1)]ms"
return msg
/datum/controller/subsystem/profiler/Initialize()
if(CONFIG_GET(flag/auto_profile))
StartProfiling()
else
StopProfiling() //Stop the early start profiler
wait = CONFIG_GET(number/profiler_interval)
return SS_INIT_SUCCESS
/datum/controller/subsystem/profiler/OnConfigLoad()
if(CONFIG_GET(flag/auto_profile))
StartProfiling()
can_fire = TRUE
else
StopProfiling()
can_fire = FALSE
/datum/controller/subsystem/profiler/fire()
DumpFile()
/datum/controller/subsystem/profiler/Shutdown()
if(CONFIG_GET(flag/auto_profile))
DumpFile(allow_yield = FALSE)
world.Profile(PROFILE_CLEAR, type = "sendmaps")
return ..()
/datum/controller/subsystem/profiler/proc/StartProfiling()
world.Profile(PROFILE_START)
world.Profile(PROFILE_START, type = "sendmaps")
/datum/controller/subsystem/profiler/proc/StopProfiling()
world.Profile(PROFILE_STOP)
world.Profile(PROFILE_STOP, type = "sendmaps")
/datum/controller/subsystem/profiler/proc/DumpFile(allow_yield = TRUE)
var/timer = TICK_USAGE_REAL
var/current_profile_data = world.Profile(PROFILE_REFRESH, format = "json")
var/current_sendmaps_data = world.Profile(PROFILE_REFRESH, type = "sendmaps", format="json")
fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(allow_yield)
CHECK_TICK
if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
stack_trace("Warning, profiling stopped manually before dump.")
var/prof_file = file("[GLOB.log_directory]/profiler/profiler-[round(world.time * 0.1, 10)].json")
if(fexists(prof_file))
fdel(prof_file)
if(!length(current_sendmaps_data)) //Would be nice to have explicit proc to check this
stack_trace("Warning, sendmaps profiling stopped manually before dump.")
var/sendmaps_file = file("[GLOB.log_directory]/profiler/sendmaps-[round(world.time * 0.1, 10)].json")
if(fexists(sendmaps_file))
fdel(sendmaps_file)
timer = TICK_USAGE_REAL
WRITE_FILE(prof_file, current_profile_data)
WRITE_FILE(sendmaps_file, current_sendmaps_data)
write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+3 -1
View File
@@ -2,11 +2,13 @@
SUBSYSTEM_DEF(research)
name = "Research"
// priority = FIRE_PRIORITY_RESEARCH
init_order = INIT_ORDER_RESEARCH
wait = 10
// dependencies = list(
// /datum/controller/subsystem/processing/station,
// )
dependencies = list(
/datum/controller/subsystem/mapping
)
//TECHWEB STATIC
var/list/techweb_nodes = list() //associative id = node datum
var/list/techweb_designs = list() //associative id = node datum
+3 -1
View File
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(robot_sprites)
name = "Robot Sprites"
init_order = INIT_ORDER_ROBOT_SPRITES
dependencies = list(
/datum/controller/subsystem/garbage
)
flags = SS_NO_FIRE
var/list/all_cyborg_sprites = list()
var/list/cyborg_sprites_by_module = list()
+1 -2
View File
@@ -5,8 +5,7 @@ SUBSYSTEM_DEF(server_maint)
wait = 6
flags = SS_POST_FIRE_TIMING
priority = FIRE_PRIORITY_SERVER_MAINT
init_order = INIT_ORDER_SERVER_MAINT
//init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_FIRST
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun
///Associated list of list names to lists to clear of nulls
+5 -1
View File
@@ -8,7 +8,11 @@ SUBSYSTEM_DEF(shuttles)
name = "Shuttles"
wait = 2 SECONDS
priority = FIRE_PRIORITY_SHUTTLES
init_order = INIT_ORDER_SHUTTLES
dependencies = list(
/datum/controller/subsystem/machines,
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/radio
)
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
+3 -3
View File
@@ -2,7 +2,7 @@
//Exists to handle a few global variables that change enough to justify this. Technically a parallax, but it exhibits a skybox effect.
SUBSYSTEM_DEF(skybox)
name = "Space skybox"
init_order = INIT_ORDER_SKYBOX
init_stage = INITSTAGE_LAST
flags = SS_NO_FIRE
var/static/list/skybox_cache = list()
@@ -64,7 +64,7 @@ SUBSYSTEM_DEF(skybox)
speedspace_cache["EW_[i]"] = MA
//Over-the-edge images
for (var/dir in GLOB.alldirs)
for (var/dir in ALL_POSSIBLE_DIRS)
var/mutable_appearance/MA = new(normal_space)
var/matrix/M = matrix()
var/horizontal = (dir & (WEST|EAST))
@@ -93,7 +93,7 @@ SUBSYSTEM_DEF(skybox)
return SS_INIT_SUCCESS
/datum/controller/subsystem/skybox/proc/get_skybox(z)
if(!subsystem_initialized)
if(!initialized)
return // WAIT
if(!skybox_cache["[z]"])
skybox_cache["[z]"] = generate_skybox(z)
+3 -1
View File
@@ -3,7 +3,9 @@
SUBSYSTEM_DEF(sounds)
name = "Sounds"
flags = SS_NO_FIRE
init_order = INIT_ORDER_SOUNDS
dependencies = list(
/datum/controller/subsystem/mapping
)
var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
/// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
var/static/random_channels_min = 50
+3 -1
View File
@@ -3,7 +3,9 @@
// however this makes it a lot easier to test, and it is natively supported by BYOND.
SUBSYSTEM_DEF(sqlite)
name = "SQLite"
init_order = INIT_ORDER_SQLITE
dependencies = list(
/datum/controller/subsystem/dbcore
)
flags = SS_NO_FIRE
var/database/sqlite_db = null
+3 -1
View File
@@ -3,9 +3,11 @@
//
SUBSYSTEM_DEF(starmover)
name = "Shuttle Star Movement"
init_order = INIT_ORDER_STARMOVER
priority = FIRE_PRIORITY_STARMOVER
wait = 1 // This needs to be done pretty quickly
dependencies = list(
/datum/controller/subsystem/points_of_interest
)
var/list/zqueue = list()
var/list/current_movement = null
//list used to track which zlevels are being 'moved' by the proc below
+32 -7
View File
@@ -1,8 +1,6 @@
SUBSYSTEM_DEF(statpanels)
name = "Stat Panels"
wait = 4
init_order = INIT_ORDER_STATPANELS
//init_stage = INITSTAGE_EARLY
priority = FIRE_PRIORITY_STATPANEL
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
flags = SS_NO_INIT
@@ -42,6 +40,15 @@ SUBSYSTEM_DEF(statpanels)
var/ETA = emergency_shuttle.get_status_panel_eta()
if(ETA)
global_data += "[ETA]"
if(SSticker.reboot_timer)
var/reboot_time = timeleft(SSticker.reboot_timer)
if(reboot_time)
global_data += "Reboot: [DisplayTimeText(reboot_time, 1)]"
// admin must have delayed round end
else if(SSticker.ready_for_reboot)
global_data += "Reboot: DELAYED"
src.currentrun = GLOB.clients.Copy()
mc_data = null
@@ -140,7 +147,7 @@ SUBSYSTEM_DEF(statpanels)
var/coord_entry = COORD(eye_turf)
if(!mc_data)
generate_mc_data()
target.stat_panel.send_message("update_mc", list(mc_data = mc_data, coord_entry = coord_entry))
target.stat_panel.send_message("update_mc", list("mc_data" = mc_data, "coord_entry" = coord_entry))
/datum/controller/subsystem/statpanels/proc/set_examine_tab(client/target)
var/description_holders = target.description_holders
@@ -206,13 +213,31 @@ SUBSYSTEM_DEF(statpanels)
list("CPU:", world.cpu),
list("Instances:", "[num2text(world.contents.len, 10)]"),
list("World Time:", "[world.time]"),
list("Globals:", GLOB.stat_entry(), "\ref[GLOB]"),
list("[config]:", config.stat_entry(), "\ref[config]"),
list("Globals:", GLOB.stat_entry(), text_ref(GLOB)),
list("[config]:", config.stat_entry(), text_ref(config)),
list("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)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
list("Master Controller:", Master.stat_entry(), "\ref[Master]"),
list("Failsafe Controller:", Failsafe.stat_entry(), "\ref[Failsafe]"),
list("Master Controller:", Master.stat_entry(), text_ref(Master)),
list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)),
list("","")
)
#if defined(MC_TAB_TRACY_INFO) || defined(SPACEMAN_DMM)
var/static/tracy_dll
var/static/tracy_present
if(isnull(tracy_dll))
tracy_dll = TRACY_DLL_PATH
tracy_present = fexists(tracy_dll)
if(tracy_present)
if(Tracy.enabled)
mc_data.Insert(2, list(list("byond-tracy:", "Active (reason: [Tracy.init_reason || "N/A"])")))
else if(Tracy.error)
mc_data.Insert(2, list(list("byond-tracy:", "Errored ([Tracy.error])")))
else if(fexists(TRACY_ENABLE_PATH))
mc_data.Insert(2, list(list("byond-tracy:", "Queued for next round")))
else
mc_data.Insert(2, list(list("byond-tracy:", "Inactive")))
else
mc_data.Insert(2, list(list("byond-tracy:", "[tracy_dll] not present")))
#endif
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems)
mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
mc_data[++mc_data.len] = list("Camera Net", "Cameras: [global.cameranet.cameras.len] | Chunks: [global.cameranet.chunks.len]", "\ref[global.cameranet]")
+313 -414
View File
@@ -1,59 +1,85 @@
//
// Ticker controls the state of the game, being responsible for round start, game mode, and round end.
//
SUBSYSTEM_DEF(ticker)
name = "Gameticker"
wait = 2 SECONDS
init_order = INIT_ORDER_TICKER
name = "Ticker"
priority = FIRE_PRIORITY_TICKER
flags = SS_NO_TICK_CHECK | SS_KEEP_TIMING
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // Every runlevel!
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME
var/const/restart_timeout = 4 MINUTES // Default time to wait before rebooting in desiseconds.
var/current_state = GAME_STATE_INIT // We aren't even at pregame yet // TODO replace with CURRENT_GAME_STATE
/// state of current round (used by process()) Use the defines GAME_STATE_* !
var/current_state = GAME_STATE_STARTUP
/// Boolean to track if round should be forcibly ended next ticker tick.
/// Set by admin intervention ([ADMIN_FORCE_END_ROUND])
/// or a "round-ending" event, like summoning Nar'Sie, a blob victory, the nuke going off, etc. ([FORCE_END_ROUND])
var/force_ending = END_ROUND_AS_NORMAL
/// If TRUE, there is no lobby phase, the game starts immediately.
var/start_immediately = FALSE
/// Boolean to track and check if our subsystem setup is done.
var/setup_done = FALSE
/* Relies upon the following globals (TODO move those in here) */
// var/GLOB.master_mode = "extended" //The underlying game mode (so "secret" or the voted mode).
// Set by SSvote when VOTE_GAMEMODE finishes.
// var/round_progressing = 1 //Whether the lobby clock is ticking down.
var/hide_mode = FALSE
var/datum/game_mode/mode = null
var/pregame_timeleft = 0 // Time remaining until game starts in seconds. Set by config
var/start_immediately = FALSE // If true there is no lobby phase, the game starts immediately.
var/login_music //music played in pregame lobby
var/round_end_sound //music/jingle played when the world reboots
var/round_end_sound_sent = TRUE //If all clients have loaded it
var/hide_mode = FALSE // If the true game mode should be hidden (because we chose "secret")
var/datum/game_mode/mode = null // The actual gamemode, if selected.
var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking.
var/end_game_state = END_GAME_NOT_OVER // Track where we are ending game/round
var/restart_timeleft // Time remaining until restart in desiseconds
var/last_restart_notify // world.time of last restart warning.
var/delay_end = FALSE // If set, the round will not restart on its own.
var/delay_end = FALSE //if set true, the round will not restart on it's own
var/admin_delay_notice = "" //a message to display to anyone who tries to restart the world after a delay
var/ready_for_reboot = FALSE //all roundend preparation done with, all that's left is reboot
// var/login_music // music played in pregame lobby // VOREStation Edit - We do music differently
var/tipped = FALSE //Did we broadcast the tip of the day yet?
var/selected_tip // What will be the tip of the day?
var/list/datum/mind/minds = list() // The people in the game. Used for objective tracking.
var/timeLeft //pregame timer
var/start_at
var/random_players = FALSE // If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
var/gametime_offset = 432000 //Deciseconds to add to world.time for station time.
var/station_time_rate_multiplier = 12 //factor of station time progressal vs real time.
// TODO - Should this go here or in the job subsystem?
var/triai = FALSE // Global flag for Triumvirate AI being enabled
/// Num of players, used for pregame stats on statpanel
var/totalPlayers = 0
/// Num of ready players, used for pregame stats on statpanel (only viewable by admins)
var/totalPlayersReady = 0
/// Num of ready admins, used for pregame stats on statpanel (only viewable by admins)
var/total_admins_ready = 0
//station_explosion used to be a variable for every mob's hud. Which was a waste!
//Now we have a general cinematic centrally held within the gameticker....far more efficient!
var/obj/screen/cinematic = null
var/queue_delay = 0
var/list/queued_players = list() //used for join queues when the server exceeds the hard population cap
/// What is going to be reported to other stations at end of round?
var/news_report
var/roundend_check_paused = FALSE
var/round_start_time = 0
var/list/round_start_events
var/list/round_end_events
var/mode_result = "undefined"
var/end_state = "undefined"
/// People who have been commended and will receive a heart
var/list/hearts
/// Why an emergency shuttle was called
var/emergency_reason
// This global variable exists for legacy support so we don't have to rename every 'ticker' to 'SSticker' yet.
var/global/datum/controller/subsystem/ticker/ticker
/datum/controller/subsystem/ticker/PreInit()
global.ticker = src // TODO - Remove this! Change everything to point at SSticker intead
/// ID of round reboot timer, if it exists
var/reboot_timer = null
/// ### LEGACY VARS ###
/// Default time to wait before rebooting in desiseconds.
var/const/restart_timeout = 4 MINUTES
/// Track where we are ending game/round
var/end_game_state = END_GAME_NOT_OVER
/// Time remaining until restart in desiseconds
var/restart_timeleft
/// world.time of last restart warning.
var/last_restart_notify
/datum/controller/subsystem/ticker/Initialize()
pregame_timeleft = CONFIG_GET(number/pregame_time)
send2mainirc("Server lobby is loaded and open at byond://[CONFIG_GET(string/serverurl) ? CONFIG_GET(string/serverurl) : (CONFIG_GET(string/server) ? CONFIG_GET(string/server) : "[world.address]:[world.port]")]")
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
SSwebhooks.send(
WEBHOOK_ROUNDPREP,
list(
@@ -61,63 +87,185 @@ var/global/datum/controller/subsystem/ticker/ticker
"url" = get_world_url()
)
)
GLOB.autospeaker = new (null, FALSE, null, null, TRUE) //Set up Global Announcer
return SS_INIT_SUCCESS
/datum/controller/subsystem/ticker/fire(resumed = FALSE)
switch(current_state)
if(GAME_STATE_INIT)
pregame_welcome()
if(GAME_STATE_STARTUP)
if(Master.initializations_finished_with_no_players_logged_in)
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, span_notice("<b>Welcome to [station_name()]!</b>"))
//for(var/channel_tag in CONFIG_GET(str_list/channel_announce_new_game))
// send2chat(new /datum/tgs_message_content("New round starting on [SSmapping.current_map.map_name]!"), channel_tag)
current_state = GAME_STATE_PREGAME
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME)
fire()
if(GAME_STATE_PREGAME)
pregame_tick()
//lobby stats for statpanels
if(isnull(timeLeft))
timeLeft = max(0,start_at - world.time)
totalPlayers = LAZYLEN(GLOB.new_player_list)
totalPlayersReady = 0
total_admins_ready = 0
for(var/mob/new_player/player as anything in GLOB.new_player_list)
if(player.ready == PLAYER_READY_TO_PLAY)
++totalPlayersReady
if(player.client?.holder)
++total_admins_ready
if(start_immediately)
timeLeft = 0
//countdown
if(timeLeft < 0)
return
timeLeft -= wait
//if(timeLeft <= 300 && !tipped)
// send_tip_of_the_round(world, selected_tip)
// tipped = TRUE
if(timeLeft <= 0)
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_SETTING_UP)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
if(start_immediately)
fire()
if(GAME_STATE_SETTING_UP)
setup_tick()
if(!setup())
//setup failed
current_state = GAME_STATE_STARTUP
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
timeLeft = null
Master.SetRunLevel(RUNLEVEL_LOBBY)
SEND_SIGNAL(src, COMSIG_TICKER_ERROR_SETTING_UP)
if(GAME_STATE_PLAYING)
playing_tick()
mode.process() // So THIS is where we run mode.process() huh? Okay
if(mode.explosion_in_progress)
return // wait until explosion is done.
if(force_ending)
current_state = GAME_STATE_FINISHED
declare_completion(force_ending)
Master.SetRunLevel(RUNLEVEL_POSTGAME)
else
// Calculate if game and/or mode are finished (Complicated by the continuous_rounds config option)
var/game_finished = FALSE
var/mode_finished = FALSE
if (CONFIG_GET(flag/continuous_rounds)) // Game keeps going after mode ends.
game_finished = (emergency_shuttle.returned() || mode.station_was_nuked)
mode_finished = ((end_game_state >= END_GAME_MODE_FINISHED) || mode.check_finished()) // Short circuit if already finished.
else // Game ends when mode does
game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || GLOB.universe_has_ended
mode_finished = game_finished
if(game_finished && mode_finished)
end_game_state = END_GAME_READY_TO_END
current_state = GAME_STATE_FINISHED
Master.SetRunLevel(RUNLEVEL_POSTGAME)
INVOKE_ASYNC(src, PROC_REF(declare_completion))
else if (mode_finished && (end_game_state < END_GAME_MODE_FINISHED))
end_game_state = END_GAME_MODE_FINISHED // Only do this cleanup once!
mode.cleanup()
//call a transfer shuttle vote
to_world(span_boldannounce("The round has ended!"))
SSvote.start_vote(new /datum/vote/crew_transfer)
// FIXME: IMPROVE THIS LATER!
if(GAME_STATE_FINISHED)
post_game_tick()
/datum/controller/subsystem/ticker/proc/pregame_welcome()
to_world(span_boldannounce(span_notice("<em>Welcome to the pregame lobby!</em>")))
to_world(span_boldannounce(span_notice("Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.")))
world << sound('sound/misc/server-ready.ogg', volume = 100)
if (world.time - last_restart_notify >= 1 MINUTE && !delay_end)
to_world(span_boldannounce("Restarting in [round(restart_timeleft/600, 1)] minute\s."))
last_restart_notify = world.time
// Called during GAME_STATE_PREGAME (RUNLEVEL_LOBBY)
/datum/controller/subsystem/ticker/proc/pregame_tick()
if(GLOB.round_progressing && last_fire)
pregame_timeleft -= (world.time - last_fire) / (1 SECOND)
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, span_boldannounce("Starting game..."))
var/init_start = world.timeofday
if(start_immediately)
pregame_timeleft = 0
else if(SSvote.active_vote)
return // vote still going, wait for it.
CHECK_TICK
setup_choose_gamemode()
// TODO
// Time to start the game!
if(pregame_timeleft <= 0)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
if(start_immediately)
fire() // Don't wait for next tick, do it now!
return
CHECK_TICK
setup_economy()
create_characters() //Create player characters
collect_minds()
equip_characters()
//if(pregame_timeleft <= config.vote_autogamemode_timeleft && !SSvote.gamemode_vote_called)
//.autogamemode() // Start the game mode vote (if we haven't had one already)
// data_core.manifest()
// Called during GAME_STATE_SETTING_UP (RUNLEVEL_SETUP)
/datum/controller/subsystem/ticker/proc/setup_tick(resumed = FALSE)
round_start_time = world.time // otherwise round_start_time would be 0 for the signals
if(!setup_choose_gamemode())
// It failed, go back to lobby state and re-send the welcome message
pregame_timeleft = CONFIG_GET(number/pregame_time)
// SSvote.gamemode_vote_called = FALSE // Allow another autogamemode vote
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
pregame_welcome()
return
// If we got this far we succeeded in picking a game mode. Punch it!
setup_startgame()
return
for(var/I in round_start_events)
var/datum/callback/cb = I
cb.InvokeAsync()
LAZYCLEARLIST(round_start_events)
round_start_time = world.time //otherwise round_start_time would be 0 for the signals
SEND_SIGNAL(src, COMSIG_TICKER_ROUND_STARTING, world.time)
callHook("roundstart")
log_world("Game start took [(world.timeofday - init_start)/10]s")
INVOKE_ASYNC(SSdbcore, TYPE_PROC_REF(/datum/controller/subsystem/dbcore,SetRoundStart))
to_chat(world, span_notice(span_bold("Welcome to [station_name()], enjoy your stay!")))
world << sound('sound/AI/welcome.ogg') // Skie
//SEND_SOUND(world, sound(SSstation.announcer.get_rand_welcome_sound()))
current_state = GAME_STATE_PLAYING
Master.SetRunLevel(RUNLEVEL_GAME)
//Holiday Round-start stuff ~Carn
Holiday_Game_Start()
// TODO END
PostSetup()
return TRUE
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
mode.post_setup()
// TODO
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
// TODO: IMPLEMENT: send2adminchat("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]" : ""] has started[allmins.len ? ".":" with no active admins online!"]")
if(!allmins.len)
send2adminirc("A round has started with no admins online.")
setup_done = TRUE
// TODO START
// TODO END
for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
//Deleting Startpoints but we need the ai point to AI-ize people later
if (S.name != "AI")
qdel(S)
if(CONFIG_GET(flag/sql_enabled))
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
//These callbacks will fire after roundstart key transfer
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
if(!HasRoundStarted())
LAZYADD(round_start_events, cb)
else
cb.InvokeAsync()
//These callbacks will fire before roundend report
/datum/controller/subsystem/ticker/proc/OnRoundend(datum/callback/cb)
if(current_state >= GAME_STATE_FINISHED)
cb.InvokeAsync()
else
LAZYADD(round_end_events, cb)
// Formerly the first half of setup() - The part that chooses the game mode.
// Returns 0 if failed to pick a mode, otherwise 1
@@ -170,74 +318,6 @@ var/global/datum/controller/subsystem/ticker/ticker
src.mode.announce()
return 1
// Formerly the second half of setup() - The part that actually initializes everything and starts the game.
/datum/controller/subsystem/ticker/proc/setup_startgame()
setup_economy()
create_characters() //Create player characters and transfer them.
collect_minds()
equip_characters()
// data_core.manifest()
callHook("roundstart")
for(var/I in round_start_events)
var/datum/callback/cb = I
cb.InvokeAsync()
LAZYCLEARLIST(round_start_events)
spawn(0)//Forking here so we dont have to wait for this to finish
mode.post_setup()
//Cleanup some stuff
for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
//Deleting Startpoints but we need the ai point to AI-ize people later
if (S.name != "AI")
qdel(S)
to_world(span_boldannounce(span_notice("<em>Enjoy the game!</em>")))
world << sound('sound/AI/welcome.ogg') // Skie
//Holiday Round-start stuff ~Carn
Holiday_Game_Start()
var/list/adm = get_admin_counts()
if(adm["total"] == 0)
send2adminirc("A round has started with no admins online.")
current_state = GAME_STATE_PLAYING
Master.SetRunLevel(RUNLEVEL_GAME)
if(CONFIG_GET(flag/sql_enabled))
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
return 1
// Called during GAME_STATE_PLAYING (RUNLEVEL_GAME)
/datum/controller/subsystem/ticker/proc/playing_tick(resumed = FALSE)
mode.process() // So THIS is where we run mode.process() huh? Okay
if(mode.explosion_in_progress)
return // wait until explosion is done.
// Calculate if game and/or mode are finished (Complicated by the continuous_rounds config option)
var/game_finished = FALSE
var/mode_finished = FALSE
if (CONFIG_GET(flag/continuous_rounds)) // Game keeps going after mode ends.
game_finished = (emergency_shuttle.returned() || mode.station_was_nuked)
mode_finished = ((end_game_state >= END_GAME_MODE_FINISHED) || mode.check_finished()) // Short circuit if already finished.
else // Game ends when mode does
game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || GLOB.universe_has_ended
mode_finished = game_finished
if(game_finished && mode_finished)
end_game_state = END_GAME_READY_TO_END
current_state = GAME_STATE_FINISHED
Master.SetRunLevel(RUNLEVEL_POSTGAME)
INVOKE_ASYNC(src, PROC_REF(declare_completion))
else if (mode_finished && (end_game_state < END_GAME_MODE_FINISHED))
end_game_state = END_GAME_MODE_FINISHED // Only do this cleanup once!
mode.cleanup()
//call a transfer shuttle vote
to_world(span_boldannounce("The round has ended!"))
SSvote.start_vote(new /datum/vote/crew_transfer)
// Called during GAME_STATE_FINISHED (RUNLEVEL_POSTGAME)
/datum/controller/subsystem/ticker/proc/post_game_tick()
switch(end_game_state)
@@ -259,148 +339,6 @@ var/global/datum/controller/subsystem/ticker/ticker
end_game_state = END_GAME_ENDING
return
if(END_GAME_ENDING)
restart_timeleft -= (world.time - last_fire)
if(delay_end)
to_world(span_boldannounce("An admin has delayed the round end."))
end_game_state = END_GAME_DELAYED
else if(restart_timeleft <= 0)
to_world(span_boldannounce("Restarting world!"))
sleep(5)
world.Reboot()
else if (world.time - last_restart_notify >= 1 MINUTE)
to_world(span_boldannounce("Restarting in [round(restart_timeleft/600, 1)] minute\s."))
last_restart_notify = world.time
return
if(END_GAME_DELAYED)
restart_timeleft -= (world.time - last_fire)
if(!delay_end)
end_game_state = END_GAME_ENDING
else
log_error("Ticker arrived at round end in an unexpected endgame state '[end_game_state]'.")
end_game_state = END_GAME_READY_TO_END
// ----------------------------------------------------------------------
// These two below are not used! But they could be
// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
/datum/controller/subsystem/ticker/proc/PreRoundStart()
return (current_state < GAME_STATE_PLAYING)
/datum/controller/subsystem/ticker/proc/IsSettingUp()
return (current_state == GAME_STATE_SETTING_UP)
/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
return (current_state == GAME_STATE_PLAYING)
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return (current_state >= GAME_STATE_PLAYING)
// ------------------------------------------------------------------------
// HELPER PROCS!
// ------------------------------------------------------------------------
//Plus it provides an easy way to make cinematics for other events. Just use this as a template :)
/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
if( cinematic ) return //already a cinematic in progress!
//initialise our cinematic screen object
cinematic = new(src)
cinematic.icon = 'icons/effects/station_explosion.dmi'
cinematic.icon_state = "station_intact"
cinematic.layer = 100
cinematic.plane = PLANE_PLAYER_HUD
cinematic.mouse_opacity = 0
cinematic.screen_loc = "1,0"
var/obj/structure/bed/temp_buckle = new(src)
//Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
if(station_missed)
for(var/mob/living/M in GLOB.living_mob_list)
M.buckled = temp_buckle //buckles the mob so it can't do anything
if(M.client)
M.client.screen += cinematic //show every client the cinematic
else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
for(var/mob/living/M in GLOB.living_mob_list)
M.buckled = temp_buckle
if(M.client)
M.client.screen += cinematic
switch(M.z)
if(0) //inside a crate or something
var/turf/T = get_turf(M)
if(T && (T.z in using_map.station_levels)) //we don't use M.death(0) because it calls a for(/mob) loop and
M.health = 0
M.set_stat(DEAD)
if(1) //on a z-level 1 turf.
M.health = 0
M.set_stat(DEAD)
//Now animate the cinematic
switch(station_missed)
if(1) //nuke was nearby but (mostly) missed
if( mode && !override )
override = mode.name
switch( override )
if("mercenary") //Nuke wasn't on station when it blew up
flick("intro_nuke",cinematic)
sleep(35)
world << sound('sound/effects/explosionfar.ogg')
flick("station_intact_fade_red",cinematic)
cinematic.icon_state = "summary_nukefail"
else
flick("intro_nuke",cinematic)
sleep(35)
world << sound('sound/effects/explosionfar.ogg')
//flick("end",cinematic)
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
sleep(50)
world << sound('sound/effects/explosionfar.ogg')
else //station was destroyed
if( mode && !override )
override = mode.name
switch( override )
if("mercenary") //Nuke Ops successfully bombed the station
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_nukewin"
if("AI malfunction") //Malf (screen,explosion,summary)
flick("intro_malf",cinematic)
sleep(76)
flick("station_explode_fade_red",cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_malf"
if("blob") //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_selfdes"
else //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red", cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_selfdes"
for(var/mob/living/M in GLOB.living_mob_list)
if(M.loc.z in using_map.station_levels)
M.death()//No mercy
//If its actually the end of the round, wait for it to end.
//Otherwise if its a verb it will continue on afterwards.
sleep(300)
if(cinematic) qdel(cinematic) //end the cinematic
if(temp_buckle) qdel(temp_buckle) //release everybody
return
/datum/controller/subsystem/ticker/proc/create_characters()
for(var/mob/new_player/player in GLOB.player_list)
@@ -431,12 +369,13 @@ var/global/datum/controller/subsystem/ticker/ticker
// If they're a carbon, they can get manifested
if(J?.mob_type & JOB_CARBON)
GLOB.data_core.manifest_inject(new_char)
CHECK_TICK
/datum/controller/subsystem/ticker/proc/collect_minds()
for(var/mob/living/player in GLOB.player_list)
if(player.mind)
minds += player.mind
CHECK_TICK
/datum/controller/subsystem/ticker/proc/equip_characters()
var/captainless=1
@@ -457,147 +396,107 @@ var/global/datum/controller/subsystem/ticker/ticker
if(imp.handle_implant(player,player.zone_sel.selecting))
imp.post_implant(player)
//VOREStation Addition End
CHECK_TICK
if(captainless)
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M))
to_chat(M, span_notice("Site Management is not forced on anyone."))
///Whether the game has started, including roundend.
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
/datum/controller/subsystem/ticker/proc/declare_completion()
to_world(span_filter_system("<br><br><br><H1>A round of [mode.name] has ended!</H1>"))
for(var/mob/Player in GLOB.player_list)
if(Player.mind && !isnewplayer(Player))
if(Player.stat != DEAD)
var/turf/playerTurf = get_turf(Player)
if(emergency_shuttle.departed && emergency_shuttle.evac)
if(isNotAdminLevel(playerTurf.z))
to_chat(Player, span_filter_system(span_blue(span_bold("You survived the round, but remained on [station_name()] as [Player.real_name]."))))
else
to_chat(Player, span_filter_system(span_green(span_bold("You managed to survive the events on [station_name()] as [Player.real_name]."))))
else if(isAdminLevel(playerTurf.z))
to_chat(Player, span_filter_system(span_green(span_bold("You successfully underwent crew transfer after events on [station_name()] as [Player.real_name]."))))
else if(issilicon(Player))
to_chat(Player, span_filter_system(span_green(span_bold("You remain operational after the events on [station_name()] as [Player.real_name]."))))
else
to_chat(Player, span_filter_system(span_blue(span_bold("You missed the crew transfer after the events on [station_name()] as [Player.real_name]."))))
else
if(isobserver(Player))
var/mob/observer/dead/O = Player
if(!O.started_as_observer)
to_chat(Player, span_filter_system(span_red(span_bold("You did not survive the events on [station_name()]..."))))
else
to_chat(Player, span_filter_system(span_red(span_bold("You did not survive the events on [station_name()]..."))))
to_world(span_filter_system("<br>"))
///Whether the game is currently in progress, excluding roundend
/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
return current_state == GAME_STATE_PLAYING
for (var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list)
if (aiPlayer.stat != 2)
to_world(span_filter_system(span_bold("[aiPlayer.name]'s laws at the end of the round were:"))) // VOREStation edit
else
to_world(span_filter_system(span_bold("[aiPlayer.name]'s laws when it was deactivated were:"))) // VOREStation edit
aiPlayer.show_laws(1)
if (aiPlayer.connected_robots.len)
var/robolist = span_bold("The AI's loyal minions were:") + " "
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
robolist += "[robo.name][robo.stat?" (Deactivated), ":", "]" // VOREStation edit
to_world(span_filter_system("[robolist]"))
var/dronecount = 0
for (var/mob/living/silicon/robot/robo in GLOB.mob_list)
if(istype(robo, /mob/living/silicon/robot/platform))
var/mob/living/silicon/robot/platform/tank = robo
if(!tank.has_had_player)
continue
if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm))
dronecount++
continue
if (!robo.connected_ai)
if (robo.stat != 2)
to_world(span_filter_system(span_bold("[robo.name] survived as an AI-less stationbound synthetic! Its laws were:"))) // VOREStation edit
else
to_world(span_filter_system(span_bold("[robo.name] was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:"))) // VOREStation edit
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
robo.laws.show_laws(world)
if(dronecount)
to_world(span_filter_system(span_bold("There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] at the end of this round.")))
mode.declare_completion()//To declare normal completion.
//Ask the event manager to print round end information
SSevents.RoundEnd()
//Print a list of antagonists to the server log
var/list/total_antagonists = list()
//Look into all mobs in world, dead or alive
for(var/datum/mind/Mind in minds)
var/temprole = Mind.special_role
if(temprole) //if they are an antagonist of some sort.
if(temprole in total_antagonists) //If the role exists already, add the name to it
total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
else
total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
//Now print them all into the log!
log_game("Antagonists at round end were...")
for(var/i in total_antagonists)
log_game("[i]s[total_antagonists[i]].")
SSdbcore.SetRoundEnd()
return 1
/datum/controller/subsystem/ticker/stat_entry(msg)
switch(current_state)
if(GAME_STATE_INIT)
..()
if(GAME_STATE_PREGAME) // RUNLEVEL_LOBBY
msg = "START [GLOB.round_progressing ? "[round(pregame_timeleft)]s" : "(PAUSED)"]"
if(GAME_STATE_SETTING_UP) // RUNLEVEL_SETUP
msg = "SETUP"
if(GAME_STATE_PLAYING) // RUNLEVEL_GAME
msg = "GAME"
if(GAME_STATE_FINISHED) // RUNLEVEL_POSTGAME
switch(end_game_state)
if(END_GAME_MODE_FINISHED)
msg = "MODE OVER, WAITING"
if(END_GAME_READY_TO_END)
msg = "ENDGAME PROCESSING"
if(END_GAME_ENDING)
msg = "END IN [round(restart_timeleft/10)]s"
if(END_GAME_DELAYED)
msg = "END PAUSED"
else
msg = "ENDGAME ERROR:[end_game_state]"
return ..()
///Whether the game is currently in progress, excluding roundend
/datum/controller/subsystem/ticker/proc/IsPostgame()
return current_state == GAME_STATE_FINISHED
/datum/controller/subsystem/ticker/Recover()
flags |= SS_NO_INIT // Don't initialize again
current_state = SSticker.current_state
mode = SSticker.mode
pregame_timeleft = SSticker.pregame_timeleft
force_ending = SSticker.force_ending
end_game_state = SSticker.end_game_state
delay_end = SSticker.delay_end
restart_timeleft = SSticker.restart_timeleft
login_music = SSticker.login_music
round_end_sound = SSticker.round_end_sound
minds = SSticker.minds
random_players = SSticker.random_players
delay_end = SSticker.delay_end
tipped = SSticker.tipped
selected_tip = SSticker.selected_tip
timeLeft = SSticker.timeLeft
totalPlayers = SSticker.totalPlayers
totalPlayersReady = SSticker.totalPlayersReady
total_admins_ready = SSticker.total_admins_ready
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
round_start_time = SSticker.round_start_time
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
//These callbacks will fire after roundstart key transfer
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
if(!HasRoundStarted())
LAZYADD(round_start_events, cb)
else
cb.InvokeAsync()
if (Master) //Set Masters run level if it exists
switch (current_state)
if(GAME_STATE_SETTING_UP)
Master.SetRunLevel(RUNLEVEL_SETUP)
if(GAME_STATE_PLAYING)
Master.SetRunLevel(RUNLEVEL_GAME)
if(GAME_STATE_FINISHED)
Master.SetRunLevel(RUNLEVEL_POSTGAME)
/datum/controller/subsystem/ticker/proc/Reboot(reason, end_string, delay)
set waitfor = FALSE
if(usr && !check_rights(R_SERVER, TRUE))
return
if(!delay)
delay = CONFIG_GET(number/round_end_countdown) * 10
var/skip_delay = check_rights()
if(delay_end && !skip_delay)
to_chat(world, span_boldannounce("An admin has delayed the round end."))
return
to_chat(world, span_boldannounce("Rebooting World in [DisplayTimeText(delay)]. [reason]"))
// We dont have those
//var/statspage = CONFIG_GET(string/roundstatsurl)
//var/gamelogloc = CONFIG_GET(string/gamelogurl)
//if(statspage)
// to_chat(world, span_info("Round statistics and logs can be viewed <a href=\"[statspage][GLOB.round_id]\">at this website!</a>"))
//else if(gamelogloc)
// to_chat(world, span_info("Round logs can be located <a href=\"[gamelogloc]\">at this website!</a>"))
var/start_wait = world.time
UNTIL(round_end_sound_sent || (world.time - start_wait) > (delay * 2)) //don't wait forever
reboot_timer = addtimer(CALLBACK(src, PROC_REF(reboot_callback), reason, end_string), delay - (world.time - start_wait), TIMER_STOPPABLE)
/datum/controller/subsystem/ticker/proc/reboot_callback(reason, end_string)
if(end_string)
end_state = end_string
log_game(span_boldannounce("Rebooting World. [reason]"))
world.Reboot()
/**
* Deletes the current reboot timer and nulls the var
*
* Arguments:
* * user - the user that cancelled the reboot, may be null
*/
/datum/controller/subsystem/ticker/proc/cancel_reboot(mob/user)
if(!reboot_timer)
to_chat(user, span_warning("There is no pending reboot!"))
return FALSE
to_chat(world, span_boldannounce("An admin has delayed the round end."))
deltimer(reboot_timer)
reboot_timer = null
return TRUE
+3 -1
View File
@@ -17,9 +17,11 @@
SUBSYSTEM_DEF(timer)
name = "Timer"
wait = 1 // SS_TICKER subsystem, so wait is in ticks
init_order = INIT_ORDER_TIMER
priority = FIRE_PRIORITY_TIMER
flags = SS_TICKER|SS_NO_INIT
dependencies = list(
/datum/controller/subsystem/machines
)
/// Queue used for storing timers that do not fit into the current buckets
var/list/datum/timedevent/second_queue = list()
+3 -1
View File
@@ -12,7 +12,9 @@ SUBSYSTEM_DEF(transcore)
wait = 3 MINUTES
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME
init_order = INIT_ORDER_TRANSCORE
dependencies = list(
/datum/controller/subsystem/mapping
)
// THINGS
var/overdue_time = 6 MINUTES // Has to be a multiple of wait var, or else will just round up anyway.
+2 -2
View File
@@ -78,7 +78,7 @@ SUBSYSTEM_DEF(verb_manager)
//we want unit tests to be able to directly call verbs that attempt to queue, and since unit tests should test internal behavior, we want the queue
//to happen as if it was actually from player input if its called on a mob.
#ifdef UNIT_TEST
#ifdef UNIT_TESTS
if(QDELETED(usr) && ismob(incoming_callback.object))
incoming_callback.user = WEAKREF(incoming_callback.object)
var/datum/callback/new_us = CALLBACK(arglist(list(GLOBAL_PROC, GLOBAL_PROC_REF(_queue_verb)) + args.Copy()))
@@ -120,7 +120,7 @@ SUBSYSTEM_DEF(verb_manager)
return TRUE
if((usr.client?.holder && !can_queue_admin_verbs) \
|| (!subsystem_initialized && !(flags & SS_NO_INIT)) \
|| (!initialized && !(flags & SS_NO_INIT)) \
|| FOR_ADMINS_IF_VERBS_FUCKED_immediately_execute_all_verbs \
|| !(runlevels & Master.current_runlevel))
return FALSE
@@ -2,7 +2,6 @@ SUBSYSTEM_DEF(vis_overlays)
name = "Vis contents overlays"
wait = 1 MINUTES
priority = FIRE_PRIORITY_VIS
init_order = INIT_ORDER_VIS
var/list/vis_overlay_cache
var/list/currentrun
+5 -5
View File
@@ -24,7 +24,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/fire(resumed)
if(mode)
time_remaining = round((started_time + duration - world.time)/10)
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
if(mode == VOTE_GAMEMODE && SSticker.current_state >= GAME_STATE_SETTING_UP)
to_chat(world, span_bold("Gamemode vote aborted: Game has already started."))
reset()
return
@@ -151,7 +151,7 @@ SUBSYSTEM_DEF(vote)
if(VOTE_GAMEMODE)
if(GLOB.master_mode != .)
world.save_mode(.)
if(ticker && ticker.mode)
if(SSticker && SSticker.mode)
restart = 1
else
GLOB.master_mode = .
@@ -203,7 +203,7 @@ SUBSYSTEM_DEF(vote)
if(VOTE_RESTART)
choices.Add("Restart Round", "Continue Playing")
if(VOTE_GAMEMODE)
if(ticker.current_state >= GAME_STATE_SETTING_UP)
if(SSticker.current_state >= GAME_STATE_SETTING_UP)
return 0
choices.Add(config.votable_modes)
for(var/F in choices)
@@ -218,13 +218,13 @@ SUBSYSTEM_DEF(vote)
if(get_security_level() == "red" || get_security_level() == "delta")
to_chat(initiator_key, "The current alert status is too high to call for a crew transfer!")
return 0
if(ticker.current_state <= GAME_STATE_SETTING_UP)
if(SSticker.current_state <= GAME_STATE_SETTING_UP)
to_chat(initiator_key, "The crew transfer button has been disabled!")
return 0
question = "Your PDA beeps with a message from Central. Would you like an additional hour to finish ongoing projects?" //VOREStation Edit
choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit
if(VOTE_ADD_ANTAGONIST)
if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP)
if(!config.allow_extra_antags || SSticker.current_state >= GAME_STATE_SETTING_UP)
return 0
for(var/antag_type in all_antag_types)
var/datum/antagonist/antag = all_antag_types[antag_type]
+4 -2
View File
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(webhooks)
name = "Webhooks"
init_order = INIT_ORDER_WEBHOOKS
dependencies = list(
/datum/controller/subsystem/server_maint,
)
flags = SS_NO_FIRE
var/list/webhook_decls = list()
@@ -63,7 +65,7 @@ SUBSYSTEM_DEF(webhooks)
if(!check_rights_for(src, R_HOLDER))
return
if(!SSwebhooks.subsystem_initialized)
if(!SSwebhooks.initialized)
to_chat(usr, span_warning("Let the webhook subsystem initialize before trying to reload it."))
return
+1 -1
View File
@@ -14,8 +14,8 @@
//
SUBSYSTEM_DEF(xenoarch)
name = "Xenoarch"
init_order = INIT_ORDER_XENOARCH
flags = SS_NO_FIRE
init_stage = INITSTAGE_LAST
var/list/artifact_spawning_turfs = list()
var/list/digsite_spawning_turfs = list()