12/21 modernizations from TG live (#103)

* sync (#3)

* shuttle auto call

* Merge /vore into /master (#39)

* progress

* Compile errors fixed

No idea if it's test worthy tho as conflicts with race overhaul and
narky removal.

* Update admins.txt

* efforts continue

Fuck grab code, seriously

* grab code is cancer

* Execute the Narkism

Do not hesitate.

Show no mercy.

* holy shit grab code is awful

* have I bitched about grab code

My bitching, let me show you it

* código de agarre es una mierda

No really it is

* yeah I don't even know anymore.

* Lolnope. Fuck grab code

* I'm not even sure what to fix anymore

* Self eating is not an acceptable fate

* Taste the void, son.

* My code doesn't pass it's own sanity check.

Maybe it's a sign of things to come.

* uncommented and notes

* It Works and I Don't Know Why (#38)

* shuttle auto call

* it works and I don't know why

* Subsystem 12/21

Most Recent TG subsystem folder

* globalvars 12/21

Tossed out the flavor_misc and parallax files

* Onclick 12/21

as well as .dme updates

* _defines 12/21

ommited old _MC.dm

* _HELPERS 12/21

Preserved snowflake placement of furry sprites

* _defeines/genetics

reapplied narkism holdover for snowflake races.

* Oops forgot mutant colors

* modules porting 12/21 + Sounds/icons

Admin, Client and most of mob life files ommitted

* enviroment file

* Admin optimizations

ahelp log system kept

* Mob ports 12/21

Flavor text preserved

* datums ported 12/21

* Game ported 12/21

* batch of duplicate fixes/dogborg work

Dogborgs need to be modernized to refractored borg standards.

* moar fixes

* Maps and futher compile fixes
This commit is contained in:
Poojawa
2016-12-22 03:57:55 -06:00
committed by GitHub
parent f5e143a452
commit cf59ac1c3d
2215 changed files with 707445 additions and 87041 deletions
+94 -9
View File
@@ -1,4 +1,4 @@
f//Configuraton defines //TODO: Move all yes/no switches into bitflags
//Configuraton defines //TODO: Move all yes/no switches into bitflags
//Used by jobs_have_maint_access
#define ASSISTANTS_HAVE_MAINT_ACCESS 1
@@ -18,6 +18,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/server_suffix = 0 // generate numeric suffix based on server port
var/lobby_countdown = 120 // In between round countdown.
var/round_end_countdown = 25 // Post round murder death kill countdown
var/hub = 0
var/log_ooc = 0 // log OOC channel
var/log_access = 0 // log login/logout
@@ -33,6 +34,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/log_adminchat = 0 // log admin chat messages
var/log_pda = 0 // log pda messages
var/log_hrefs = 0 // log all links clicked in-game. Could be used for debugging and tracking down exploits
var/log_twitter = 0 // log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
var/log_world_topic = 0 // log all world.Topic() calls
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
@@ -70,6 +72,11 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/forbid_singulo_possession = 0
var/useircbot = 0
var/check_randomizer = 0
var/allow_panic_bunker_bounce = 0 //Send new players somewhere else
var/panic_server_name = "somewhere else"
//IP Intel vars
var/ipintel_email
var/ipintel_rating_bad = 1
@@ -80,6 +87,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_account_age_for_jobs = 0 //Uses the time they made the account for the job restriction stuff. New player joining alerts should be unaffected.
var/see_own_notes = 0 //Can players see their own admin notes (read-only)? Config option in config.txt
//Population cap vars
@@ -96,6 +104,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/list/modes = list() // allowed modes
var/list/votable_modes = list() // votable modes
var/list/probabilities = list() // relative probability of each mode
var/list/min_pop = list() // overrides for acceptible player counts in a mode
var/list/max_pop = list()
var/humans_need_surnames = 0
var/allow_ai = 0 // allow ai job
@@ -137,9 +147,6 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/alert_desc_red_downto = "The station's destruction has been averted. There is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised."
var/alert_desc_delta = "Destruction of the station is imminent. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill."
var/health_threshold_crit = 0
var/health_threshold_dead = -100
var/revival_pod_plants = 1
var/revival_cloning = 1
var/revival_brain_life = -1
@@ -167,6 +174,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/silent_ai = 0
var/silent_borg = 0
var/damage_multiplier = 1 //Modifier for damage to all mobs. Impacts healing as well.
var/allowwebclient = 0
var/webclientmembersonly = 0
@@ -174,6 +183,9 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/default_laws = 0 //Controls what laws the AI spawns with.
var/silicon_max_law_amount = 12
var/list/lawids = list()
var/list/law_weights = list()
var/assistant_cap = -1
@@ -182,6 +194,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/grey_assistants = 0
var/lavaland_budget = 60
var/space_budget = 16
var/aggressive_changelog = 0
@@ -213,6 +226,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
var/client_error_message = "Your version of byond is too old, may have issues, and is blocked from accessing this server."
var/cross_name = "Other server"
var/showircname = 0
/datum/configuration/New()
var/list/L = subtypesof(/datum/game_mode)
@@ -260,12 +274,16 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
if(type == "config")
switch(name)
if("hub")
config.hub = 1
if("admin_legacy_system")
config.admin_legacy_system = 1
if("ban_legacy_system")
config.ban_legacy_system = 1
if("use_age_restriction_for_jobs")
config.use_age_restriction_for_jobs = 1
if("use_account_age_for_jobs")
config.use_account_age_for_jobs = 1
if("lobby_countdown")
config.lobby_countdown = text2num(value)
if("round_end_countdown")
@@ -298,6 +316,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
config.log_pda = 1
if("log_hrefs")
config.log_hrefs = 1
if("log_twitter")
config.log_twitter = 1
if("log_world_topic")
config.log_world_topic = 1
if("allow_admin_ooccolor")
@@ -341,7 +361,7 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
if("guest_ban")
guests_allowed = 0
if("usewhitelist")
config.usewhitelist = 1
config.usewhitelist = TRUE
if("allow_metadata")
config.allow_Metadata = 1
if("kick_inactive")
@@ -376,6 +396,18 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
global.cross_allowed = 1
if("cross_comms_name")
cross_name = value
if("panic_server_name")
panic_server_name = value
if("panic_server_address")
global.panic_address = value
if(value != "byond:\\address:port")
allow_panic_bunker_bounce = 1
if("medal_hub_address")
global.medal_hub = value
if("medal_hub_password")
global.medal_pass = value
if("show_irc_name")
config.showircname = 1
if("see_own_notes")
config.see_own_notes = 1
if("soft_popcap")
@@ -396,6 +428,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
config.notify_new_player_age = text2num(value)
if("irc_first_connection_alert")
config.irc_first_connection_alert = 1
if("check_randomizer")
config.check_randomizer = 1
if("ipintel_email")
if (value != "ch@nge.me")
config.ipintel_email = value
@@ -448,10 +482,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
else if(type == "game_options")
switch(name)
if("health_threshold_crit")
config.health_threshold_crit = text2num(value)
if("health_threshold_dead")
config.health_threshold_dead = text2num(value)
if("damage_multiplier")
config.damage_multiplier = text2num(value)
if("revival_pod_plants")
config.revival_pod_plants = text2num(value)
if("revival_cloning")
@@ -520,6 +552,34 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
config.midround_antag_time_check = text2num(value)
if("midround_antag_life_check")
config.midround_antag_life_check = text2num(value)
if("min_pop")
var/pop_pos = findtext(value, " ")
var/mode_name = null
var/mode_value = null
if(pop_pos)
mode_name = lowertext(copytext(value, 1, pop_pos))
mode_value = copytext(value, pop_pos + 1)
if(mode_name in config.modes)
config.min_pop[mode_name] = text2num(mode_value)
else
diary << "Unknown minimum population configuration definition: [mode_name]."
else
diary << "Incorrect minimum population configuration definition: [mode_name] [mode_value]."
if("max_pop")
var/pop_pos = findtext(value, " ")
var/mode_name = null
var/mode_value = null
if(pop_pos)
mode_name = lowertext(copytext(value, 1, pop_pos))
mode_value = copytext(value, pop_pos + 1)
if(mode_name in config.modes)
config.max_pop[mode_name] = text2num(mode_value)
else
diary << "Unknown maximum population configuration definition: [mode_name]."
else
diary << "Incorrect maximum population configuration definition: [mode_name] [mode_value]."
if("shuttle_refuel_delay")
config.shuttle_refuel_delay = text2num(value)
if("show_game_type_odds")
@@ -589,6 +649,19 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
config.sandbox_autoclose = 1
if("default_laws")
config.default_laws = text2num(value)
if("random_laws")
var/law_id = lowertext(value)
lawids += law_id
if("law_weight")
// Value is in the form "LAWID,NUMBER"
var/list/L = splittext(value, ",")
if(L.len != 2)
diary << "Invalid LAW_WEIGHT: " + t
continue
var/lawid = L[1]
var/weight = text2num(L[2])
law_weights[lawid] = weight
if("silicon_max_law_amount")
config.silicon_max_law_amount = text2num(value)
if("join_with_mutant_race")
@@ -609,6 +682,8 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
config.grey_assistants = 1
if("lavaland_budget")
config.lavaland_budget = text2num(value)
if("space_budget")
config.space_budget = text2num(value)
if("no_summon_guns")
config.no_summon_guns = 1
if("no_summon_magic")
@@ -753,6 +828,10 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.can_start())
runnable_modes[M] = probabilities[M.config_tag]
//world << "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]"
@@ -768,7 +847,13 @@ f//Configuraton defines //TODO: Move all yes/no switches into bitflags
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.required_players <= crew)
if(M.maximum_players >= 0 && M.maximum_players < crew)
continue
runnable_modes[M] = probabilities[M.config_tag]
return runnable_modes
+10 -2
View File
@@ -35,6 +35,8 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
var/init_time
var/tickdrift = 0
var/sleep_delta
var/make_runtime = 0
// Has round started? (So we know what subsystems to run)
@@ -65,6 +67,11 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
// Tell qdel() to Del() this object.
return QDEL_HINT_HARDDEL_NOW
/datum/controller/master/proc/Shutdown()
processing = FALSE
for(var/datum/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()
@@ -165,8 +172,8 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
message_admins("MC crashed or runtimed, restarting")
var/rtn2 = Recreate_MC()
if (rtn2 <= 0)
log_game("Failed to recreate MC (Error code: [rtn2]), its up to the failsafe now")
message_admins("Failed to recreate MC (Error code: [rtn2]), its up to the failsafe now")
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.
@@ -280,6 +287,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
sleep(world.tick_lag * (processing + sleep_delta))
+14 -5
View File
@@ -37,6 +37,10 @@
// Used to initialize the subsystem BEFORE the map has loaded
/datum/subsystem/New()
//cleanup actions
/datum/subsystem/proc/Shutdown()
return
//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!
@@ -140,7 +144,7 @@
statclick = new/obj/effect/statclick/debug("Initializing...", src)
if(can_fire)
msg = "[round(cost*ticks,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
msg = "[round(cost,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
@@ -157,7 +161,12 @@
/datum/subsystem/proc/Recover()
//this is so the subsystem doesn't rapid fire to make up missed ticks causing more lag
/datum/subsystem/on_varedit(edited_var)
if (edited_var == "can_fire" && can_fire)
next_fire = world.time + wait
..()
/datum/subsystem/vv_edit_var(var_name, var_value)
switch (var_name)
if ("can_fire")
if (var_value)
next_fire = world.time + wait
if ("queued_priority") //editing this breaks things.
return 0
. = ..()
+42
View File
@@ -0,0 +1,42 @@
var/datum/subsystem/acid/SSacid
/datum/subsystem/acid
name = "Acid"
priority = 40
flags = SS_NO_INIT|SS_BACKGROUND
var/list/currentrun = list()
var/list/processing = list()
/datum/subsystem/acid/New()
NEW_SS_GLOBAL(SSacid)
/datum/subsystem/acid/stat_entry()
..("P:[processing.len]")
/datum/subsystem/acid/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while (currentrun.len)
var/obj/O = currentrun[currentrun.len]
currentrun.len--
if (!O || qdeleted(O))
processing -= O
if (MC_TICK_CHECK)
return
continue
if(O.acid_level && O.acid_processing())
else
O.overlays -= acid_overlay
O.priority_overlays -= acid_overlay
processing -= O
if (MC_TICK_CHECK)
return
+84
View File
@@ -0,0 +1,84 @@
var/datum/subsystem/augury/SSaugury
/datum/subsystem/augury
name = "Augury"
flags = SS_NO_INIT
var/list/watchers = list()
var/list/doombringers = list()
var/list/observers_given_action = list()
/datum/subsystem/augury/New()
NEW_SS_GLOBAL(SSaugury)
/datum/subsystem/augury/stat_entry(msg)
..("W:[watchers.len]|D:[doombringers.len]")
/datum/subsystem/augury/proc/register_doom(atom/A, severity)
doombringers[A] = severity
/datum/subsystem/augury/fire()
var/biggest_doom = null
var/biggest_threat = null
for(var/d in doombringers)
if(!d || qdeleted(d))
doombringers -= d
continue
var/threat = doombringers[d]
if((biggest_threat == null) || (biggest_threat < threat))
biggest_doom = d
biggest_threat = threat
if(doombringers.len)
for(var/i in player_list)
if(isobserver(i) && (!(observers_given_action[i])))
var/datum/action/innate/augury/A = new
A.Grant(i)
observers_given_action[i] = TRUE
else
for(var/i in observers_given_action)
if(observers_given_action[i] && isobserver(i))
var/mob/dead/observer/O = i
for(var/datum/action/innate/augury/A in O.actions)
qdel(A)
observers_given_action -= i
for(var/w in watchers)
if(!w)
watchers -= w
continue
var/mob/dead/observer/O = w
if(biggest_doom && (!O.orbiting || O.orbiting.orbiting != biggest_doom))
O.ManualFollow(biggest_doom)
/datum/action/innate/augury
name = "Auto Follow Debris"
icon_icon = 'icons/obj/meteor.dmi'
button_icon_state = "flaming"
background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND
/datum/action/innate/augury/Destroy()
if(owner)
SSaugury.watchers -= owner
return ..()
/datum/action/innate/augury/Activate()
SSaugury.watchers += owner
owner << "<span class='notice'>You are now auto-following debris.</span>"
active = TRUE
UpdateButtonIcon()
/datum/action/innate/augury/Deactivate()
SSaugury.watchers -= owner
owner << "<span class='notice'>You are no longer auto-following debris.</span>"
active = FALSE
UpdateButtonIcon()
/datum/action/innate/augury/UpdateButtonIcon()
..()
if(active)
button.icon_state = "template_active"
else
button.icon_state = "template"
+2 -2
View File
@@ -110,8 +110,8 @@ var/datum/subsystem/events/SSevent
/datum/round_event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in!
var/list/safe_areas = list(
/area/turret_protected/ai,
/area/turret_protected/ai_upload,
/area/ai_monitored/turret_protected/ai,
/area/ai_monitored/turret_protected/ai_upload,
/area/engine,
/area/solar,
/area/holodeck,
-33
View File
@@ -1,33 +0,0 @@
var/datum/subsystem/fastprocess/SSfastprocess
/datum/subsystem/fastprocess
name = "Fast Process"
priority = 25
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 2
var/list/processing = list()
var/list/currentrun = list()
/datum/subsystem/fastprocess/New()
NEW_SS_GLOBAL(SSfastprocess)
/datum/subsystem/fastprocess/stat_entry()
..("FP:[processing.len]")
/datum/subsystem/fastprocess/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/thing = currentrun[currentrun.len]
currentrun.len--
if(thing)
thing.process(wait)
else
SSfastprocess.processing -= thing
if (MC_TICK_CHECK)
return
@@ -0,0 +1,42 @@
var/datum/subsystem/fire_burning/SSfire_burning
/datum/subsystem/fire_burning
name = "Fire Burning"
priority = 40
flags = SS_NO_INIT|SS_BACKGROUND
var/list/currentrun = list()
var/list/processing = list()
/datum/subsystem/fire_burning/New()
NEW_SS_GLOBAL(SSfire_burning)
/datum/subsystem/fire_burning/stat_entry()
..("P:[processing.len]")
/datum/subsystem/fire_burning/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/O = currentrun[currentrun.len]
currentrun.len--
if (!O || qdeleted(O))
processing -= O
if (MC_TICK_CHECK)
return
continue
if(O.resistance_flags & ON_FIRE)
O.take_damage(20, BURN, "fire", 0)
else
processing -= O
if (MC_TICK_CHECK)
return
+28 -18
View File
@@ -70,7 +70,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
Queue(ref)
tobequeued.Cut(1, 2)
/datum/subsystem/garbage_collector/proc/HandleQueue(time_to_stop)
/datum/subsystem/garbage_collector/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()
@@ -121,7 +121,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
++totalgcs
/datum/subsystem/garbage_collector/proc/QueueForQueuing(datum/A)
if (istype(A) && isnull(A.gc_destroyed))
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_QUEUING
@@ -142,7 +142,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
queue[refid] = gctime
/datum/subsystem/garbage_collector/proc/HardQueue(datum/A)
if (istype(A) && isnull(A.gc_destroyed))
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
@@ -158,11 +158,12 @@ var/datum/subsystem/garbage_collector/SSgarbage
if(!D)
return
#ifdef TESTING
SSgarbage.qdel_list += "[A.type]"
SSgarbage.qdel_list += "[D.type]"
#endif
if(!istype(D))
del(D)
else if(isnull(D.gc_destroyed))
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
if(!D)
return
@@ -171,6 +172,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
SSgarbage.QueueForQueuing(D)
if (QDEL_HINT_LETMELIVE, QDEL_HINT_IWILLGC) //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
@@ -191,13 +193,15 @@ var/datum/subsystem/garbage_collector/SSgarbage
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
A.find_references()
D.find_references()
#endif
else
if(!SSgarbage.noqdelhint["[D.type]"])
SSgarbage.noqdelhint["[D.type]"] = "[D.type]"
testing("WARNING: [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")
// Returns 1 if the object has been queued for deletion.
/proc/qdeleted(datum/D)
@@ -207,6 +211,14 @@ var/datum/subsystem/garbage_collector/SSgarbage
return TRUE
return FALSE
// Returns true if the object's destroy has been called (set just before it is called)
/proc/qdestroying(datum/D)
if(!istype(D))
return FALSE
if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
return TRUE
return FALSE
// 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.
@@ -217,15 +229,18 @@ var/datum/subsystem/garbage_collector/SSgarbage
/datum/var/gc_destroyed //Time when this object was destroyed.
#ifdef TESTING
/client/var/running_find_references
/datum/var/running_find_references
/datum/verb/find_references()
/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)
set background = 1
running_find_references = type
if(usr && usr.client)
if(usr.client.running_find_references)
@@ -237,9 +252,10 @@ var/datum/subsystem/garbage_collector/SSgarbage
SSgarbage.next_fire = world.time + world.tick_lag
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")
running_find_references = null
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
@@ -248,13 +264,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
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
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
for(var/datum/thing in things)
for(var/datum/thing in world)
if(usr && usr.client && !usr.client.running_find_references) return
for(var/varname in thing.vars)
var/variable = thing.vars[varname]
@@ -290,7 +300,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
qdel(src)
if(!running_find_references)
find_references()
find_references(TRUE)
/client/verb/show_qdeleted()
set category = "Debug"
+8 -4
View File
@@ -16,7 +16,7 @@ var/datum/subsystem/icon_smooth/SSicon_smooth
while(smooth_queue.len)
var/atom/A = smooth_queue[smooth_queue.len]
smooth_queue.len--
ss_smooth_icon(A)
smooth_icon(A)
if (MC_TICK_CHECK)
return
if (!smooth_queue.len)
@@ -25,8 +25,12 @@ var/datum/subsystem/icon_smooth/SSicon_smooth
/datum/subsystem/icon_smooth/Initialize()
smooth_zlevel(1,TRUE)
smooth_zlevel(2,TRUE)
for(var/V in smooth_queue)
var/queue = smooth_queue
smooth_queue = list()
for(var/V in queue)
var/atom/A = V
if(A.z == 1 || A.z == 2)
smooth_queue -= A
if(!A || A.z <= 2)
continue
smooth_icon(A)
..()
+14 -6
View File
@@ -328,7 +328,7 @@ var/datum/subsystem/job/SSjob
for(var/mob/new_player/player in unassigned)
if(PopcapReached())
RejectPlayer(player)
else if(player.client.prefs.userandomjob)
else if(player.client.prefs.joblessrole == BERANDOMJOB)
GiveRandomJob(player)
Debug("DO, Standard Check end")
@@ -339,8 +339,15 @@ var/datum/subsystem/job/SSjob
for(var/mob/new_player/player in unassigned)
if(PopcapReached())
RejectPlayer(player)
Debug("AC2 Assistant located, Player: [player]")
AssignRole(player, "Assistant")
if(player.client.prefs.joblessrole == BEASSISTANT)
Debug("AC2 Assistant located, Player: [player]")
AssignRole(player, "Assistant")
else // For those who don't want to play if their preference were filled, back you go.
RejectPlayer(player)
for(var/mob/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
GiveRandomJob(player)
return 1
//Gives the player the stuff he should have with his rank
@@ -375,7 +382,7 @@ var/datum/subsystem/job/SSjob
if(clear)
S = T
continue
if(istype(S, /obj/effect/landmark) && istype(S.loc, /turf))
if(istype(S, /obj/effect/landmark) && isturf(S.loc))
H.loc = S.loc
if(H.mind)
@@ -394,7 +401,7 @@ var/datum/subsystem/job/SSjob
H << "<b>You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.</b>"
if(config.minimal_access_threshold)
H << "<FONT color='blue'><B>As this station was initially staffed with a [config.jobs_have_minimal_access ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card.</B></font>"
return 1
return H
/datum/subsystem/job/proc/setup_officer_positions()
@@ -470,7 +477,8 @@ var/datum/subsystem/job/SSjob
/datum/subsystem/job/proc/RejectPlayer(mob/new_player/player)
if(player.mind && player.mind.special_role)
return
Debug("Popcap overflow Check observer located, Player: [player]")
if(PopcapReached())
Debug("Popcap overflow Check observer located, Player: [player]")
player << "<b>You have failed to qualify for any job you desired.</b>"
unassigned -= player
player.ready = 0
+94 -43
View File
@@ -1,65 +1,116 @@
var/datum/subsystem/lighting/SSlighting
/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.
#define SSLIGHTING_LIGHTS 1
#define SSLIGHTING_TURFS 2
/datum/subsystem/lighting
name = "lighting"
name = "Lighting"
init_order = 1
wait = 2
display_order = 5
priority = 40
wait = 1
flags = SS_POST_FIRE_TIMING
priority = 40
display_order = 5
var/list/changed_lights = list() //list of all datum/light_source that need updating
var/changed_lights_workload = 0 //stats on the largest number of lights (max changed_lights.len)
var/list/changed_turfs = list() //list of all turfs which may have a different light level
var/changed_turfs_workload = 0 //stats on the largest number of turfs changed (max changed_turfs.len)
/datum/subsystem/lighting/New()
NEW_SS_GLOBAL(SSlighting)
return ..()
/datum/subsystem/lighting/Initialize()
/datum/subsystem/lighting/stat_entry()
..("L:[round(changed_lights_workload,1)]|T:[round(changed_turfs_workload,1)]")
//Workhorse of lighting. It cycles through each light that needs updating. It updates their
//effects and then processes every turf in the queue, updating their lighting object's appearance
//Any light that returns 1 in check() deletes itself
//By using queues we are ensuring we don't perform more updates than are necessary
/datum/subsystem/lighting/fire(resumed = 0)
var/ticklimit = CURRENT_TICKLIMIT
//split our tick allotment in half so we don't spend it all on lightshift checks
CURRENT_TICKLIMIT = world.tick_usage + ((ticklimit-world.tick_usage)/2)
var/list/changed_lights = src.changed_lights
if (!resumed)
changed_lights_workload = MC_AVERAGE(changed_lights_workload, changed_lights.len)
var/i = 1
while (i <= changed_lights.len)
var/datum/light_source/LS = changed_lights[i++]
LS.check()
if (MC_TICK_CHECK)
break
if (i > 1)
changed_lights.Cut(1,i)
CURRENT_TICKLIMIT = ticklimit
var/list/changed_turfs = src.changed_turfs
if (!resumed)
changed_turfs_workload = MC_AVERAGE(changed_turfs_workload, changed_turfs.len)
i = 1
while (i <= changed_turfs.len)
var/turf/T = changed_turfs[i++]
if(T.lighting_changed)
T.redraw_lighting()
if (MC_TICK_CHECK)
break
if (i > 1)
changed_turfs.Cut(1,i)
//same as above except it attempts to shift ALL turfs in the world regardless of lighting_changed status
/datum/subsystem/lighting/Initialize(timeofday)
var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))
if (config.starlight)
for(var/area/A in world)
if (A.dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT)
if (A.lighting_use_dynamic == DYNAMIC_LIGHTING_IFSTARLIGHT)
A.luminosity = 0
create_all_lighting_overlays()
for(var/thing in changed_lights)
var/datum/light_source/LS = thing
LS.check()
changed_lights.Cut()
/datum/subsystem/lighting/fire()
for(var/thing in turfs_to_init)
var/turf/T = thing
T.init_lighting()
changed_turfs.Cut()
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()
//Used to strip valid information from an existing instance and transfer it to the replacement. i.e. when a crash occurs
//It works by using spawn(-1) to transfer the data, if there is a runtime the data does not get transfered but the loop
//does not crash
/datum/subsystem/lighting/Recover()
if(!istype(SSlighting.changed_turfs))
SSlighting.changed_turfs = list()
if(!istype(SSlighting.changed_lights))
SSlighting.changed_lights = list()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
for(var/thing in SSlighting.changed_lights)
var/datum/light_source/LS = thing
spawn(-1) //so we don't crash the loop (inefficient)
LS.check()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
for(var/thing in changed_turfs)
var/turf/T = thing
if(T.lighting_changed)
spawn(-1)
T.redraw_lighting()
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
lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = list()
for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old)
O.update_overlay()
O.needs_update = 0
var/msg = "## DEBUG: [time2text(world.timeofday)] [name] subsystem restarted. Reports:\n"
for(var/varname in SSlighting.vars)
switch(varname)
if("tag","bestF","type","parent_type","vars")
continue
else
var/varval1 = SSlighting.vars[varname]
var/varval2 = vars[varname]
if(istype(varval1,/list))
varval1 = "/list([length(varval1)])"
varval2 = "/list([length(varval2)])"
msg += "\t [varname] = [varval1] -> [varval2]\n"
world.log << msg
+1 -1
View File
@@ -34,7 +34,7 @@ var/datum/subsystem/mapping/SSmapping
else
space_zlevels += i
seedRuins(space_zlevels, rand(8,16), /area/space, space_ruins_templates)
seedRuins(space_zlevels, config.space_budget, /area/space, space_ruins_templates)
// Set up Z-level transistions.
setup_map_transitions()
+52 -35
View File
@@ -13,25 +13,55 @@ var/datum/subsystem/minimap/SSminimap
NEW_SS_GLOBAL(SSminimap)
/datum/subsystem/minimap/Initialize(timeofday)
if(!config.generate_minimaps)
world << "Minimap generation disabled... Skipping"
return
var/hash = md5(file2text("_maps/[MAP_PATH]/[MAP_FILE]"))
if(hash == trim(file2text(hash_path())))
return ..()
for(var/z in z_levels)
generate(z)
register_asset("minimap_[z].png", fcopy_rsc(map_path(z)))
fdel(hash_path())
text2file(hash, hash_path())
if(config.generate_minimaps)
if(hash == trim(file2text(hash_path())))
for(var/z in z_levels) //We have these files cached, let's register them
register_asset("minimap_[z].png", fcopy_rsc(map_path(z)))
return ..()
for(var/z in z_levels)
generate(z)
register_asset("minimap_[z].png", fcopy_rsc(map_path(z)))
fdel(hash_path())
text2file(hash, hash_path())
else
world << "<span class='boldannounce'>Minimap generation disabled. Loading from cache...</span>"
var/fileloc = 0
if(check_files(0)) //Let's first check if we have maps cached in the data folder. NOTE: This will override the backup files even if this map is older.
world.log << "cache"
if(hash != trim(file2text(hash_path())))
world << "<span class='boldannounce'>Loaded cached minimap is outdated. There may be minor discrepancies in layout.</span>" //Disclaimer against players saying map is wrong.
fileloc = 0
else
if(!check_files(1))
world << "<span class='boldannounce'>Failed to load backup minimap file. Aborting.</span>" //We couldn't find something. Bail to prevent issues with null files
return
fileloc = 1 //No map image cached with the current map, and we have a backup. Let's fall back to it.
world << "<span class='boldannounce'>No cached minimaps detected. Backup files loaded.</span>"
for(var/z in z_levels)
register_asset("minimap_[z].png", fcopy_rsc(map_path(z,fileloc)))
..()
/datum/subsystem/minimap/proc/hash_path()
return "data/minimaps/[MAP_NAME].md5"
/datum/subsystem/minimap/proc/check_files(backup) // If the backup argument is true, looks in the icons folder. If false looks in the data folder.
for(var/z in z_levels)
if(!fexists(file(map_path(z,backup)))) //Let's make sure we have a file for this map
if(backup)
world.log << "Failed to find backup file for map [MAP_NAME] on zlevel [z]."
return FALSE
return TRUE
/datum/subsystem/minimap/proc/map_path(z)
return "data/minimaps/[MAP_NAME]_[z].png"
/datum/subsystem/minimap/proc/hash_path(backup)
if(backup)
return "icons/minimaps/[MAP_NAME].md5"
else
return "data/minimaps/[MAP_NAME].md5"
/datum/subsystem/minimap/proc/map_path(z,backup)
if(backup)
return "icons/minimaps/[MAP_NAME]_[z].png"
else
return "data/minimaps/[MAP_NAME]_[z].png"
/datum/subsystem/minimap/proc/send(client/client)
for(var/z in z_levels)
@@ -43,24 +73,10 @@ var/datum/subsystem/minimap/SSminimap
// Scale it up to our target size.
minimap.Scale(MINIMAP_SIZE, MINIMAP_SIZE)
var/counter = 512
// Loop over turfs and generate icons.
for(var/T in block(locate(x1, y1, z), locate(x2, y2, z)))
generate_tile(T, minimap)
//byond bug, this fixes OOM crashes by flattening and reseting the minimap icon holder every so and so tiles
counter--
if(counter <= 0)
counter = 512
var/icon/flatten = new /icon()
flatten.Insert(minimap, "", SOUTH, 1, 0)
del(minimap)
minimap = flatten
stoplag() //we have to sleep in order to get byond to clear out the proc's garbage bin
CHECK_TICK
// Create a new icon and insert the generated minimap, so that BYOND doesn't generate different directions.
var/icon/final = new /icon()
final.Insert(minimap, "", SOUTH, 1, 0)
@@ -69,9 +85,9 @@ var/datum/subsystem/minimap/SSminimap
/datum/subsystem/minimap/proc/generate_tile(turf/tile, icon/minimap)
var/icon/tile_icon
var/obj/obj
var/list/obj_icons = list()
var/list/obj_icons
// Don't use icons for space, just add objects in space if they exist.
if(istype(tile, /turf/open/space))
if(isspaceturf(tile))
obj = locate(/obj/structure/lattice/catwalk) in tile
if(obj)
tile_icon = new /icon('icons/obj/smooth_structures/catwalk.dmi', "catwalk", SOUTH)
@@ -86,18 +102,20 @@ var/datum/subsystem/minimap/SSminimap
tile_icon = new /icon('icons/obj/atmospherics/pipes/transit_tube.dmi', obj.icon_state, obj.dir)
else
tile_icon = new /icon(tile.icon, tile.icon_state, tile.dir)
obj_icons.Cut()
obj_icons = list()
obj = locate(/obj/structure) in tile
if(obj)
obj_icons += getFlatIcon(obj)
obj_icons += new /icon(obj.icon, obj.icon_state, obj.dir, 1, 0)
obj = locate(/obj/machinery) in tile
if(obj)
obj_icons += new /icon(obj.icon, obj.icon_state, obj.dir, 1, 0)
obj = locate(/obj/structure/window) in tile
if(obj)
obj_icons += new /icon('icons/obj/smooth_structures/window.dmi', "window", SOUTH)
obj = locate(/obj/structure/table) in tile
if(obj)
obj_icons += new /icon('icons/obj/smooth_structures/table.dmi', "table", SOUTH)
for(var/I in obj_icons)
var/icon/obj_icon = I
tile_icon.Blend(obj_icon, ICON_OVERLAY)
@@ -107,4 +125,3 @@ var/datum/subsystem/minimap/SSminimap
tile_icon.Scale(TILE_SIZE, TILE_SIZE)
// Add the tile to the minimap.
minimap.Blend(tile_icon, ICON_OVERLAY, ((tile.x - 1) * TILE_SIZE), ((tile.y - 1) * TILE_SIZE))
del(tile_icon)
+50
View File
@@ -0,0 +1,50 @@
var/datum/subsystem/orbit/SSorbit
/datum/subsystem/orbit
name = "Orbits"
priority = 35
wait = 2
flags = SS_NO_INIT|SS_TICKER
var/list/currentrun = list()
var/list/processing = list()
/datum/subsystem/orbit/New()
NEW_SS_GLOBAL(SSorbit)
/datum/subsystem/orbit/stat_entry()
..("P:[processing.len]")
/datum/subsystem/orbit/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while (currentrun.len)
var/datum/orbit/O = currentrun[currentrun.len]
currentrun.len--
if (!O)
processing -= O
if (MC_TICK_CHECK)
return
continue
if (!O.orbiter)
qdel(O)
if (MC_TICK_CHECK)
return
continue
if (O.lastprocess >= world.time) //we already checked recently
if (MC_TICK_CHECK)
return
continue
var/targetloc = get_turf(O.orbiting)
if (targetloc != O.lastloc || O.orbiter.loc != targetloc)
O.Check(targetloc)
if (MC_TICK_CHECK)
return
+20 -39
View File
@@ -1,14 +1,14 @@
var/datum/subsystem/pai/SSpai
var/list/obj/item/device/paicard/pai_card_list = list()
/datum/subsystem/pai
name = "pAI"
init_order = 20
flags = SS_NO_FIRE|SS_NO_INIT
var/askDelay = 600
var/const/NEVER_FOR_THIS_ROUND = -1
flags = SS_NO_INIT|SS_NO_FIRE
var/list/candidates = list()
var/list/asked = list()
var/ghost_spam = FALSE
var/spam_delay = 100
/datum/subsystem/pai/New()
NEW_SS_GLOBAL(SSpai)
@@ -29,7 +29,6 @@ var/datum/subsystem/pai/SSpai
pai.key = candidate.key
card.setPersonality(pai)
card.looking_for_personality = 0
ticker.mode.update_cult_icons_removed(card.pai.mind)
ticker.mode.update_rev_icons_removed(card.pai.mind)
@@ -76,8 +75,8 @@ var/datum/subsystem/pai/SSpai
if("submit")
if(candidate)
candidate.ready = 1
for(var/obj/item/device/paicard/p in world)
if(p.looking_for_personality == 1)
for(var/obj/item/device/paicard/p in pai_card_list)
if(!p.pai)
p.alertUpdate()
usr << browse(null, "window=paiRecruit")
return
@@ -134,8 +133,20 @@ var/datum/subsystem/pai/SSpai
M << browse(dat, "window=paiRecruit")
/datum/subsystem/pai/proc/spam_again()
ghost_spam = FALSE
/datum/subsystem/pai/proc/findPAI(obj/item/device/paicard/p, mob/user)
requestRecruits()
if(!ghost_spam)
ghost_spam = TRUE
for(var/mob/dead/observer/G in player_list)
if(!G.key || !G.client)
continue
if(!(ROLE_PAI in G.client.prefs.be_special))
continue
G << 'sound/misc/server-ready.ogg' //Alerting them to their consideration
G << "<span class='ghostalert'>Someone is requesting a pAI personality! Use the pAI button to submit yourself as one.</span>"
addtimer(src, "spam_again", spam_delay)
var/list/available = list()
for(var/datum/paiCandidate/c in SSpai.candidates)
if(c.ready)
@@ -180,36 +191,6 @@ var/datum/subsystem/pai/SSpai
user << browse(dat, "window=findPai")
/datum/subsystem/pai/proc/requestRecruits()
for(var/mob/dead/observer/O in player_list)
if(jobban_isbanned(O, ROLE_PAI))
continue
if(asked[O.ckey])
if(world.time < asked[O.ckey] + askDelay || asked[O.ckey] == NEVER_FOR_THIS_ROUND)
continue
else
asked.Remove(O.ckey)
if(O.client)
var/hasSubmitted = 0
for(var/datum/paiCandidate/c in SSpai.candidates)
if(c.key == O.key)
hasSubmitted = 1
if(!hasSubmitted && (ROLE_PAI in O.client.prefs.be_special))
question(O.client)
/datum/subsystem/pai/proc/question(client/C)
set waitfor = 0
if(!C)
return
asked[C.ckey] = world.time
var/response = tgalert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round", StealFocus=0, Timeout=askDelay)
if(!C)
return //handle logouts that happen whilst the alert is waiting for a response.
if(response == "Yes")
recruitWindow(C.mob)
else if (response == "Never for this round")
asked[C.ckey] = NEVER_FOR_THIS_ROUND
/datum/paiCandidate
var/name
var/key
+36 -6
View File
@@ -1,12 +1,42 @@
var/datum/subsystem/parallax/SSparallax
/datum/subsystem/parallax
name = "Space Parallax"
init_order = 18
flags = SS_NO_FIRE
name = "parallax"
wait = 2
flags = SS_POST_FIRE_TIMING | SS_FIRE_IN_LOBBY | SS_BACKGROUND | SS_NO_INIT
priority = 65
var/list/currentrun
/datum/subsystem/parallax/New()
NEW_SS_GLOBAL(SSparallax)
NEW_SS_GLOBAL(SSparallax)
return ..()
/datum/subsystem/parallax/Initialize()
create_global_parallax_icons()
/datum/subsystem/parallax/fire(resumed = 0)
if (!resumed)
src.currentrun = clients.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(length(currentrun))
var/client/C = currentrun[currentrun.len]
currentrun.len--
if (!C || !C.eye)
if (MC_TICK_CHECK)
return
continue
var/atom/movable/A = C.eye
if(!A)
return
for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc);
if(A != C.movingmob)
if(C.movingmob != null)
C.movingmob.client_mobs_in_contents -= C.mob
UNSETEMPTY(C.movingmob.client_mobs_in_contents)
LAZYINITLIST(A.client_mobs_in_contents)
A.client_mobs_in_contents += C.mob
C.movingmob = A
if (MC_TICK_CHECK)
return
currentrun = null
+87
View File
@@ -0,0 +1,87 @@
var/datum/subsystem/persistence/SSpersistence
/datum/subsystem/persistence
name = "Persistence"
init_order = -100
flags = SS_NO_FIRE
var/savefile/secret_satchels
var/list/satchel_blacklist = list() //this is a typecache
var/list/new_secret_satchels = list() //these are objects
var/old_secret_satchels = ""
/datum/subsystem/persistence/New()
NEW_SS_GLOBAL(SSpersistence)
/datum/subsystem/persistence/Initialize()
secret_satchels = new /savefile("data/npc_saves/SecretSatchels.sav")
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/weapon/crowbar))
secret_satchels[MAP_NAME] >> old_secret_satchels
var/list/expanded_old_satchels = list()
var/placed_satchels = 0
if(!isnull(old_secret_satchels))
expanded_old_satchels = splittext(old_secret_satchels,"#")
if(PlaceSecretSatchel(expanded_old_satchels))
placed_satchels++
else
expanded_old_satchels.len = 0
var/list/free_satchels = list()
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !istype(T,/turf/open/floor/plating/))
free_satchels += new /obj/item/weapon/storage/backpack/satchel/flat/secret(T)
if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchels) >= (50 - expanded_old_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves
break
for(var/mob/living/simple_animal/parrot/Poly/P in living_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
..()
/datum/subsystem/persistence/proc/CollectData()
CollectSecretSatchels()
/datum/subsystem/persistence/proc/PlaceSecretSatchel(list/expanded_old_satchels)
var/satchel_string
if(expanded_old_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear.
satchel_string = pick_n_take(expanded_old_satchels)
old_secret_satchels = jointext(expanded_old_satchels,"#")
secret_satchels[MAP_NAME] << old_secret_satchels
var/list/chosen_satchel = splittext(satchel_string,"|")
if(!chosen_satchel || isemptylist(chosen_satchel) || chosen_satchel.len != 3) //Malformed
return 0
var/path = text2path(chosen_satchel[3]) //If the item no longer exist, this returns null
if(!path)
return 0
var/obj/item/weapon/storage/backpack/satchel/flat/F = new()
F.x = text2num(chosen_satchel[1])
F.y = text2num(chosen_satchel[2])
F.z = ZLEVEL_STATION
if(isfloorturf(F.loc) && !istype(F.loc,/turf/open/floor/plating/))
F.hide(1)
new path(F)
return 1
/datum/subsystem/persistence/proc/CollectSecretSatchels()
for(var/A in new_secret_satchels)
var/obj/item/weapon/storage/backpack/satchel/flat/F = A
if(qdeleted(F) || F.z != ZLEVEL_STATION || F.invisibility != INVISIBILITY_MAXIMUM)
continue
var/list/savable_obj = list()
for(var/obj/O in F)
if(is_type_in_typecache(O, satchel_blacklist) || O.admin_spawned)
continue
if(O.persistence_replacement)
savable_obj += O.persistence_replacement
else
savable_obj += O.type
if(isemptylist(savable_obj))
continue
old_secret_satchels += "[F.x]|[F.y]|[pick(savable_obj)]#"
secret_satchels[MAP_NAME] << old_secret_satchels
+48
View File
@@ -0,0 +1,48 @@
var/datum/subsystem/pool/SSpool
/datum/subsystem/pool
name = "Pool"
init_order = 20
flags = SS_BACKGROUND | SS_FIRE_IN_LOBBY
var/list/global_pool
var/list/pool_levels = list()
var/sum = 0
var/list/maintained_types = list(
/obj/item/stack/tile/plasteel = 100
)
var/list/stats_placed_in_pool = list()
var/list/stats_pooled_or_newed = list()
var/list/stats_reused = list()
var/list/stats_created_new = list()
/datum/subsystem/pool/New()
NEW_SS_GLOBAL(SSpool)
/datum/subsystem/pool/Initialize(timeofday)
global_pool = GlobalPool
/datum/subsystem/pool/stat_entry(msg)
if(global_pool)
msg += "Types: [global_pool.len]|Total Pooled Objects: [sum]"
else
msg += "NULL POOL"
..(msg)
/datum/subsystem/pool/fire()
sum = 0
for(var/type in global_pool + maintained_types)
var/list/L = global_pool[type]
var/required_number = maintained_types[type] || 0
// Update pool levels and tracker
var/amount = 0
if(L)
amount = L.len
sum += amount
// why yes, just inflate the pool at one item per tick
if(amount < required_number)
var/diver = new type
qdel(diver)
@@ -0,0 +1,10 @@
//Fires five times every second.
var/datum/subsystem/processing/fastprocess/SSfastprocess
/datum/subsystem/processing/fastprocess
name = "Fast Processing"
wait = 2
stat_tag = "FP"
/datum/subsystem/processing/fastprocess/New()
NEW_SS_GLOBAL(SSfastprocess)
@@ -14,14 +14,13 @@ var/datum/subsystem/objects/SSobj
var/list/atom_spawners = list()
var/list/processing = list()
var/list/currentrun = list()
var/list/burning = list()
/datum/subsystem/objects/New()
NEW_SS_GLOBAL(SSobj)
/datum/subsystem/objects/Initialize(timeofdayl)
trigger_atom_spawners()
setupGenetics()
setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
for(var/thing in world)
var/atom/A = thing
A.initialize()
@@ -55,12 +54,6 @@ var/datum/subsystem/objects/SSobj
if (MC_TICK_CHECK)
return
for(var/obj/burningobj in SSobj.burning)
if(burningobj && (burningobj.burn_state == ON_FIRE))
if(burningobj.burn_world_time < world.time)
burningobj.burn()
else
SSobj.burning.Remove(burningobj)
/datum/subsystem/objects/proc/setup_template_objects(list/objects)
trigger_atom_spawners(0, ignore_z=TRUE)
@@ -73,5 +66,3 @@ var/datum/subsystem/objects/SSobj
atom_spawners = SSobj.atom_spawners
if (istype(SSobj.processing))
processing = SSobj.processing
if (istype(SSobj.burning))
burning = SSobj.burning
@@ -0,0 +1,34 @@
//Used to process objects. Fires once every second.
var/datum/subsystem/processing/SSprocessing
/datum/subsystem/processing
name = "Processing"
priority = 25
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 10
var/stat_tag = "P" //Used for logging
var/list/processing = list()
var/list/currentrun = list()
/datum/subsystem/processing/New()
NEW_SS_GLOBAL(SSprocessing)
/datum/subsystem/processing/stat_entry()
..("[stat_tag]:[processing.len]")
/datum/subsystem/processing/fire(resumed = 0)
if (!resumed)
currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/current_run = currentrun
while(current_run.len)
var/datum/thing = current_run[current_run.len]
current_run.len--
if(thing)
thing.process(wait)
else
processing -= thing
if (MC_TICK_CHECK)
return
+17
View File
@@ -0,0 +1,17 @@
var/datum/subsystem/religion/SSreligion
/datum/subsystem/religion
name = "Religion"
init_order = 19
flags = SS_NO_FIRE|SS_NO_INIT
var/bible_deity_name
var/Bible_icon_state
var/Bible_item_state
var/Bible_name
var/Bible_deity_name
var/holy_weapon
/datum/subsystem/religion/New()
NEW_SS_GLOBAL(SSreligion)
@@ -3,12 +3,16 @@ var/datum/subsystem/server_maint/SSserver
/datum/subsystem/server_maint
name = "Server Tasks"
wait = 6000
init_order = 19
flags = SS_NO_TICK_CHECK|SS_NO_INIT
flags = SS_NO_TICK_CHECK
/datum/subsystem/server_maint/New()
NEW_SS_GLOBAL(SSserver)
/datum/subsystem/server_maint/Initialize(timeofday)
if (config.hub)
world.visibility = 1
..()
/datum/subsystem/server_maint/fire()
//handle kicking inactive players
if(config.kick_inactive > 0)
@@ -17,7 +21,7 @@ var/datum/subsystem/server_maint/SSserver
if(!istype(C.mob, /mob/dead))
log_access("AFK: [key_name(C)]")
C << "<span class='danger'>You have been inactive for more than 10 minutes and have been disconnected.</span>"
del(C)
qdel(C)
if(config.sql_enabled)
sql_poll_players()
+279 -20
View File
@@ -1,3 +1,5 @@
#define HIGHLIGHT_DYNAMIC_TRANSIT 1
var/datum/subsystem/shuttle/SSshuttle
/datum/subsystem/shuttle
@@ -10,6 +12,10 @@ var/datum/subsystem/shuttle/SSshuttle
var/list/stationary = list()
var/list/transit = list()
var/list/turf/transit_turfs = list()
var/list/transit_requesters = list()
var/clear_transit = FALSE
//emergency shuttle stuff
var/obj/docking_port/mobile/emergency/emergency
var/obj/docking_port/mobile/emergency/backup/backup_shuttle
@@ -18,6 +24,7 @@ var/datum/subsystem/shuttle/SSshuttle
var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
var/area/emergencyLastCallLoc
var/emergencyNoEscape
var/list/hostileEnvironments = list()
//supply shuttle stuff
var/obj/docking_port/mobile/supply/supply
@@ -33,6 +40,9 @@ var/datum/subsystem/shuttle/SSshuttle
var/datum/round_event/shuttle_loan/shuttle_loan
var/shuttle_purchased = FALSE //If the station has purchased a replacement escape shuttle this round
var/list/shuttle_purchase_requirements_met = list() //For keeping track of ingame events that would unlock new shuttles, such as defeating a boss or discovering a secret item
/datum/subsystem/shuttle/New()
NEW_SS_GLOBAL(SSshuttle)
@@ -53,16 +63,87 @@ var/datum/subsystem/shuttle/SSshuttle
supply_packs[P.type] = P
initial_move()
..()
setup_transit_zone()
#ifdef HIGHLIGHT_DYNAMIC_TRANSIT
color_space()
#endif
/datum/subsystem/shuttle/proc/setup_transit_zone()
if(transit_markers.len == 0)
WARNING("No /obj/effect/landmark/transit placed on the map!")
return
// transit zone
var/turf/A = get_turf(transit_markers[1])
var/turf/B = get_turf(transit_markers[2])
for(var/i in block(A, B))
var/turf/T = i
T.ChangeTurf(/turf/open/space)
transit_turfs += T
T.flags |= UNUSED_TRANSIT_TURF
#ifdef HIGHLIGHT_DYNAMIC_TRANSIT
/datum/subsystem/shuttle/proc/color_space()
if(transit_markers.len == 0)
WARNING("No /obj/effect/landmark/transit placed on the map!")
return
var/turf/A = get_turf(transit_markers[1])
var/turf/B = get_turf(transit_markers[2])
for(var/i in block(A, B))
var/turf/T = i
// Only dying the "pure" space, not the transit tiles
if(istype(T, /turf/open/space/transit) || !isspaceturf(T))
continue
if((T.x == A.x) || (T.x == B.x) || (T.y == A.y) || (T.y == B.y))
T.color = "#ffff00"
else
T.color = "#00ffff"
#endif
//world.log << "[transit_turfs.len] transit turfs registered"
/datum/subsystem/shuttle/fire()
for(var/thing in mobile)
if(thing)
var/obj/docking_port/mobile/P = thing
P.check()
if(!thing)
mobile.Remove(thing)
continue
mobile.Remove(thing)
var/obj/docking_port/mobile/P = thing
P.check()
var/changed_transit = FALSE
for(var/thing in transit)
var/obj/docking_port/stationary/transit/T = thing
if(!T.owner)
qdel(T, force=TRUE)
changed_transit = TRUE
// This next one removes transit docks/zones that aren't
// immediately being used. This will mean that the zone creation
// code will be running a lot.
var/obj/docking_port/mobile/owner = T.owner
if(owner)
var/idle = owner.mode == SHUTTLE_IDLE
var/not_centcom_evac = owner.launch_status == NOLAUNCH
var/not_in_use = (!T.get_docked())
if(idle && not_centcom_evac && not_in_use)
qdel(T, force=TRUE)
changed_transit = TRUE
if(clear_transit)
transit_requesters.Cut()
for(var/i in transit)
qdel(i, force=TRUE)
setup_transit_zone()
clear_transit = FALSE
changed_transit = TRUE
#ifdef HIGHLIGHT_DYNAMIC_TRANSIT
if(changed_transit)
color_space()
#endif
while(transit_requesters.len)
var/requester = popleft(transit_requesters)
var/success = generate_transit_dock(requester)
if(!success) // BACK OF THE QUEUE
transit_requesters += requester
if(MC_TICK_CHECK)
return
/datum/subsystem/shuttle/proc/getShuttle(id)
for(var/obj/docking_port/mobile/M in mobile)
@@ -105,6 +186,9 @@ var/datum/subsystem/shuttle/SSshuttle
if(SHUTTLE_DOCKED)
user << "The emergency shuttle is already here."
return
if(SHUTTLE_IGNITING)
user << "The emergency shuttle is firing its engines to leave."
return
if(SHUTTLE_ESCAPE)
user << "The emergency shuttle is moving away to a safe distance."
return
@@ -114,22 +198,24 @@ var/datum/subsystem/shuttle/SSshuttle
call_reason = trim(html_encode(call_reason))
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH)
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && seclevel2num(get_security_level()) > SEC_LEVEL_GREEN)
user << "You must provide a reason."
return
var/area/signal_origin = get_area(user)
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
if(seclevel2num(get_security_level()) == SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
emergency.request(null, 0.5, signal_origin, html_decode(emergency_reason), 1)
else
emergency.request(null, 1, signal_origin, html_decode(emergency_reason), 0)
var/security_num = seclevel2num(get_security_level())
switch(security_num)
if(SEC_LEVEL_GREEN)
emergency.request(null, 2, signal_origin, html_decode(emergency_reason), 0)
if(SEC_LEVEL_BLUE)
emergency.request(null, 1, signal_origin, html_decode(emergency_reason), 0)
else
emergency.request(null, 0.5, signal_origin, html_decode(emergency_reason), 1) // There is a serious threat we gotta move no time to give them five minutes.
log_game("[key_name(user)] has called the shuttle.")
message_admins("[key_name_admin(user)] has called the shuttle.")
return
// Called when an emergency shuttle mobile docking port is
// destroyed, which will only happen with admin intervention
/datum/subsystem/shuttle/proc/emergencyDeregister()
@@ -149,19 +235,24 @@ var/datum/subsystem/shuttle/SSshuttle
return
if(ticker.mode.name == "meteor")
return
if(seclevel2num(get_security_level()) == SEC_LEVEL_RED)
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
return
else
if(emergency.timeLeft(1) < emergencyCallTime * 0.5)
return
var/security_num = seclevel2num(get_security_level())
switch(security_num)
if(SEC_LEVEL_GREEN)
if(emergency.timeLeft(1) < emergencyCallTime)
return
if(SEC_LEVEL_BLUE)
if(emergency.timeLeft(1) < emergencyCallTime * 0.5)
return
else
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
return
return 1
/datum/subsystem/shuttle/proc/autoEvac()
var/callShuttle = 1
for(var/thing in shuttle_caller_list)
if(istype(thing, /mob/living/silicon/ai))
if(isAI(thing))
var/mob/living/silicon/ai/AI = thing
if(AI.stat || !AI.client)
continue
@@ -181,6 +272,34 @@ var/datum/subsystem/shuttle/SSshuttle
log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
/datum/subsystem/shuttle/proc/registerHostileEnvironment(datum/bad)
hostileEnvironments[bad] = TRUE
checkHostileEnvironment()
/datum/subsystem/shuttle/proc/clearHostileEnvironment(datum/bad)
hostileEnvironments -= bad
checkHostileEnvironment()
/datum/subsystem/shuttle/proc/checkHostileEnvironment()
for(var/datum/d in hostileEnvironments)
if(!istype(d) || qdeleted(d))
hostileEnvironments -= d
emergencyNoEscape = hostileEnvironments.len
if(emergencyNoEscape && (emergency.mode == SHUTTLE_IGNITING))
emergency.mode = SHUTTLE_STRANDED
emergency.timer = null
emergency.sound_played = FALSE
priority_announce("Hostile environment detected. \
Departure has been postponed indefinitely pending \
conflict resolution.", null, 'sound/misc/notice1.ogg', "Priority")
if(!emergencyNoEscape && (emergency.mode == SHUTTLE_STRANDED))
emergency.mode = SHUTTLE_DOCKED
emergency.setTimer(emergencyDockTime)
priority_announce("Hostile environment resolved. \
You have 3 minutes to board the Emergency Shuttle.",
null, 'sound/AI/shuttledock.ogg', "Priority")
//try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons
/datum/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
@@ -213,11 +332,149 @@ var/datum/subsystem/shuttle/SSshuttle
return 2
return 0 //dock successful
/datum/subsystem/shuttle/proc/request_transit_dock(obj/docking_port/mobile/M)
if(!istype(M))
throw EXCEPTION("[M] is not a mobile docking port")
if(M.assigned_transit)
return
else
if(!(M in transit_requesters))
transit_requesters += M
/datum/subsystem/shuttle/proc/generate_transit_dock(obj/docking_port/mobile/M)
// First, determine the size of the needed zone
// Because of shuttle rotation, the "width" of the shuttle is not
// always x.
var/travel_dir = M.preferred_direction
// Remember, the direction is the direction we appear to be
// coming from
var/dock_angle = dir2angle(M.preferred_direction) + M.port_angle + 180
var/dock_dir = angle2dir(dock_angle)
var/transit_width = SHUTTLE_TRANSIT_BORDER * 2
var/transit_height = SHUTTLE_TRANSIT_BORDER * 2
// Shuttles travelling on their side have their dimensions swapped
// from our perspective
switch(dock_dir)
if(NORTH, SOUTH)
transit_width += M.width
transit_height += M.height
if(EAST, WEST)
transit_width += M.height
transit_height += M.width
/*
world << "The attempted transit dock will be [transit_width] width, and \
[transit_height] in height. The travel dir is [travel_dir]."
*/
// Then find a place to put the zone
var/list/proposed_zone
base:
for(var/i in transit_turfs)
CHECK_TICK
var/turf/topleft = i
if(!(topleft.flags & UNUSED_TRANSIT_TURF))
continue
var/turf/bottomright = locate(topleft.x + transit_width,
topleft.y + transit_height, topleft.z)
if(!bottomright)
continue
if(!(bottomright.flags & UNUSED_TRANSIT_TURF))
continue
proposed_zone = block(topleft, bottomright)
if(!proposed_zone)
continue
for(var/j in proposed_zone)
var/turf/T = j
if(!T)
continue base
if(!(T.flags & UNUSED_TRANSIT_TURF))
continue base
//world << "[COORD(topleft)] and [COORD(bottomright)]"
break base
if((!proposed_zone) || (!proposed_zone.len))
return FALSE
var/turf/topleft = proposed_zone[1]
//world << "[COORD(topleft)] is TOPLEFT"
// Then create a transit docking port in the middle
var/coords = M.return_coords(0, 0, dock_dir)
//world << json_encode(coords)
/* 0------2
| |
| |
| x |
3------1
*/
var/x0 = coords[1]
var/y0 = coords[2]
var/x1 = coords[3]
var/y1 = coords[4]
// Then we want the point closest to -infinity,-infinity
var/x2 = min(x0, x1)
var/y2 = min(y0, y1)
/*
var/lowx = topleft.x + SHUTTLE_TRANSIT_BORDER
var/lowy = topleft.y + SHUTTLE_TRANSIT_BORDER
var/turf/low_point = locate(lowx, lowy, topleft.z)
new /obj/effect/landmark/stationary(low_point)
world << "Starting at the low point, we go [x2],[y2]"
*/
// Then invert the numbers
var/transit_x = topleft.x + SHUTTLE_TRANSIT_BORDER + abs(x2)
var/transit_y = topleft.y + SHUTTLE_TRANSIT_BORDER + abs(y2)
var/transit_path = /turf/open/space/transit
switch(travel_dir)
if(NORTH)
transit_path = /turf/open/space/transit/north
if(SOUTH)
transit_path = /turf/open/space/transit/south
if(EAST)
transit_path = /turf/open/space/transit/east
if(WEST)
transit_path = /turf/open/space/transit/west
//world << "Docking port at [transit_x], [transit_y], [topleft.z]"
var/turf/midpoint = locate(transit_x, transit_y, topleft.z)
if(!midpoint)
return FALSE
//world << "Making transit dock at [COORD(midpoint)]"
var/area/shuttle/transit/A = new()
A.parallax_movedir = travel_dir
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
new_transit_dock.assigned_turfs = proposed_zone
new_transit_dock.name = "Transit for [M.id]/[M.name]"
new_transit_dock.turf_type = transit_path
new_transit_dock.owner = M
new_transit_dock.assigned_area = A
// Add 180, because ports point inwards, rather than outwards
new_transit_dock.setDir(angle2dir(dock_angle))
for(var/i in new_transit_dock.assigned_turfs)
var/turf/T = i
T.ChangeTurf(transit_path)
T.flags &= ~(UNUSED_TRANSIT_TURF)
A.contents += T
M.assigned_transit = new_transit_dock
return TRUE
/datum/subsystem/shuttle/proc/initial_move()
for(var/obj/docking_port/mobile/M in mobile)
if(!M.roundstart_move)
continue
moveShuttle(M.id, "[M.roundstart_move]", 0)
M.dockRoundstart()
CHECK_TICK
/datum/subsystem/shuttle/Recover()
@@ -239,6 +496,8 @@ var/datum/subsystem/shuttle/SSshuttle
backup_shuttle = SSshuttle.backup_shuttle
if (istype(SSshuttle.supply))
supply = SSshuttle.supply
if (istype(SSshuttle.transit_turfs))
transit_turfs = SSshuttle.transit_turfs
centcom_message = SSshuttle.centcom_message
ordernum = SSshuttle.ordernum
+3 -2
View File
@@ -2,9 +2,9 @@ var/datum/subsystem/spacedrift/SSspacedrift
/datum/subsystem/spacedrift
name = "Space Drift"
priority = 40
priority = 30
wait = 5
flags = SS_NO_INIT|SS_BACKGROUND
flags = SS_NO_INIT|SS_KEEP_TIMING
var/list/currentrun = list()
var/list/processing = list()
@@ -61,3 +61,4 @@ var/datum/subsystem/spacedrift/SSspacedrift
AM.inertia_last_loc = AM.loc
if (MC_TICK_CHECK)
return
+44
View File
@@ -0,0 +1,44 @@
var/datum/subsystem/squeak/SSsqueak
// The Squeak
// because this is about placement of mice mobs, and nothing to do with
// mice - the computer peripheral
/datum/subsystem/squeak
name = "Squeak"
priority = 40
flags = SS_NO_FIRE
var/list/exposed_wires = list()
/datum/subsystem/squeak/New()
NEW_SS_GLOBAL(SSsqueak)
/datum/subsystem/squeak/Initialize(timeofday)
trigger_migration()
/datum/subsystem/squeak/proc/trigger_migration(num_mice=10)
find_exposed_wires()
var/mob/living/simple_animal/mouse/M
var/turf/proposed_turf
while((num_mice > 0) && exposed_wires.len)
proposed_turf = pick_n_take(exposed_wires)
if(!M)
M = new(proposed_turf)
else
M.forceMove(proposed_turf)
if(M.environment_is_safe())
num_mice -= 1
M = null
/datum/subsystem/squeak/proc/find_exposed_wires()
exposed_wires.Cut()
var/list/all_turfs = block(locate(1,1,1), locate(world.maxx,world.maxy,1))
for(var/turf/open/floor/plating/T in all_turfs)
if(is_blocked_turf(T))
continue
if(locate(/obj/structure/cable) in T)
exposed_wires += T
+37
View File
@@ -0,0 +1,37 @@
var/datum/subsystem/stickyban/SSstickyban
/datum/subsystem/stickyban
name = "Sticky Ban"
init_order = -10
flags = SS_NO_FIRE
var/list/cache = list()
/datum/subsystem/stickyban/New()
NEW_SS_GLOBAL(SSstickyban)
/datum/subsystem/stickyban/Initialize(timeofday)
var/list/bannedkeys = world.GetConfig("ban")
//sanitize the sticky ban list
for (var/bannedkey in bannedkeys)
var/ckey = ckey(bannedkey)
var/list/ban = stickyban2list(world.GetConfig("ban", bannedkey))
//byond stores sticky bans by key, that can end up confusing things
//i also remove it here so that if any stickybans cause a runtime, they just stop existing
world.SetConfig("ban", bannedkey, null)
if (!ban["ckey"])
ban["ckey"] = ckey
//storing these can break things and isn't needed for sticky ban tracking
ban -= "IP"
ban -= "computer_id"
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
cache[ckey] = ban
for (var/bannedckey in cache)
world.SetConfig("ban", bannedckey, list2stickyban(cache[bannedckey]))
+3
View File
@@ -18,6 +18,9 @@ var/datum/subsystem/tgui/SStgui
NEW_SS_GLOBAL(SStgui)
/datum/subsystem/tgui/Shutdown()
close_all_uis()
/datum/subsystem/tgui/stat_entry()
..("P:[processing_uis.len]")
+124 -36
View File
@@ -22,21 +22,19 @@ var/datum/subsystem/ticker/ticker
var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking.
//These bible variables should be a preference
var/Bible_icon_state //icon_state the chaplain has chosen for his bible
var/Bible_item_state //item_state the chaplain has chosen for his bible
var/Bible_name //name of the bible
var/Bible_deity_name //name of chaplin's deity
var/list/syndicate_coalition = list() //list of traitor-compatible factions
var/list/factions = list() //list of all factions
var/list/availablefactions = list() //list of factions with openings
var/list/scripture_states = list(SCRIPTURE_DRIVER = TRUE, \
SCRIPTURE_SCRIPT = FALSE, \
SCRIPTURE_APPLICATION = FALSE, \
SCRIPTURE_REVENANT = FALSE, \
SCRIPTURE_JUDGEMENT = FALSE) //list of clockcult scripture states for announcements
var/delay_end = 0 //if set true, the round will not restart on it's own
var/triai = 0 //Global holder for Triumvirate
var/tipped = 0 //Did we broadcast the tip of the day yet?
var/modevoted = 0 //Have we sent a vote for the gamemode?
var/selected_tip // What will be the tip of the day?
var/timeLeft = 1200 //pregame timer
@@ -51,11 +49,12 @@ var/datum/subsystem/ticker/ticker
var/maprotatechecked = 0
var/news_report
/datum/subsystem/ticker/New()
NEW_SS_GLOBAL(ticker)
login_music = pickweight(list('sound/ambience/title1.ogg' = 30, 'sound/ambience/title2.ogg' = 30, 'sound/ambience/title3.ogg' = 30, 'sound/ambience/title4.ogg' = 30, 'sound/ambience/title5.ogg' = 30, 'sound/ambience/title6.ogg' = 30, 'sound/ambience/title7.ogg' = 30, 'sound/ambience/clown.ogg' = 3)) // choose title music!
login_music = pickweight(list('sound/ambience/title2.ogg' = 15, 'sound/ambience/title1.ogg' =15, 'sound/ambience/title3.ogg' =14, 'sound/ambience/title4.ogg' =14, 'sound/misc/i_did_not_grief_them.ogg' =14, 'sound/ambience/clown.ogg' = 9)) // choose title music!
if(SSevent.holidays && SSevent.holidays[APRIL_FOOLS])
login_music = 'sound/ambience/clown.ogg'
@@ -64,15 +63,14 @@ var/datum/subsystem/ticker/ticker
syndicate_code_phrase = generate_code_phrase()
if(!syndicate_code_response)
syndicate_code_response = generate_code_phrase()
setupFactions()
..()
/datum/subsystem/ticker/fire()
switch(current_state)
if(GAME_STATE_STARTUP)
timeLeft = config.lobby_countdown * 10
world << "<b><font color='blue'>Welcome to the pre-game lobby!</font></b>"
world << "Please, setup your character and select ready. Game will start in [config.lobby_countdown] seconds"
world << "<span class='boldnotice'>Welcome to [station_name()]!</span>"
world << "Please set up your character and select \"Ready\". The game will start in [config.lobby_countdown] seconds."
current_state = GAME_STATE_PREGAME
if(GAME_STATE_PREGAME)
@@ -89,10 +87,6 @@ var/datum/subsystem/ticker/ticker
return
timeLeft -= wait
if(timeLeft <= 1200 && !modevoted) //Vote for the round type
send_gamemode_vote()
modevoted = TRUE
if(timeLeft <= 300 && !tipped)
send_tip_of_the_round()
tipped = TRUE
@@ -109,6 +103,7 @@ var/datum/subsystem/ticker/ticker
mode.process(wait * 0.1)
check_queue()
check_maprotate()
scripture_states = scripture_unlock_alert(scripture_states)
if(!mode.explosion_in_progress && mode.check_finished() || force_ending)
current_state = GAME_STATE_FINISHED
@@ -159,19 +154,19 @@ var/datum/subsystem/ticker/ticker
if(!can_continue)
qdel(mode)
mode = null
world << "<B>Error setting up [master_mode]. It's likely that there are no available antagonists for the selected mode.</B> Reverting to pre-game lobby."
world << "<B>Error setting up [master_mode].</B> Reverting to pre-game lobby."
SSjob.ResetOccupations()
return 0
else
world << "<span class='notice'>DEBUG: Bypassing prestart checks..."
message_admins("<span class='notice'>DEBUG: Bypassing prestart checks...</span>")
if(hide_mode)
var/list/modes = new
for (var/datum/game_mode/M in runnable_modes)
modes += M.name
modes = sortList(modes)
world << "<B>The current game mode is - Secret!</B>"
world << "<B>Possibilities:</B> [english_list(modes)]"
world << "<b>The gamemode is: secret!\n\
Possibilities:</B> [english_list(modes)]"
else
mode.announce()
@@ -222,25 +217,25 @@ var/datum/subsystem/ticker/ticker
//initialise our cinematic screen object
cinematic = new /obj/screen{icon='icons/effects/station_explosion.dmi';icon_state="station_intact";layer=21;mouse_opacity=0;screen_loc="1,0";}(src)
var/obj/structure/bed/temp_buckle = new(src)
if(station_missed)
for(var/mob/M in mob_list)
M.buckled = temp_buckle //buckles the mob so it can't do anything
M.notransform = TRUE //stop everything moving
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/M in mob_list)
M.buckled = temp_buckle
if(M.client)
M.client.screen += cinematic
if(M.stat != DEAD)
var/turf/T = get_turf(M)
if(T && T.z==1)
M.death(0) //no mercy
else
M.notransform=TRUE //no moving for you
//Now animate the cinematic
switch(station_missed)
if(1) //nuke was nearby but (mostly) missed
if(NUKE_NEAR_MISS) //nuke was nearby but (mostly) missed
if( mode && !override )
override = mode.name
switch( override )
@@ -266,7 +261,7 @@ var/datum/subsystem/ticker/ticker
//flick("end",cinematic)
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
if(NUKE_MISS_STATION || NUKE_SYNDICATE_BASE) //nuke was nowhere nearby //TODO: a really distant explosion animation
sleep(50)
world << sound('sound/effects/explosionfar.ogg')
else //station was destroyed
@@ -300,8 +295,8 @@ var/datum/subsystem/ticker/ticker
if(cinematic)
qdel(cinematic)
cinematic = null
if(temp_buckle)
qdel(temp_buckle)
for(var/mob/M in mob_list)
M.notransform = FALSE
return //Faster exit, since nothing happened
else //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
@@ -314,8 +309,8 @@ var/datum/subsystem/ticker/ticker
spawn(300)
if(cinematic)
qdel(cinematic) //end the cinematic
if(temp_buckle)
qdel(temp_buckle) //release everybody
for(var/mob/M in mob_list)
M.notransform = FALSE //gratz you survived
return
@@ -350,7 +345,7 @@ var/datum/subsystem/ticker/ticker
SSjob.EquipRank(player, player.mind.assigned_role, 0)
if(captainless)
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player))
if(!isnewplayer(M))
M << "Captainship not forced on anyone."
@@ -385,10 +380,15 @@ var/datum/subsystem/ticker/ticker
world << "<BR>[TAB]Shift Duration: <B>[round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]</B>"
world << "<BR>[TAB]Station Integrity: <B>[mode.station_was_nuked ? "<font color='red'>Destroyed</font>" : "[station_integrity]%"]</B>"
if(mode.station_was_nuked)
ticker.news_report = STATION_DESTROYED_NUKE
if(joined_player_list.len)
world << "<BR>[TAB]Total Population: <B>[joined_player_list.len]</B>"
if(station_evacuated)
world << "<BR>[TAB]Evacuation Rate: <B>[num_escapees] ([round((num_escapees/joined_player_list.len)*100, 0.1)]%)</B>"
news_report = STATION_EVACUATED
if(SSshuttle.emergency.is_hijacked())
news_report = SHUTTLE_HIJACK
world << "<BR>[TAB]Survival Rate: <B>[num_survivors] ([round((num_survivors/joined_player_list.len)*100, 0.1)]%)</B>"
world << "<BR>"
@@ -421,6 +421,9 @@ var/datum/subsystem/ticker/ticker
mode.declare_completion()//To declare normal completion.
if(cross_allowed)
send_news_report()
//calls auto_declare_completion_* for all modes
for(var/handler in typesof(/datum/game_mode/proc))
if (findtext("[handler]","auto_declare_completion_"))
@@ -443,6 +446,44 @@ var/datum/subsystem/ticker/ticker
for(var/i in total_antagonists)
log_game("[i]s[total_antagonists[i]].")
//Borers
var/borerwin = FALSE
if(borers.len)
var/borertext = "<br><font size=3><b>The borers were:</b></font>"
for(var/mob/living/simple_animal/borer/B in borers)
if((B.key || B.controlling) && B.stat != DEAD)
borertext += "<br>[B.controlling ? B.victim.key : B.key] was [B.truename] ("
var/turf/location = get_turf(B)
if(location.z == ZLEVEL_CENTCOM && B.victim)
borertext += "escaped with host"
else
borertext += "failed"
borertext += ")"
world << borertext
var/total_borers = 0
for(var/mob/living/simple_animal/borer/B in borers)
if((B.key || B.victim) && B.stat != DEAD)
total_borers++
if(total_borers)
var/total_borer_hosts = 0
for(var/mob/living/carbon/C in mob_list)
var/mob/living/simple_animal/borer/D = C.has_brain_worms()
var/turf/location = get_turf(C)
if(location.z == ZLEVEL_CENTCOM && D && D.stat != DEAD)
total_borer_hosts++
if(total_borer_hosts_needed <= total_borer_hosts)
borerwin = TRUE
world << "<b>There were [total_borers] borers alive at round end!</b>"
world << "<b>A total of [total_borer_hosts] borers with hosts escaped on the shuttle alive. The borers needed [total_borer_hosts_needed] hosts to escape.</b>"
if(borerwin)
world << "<b><font color='green'>The borers were successful!</font></b>"
else
world << "<b><font color='red'>The borers have failed!</font></b>"
return TRUE
mode.declare_station_goal_completion()
//Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
if(SSgarbage.didntgc.len)
var/dellog = ""
@@ -451,6 +492,8 @@ var/datum/subsystem/ticker/ticker
dellog += "Failures : [SSgarbage.didntgc[path]] \n"
world.log << dellog
//Collects persistence features
SSpersistence.CollectData()
return 1
/datum/subsystem/ticker/proc/send_tip_of_the_round()
@@ -526,11 +569,6 @@ var/datum/subsystem/ticker/ticker
minds = ticker.minds
Bible_icon_state = ticker.Bible_icon_state
Bible_item_state = ticker.Bible_item_state
Bible_name = ticker.Bible_name
Bible_deity_name = ticker.Bible_deity_name
syndicate_coalition = ticker.syndicate_coalition
factions = ticker.factions
availablefactions = ticker.availablefactions
@@ -551,5 +589,55 @@ var/datum/subsystem/ticker/ticker
cinematic = ticker.cinematic
maprotatechecked = ticker.maprotatechecked
/datum/subsystem/ticker/proc/send_gamemode_vote(var/)
SSvote.initiate_vote("roundtype","server")
/datum/subsystem/ticker/proc/send_news_report()
var/news_message
var/news_source = "Nanotrasen News Network"
switch(news_report)
if(NUKE_SYNDICATE_BASE)
news_message = "In a daring raid, the heroic crew of [station_name()] detonated a nuclear device in the heart of a terrorist base."
if(STATION_DESTROYED_NUKE)
news_message = "We would like to reassure all employees that the reports of a Syndicate backed nuclear attack on [station_name()] are, in fact, a hoax. Have a secure day!"
if(STATION_EVACUATED)
news_message = "The crew of [station_name()] has been evacuated amid unconfirmed reports of enemy activity."
if(GANG_LOSS)
news_message = "Organized crime aboard [station_name()] has been stamped out by members of our ever vigilant security team. Remember to thank your assigned officers today!"
if(GANG_TAKEOVER)
news_message = "Contact with [station_name()] has been lost after a sophisticated hacking attack by organized criminal elements. Stay vigilant!"
if(BLOB_WIN)
news_message = "[station_name()] was overcome by an unknown biological outbreak, killing all crew on board. Don't let it happen to you! Remember, a clean work station is a safe work station."
if(BLOB_NUKE)
news_message = "[station_name()] is currently undergoing decontanimation after a controlled burst of radiation was used to remove a biological ooze. All employees were safely evacuated prior, and are enjoying a relaxing vacation."
if(BLOB_DESTROYED)
news_message = "[station_name()] is currently undergoing decontamination procedures after the destruction of a biological hazard. As a reminder, any crew members experiencing cramps or bloating should report immediately to security for incineration."
if(CULT_ESCAPE)
news_message = "Security Alert: A group of religious fanatics have escaped from [station_name()]."
if(CULT_FAILURE)
news_message = "Following the dismantling of a restricted cult aboard [station_name()], we would like to remind all employees that worship outside of the Chapel is strictly prohibited, and cause for termination."
if(CULT_SUMMON)
news_message = "Company officials would like to clarify that [station_name()] was scheduled to be decommissioned following meteor damage earlier this year. Earlier reports of an unknowable eldritch horror were made in error."
if(NUKE_MISS)
news_message = "The Syndicate have bungled a terrorist attack [station_name()], detonating a nuclear weapon in empty space near by."
if(OPERATIVES_KILLED)
news_message = "Repairs to [station_name()] are underway after an elite Syndicate death squad was wiped out by the crew."
if(OPERATIVE_SKIRMISH)
news_message = "A skirmish between security forces and Syndicate agents aboard [station_name()] ended with both sides bloodied but intact."
if(REVS_WIN)
news_message = "Company officials have reassured investors that despite a union led revolt aboard [station_name()] that there will be no wage increases for workers."
if(REVS_LOSE)
news_message = "[station_name()] quickly put down a misguided attempt at mutiny. Remember, unionizing is illegal!"
if(WIZARD_KILLED)
news_message = "Tensions have flared with the Wizard's Federation following the death of one of their members aboard [station_name()]."
if(STATION_NUKED)
news_message = "[station_name()] activated its self destruct device for unknown reasons. Attempts to clone the Captain so he can be arrested and executed are under way."
if(CLOCK_SUMMON)
news_message = "The garbled messages about hailing a mouse and strange energy readings from [station_name()] have been discovered to be an ill-advised, if thorough, prank by a clown."
if(CLOCK_SILICONS)
news_message = "The project started by [station_name()] to upgrade their silicon units with advanced equipment have been largely successful, though they have thus far refused to release schematics in a violation of company policy."
if(CLOCK_PROSELYTIZATION)
news_message = "The burst of energy released near [station_name()] has been confirmed as merely a test of a new weapon. However, due to an unexpected mechanical error, their communications system has been knocked offline."
if(SHUTTLE_HIJACK)
news_message = "During routine evacuation procedures, the emergency shuttle of [station_name()] had its navigation protocols corrupted and went off course, but was recovered shortly after."
if(news_message)
send2otherserver(news_source, news_message,"News_Report")
+11 -5
View File
@@ -62,7 +62,7 @@ var/datum/subsystem/timer/SStimer
SStimer.hashes -= hash
return QDEL_HINT_IWILLGC
/proc/addtimer(thingToCall, procToCall, wait, unique = FALSE, ...)
/proc/addtimer(thingToCall, procToCall, wait, unique = TIMER_NORMAL, ...)
if (!thingToCall || !procToCall)
return
if (!SStimer.can_fire)
@@ -75,12 +75,18 @@ var/datum/subsystem/timer/SStimer
var/hashlist = args.Copy()
hashlist[1] = "[thingToCall](\ref[thingToCall])"
event.hash = jointext(args, null)
if(args.len > 4)
event.argList = args.Copy(5)
event.hash = jointext(hashlist, null)
var/bad_args = unique != TIMER_NORMAL && unique != TIMER_UNIQUE
if(args.len > 4 || bad_args)
if(bad_args)
stack_trace("Invalid arguments in call to addtimer! Attempt to call [thingToCall] proc [procToCall]")
event.argList = args.Copy(4)
else
event.argList = args.Copy(5)
// Check for dupes if unique = 1.
if(unique)
if(!bad_args && unique != TIMER_NORMAL) //faster than checking if(unique == TIMER_UNIQUE)
var/datum/timedevent/hash_event = SStimer.hashes[event.hash]
if(hash_event)
return hash_event.id
+9 -14
View File
@@ -58,15 +58,20 @@ var/datum/subsystem/vote/SSvote
greatest_votes = votes
//default-vote for everyone who didn't vote
if(!config.vote_no_default && choices.len)
var/non_voters = (clients.len - total_votes)
if(non_voters > 0)
var/list/non_voters = directory.Copy()
non_voters -= voted
for (var/non_voter_ckey in non_voters)
var/client/C = non_voters[non_voter_ckey]
if (!C || C.is_afk())
non_voters -= non_voter_ckey
if(non_voters.len > 0)
if(mode == "restart")
choices["Continue Playing"] += non_voters
choices["Continue Playing"] += non_voters.len
if(choices["Continue Playing"] >= greatest_votes)
greatest_votes = choices["Continue Playing"]
else if(mode == "gamemode")
if(master_mode in choices)
choices[master_mode] += non_voters
choices[master_mode] += non_voters.len
if(choices[master_mode] >= greatest_votes)
greatest_votes = choices[master_mode]
//get all options with that many votes and return them in a list
@@ -121,13 +126,6 @@ var/datum/subsystem/vote/SSvote
restart = 1
else
master_mode = .
if("roundtype")
if(ticker && ticker.mode)
message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
else if(master_mode != .)
world.save_mode(.)
master_mode = .
world << "<span class='adminnotice'><b>The mode is now: [master_mode]</b></span>"
if(restart)
var/active_admins = 0
for(var/client/C in admins)
@@ -176,9 +174,6 @@ var/datum/subsystem/vote/SSvote
choices.Add("Restart Round","Continue Playing")
if("gamemode")
choices.Add(config.votable_modes)
if("roundtype")
choices.Add("secret")
choices.Add("extended")
if("custom")
question = stripped_input(usr,"What is the vote for?")
if(!question)
+2 -2
View File
@@ -29,13 +29,13 @@ var/datum/subsystem/weather/SSweather
var/datum/weather/W = pickweight(possible_weather_for_this_z)
run_weather(W.name, Z)
eligible_zlevels -= Z
addtimer(src, "make_z_eligible", rand(3000, 6000) + W.weather_duration_upper, TRUE, Z) //Around 5-10 minutes between weathers
addtimer(src, "make_z_eligible", rand(3000, 6000) + W.weather_duration_upper, TIMER_UNIQUE, Z) //Around 5-10 minutes between weathers
/datum/subsystem/weather/Initialize(start_timeofday)
..()
for(var/V in subtypesof(/datum/weather))
var/datum/weather/W = V
existing_weather |= new W
new W //weather->New will handle adding itself to the list
/datum/subsystem/weather/proc/run_weather(weather_name, Z)
if(!weather_name)