Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into pr/SandPoot/15486

This commit is contained in:
SandPoot
2024-04-12 20:48:41 -03:00
4164 changed files with 631256 additions and 213055 deletions
-37
View File
@@ -1,37 +0,0 @@
SUBSYSTEM_DEF(acid)
name = "Acid"
priority = FIRE_PRIORITY_ACID
flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
var/list/processing = list()
/datum/controller/subsystem/acid/stat_entry(msg)
msg = "P:[length(processing)]"
return ..()
/datum/controller/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.update_icon()
processing -= O
if (MC_TICK_CHECK)
return
+8 -4
View File
@@ -9,8 +9,9 @@ SUBSYSTEM_DEF(activity)
var/list/threats = list()
/datum/controller/subsystem/activity/Initialize(timeofday)
RegisterSignal(SSdcs,COMSIG_GLOB_EXPLOSION,.proc/on_explosion)
RegisterSignal(SSdcs,COMSIG_GLOB_MOB_DEATH,.proc/on_death)
RegisterSignal(SSdcs,COMSIG_GLOB_EXPLOSION, PROC_REF(on_explosion))
RegisterSignal(SSdcs,COMSIG_GLOB_MOB_DEATH, PROC_REF(on_death))
return ..()
/datum/controller/subsystem/activity/fire(resumed = 0)
calculate_threat()
@@ -47,14 +48,17 @@ SUBSYSTEM_DEF(activity)
/datum/controller/subsystem/activity/proc/get_average_threat()
if(!length(threat_history))
return 0
return FALSE
var/total_weight = 0
var/total_amt = 0
for(var/i in 1 to threat_history.len-1)
var/weight = (text2num(threat_history[i+1])-text2num(threat_history[i]))
total_weight += weight
total_amt += weight * (threat_history[threat_history[i]])
return round(total_amt / total_weight,0.1)
if(total_weight == 0)
return total_amt
else
return round(total_amt / total_weight,0.1)
/datum/controller/subsystem/activity/proc/get_max_threat()
. = 0
@@ -1,36 +0,0 @@
SUBSYSTEM_DEF(adjacent_air)
name = "Atmos Adjacency"
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 10
priority = FIRE_PRIORITY_ATMOS_ADJACENCY
var/list/queue = list()
/datum/controller/subsystem/adjacent_air/stat_entry(msg)
#ifdef TESTING
msg = "P:[length(queue)], S:[GLOB.atmos_adjacent_savings[1]], T:[GLOB.atmos_adjacent_savings[2]]"
#else
msg = "P:[length(queue)]"
#endif
return ..()
/datum/controller/subsystem/adjacent_air/Initialize()
while(length(queue))
fire(mc_check = FALSE)
return ..()
/datum/controller/subsystem/adjacent_air/fire(resumed = FALSE, mc_check = TRUE)
var/list/queue = src.queue
while (length(queue))
var/turf/currT = queue[1]
queue.Cut(1,2)
currT.ImmediateCalculateAdjacentTurfs()
if(mc_check)
if(MC_TICK_CHECK)
break
else
CHECK_TICK
+169 -210
View File
@@ -6,15 +6,30 @@ SUBSYSTEM_DEF(air)
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cached_cost = 0
var/cost_turfs = 0
var/cost_groups = 0
var/cost_highpressure = 0
var/cost_deferred_airs
var/cost_hotspots = 0
var/cost_post_process = 0
var/cost_superconductivity = 0
var/cost_pipenets = 0
var/cost_rebuilds = 0
var/cost_atmos_machinery = 0
var/cost_equalize = 0
var/thread_wait_ticks = 0
var/cur_thread_wait_ticks = 0
var/low_pressure_turfs = 0
var/high_pressure_turfs = 0
var/num_group_turfs_processed = 0
var/num_equalize_processed = 0
var/gas_mixes_count = 0
var/gas_mixes_allocated = 0
var/list/hotspots = list()
var/list/networks = list()
@@ -24,9 +39,9 @@ SUBSYSTEM_DEF(air)
//atmos singletons
var/list/gas_reactions = list()
var/list/atmos_gen
var/list/planetary = list() //auxmos already caches static planetary mixes but could be convenient to do so here too
//Special functions lists
var/list/turf/active_super_conductivity = list()
var/list/turf/open/high_pressure_delta = list()
@@ -34,54 +49,99 @@ SUBSYSTEM_DEF(air)
var/currentpart = SSAIR_REBUILD_PIPENETS
var/map_loading = TRUE
var/list/queued_for_activation
var/log_explosive_decompression = TRUE // If things get spammy, admemes can turn this off.
var/monstermos_turf_limit = 10
var/monstermos_hard_turf_limit = 2000
var/monstermos_enabled = TRUE
// Max number of turfs equalization will grab.
var/equalize_turf_limit = 10
// Max number of turfs to look for a space turf, and max number of turfs that will be decompressed.
var/equalize_hard_turf_limit = 2000
// Whether equalization is enabled. Can be disabled for performance reasons.
var/equalize_enabled = FALSE
// Whether turf-to-turf heat exchanging should be enabled.
var/heat_enabled = FALSE
// Max number of times process_turfs will share in a tick.
var/share_max_steps = 3
// Target for share_max_steps; can go below this, if it determines the thread is taking too long.
var/share_max_steps_target = 3
// Excited group processing will try to equalize groups with total pressure difference less than this amount.
var/excited_group_pressure_goal = 1
// Target for excited_group_pressure_goal; can go below this, if it determines the thread is taking too long.
var/excited_group_pressure_goal_target = 1
/datum/controller/subsystem/air/stat_entry(msg)
msg += "C:{"
msg += "EQ:[round(cost_equalize,1)]|"
msg += "AT:[round(cost_turfs,1)]|"
msg += "EG:[round(cost_groups,1)]|"
msg += "HP:[round(cost_highpressure,1)]|"
msg += "HS:[round(cost_hotspots,1)]|"
msg += "HE:[round(heat_process_time(),1)]|"
msg += "SC:[round(cost_superconductivity,1)]|"
msg += "PN:[round(cost_pipenets,1)]|"
msg += "AM:[round(cost_atmos_machinery,1)]"
msg += "} "
var/active_turfs_len = get_amt_active_turfs()
msg += "AT:[active_turfs_len]|"
msg += "EG:[get_amt_excited_groups()]|"
msg += "TC:{"
msg += "AT:[round(cost_turfs,1)]|"
msg += "EG:[round(cost_groups,1)]|"
msg += "EQ:[round(cost_equalize,1)]|"
msg += "PO:[round(cost_post_process,1)]"
msg += "}"
msg += "TH:[round(thread_wait_ticks,1)]|"
msg += "HS:[hotspots.len]|"
msg += "PN:[networks.len]|"
msg += "HP:[high_pressure_delta.len]|"
msg += "AS:[active_super_conductivity.len]|"
msg += "GA:[get_amt_gas_mixes()]|"
msg += "MG:[get_max_gas_mixes()]|"
msg += "AT/MS:[round((cost ? active_turfs_len/cost : 0),0.1)]"
msg += "HT:[high_pressure_turfs]|"
msg += "LT:[low_pressure_turfs]|"
msg += "ET:[num_equalize_processed]|"
msg += "GT:[num_group_turfs_processed]|"
msg += "GA:[gas_mixes_count]|"
msg += "MG:[gas_mixes_allocated]"
return ..()
/datum/controller/subsystem/air/Initialize(timeofday)
extools_update_ssair()
map_loading = FALSE
setup_allturfs()
setup_atmos_machinery()
setup_pipenets()
gas_reactions = init_gas_reactions()
extools_update_reactions()
auxtools_update_reactions()
equalize_enabled = CONFIG_GET(flag/atmos_equalize_enabled)
return ..()
/datum/controller/subsystem/air/proc/extools_update_ssair()
/datum/controller/subsystem/air/proc/extools_update_reactions()
/datum/controller/subsystem/air/proc/add_reaction(datum/gas_reaction/r)
gas_reactions += r
sortTim(gas_reactions, GLOBAL_PROC_REF(cmp_gas_reaction))
auxtools_update_reactions()
/proc/reset_all_air()
SSair.can_fire = 0
message_admins("Air reset begun.")
for(var/turf/open/T in world)
T.Initalize_Atmos(0)
CHECK_TICK
message_admins("Air reset done.")
SSair.can_fire = 1
/proc/fix_corrupted_atmos()
/datum/admins/proc/fixcorruption()
set category = "Debug"
set desc="Fixes air that has weird NaNs (-1.#IND and such). Hopefully."
set name="Fix Infinite Air"
fix_corrupted_atmos()
/datum/controller/subsystem/air/fire(resumed = 0)
var/timer = TICK_USAGE_REAL
thread_wait_ticks = MC_AVERAGE(thread_wait_ticks, cur_thread_wait_ticks)
cur_thread_wait_ticks = 0
gas_mixes_count = get_amt_gas_mixes()
gas_mixes_allocated = get_max_gas_mixes()
if(currentpart == SSAIR_REBUILD_PIPENETS)
timer = TICK_USAGE_REAL
var/list/pipenet_rebuilds = pipenets_needing_rebuilt
for(var/thing in pipenet_rebuilds)
var/obj/machinery/atmospherics/AT = thing
@@ -96,44 +156,53 @@ SUBSYSTEM_DEF(air)
currentpart = SSAIR_PIPENETS
if(currentpart == SSAIR_PIPENETS || !resumed)
timer = TICK_USAGE_REAL
if(!resumed)
cached_cost = 0
process_pipenets(resumed)
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
cached_cost += TICK_USAGE_REAL - timer
if(state != SS_RUNNING)
return
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(cached_cost))
resumed = 0
currentpart = SSAIR_ATMOSMACHINERY
if(currentpart == SSAIR_ATMOSMACHINERY)
timer = TICK_USAGE_REAL
if(!resumed)
cached_cost = 0
process_atmos_machinery(resumed)
cost_atmos_machinery = MC_AVERAGE(cost_atmos_machinery, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = monstermos_enabled ? SSAIR_EQUALIZE : SSAIR_ACTIVETURFS
if(currentpart == SSAIR_EQUALIZE)
timer = TICK_USAGE_REAL
process_turf_equalize(resumed)
cost_equalize = MC_AVERAGE(cost_equalize, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
cached_cost += TICK_USAGE_REAL - timer
if(state != SS_RUNNING)
return
resumed = 0
cost_atmos_machinery = MC_AVERAGE(cost_atmos_machinery, TICK_DELTA_TO_MS(cached_cost))
currentpart = SSAIR_ACTIVETURFS
if(currentpart == SSAIR_ACTIVETURFS)
timer = TICK_USAGE_REAL
process_active_turfs(resumed)
cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
process_turfs(resumed)
if(state != SS_RUNNING)
return
resumed = 0
currentpart = equalize_enabled ? SSAIR_EQUALIZE : SSAIR_EXCITEDGROUPS
if(currentpart == SSAIR_EQUALIZE)
process_turf_equalize(resumed)
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_EXCITEDGROUPS
if(currentpart == SSAIR_EXCITEDGROUPS)
timer = TICK_USAGE_REAL
process_excited_groups(resumed)
cost_groups = MC_AVERAGE(cost_groups, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_FINALIZE_TURFS
if(currentpart == SSAIR_FINALIZE_TURFS)
finish_turf_processing(resumed)
if(state != SS_RUNNING)
return
resumed = 0
@@ -141,32 +210,38 @@ SUBSYSTEM_DEF(air)
if(currentpart == SSAIR_HIGHPRESSURE)
timer = TICK_USAGE_REAL
if(!resumed)
cached_cost = 0
process_high_pressure_delta(resumed)
cost_highpressure = MC_AVERAGE(cost_highpressure, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
cached_cost += TICK_USAGE_REAL - timer
if(state != SS_RUNNING)
return
cost_highpressure = MC_AVERAGE(cost_highpressure, TICK_DELTA_TO_MS(cached_cost))
resumed = 0
currentpart = SSAIR_HOTSPOTS
if(currentpart == SSAIR_HOTSPOTS)
timer = TICK_USAGE_REAL
if(!resumed)
cached_cost = 0
process_hotspots(resumed)
cost_hotspots = MC_AVERAGE(cost_hotspots, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
cached_cost += TICK_USAGE_REAL - timer
if(state != SS_RUNNING)
return
cost_hotspots = MC_AVERAGE(cost_hotspots, TICK_DELTA_TO_MS(cached_cost))
resumed = 0
currentpart = SSAIR_SUPERCONDUCTIVITY
currentpart = heat_enabled ? SSAIR_TURF_CONDUCTION : SSAIR_REBUILD_PIPENETS
if(currentpart == SSAIR_SUPERCONDUCTIVITY)
// Heat -- slow and of questionable usefulness. Off by default for this reason. Pretty cool, though.
if(currentpart == SSAIR_TURF_CONDUCTION)
timer = TICK_USAGE_REAL
process_super_conductivity(resumed)
if(process_turf_heat(TICK_REMAINING_MS))
pause()
cost_superconductivity = MC_AVERAGE(cost_superconductivity, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_REBUILD_PIPENETS
currentpart = SSAIR_REBUILD_PIPENETS
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
if (!resumed)
@@ -201,19 +276,6 @@ SUBSYSTEM_DEF(air)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_super_conductivity(resumed = 0)
if (!resumed)
src.currentrun = active_super_conductivity.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/T = currentrun[currentrun.len]
currentrun.len--
T.super_conduct()
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_hotspots(resumed = 0)
if (!resumed)
src.currentrun = hotspots.Copy()
@@ -241,114 +303,40 @@ SUBSYSTEM_DEF(air)
return
/datum/controller/subsystem/air/proc/process_turf_equalize(resumed = 0)
if(process_turf_equalize_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
if(process_turf_equalize_auxtools(TICK_REMAINING_MS))
pause()
/*
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
src.currentrun = active_turfs.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/open/T = currentrun[currentrun.len]
currentrun.len--
if (T)
T.equalize_pressure_in_zone(fire_count)
//equalize_pressure_in_zone(T, fire_count)
if (MC_TICK_CHECK)
return
*/
/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
if(process_active_turfs_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
/datum/controller/subsystem/air/proc/process_turfs(resumed = 0)
if(process_turfs_auxtools(TICK_REMAINING_MS))
pause()
/*
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
src.currentrun = active_turfs.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/open/T = currentrun[currentrun.len]
currentrun.len--
if (T)
T.process_cell(fire_count)
if (MC_TICK_CHECK)
return
*/
/datum/controller/subsystem/air/proc/process_excited_groups(resumed = 0)
if(process_excited_groups_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
if(process_excited_groups_auxtools(TICK_REMAINING_MS))
pause()
/*
if (!resumed)
src.currentrun = excited_groups.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/excited_group/EG = currentrun[currentrun.len]
currentrun.len--
EG.breakdown_cooldown++
EG.dismantle_cooldown++
if(EG.breakdown_cooldown >= EXCITED_GROUP_BREAKDOWN_CYCLES)
EG.self_breakdown()
else if(EG.dismantle_cooldown >= EXCITED_GROUP_DISMANTLE_CYCLES)
EG.dismantle()
if (MC_TICK_CHECK)
return
*/
/datum/controller/subsystem/air/proc/process_active_turfs_extools()
/datum/controller/subsystem/air/proc/process_turf_equalize_extools()
/datum/controller/subsystem/air/proc/process_excited_groups_extools()
/datum/controller/subsystem/air/proc/get_amt_excited_groups()
/datum/controller/subsystem/air/proc/get_amt_active_turfs()
/datum/controller/subsystem/air/proc/get_amt_gas_mixes()
/datum/controller/subsystem/air/proc/get_max_gas_mixes()
/datum/controller/subsystem/air/proc/add_to_active_extools()
/datum/controller/subsystem/air/proc/remove_from_active_extools()
/datum/controller/subsystem/air/proc/get_active_turfs()
/datum/controller/subsystem/air/proc/clear_active_turfs()
/datum/controller/subsystem/air/proc/finish_turf_processing(resumed = 0)
if(finish_turf_processing_auxtools(TICK_REMAINING_MS))
pause()
/datum/controller/subsystem/air/proc/remove_from_active(turf/open/T)
remove_from_active_extools(T)
#ifdef VISUALIZE_ACTIVE_TURFS
T.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#00ff00")
#endif
if(istype(T))
T.set_excited(FALSE)
T.eg_garbage_collect()
/datum/controller/subsystem/air/proc/equalize_turfs(resumed = 0)
if(equalize_turfs_auxtools(TICK_REMAINING_MS))
pause()
/datum/controller/subsystem/air/proc/add_to_active(turf/open/T, blockchanges = 1)
if(istype(T) && T.air)
#ifdef VISUALIZE_ACTIVE_TURFS
T.add_atom_colour("#00ff00", TEMPORARY_COLOUR_PRIORITY)
#endif
T.set_excited(TRUE)
add_to_active_extools(T)
if(blockchanges)
T.eg_garbage_collect()
else if(T.flags_1 & INITIALIZED_1)
for(var/turf/S in T.atmos_adjacent_turfs)
add_to_active(S)
else if(map_loading)
if(queued_for_activation)
queued_for_activation[T] = T
return
else
T.requires_activation = TRUE
/datum/controller/subsystem/air/proc/post_process_turfs(resumed = 0)
if(post_process_turfs_auxtools(TICK_REMAINING_MS))
pause()
/datum/controller/subsystem/air/proc/equalize_turfs_auxtools()
/datum/controller/subsystem/air/proc/post_process_turfs_auxtools()
/datum/controller/subsystem/air/proc/turf_process_time()
/datum/controller/subsystem/air/proc/heat_process_time()
/datum/controller/subsystem/air/proc/process_turf_heat()
/datum/controller/subsystem/air/StartLoadingMap()
LAZYINITLIST(queued_for_activation)
map_loading = TRUE
/datum/controller/subsystem/air/StopLoadingMap()
map_loading = FALSE
for(var/T in queued_for_activation)
add_to_active(T)
queued_for_activation.Cut()
/datum/controller/subsystem/air/proc/setup_allturfs()
var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))
@@ -356,7 +344,6 @@ SUBSYSTEM_DEF(air)
// Clear active turfs - faster than removing every single turf in the world
// one-by-one, and Initalize_Atmos only ever adds `src` back in.
clear_active_turfs()
for(var/thing in turfs_to_init)
var/turf/T = thing
@@ -364,64 +351,6 @@ SUBSYSTEM_DEF(air)
continue
T.Initalize_Atmos(times_fired)
CHECK_TICK
var/starting_ats = get_amt_active_turfs()
if(starting_ats)
sleep(world.tick_lag)
var/timer = world.timeofday
log_mapping("There are [starting_ats] active turfs at roundstart caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required).")
var/list/turfs_to_check = get_active_turfs()
for(var/T in turfs_to_check)
GLOB.active_turfs_startlist += T
//now lets clear out these active turfs
do
var/list/new_turfs_to_check = list()
for(var/turf/open/T in turfs_to_check)
new_turfs_to_check += T.resolve_active_graph()
CHECK_TICK
for(var/T in new_turfs_to_check)
add_to_active_extools(T)
turfs_to_check = new_turfs_to_check
while (turfs_to_check.len)
var/ending_ats = get_amt_active_turfs()
/*for(var/thing in excited_groups)
var/datum/excited_group/EG = thing
//EG.self_breakdown(space_is_all_consuming = 1)
//EG.dismantle()
CHECK_TICK*/
var/msg = "HEY! LISTEN! [DisplayTimeText(world.timeofday - timer)] were wasted processing [starting_ats] turf(s) (connected to [ending_ats] other turfs) with atmos differences at round start."
to_chat(world, "<span class='boldannounce'>[msg]</span>")
warning(msg)
/turf/open/proc/resolve_active_graph()
. = list()
/*
var/datum/excited_group/EG = excited_group
if (blocks_air || !air)
return
if (!EG)
EG = new
EG.add_turf(src)
for (var/turf/open/ET in atmos_adjacent_turfs)
if ( ET.blocks_air || !ET.air)
continue
var/ET_EG = ET.excited_group
if (ET_EG)
if (ET_EG != EG)
EG.merge_groups(ET_EG)
EG = excited_group //merge_groups() may decide to replace our current EG
else
EG.add_turf(ET)
if (!ET.excited)
ET.excited = 1
. += ET*/
/turf/open/space/resolve_active_graph()
return list()
/datum/controller/subsystem/air/proc/setup_atmos_machinery()
for (var/obj/machinery/atmospherics/AM in atmos_machinery)
@@ -460,12 +389,42 @@ SUBSYSTEM_DEF(air)
return pipe_init_dirs_cache[type]["[dir]"]
/proc/get_extools_benchmarks()
/datum/controller/subsystem/air/proc/generate_atmos()
atmos_gen = list()
for(var/T in subtypesof(/datum/atmosphere))
var/datum/atmosphere/atmostype = T
atmos_gen[initial(atmostype.id)] = new atmostype
/datum/controller/subsystem/air/proc/preprocess_gas_string(gas_string)
if(!atmos_gen)
generate_atmos()
if(!atmos_gen[gas_string])
return gas_string
var/datum/atmosphere/mix = atmos_gen[gas_string]
return mix.gas_string
/datum/controller/subsystem/air/proc/start_processing_machine(obj/machinery/machine)
if(machine.atmos_processing)
return
machine.atmos_processing = TRUE
atmos_machinery += machine
/datum/controller/subsystem/air/proc/stop_processing_machine(obj/machinery/machine)
if(!machine.atmos_processing)
return
machine.atmos_processing = FALSE
atmos_machinery -= machine
currentrun -= machine
#undef SSAIR_PIPENETS
#undef SSAIR_ATMOSMACHINERY
#undef SSAIR_ACTIVETURFS
#undef SSAIR_EXCITEDGROUPS
#undef SSAIR_HIGHPRESSURE
#undef SSAIR_HOTSPOTS
#undef SSAIR_SUPERCONDUCTIVITY
#undef SSAIR_TURF_CONDUCTION
#undef SSAIR_REBUILD_PIPENETS
#undef SSAIR_EQUALIZE
#undef SSAIR_ACTIVETURFS
#undef SSAIR_TURF_POST_PROCESS
#undef SSAIR_FINALIZE_TURFS
#undef SSAIR_ATMOSMACHINERY_AIR
+18 -1
View File
@@ -14,6 +14,9 @@ SUBSYSTEM_DEF(atoms)
var/list/BadInitializeCalls = list()
/// Atoms that will be deleted once the subsystem is initialized
var/list/queued_deletions = list()
initialized = INITIALIZATION_INSSATOMS
/datum/controller/subsystem/atoms/Initialize(timeofday)
@@ -62,6 +65,12 @@ SUBSYSTEM_DEF(atoms)
testing("Late initialized [late_loaders.len] atoms")
late_loaders.Cut()
for (var/queued_deletion in queued_deletions)
qdel(queued_deletion)
testing("[queued_deletions.len] atoms were queued for deletion.")
queued_deletions.Cut()
/// Init this specific atom
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments)
var/the_type = A.type
@@ -143,7 +152,7 @@ SUBSYSTEM_DEF(atoms)
. += "Path : [path] \n"
var/fails = BadInitializeCalls[path]
if(fails & BAD_INIT_DIDNT_INIT)
. += "- Didn't call atom/Initialize()\n"
. += "- Didn't call atom/Initialize(mapload)\n"
if(fails & BAD_INIT_NO_HINT)
. += "- Didn't return an Initialize hint\n"
if(fails & BAD_INIT_QDEL_BEFORE)
@@ -151,6 +160,14 @@ SUBSYSTEM_DEF(atoms)
if(fails & BAD_INIT_SLEPT)
. += "- Slept during Initialize()\n"
/// 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)
// Atoms SS has already completed, just kill it now.
qdel(target)
else
queued_deletions += WEAKREF(target)
/datum/controller/subsystem/atoms/Shutdown()
var/initlog = InitLog()
if(initlog)
+3 -3
View File
@@ -69,15 +69,15 @@ SUBSYSTEM_DEF(augury)
SSaugury.watchers += owner
to_chat(owner, "<span class='notice'>You are now auto-following debris.</span>")
active = TRUE
UpdateButtonIcon()
UpdateButtons()
/datum/action/innate/augury/Deactivate()
SSaugury.watchers -= owner
to_chat(owner, "<span class='notice'>You are no longer auto-following debris.</span>")
active = FALSE
UpdateButtonIcon()
UpdateButtons()
/datum/action/innate/augury/UpdateButtonIcon(status_only = FALSE, force)
/datum/action/innate/augury/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force)
..()
if(active)
button.icon_state = "template_active"
+1 -1
View File
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(autotransfer)
if(world.time < targettime)
return
if(maxvotes == NO_MAXVOTES_CAP || maxvotes > curvotes)
SSvote.initiate_vote("transfer","server")
SSvote.initiate_vote("transfer","server", votesystem=APPROVAL_VOTING)
targettime = targettime + voteinterval
curvotes++
else
+1 -1
View File
@@ -60,7 +60,7 @@ SUBSYSTEM_DEF(blackmarket)
if (!targetturf) // This shouldn't happen.
continue
to_chat(recursive_loc_check(purchase.uplink.loc, /mob), "<span class='notice'>[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds.</span>")
addtimer(CALLBACK(src, /datum/controller/subsystem/blackmarket/proc/fake_teleport, purchase.entry.spawn_item(), targetturf), 60 SECONDS) // do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks.
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket, fake_teleport), purchase.entry.spawn_item(), targetturf), 60 SECONDS) // do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks.
queued_purchases -= purchase
qdel(purchase)
if(SHIPPING_METHOD_LAUNCH) // Get the current location of the uplink if it exists, then throws the item from space at the station from a random direction.
+9
View File
@@ -0,0 +1,9 @@
SUBSYSTEM_DEF(callbacks)
name = "Auxtools Callbacks"
flags = SS_TICKER | SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CALLBACKS
/datum/controller/subsystem/callbacks/fire()
if(process_atmos_callbacks(TICK_REMAINING_MS))
pause()
+47 -13
View File
@@ -1,33 +1,67 @@
#define COMMUNICATION_COOLDOWN 300
#define COMMUNICATION_COOLDOWN_AI 300
#define COMMUNICATION_COOLDOWN (30 SECONDS)
#define COMMUNICATION_COOLDOWN_AI (30 SECONDS)
#define COMMUNICATION_COOLDOWN_MEETING (5 MINUTES)
SUBSYSTEM_DEF(communications)
name = "Communications"
flags = SS_NO_INIT | SS_NO_FIRE
var/silicon_message_cooldown
var/nonsilicon_message_cooldown
COOLDOWN_DECLARE(silicon_message_cooldown)
COOLDOWN_DECLARE(nonsilicon_message_cooldown)
COOLDOWN_DECLARE(emergency_meeting_cooldown)
/datum/controller/subsystem/communications/proc/can_announce(mob/living/user, is_silicon)
if(is_silicon && silicon_message_cooldown > world.time)
. = FALSE
else if(!is_silicon && nonsilicon_message_cooldown > world.time)
. = FALSE
if(is_silicon && COOLDOWN_FINISHED(src, silicon_message_cooldown))
return TRUE
else if(!is_silicon && COOLDOWN_FINISHED(src, nonsilicon_message_cooldown))
return TRUE
else
. = TRUE
return FALSE
/datum/controller/subsystem/communications/proc/make_announcement(mob/living/user, is_silicon, input)
if(!can_announce(user, is_silicon))
return FALSE
if(is_silicon)
minor_announce(html_decode(input),"[user.name] Announces:")
silicon_message_cooldown = world.time + COMMUNICATION_COOLDOWN_AI
COOLDOWN_START(src, silicon_message_cooldown, COMMUNICATION_COOLDOWN_AI)
else
priority_announce(html_decode(user.treat_message(input)), null, 'sound/misc/announce.ogg', "Captain")
nonsilicon_message_cooldown = world.time + COMMUNICATION_COOLDOWN
priority_announce(html_decode(user.treat_message(input)), null, 'sound/misc/announce.ogg', "Captain", has_important_message = TRUE)
COOLDOWN_START(src, nonsilicon_message_cooldown, COMMUNICATION_COOLDOWN)
user.log_talk(input, LOG_SAY, tag="priority announcement")
message_admins("[ADMIN_LOOKUPFLW(user)] has made a priority announcement.")
/**
* Check if a mob can call an emergency meeting
*
* Should only really happen during april fools.
* Checks to see that it's been at least 5 minutes since the last emergency meeting call.
* Arguments:
* * user - Mob who called the meeting
*/
/datum/controller/subsystem/communications/proc/can_make_emergency_meeting(mob/living/user)
if(!(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
return FALSE
else if(COOLDOWN_FINISHED(src, emergency_meeting_cooldown))
return TRUE
else
return FALSE
/**
* Call an emergency meeting
*
* Communications subsystem wrapper for the call_emergency_meeting world proc.
* Checks to make sure the proc can be called, and handles
* relevant logging and timing. See that proc definition for more detail.
* Arguments:
* * user - Mob who called the meeting
*/
/datum/controller/subsystem/communications/proc/emergency_meeting(mob/living/user)
if(!can_make_emergency_meeting(user))
return FALSE
call_emergency_meeting(user, get_area(user))
COOLDOWN_START(src, emergency_meeting_cooldown, COMMUNICATION_COOLDOWN_MEETING)
message_admins("[ADMIN_LOOKUPFLW(user)] has called an emergency meeting.")
/datum/controller/subsystem/communications/proc/send_message(datum/comm_message/sending,print = TRUE,unique = FALSE)
for(var/obj/machinery/computer/communications/C in GLOB.machines)
if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
@@ -40,7 +74,7 @@ SUBSYSTEM_DEF(communications)
var/obj/item/paper/P = new /obj/item/paper(C.loc)
P.name = "paper - '[sending.title]'"
P.info = sending.content
P.update_icon()
P.update_appearance()
#undef COMMUNICATION_COOLDOWN
#undef COMMUNICATION_COOLDOWN_AI
+2 -2
View File
@@ -190,9 +190,9 @@ SUBSYSTEM_DEF(dbcore)
for (var/thing in querys)
var/datum/db_query/query = thing
if (warn)
INVOKE_ASYNC(query, /datum/db_query.proc/warn_execute)
INVOKE_ASYNC(query, TYPE_PROC_REF(/datum/db_query, warn_execute))
else
INVOKE_ASYNC(query, /datum/db_query.proc/Execute)
INVOKE_ASYNC(query, TYPE_PROC_REF(/datum/db_query, Execute))
for (var/thing in querys)
var/datum/db_query/query = thing
+1 -1
View File
@@ -55,6 +55,6 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
fullid += "[key]"
if(length(named_arguments))
named_arguments = sortList(named_arguments)
named_arguments = sort_list(named_arguments)
fullid += named_arguments
return list2params(fullid)
+7
View File
@@ -47,6 +47,13 @@ SUBSYSTEM_DEF(economy)
"rainbow" = 1000)
var/list/bank_accounts = list() //List of normal accounts (not department accounts)
var/list/dep_cards = list()
///The modifier multiplied to the value of bounties paid out.
var/bounty_modifier = 1
///The modifier multiplied to the value of cargo pack prices.
var/pack_price_modifier = 1
var/civ_bounty_tracker = 0
/// Contains the message to send to newscasters about earnings, updated on price_update()
/datum/controller/subsystem/economy/Initialize(timeofday)
var/budget_to_hand_out = round(budget_pool / department_accounts.len)
-35
View File
@@ -93,41 +93,6 @@ SUBSYSTEM_DEF(events)
else if(. == EVENT_READY)
E.runEvent(random = TRUE)
//allows a client to trigger an event
//aka Badmin Central
// > Not in modules/admin
// REEEEEEEEE
// Why the heck is this here! Took me so damn long to find!
/client/proc/forceEvent()
set name = "Trigger Event"
set category = "Admin.Events"
if(!holder ||!check_rights(R_FUN))
return
holder.forceEvent()
/datum/admins/proc/forceEvent()
var/dat = ""
var/normal = ""
var/magic = ""
var/holiday = ""
for(var/datum/round_event_control/E in SSevents.control)
dat = "<BR><A href='?src=[REF(src)];[HrefToken()];forceevent=[REF(E)]'>[E]</A>"
if(E.holidayID)
holiday += dat
else if(E.wizardevent)
magic += dat
else
normal += dat
dat = normal + "<BR>" + magic + "<BR>" + holiday
var/datum/browser/popup = new(usr, "forceevent", "Force Random Event", 300, 750)
popup.set_content(dat)
popup.open()
/*
//////////////
// HOLIDAYS //
+23 -4
View File
@@ -33,7 +33,7 @@ SUBSYSTEM_DEF(fail2topic)
while(i <= length(rate_limiting))
var/ip = rate_limiting[i]
var/last_attempt = rate_limiting[ip]
if(world.time - last_attempt > rate_limit)
if(REALTIMEOFDAY - last_attempt > rate_limit)
rate_limiting -= ip
fail_counts -= ip
else //if we remove that, and the next element is in its place. check that instead of incrementing.
@@ -44,7 +44,24 @@ SUBSYSTEM_DEF(fail2topic)
/datum/controller/subsystem/fail2topic/Shutdown()
DropFirewallRule()
/datum/controller/subsystem/fail2topic/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, rule_name))
return FALSE
return ..()
/datum/controller/subsystem/fail2topic/CanProcCall(procname)
. = ..()
if(.)
switch(procname)
if("IsRateLimited")
return FALSE
if("BanFromFirewall")
return FALSE
/datum/controller/subsystem/fail2topic/proc/IsRateLimited(ip)
if(IsAdminAdvancedProcCall())
return FALSE
var/last_attempt = rate_limiting[ip]
var/static/datum/config_entry/keyed_list/topic_rate_limit_whitelist/cached_whitelist_entry
@@ -58,12 +75,12 @@ SUBSYSTEM_DEF(fail2topic)
if (active_bans[ip])
return TRUE
rate_limiting[ip] = world.time
rate_limiting[ip] = REALTIMEOFDAY
if (isnull(last_attempt))
return FALSE
if (world.time - last_attempt > rate_limit)
if (REALTIMEOFDAY - last_attempt > rate_limit)
fail_counts -= ip
return FALSE
else
@@ -82,8 +99,10 @@ SUBSYSTEM_DEF(fail2topic)
/datum/controller/subsystem/fail2topic/proc/BanFromFirewall(ip)
if (!enabled)
return
if(IsAdminAdvancedProcCall())
return
active_bans[ip] = world.time
active_bans[ip] = REALTIMEOFDAY
fail_counts -= ip
rate_limiting -= ip
+2 -2
View File
@@ -1,5 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(fluids)
name = "Fluids"
wait = 20
wait = 10
stat_tag = "FD" //its actually Fluid Ducts
flags = SS_NO_INIT | SS_TICKER
flags = SS_NO_INIT
+98 -101
View File
@@ -29,37 +29,35 @@ SUBSYSTEM_DEF(garbage)
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_order = INIT_ORDER_GARBAGE
var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level
//Stat tracking
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/totaldels = 0
var/totalgcs = 0
var/highest_del_time = 0
var/highest_del_tickusage = 0
var/highest_del_ms = 0
var/highest_del_type_string = ""
var/list/pass_counts
var/list/fail_counts
var/list/items = list() // Holds our qdel_item statistics datums
var/list/items = list() // Holds our qdel_item statistics datums
//Queue
var/list/queues
#ifdef LEGACY_REFERENCE_TRACKING
#ifdef REFERENCE_TRACKING
var/list/reference_find_on_fail = list()
#ifdef REFERENCE_TRACKING_DEBUG
//Should we save found refs. Used for unit testing
var/should_save_refs = FALSE
#endif
#endif
/datum/controller/subsystem/garbage/PreInit()
queues = new(GC_QUEUE_COUNT)
pass_counts = new(GC_QUEUE_COUNT)
fail_counts = new(GC_QUEUE_COUNT)
for(var/i in 1 to GC_QUEUE_COUNT)
queues[i] = list()
pass_counts[i] = 0
fail_counts[i] = 0
InitQueues()
/datum/controller/subsystem/garbage/stat_entry(msg)
var/list/counts = list()
@@ -86,17 +84,22 @@ SUBSYSTEM_DEF(garbage)
var/list/dellog = list()
//sort by how long it's wasted hard deleting
sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
sortTim(items, cmp=GLOBAL_PROC_REF(cmp_qdel_item_time), associative = TRUE)
for(var/path in items)
var/datum/qdel_item/I = items[path]
dellog += "Path: [path]"
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
dellog += "\tSUSPENDED FOR LAG"
if (I.failures)
dellog += "\tFailures: [I.failures]"
dellog += "\tqdel() Count: [I.qdels]"
dellog += "\tDestroy() Cost: [I.destroy_time]ms"
if (I.hard_deletes)
dellog += "\tTotal Hard Deletes [I.hard_deletes]"
dellog += "\tTotal Hard Deletes: [I.hard_deletes]"
dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms"
dellog += "\tHighest Time Spent Hard Deleting: [I.hard_delete_max]ms"
if (I.hard_deletes_over_threshold)
dellog += "\tHard Deletes Over Threshold: [I.hard_deletes_over_threshold]"
if (I.slept_destroy)
dellog += "\tSleeps: [I.slept_destroy]"
if (I.no_respect_force)
@@ -122,6 +125,15 @@ SUBSYSTEM_DEF(garbage)
/datum/controller/subsystem/garbage/proc/InitQueues()
if (isnull(queues)) // Only init the queues if they don't already exist, prevents overriding of recovered lists
queues = new(GC_QUEUE_COUNT)
pass_counts = new(GC_QUEUE_COUNT)
fail_counts = new(GC_QUEUE_COUNT)
for(var/i in 1 to GC_QUEUE_COUNT)
queues[i] = list()
pass_counts[i] = 0
fail_counts[i] = 0
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
if (level == GC_QUEUE_CHECK)
@@ -153,7 +165,6 @@ SUBSYSTEM_DEF(garbage)
if(GCd_at_time > cut_off_time)
break // Everything else is newer, skip them
count++
var/refID = L[2]
var/datum/D
D = locate(refID)
@@ -162,8 +173,8 @@ SUBSYSTEM_DEF(garbage)
++gcedlasttick
++totalgcs
pass_counts[level]++
#ifdef LEGACY_REFERENCE_TRACKING
reference_find_on_fail -= refID //It's deleted we don't care anymore.
#ifdef REFERENCE_TRACKING
reference_find_on_fail -= refID //It's deleted we don't care anymore.
#endif
if (MC_TICK_CHECK)
return
@@ -171,35 +182,43 @@ SUBSYSTEM_DEF(garbage)
// Something's still referring to the qdel'd object.
fail_counts[level]++
#ifdef REFERENCE_TRACKING
var/ref_searching = FALSE
#endif
switch (level)
if (GC_QUEUE_CHECK)
#ifdef REFERENCE_TRACKING
D.find_references()
#elif defined(LEGACY_REFERENCE_TRACKING)
if(reference_find_on_fail[refID])
D.find_references_legacy()
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum, find_references))
ref_searching = TRUE
#ifdef GC_FAILURE_HARD_LOOKUP
else
D.find_references_legacy()
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum, find_references))
ref_searching = TRUE
#endif
reference_find_on_fail -= refID
#endif
var/type = D.type
var/datum/qdel_item/I = items[type]
#ifdef TESTING
log_world("## TESTING: GC: -- \ref[D] | [type] was unable to be GC'd --")
#ifdef TESTING
for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage
var/client/admin = c
if(!check_rights_for(admin, R_ADMIN))
continue
to_chat(admin, "## TESTING: GC: -- [ADMIN_VV(D)] | [type] was unable to be GC'd --")
testing("GC: -- \ref[src] | [type] was unable to be GC'd --")
#endif
#ifdef REFERENCE_TRACKING
GLOB.deletion_failures += D //It should no longer be bothered by the GC, manual deletion only.
continue
#endif
I.failures++
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
#ifdef REFERENCE_TRACKING
if(ref_searching)
return //ref searching intentionally cancels all further fires while running so things that hold references don't end up getting deleted, so we want to return here instead of continue
#endif
continue
if (GC_QUEUE_HARDDELETE)
HardDelete(D)
if (MC_TICK_CHECK)
@@ -208,27 +227,17 @@ SUBSYSTEM_DEF(garbage)
Queue(D, level+1)
#ifdef REFERENCE_TRACKING
if(ref_searching)
return
#endif
if (MC_TICK_CHECK)
return
if (count)
queue.Cut(1,count+1)
count = 0
#ifdef LEGACY_REFERENCE_TRACKING
/datum/controller/subsystem/garbage/proc/add_type_to_findref(type)
if(!ispath(type))
return "NOT A VAILD PATH"
reference_find_on_fail_types |= typecacheof(type)
/datum/controller/subsystem/garbage/proc/remove_type_from_findref(type)
if(!ispath(type))
return "NOT A VALID PATH"
reference_find_on_fail_types -= typesof(type)
/datum/controller/subsystem/garbage/proc/clear_findref_types()
reference_find_on_fail_types = list()
#endif
/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
if (isnull(D))
return
@@ -238,63 +247,66 @@ SUBSYSTEM_DEF(garbage)
var/gctime = world.time
var/refid = "\ref[D]"
#ifdef LEGACY_REFERENCE_TRACKING
if(reference_find_on_fail_types[D.type])
SSgarbage.reference_find_on_fail[REF(D)] = TRUE
#endif
D.gc_destroyed = gctime
var/list/queue = queues[level]
queue[++queue.len] = list(gctime, refid) // not += for byond reasons
//this is mainly to separate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
var/time = world.timeofday
var/tick = TICK_USAGE
var/ticktime = world.time
++delslasttick
++totaldels
var/type = D.type
var/refID = "\ref[D]"
var/tick_usage = TICK_USAGE
del(D)
tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100))
tick_usage = TICK_USAGE_TO_MS(tick_usage)
var/datum/qdel_item/I = items[type]
I.hard_deletes++
I.hard_delete_time += TICK_DELTA_TO_MS(tick)
I.hard_delete_time += tick_usage
if (tick_usage > I.hard_delete_max)
I.hard_delete_max = tick_usage
if (tick_usage > highest_del_ms)
highest_del_ms = tick_usage
highest_del_type_string = "[type]"
var/time = MS2DS(tick_usage)
if (tick > highest_del_tickusage)
highest_del_tickusage = tick
time = world.timeofday - time
if (!time && TICK_DELTA_TO_MS(tick) > 1)
time = TICK_DELTA_TO_MS(tick)/100
if (time > highest_del_time)
highest_del_time = time
if (time > 10)
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
if (time > 0.1 SECONDS)
postpone(time)
var/threshold = CONFIG_GET(number/hard_deletes_overrun_threshold)
if (threshold && (time > threshold SECONDS))
if (!(I.qdel_flags & QDEL_ITEM_ADMINS_WARNED))
log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).")
I.qdel_flags |= QDEL_ITEM_ADMINS_WARNED
I.hard_deletes_over_threshold++
var/overrun_limit = CONFIG_GET(number/hard_deletes_overrun_limit)
if (overrun_limit && I.hard_deletes_over_threshold >= overrun_limit)
I.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG
/datum/controller/subsystem/garbage/Recover()
InitQueues() //We first need to create the queues before recovering data
if (istype(SSgarbage.queues))
for (var/i in 1 to SSgarbage.queues.len)
queues[i] |= SSgarbage.queues[i]
/// Qdel Item: Holds statistics on each type that passes thru qdel
/datum/qdel_item
var/name = ""
var/qdels = 0 //Total number of times it's passed thru qdel.
var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy()
var/failures = 0 //Times it was queued for soft deletion but failed to soft delete.
var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions
var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type.
var/no_respect_force = 0//Number of times it's not respected force=TRUE
var/no_hint = 0 //Number of times it's not even bother to give a qdel hint
var/slept_destroy = 0 //Number of times it's slept in its destroy
var/name = "" //!Holds the type as a string for this type
var/qdels = 0 //!Total number of times it's passed thru qdel.
var/destroy_time = 0 //!Total amount of milliseconds spent processing this type's Destroy()
var/failures = 0 //!Times it was queued for soft deletion but failed to soft delete.
var/hard_deletes = 0 //!Different from failures because it also includes QDEL_HINT_HARDDEL deletions
var/hard_delete_time = 0 //!Total amount of milliseconds spent hard deleting this type.
var/hard_delete_max = 0 //!Highest time spent hard_deleting this in ms.
var/hard_deletes_over_threshold = 0 //!Number of times hard deletes took longer than the configured threshold
var/no_respect_force = 0 //!Number of times it's not respected force=TRUE
var/no_hint = 0 //!Number of times it's not even bother to give a qdel hint
var/slept_destroy = 0 //!Number of times it's slept in its destroy
var/qdel_flags = 0 //!Flags related to this type's trip thru qdel.
/datum/qdel_item/New(mytype)
name = "[mytype]"
@@ -307,12 +319,12 @@ SUBSYSTEM_DEF(garbage)
if(!istype(D))
del(D)
return
var/datum/qdel_item/I = SSgarbage.items[D.type]
if (!I)
I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type)
I.qdels++
if(isnull(D.gc_destroyed))
if (SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
return
@@ -328,12 +340,12 @@ SUBSYSTEM_DEF(garbage)
if(!D)
return
switch(hint)
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
SSgarbage.Queue(D)
if (QDEL_HINT_IWILLGC)
D.gc_destroyed = world.time
return
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
if(!force)
D.gc_destroyed = null //clear the gc variable (important!)
return
@@ -350,17 +362,17 @@ SUBSYSTEM_DEF(garbage)
I.no_respect_force++
SSgarbage.Queue(D)
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete
SSgarbage.Queue(D, GC_QUEUE_HARDDELETE)
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
SSgarbage.HardDelete(D)
#ifdef LEGACY_REFERENCE_TRACKING
if (QDEL_HINT_FINDREFERENCE) //qdel will, if LEGACY_REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
#ifdef REFERENCE_TRACKING
if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.Queue(D)
D.find_references_legacy()
if (QDEL_HINT_IFFAIL_FINDREFERENCE)
D.find_references()
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
SSgarbage.Queue(D)
SSgarbage.reference_find_on_fail[REF(D)] = TRUE
SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
#endif
else
#ifdef TESTING
@@ -371,18 +383,3 @@ SUBSYSTEM_DEF(garbage)
SSgarbage.Queue(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")
#ifdef TESTING
/proc/writeDatumCount()
var/list/datums = list()
for(var/datum/D in world)
datums[D.type] += 1
for(var/datum/D)
datums[D.type] += 1
datums = sortTim(datums, /proc/cmp_numeric_dsc, associative = TRUE)
if(fexists("data/DATUMCOUNT.txt"))
fdel("data/DATUMCOUNT.txt")
var/outfile = file("data/DATUMCOUNT.txt")
for(var/path in datums)
outfile << "[datums[path]]\t\t\t\t\t[path]"
#endif
+130 -44
View File
@@ -30,11 +30,13 @@ SUBSYSTEM_DEF(job)
var/datum/job/new_overflow = GetJob(new_overflow_role)
var/cap = CONFIG_GET(number/overflow_cap)
new_overflow.allow_bureaucratic_error = FALSE
new_overflow.spawn_positions = cap
new_overflow.total_positions = cap
if(new_overflow_role != overflow_role)
var/datum/job/old_overflow = GetJob(overflow_role)
old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
old_overflow.total_positions = initial(old_overflow.total_positions)
overflow_role = new_overflow_role
@@ -45,7 +47,7 @@ SUBSYSTEM_DEF(job)
var/list/all_jobs = subtypesof(/datum/job)
if(!all_jobs.len)
to_chat(world, "<span class='boldannounce'>Error setting up jobs, no job datums found</span>")
return 0
return FALSE
for(var/J in all_jobs)
var/datum/job/job = new J()
@@ -63,7 +65,7 @@ SUBSYSTEM_DEF(job)
name_occupations[job.title] = job
type_occupations[J] = job
return 1
return TRUE
/datum/controller/subsystem/job/proc/GetJob(rank)
@@ -197,8 +199,8 @@ SUBSYSTEM_DEF(job)
continue
var/mob/dead/new_player/candidate = pick(candidates)
if(AssignRole(candidate, command_position))
return 1
return 0
return TRUE
return FALSE
//This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level
@@ -220,7 +222,7 @@ SUBSYSTEM_DEF(job)
var/ai_selected = 0
var/datum/job/job = GetJob("AI")
if(!job)
return 0
return FALSE
for(var/i = job.total_positions, i > 0, i--)
for(var/level in level_order)
var/list/candidates = list()
@@ -231,8 +233,8 @@ SUBSYSTEM_DEF(job)
ai_selected++
break
if(ai_selected)
return 1
return 0
return TRUE
return FALSE
/** Proc DivideOccupations
@@ -365,9 +367,14 @@ SUBSYSTEM_DEF(job)
JobDebug("DO, Handling unrejectable unassigned")
//Mop up people who can't leave.
for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
if(!GiveRandomJob(player))
if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them an assistant
return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
if(player.client.prefs.joblessrole == BERANDOMJOB) //Gives the player a random role if their preferences are set to it
if(!GiveRandomJob(player))
if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them the overflow role
return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
else //If the player prefers to return to lobby or be an assistant, give them assistant
if(!AssignRole(player, SSjob.overflow_role))
if(!GiveRandomJob(player)) //The forced antagonist couldn't be assigned to overflow role (bans, client age) - give a random role
return FALSE //Somehow the forced antagonist couldn't be assigned to the overflow role or the a random role - reroll
return validate_required_jobs(required_jobs)
@@ -427,20 +434,7 @@ SUBSYSTEM_DEF(job)
//If we joined at roundstart we should be positioned at our workstation
if(!joined_late)
var/obj/S = null
for(var/obj/effect/landmark/start/sloc in GLOB.start_landmarks_list)
if(!sloc.job_spawnpoint)
continue
if(sloc.name != rank)
S = sloc //so we can revert to spawning them on top of eachother if something goes wrong
continue
if(locate(/mob/living) in sloc.loc)
continue
S = sloc
sloc.used = TRUE
break
if(length(GLOB.jobspawn_overrides[rank]))
S = pick(GLOB.jobspawn_overrides[rank])
var/atom/S = job.get_roundstart_spawn_point(H)
if(S)
S.JoinPlayerHere(H, FALSE)
if(!S) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
@@ -464,11 +458,11 @@ SUBSYSTEM_DEF(job)
SSpersistence.antag_rep_change[M.client.ckey] += job.GetAntagRep()
/* if(M.client.holder)
if(CONFIG_GET(flag/auto_deadmin_players) || (M.client.prefs?.toggles & DEADMIN_ALWAYS))
if(M.client.holder)
if(CONFIG_GET(flag/auto_deadmin_players) || (M.client.prefs?.deadmin & DEADMIN_ALWAYS))
M.client.holder.auto_deadmin()
else
handle_auto_deadmin_roles(M.client, rank) */
handle_auto_deadmin_roles(M.client, rank)
to_chat(M, "<b>You are the [rank].</b>")
if(job)
@@ -487,8 +481,8 @@ SUBSYSTEM_DEF(job)
if(job && H)
if(job.dresscodecompliant)// CIT CHANGE - dress code compliance
equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items
job.after_spawn(H, M, joined_late) // note: this happens before the mob has a key! M will always have a client, H might not.
equip_loadout(N, H, TRUE)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works
job.after_spawn(H, M.client, joined_late) // note: this happens before the mob has a key! M will always have a client, H might not.
post_equip_loadout(N, H)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works
var/list/tcg_cards
if(ishuman(H))
@@ -498,7 +492,7 @@ SUBSYSTEM_DEF(job)
tcg_cards = N.client.prefs.tcg_cards
if(tcg_cards)
var/obj/item/tcgcard_binder/binder = new(get_turf(H))
H.equip_to_slot_if_possible(binder, SLOT_IN_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)
H.equip_to_slot_if_possible(binder, ITEM_SLOT_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)
for(var/card_type in N.client.prefs.tcg_cards)
if(card_type)
if(islist(H.client.prefs.tcg_cards[card_type]))
@@ -515,19 +509,19 @@ SUBSYSTEM_DEF(job)
binder.decks = N.client.prefs.tcg_decks
return H
/*
/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank)
if(!C?.holder)
return TRUE
var/datum/job/job = GetJob(rank)
if(!job)
return
if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.toggles & DEADMIN_POSITION_HEAD)))
if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.deadmin & DEADMIN_POSITION_HEAD)))
return C.holder.auto_deadmin()
else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || (C.prefs?.toggles & DEADMIN_POSITION_SECURITY)))
else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || (C.prefs?.deadmin & DEADMIN_POSITION_SECURITY)))
return C.holder.auto_deadmin()
else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || (C.prefs?.deadmin & DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths.
return C.holder.auto_deadmin()
else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || (C.prefs?.toggles & DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths.
return C.holder.auto_deadmin()*/
/datum/controller/subsystem/job/proc/setup_officer_positions()
var/datum/job/J = SSjob.GetJob("Security Officer")
@@ -605,8 +599,8 @@ SUBSYSTEM_DEF(job)
if(hpc || epc)
var/relevent_cap = max(hpc, epc)
if((initial_players_to_assign - unassigned.len) >= relevent_cap)
return 1
return 0
return TRUE
return FALSE
/datum/controller/subsystem/job/proc/RejectPlayer(mob/dead/new_player/player)
if(player.mind && player.mind.special_role)
@@ -624,7 +618,7 @@ SUBSYSTEM_DEF(job)
var/oldjobs = SSjob.occupations
sleep(20)
for (var/datum/job/J in oldjobs)
INVOKE_ASYNC(src, .proc/RecoverJob, J)
INVOKE_ASYNC(src, PROC_REF(RecoverJob), J)
/datum/controller/subsystem/job/proc/RecoverJob(datum/job/J)
var/datum/job/newjob = GetJob(J.title)
@@ -690,7 +684,7 @@ SUBSYSTEM_DEF(job)
message_admins(msg)
CRASH(msg)
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE, can_drop = TRUE)
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, bypass_prereqs = FALSE, can_drop = TRUE)
var/mob/the_mob = N
if(!the_mob)
the_mob = M // cause this doesn't get assigned if player is a latejoiner
@@ -700,6 +694,8 @@ SUBSYSTEM_DEF(job)
return
for(var/i in chosen_gear)
var/datum/gear/G = istext(i[LOADOUT_ITEM]) ? text2path(i[LOADOUT_ITEM]) : i[LOADOUT_ITEM]
if(!ispath(G))
continue
G = GLOB.loadout_items[initial(G.category)][initial(G.subcategory)][initial(G.name)]
if(!G)
continue
@@ -708,9 +704,7 @@ SUBSYSTEM_DEF(job)
permitted = FALSE
if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
permitted = FALSE
if(!equipbackpackstuff && G.slot == SLOT_IN_BACKPACK)//snowflake check since plopping stuff in the backpack doesnt work for pre-job equip loadout stuffs
permitted = FALSE
if(equipbackpackstuff && G.slot != SLOT_IN_BACKPACK)//ditto
if(G.handle_post_equip)
permitted = FALSE
if(!permitted)
continue
@@ -719,7 +713,7 @@ SUBSYSTEM_DEF(job)
if(length(i[LOADOUT_COLOR])) //handle loadout colors
//handle polychromic items
if((G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) && length(G.loadout_initial_colors))
var/datum/element/polychromic/polychromic = I.comp_lookup["item_worn_overlays"] //stupid way to do it but GetElement does not work for this
var/datum/element/polychromic/polychromic = LAZYACCESS(I.comp_lookup, "item_worn_overlays") //stupid way to do it but GetElement does not work for this
if(polychromic && istype(polychromic))
var/list/polychromic_entry = polychromic.colors_by_atom[I]
if(polychromic_entry)
@@ -747,7 +741,70 @@ SUBSYSTEM_DEF(job)
I.forceMove(get_turf(C))
else
qdel(I)
else if(!M.equip_to_slot_if_possible(I, SLOT_IN_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
else if(!M.equip_to_slot_if_possible(I, ITEM_SLOT_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
if(can_drop)
I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
else
qdel(I)
/datum/controller/subsystem/job/proc/post_equip_loadout(mob/dead/new_player/N, mob/living/M, bypass_prereqs = FALSE, can_drop = TRUE)
var/mob/the_mob = N
if(!the_mob)
the_mob = M // cause this doesn't get assigned if player is a latejoiner
var/list/chosen_gear = the_mob.client.prefs.loadout_data["SAVE_[the_mob.client.prefs.loadout_slot]"]
if(the_mob.client && the_mob.client.prefs && (chosen_gear && chosen_gear.len))
if(!ishuman(M))//no silicons allowed
return
for(var/i in chosen_gear)
var/datum/gear/G = istext(i[LOADOUT_ITEM]) ? text2path(i[LOADOUT_ITEM]) : i[LOADOUT_ITEM]
if(!ispath(G))
continue
G = GLOB.loadout_items[initial(G.category)][initial(G.subcategory)][initial(G.name)]
if(!G)
continue
var/permitted = TRUE
if(!bypass_prereqs && G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles))
permitted = FALSE
if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
permitted = FALSE
if(!G.handle_post_equip)
permitted = FALSE
if(!permitted)
continue
var/obj/item/I = new G.path
if(I)
if(length(i[LOADOUT_COLOR])) //handle loadout colors
//handle polychromic items
if((G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) && length(G.loadout_initial_colors))
var/datum/element/polychromic/polychromic = LAZYACCESS(I.comp_lookup, "item_worn_overlays") //stupid way to do it but GetElement does not work for this
if(polychromic && istype(polychromic))
var/list/polychromic_entry = polychromic.colors_by_atom[I]
if(polychromic_entry)
if(polychromic.suits_with_helmet_typecache[I.type]) //is this one of those toggleable hood/helmet things?
polychromic.connect_helmet(I,i[LOADOUT_COLOR])
polychromic.colors_by_atom[I] = i[LOADOUT_COLOR]
I.update_icon()
else
//handle non-polychromic items (they only have one color)
I.add_atom_colour(i[LOADOUT_COLOR][1], FIXED_COLOUR_PRIORITY)
I.update_icon()
//when inputting the data it's already sanitized
if(i[LOADOUT_CUSTOM_NAME])
var/custom_name = i[LOADOUT_CUSTOM_NAME]
I.name = custom_name
if(i[LOADOUT_CUSTOM_DESCRIPTION])
var/custom_description = i[LOADOUT_CUSTOM_DESCRIPTION]
I.desc = custom_description
if(!M.equip_to_slot_if_possible(I, G.slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first
if(iscarbon(M))
var/mob/living/carbon/C = M
var/obj/item/storage/backpack/B = C.back
if(!B || !SEND_SIGNAL(B, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)) // Otherwise, try to put it in the backpack, for carbons.
if(can_drop)
I.forceMove(get_turf(C))
else
qdel(I)
else if(!M.equip_to_slot_if_possible(I, ITEM_SLOT_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
if(can_drop)
I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
else
@@ -761,6 +818,35 @@ SUBSYSTEM_DEF(job)
return FALSE
job.current_positions = max(0, job.current_positions - 1)
/datum/controller/subsystem/job/proc/get_last_resort_spawn_points()
//bad mojo
var/area/shuttle/arrival/arrivals_area = GLOB.areas_by_type[/area/shuttle/arrival]
if(arrivals_area)
//first check if we can find a chair
var/obj/structure/chair/shuttle_chair = locate() in arrivals_area
if(shuttle_chair)
return shuttle_chair
//last hurrah
var/list/turf/available_turfs = list()
for(var/turf/arrivals_turf in arrivals_area)
if(!is_blocked_turf(arrivals_turf, TRUE))
available_turfs += arrivals_turf
if(length(available_turfs))
return pick(available_turfs)
//pick an open spot on arrivals and dump em
var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival))
if(length(arrivals_turfs))
for(var/turf/arrivals_turf in arrivals_turfs)
if(!is_blocked_turf(arrivals_turf, TRUE))
return arrivals_turf
//last chance, pick ANY spot on arrivals and dump em
return pick(arrivals_turfs)
stack_trace("Unable to find last resort spawn point.")
return GET_ERROR_ROOM
///////////////////////////////////
//Keeps track of all living heads//
///////////////////////////////////
+90 -23
View File
@@ -1,3 +1,19 @@
//As a brief warning to all those who dare tread upon these grounds:
//The bulk of this code here was written years ago, back in the days of 512.
//We were incredibly drunk back then. And nowadays, we've found that being drunk is a hard requirement for working with this code.
//So if you're here to make changes? Brandish a glass. There are many sins here, but it's exactly as engineered as it needs to be.
//We physically won't be able to tell you what half of this code does. The only thing that'll help you here is the ballmer peak.
//Bottoms up, friend. And be sure to drink responsibly. Be sure to fetch some water, too; it eases the hangover. - Bhijn & Myr
// Jukelist indices
#define JUKE_TRACK 1
#define JUKE_CHANNEL 2
#define JUKE_BOX 3
#define JUKE_FALLOFF 4
#define JUKE_SOUND 5
SUBSYSTEM_DEF(jukeboxes)
name = "Jukeboxes"
wait = 5
@@ -25,27 +41,41 @@ SUBSYSTEM_DEF(jukeboxes)
var/channeltoreserve = pick(freejukeboxchannels)
if(!channeltoreserve)
return FALSE
var/sound/song_to_init = sound(T.song_path)
freejukeboxchannels -= channeltoreserve
var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff)
var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff, song_to_init)
song_to_init.status = SOUND_MUTE
song_to_init.environment = 7
song_to_init.channel = channeltoreserve
song_to_init.volume = 1
song_to_init.falloff = jukefalloff
song_to_init.echo = list(0, null, -10000, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
activejukeboxes.len++
activejukeboxes[activejukeboxes.len] = youvegotafreejukebox
//Due to changes in later versions of 512, SOUND_UPDATE no longer properly plays audio when a file is defined in the sound datum. As such, we are now required to init the audio before we can actually do anything with it.
//Downsides to this? This means that you can *only* hear the jukebox audio if you were present on the server when it started playing, and it means that it's now impossible to add loops to the jukebox track list.
var/sound/song_to_init = sound(T.song_path)
song_to_init.status = SOUND_MUTE
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
continue
M.playsound_local(M, null, 100, channel = youvegotafreejukebox[2], S = song_to_init)
SEND_SOUND(M, song_to_init)
return activejukeboxes.len
//Updates jukebox by transferring to different object or modifying falloff.
/datum/controller/subsystem/jukeboxes/proc/updatejukebox(IDtoupdate, obj/jukebox, jukefalloff)
if(islist(activejukeboxes[IDtoupdate]))
if(istype(jukebox))
activejukeboxes[IDtoupdate][JUKE_BOX] = jukebox
if(!isnull(jukefalloff))
activejukeboxes[IDtoupdate][JUKE_FALLOFF] = jukefalloff
/datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove)
if(islist(activejukeboxes[IDtoremove]))
var/jukechannel = activejukeboxes[IDtoremove][2]
var/jukechannel = activejukeboxes[IDtoremove][JUKE_CHANNEL]
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
@@ -85,37 +115,74 @@ SUBSYSTEM_DEF(jukeboxes)
if(!jukeinfo.len)
stack_trace("Active jukebox without any associated metadata.")
continue
var/datum/track/juketrack = jukeinfo[1]
var/datum/track/juketrack = jukeinfo[JUKE_TRACK]
if(!istype(juketrack))
stack_trace("Invalid jukebox track datum.")
continue
var/obj/jukebox = jukeinfo[3]
var/obj/jukebox = jukeinfo[JUKE_BOX]
if(!istype(jukebox))
stack_trace("Nonexistant or invalid object associated with jukebox.")
continue
var/sound/song_played = sound(juketrack.song_path)
var/list/audible_zlevels = get_multiz_accessible_levels(jukebox.z) //TODO - for multiz refresh, this should use the cached zlevel connections var in SSMapping. For now this is fine!
var/sound/song_played = jukeinfo[JUKE_SOUND]
var/turf/currentturf = get_turf(jukebox)
var/area/currentarea = get_area(jukebox)
var/list/hearerscache = hearers(7, jukebox)
var/targetfalloff = jukeinfo[JUKE_FALLOFF]
var/mixes = ((targetfalloff*250)-750)
var/inrange
var/pressure_factor
song_played.falloff = jukeinfo[4]
var/datum/gas_mixture/source_env = (istype(currentturf) ? currentturf.return_air() : null)
var/datum/gas_mixture/hearer_env //We init this var outside of the mob loop for the sake of performance
var/turf/hearerturf //ditto
var/source_pressure = (istype(source_env) ? source_env.return_pressure() : 0)
song_played.falloff = targetfalloff
song_played.volume = min((targetfalloff * 50), 100)
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS) || !M.can_hear())
M.stop_sound_channel(jukeinfo[2])
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
M.stop_sound_channel(jukeinfo[JUKE_CHANNEL])
continue
var/inrange = FALSE
if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
song_played.status = SOUND_UPDATE
if(get_area(M) == currentarea)
inrange = TRUE
else if(M in hearerscache)
inrange = TRUE
else
song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
inrange = FALSE
song_played.status = SOUND_MUTE | SOUND_UPDATE
if(source_pressure)
hearerturf = get_turf(M)
hearer_env = (istype(hearerturf) ? hearerturf.return_air() : null)
if(istype(hearer_env))
pressure_factor = min(source_pressure, hearer_env.return_pressure())
if(pressure_factor && targetfalloff && M.can_hear() && (hearerturf.z in audible_zlevels))
if(get_area(hearerturf) == currentarea)
inrange = TRUE
else if(M in hearerscache)
inrange = TRUE
song_played.x = (currentturf.x - hearerturf.x) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
song_played.z = (currentturf.y - hearerturf.y) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
song_played.y = (((currentturf.z - hearerturf.z) * 10 * SOUND_DEFAULT_DISTANCE_MULTIPLIER) + ((currentturf.z < hearerturf.z) ? -5 : 5))
if(pressure_factor < ONE_ATMOSPHERE)
song_played.volume = (min((targetfalloff * 50), 100) * max((pressure_factor - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 1))
song_played.echo[1] = (inrange ? 0 : -10000)
song_played.echo[3] = (inrange ? mixes : max(mixes, 0))
song_played.status = SOUND_UPDATE
SEND_SOUND(M, song_played)
CHECK_TICK
return
#undef JUKE_TRACK
#undef JUKE_CHANNEL
#undef JUKE_BOX
#undef JUKE_FALLOFF
#undef JUKE_SOUND
+4 -4
View File
@@ -81,7 +81,6 @@ SUBSYSTEM_DEF(mapping)
to_chat(world, "<span class='boldannounce'>Unable to load next or default map config, defaulting to Box Station</span>")
config = old_config
GLOB.year_integer += config.year_offset
GLOB.announcertype = (config.announcertype == "standard" ? (prob(1) ? "medibot" : "classic") : config.announcertype)
initialize_biomes()
loadWorld()
repopulate_sorted_areas()
@@ -174,7 +173,7 @@ SUBSYSTEM_DEF(mapping)
message_admins("Shuttles in transit detected. Attempting to fast travel. Timeout is [wipe_safety_delay/10] seconds.")
var/list/cleared = list()
for(var/i in in_transit)
INVOKE_ASYNC(src, .proc/safety_clear_transit_dock, i, in_transit[i], cleared)
INVOKE_ASYNC(src, PROC_REF(safety_clear_transit_dock), i, in_transit[i], cleared)
UNTIL((go_ahead < world.time) || (cleared.len == in_transit.len))
do_wipe_turf_reservations()
clearing_reserved_turfs = FALSE
@@ -424,7 +423,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
banned += generateMapList("[global.config.directory]/iceruinblacklist.txt")
banned += generateMapList("[global.config.directory]/stationruinblacklist.txt")
for(var/item in sortList(subtypesof(/datum/map_template/ruin), /proc/cmp_ruincost_priority))
for(var/item in sort_list(subtypesof(/datum/map_template/ruin), GLOBAL_PROC_REF(cmp_ruincost_priority)))
var/datum/map_template/ruin/ruin_type = item
// screen out the abstract subtypes
if(!initial(ruin_type.id))
@@ -502,8 +501,9 @@ GLOBAL_LIST_EMPTY(the_station_areas)
var/away_name
var/datum/space_level/away_level
possible_options += "Custom"
var/answer = input("What kind ? ","Away/VR") as null|anything in list(possible_options + "Custom")
var/answer = input("What kind ? ","Away/VR") as null|anything in possible_options
switch(answer)
if(null)
return
+2 -2
View File
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(materials)
)
///List of stackcrafting recipes for materials using rigid recipes
var/list/rigid_stack_recipes = list(
// new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
)
///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info)
@@ -59,7 +59,7 @@ SUBSYSTEM_DEF(materials)
var/datum/material/mat = x
var/path_name = ispath(mat) ? "[mat]" : "[mat.type]"
combo_params += "[path_name]=[materials_declaration[mat] * multiplier]"
sortTim(combo_params, /proc/cmp_text_asc) // We have to sort now in case the declaration was not in order
sortTim(combo_params, GLOBAL_PROC_REF(cmp_text_asc)) // We have to sort now in case the declaration was not in order
var/combo_index = combo_params.Join("-")
var/list/combo = material_combos[combo_index]
if(!combo)
+1
View File
@@ -42,6 +42,7 @@ SUBSYSTEM_DEF(nightshift)
update_nightshift(night_time, announcing)
/datum/controller/subsystem/nightshift/proc/update_nightshift(active, announce = TRUE, max_level_override)
set waitfor = FALSE
nightshift_active = active
if(announce)
if (active)
+1 -1
View File
@@ -29,7 +29,7 @@ SUBSYSTEM_DEF(npcpool)
invoking = TRUE
invoke_start = world.time
INVOKE_ASYNC(src, .proc/invoke_process, SA)
INVOKE_ASYNC(src, PROC_REF(invoke_process), SA)
if(invoking)
stack_trace("WARNING: [SA] ([SA.type]) slept during NPCPool processing.")
invoking = FALSE
+8 -2
View File
@@ -1,12 +1,18 @@
SUBSYSTEM_DEF(pai)
name = "pAI"
flags = SS_NO_INIT|SS_NO_FIRE
flags = SS_NO_FIRE
var/list/candidates = list()
var/ghost_spam = FALSE
var/spam_delay = 100
var/list/pai_card_list = list()
var/list/restricted_areas = list()
/datum/controller/subsystem/pai/Initialize(var/time_of_day)
restricted_areas += typesof(/area/command/heads_quarters, /area/ai_monitored) // heads quarters and AI monitored places (like the armory)
initialized = TRUE
return ..()
/datum/controller/subsystem/pai/Topic(href, href_list)
if(href_list["download"])
@@ -155,7 +161,7 @@ SUBSYSTEM_DEF(pai)
if(!G.can_reenter_round()) // this should use notify_ghosts() instead one day.
return FALSE
to_chat(G, "<span class='ghostalert'>[user] is requesting a pAI personality! Use the pAI button to submit yourself as one.</span>")
addtimer(CALLBACK(src, .proc/spam_again), spam_delay)
addtimer(CALLBACK(src, PROC_REF(spam_again)), spam_delay)
var/list/available = list()
for(var/datum/paiCandidate/c in SSpai.candidates)
available.Add(check_ready(c))
+74 -35
View File
@@ -5,21 +5,6 @@ SUBSYSTEM_DEF(parallax)
priority = FIRE_PRIORITY_PARALLAX
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun
var/planet_x_offset = 128
var/planet_y_offset = 128
var/random_layer
var/random_parallax_color
//These are cached per client so needs to be done asap so people joining at roundstart do not miss these.
/datum/controller/subsystem/parallax/PreInit()
. = ..()
if(prob(70)) //70% chance to pick a special extra layer
random_layer = pick(/obj/screen/parallax_layer/random/space_gas, /obj/screen/parallax_layer/random/asteroids)
random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. [COLOR_SILVER]
planet_y_offset = rand(100, 160)
planet_x_offset = rand(100, 160)
/datum/controller/subsystem/parallax/fire(resumed = FALSE)
if (!resumed)
@@ -28,28 +13,82 @@ SUBSYSTEM_DEF(parallax)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(length(currentrun))
var/client/processing_client = currentrun[currentrun.len]
currentrun.len--
if (QDELETED(processing_client) || !processing_client.eye)
if(times_fired % 5) // lazy tick
while(length(currentrun))
var/client/processing_client = currentrun[currentrun.len]
currentrun.len--
if (QDELETED(processing_client) || !processing_client.eye)
if (MC_TICK_CHECK)
return
continue
processing_client.parallax_holder?.Update()
if (MC_TICK_CHECK)
return
continue
var/atom/movable/movable_eye = processing_client.eye
if(!istype(movable_eye))
continue
for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc);
if(movable_eye == processing_client.movingmob)
else // full tick
while(length(currentrun))
var/client/processing_client = currentrun[currentrun.len]
currentrun.len--
if (QDELETED(processing_client) || !processing_client.eye)
if (MC_TICK_CHECK)
return
continue
processing_client.parallax_holder?.Update(TRUE)
if (MC_TICK_CHECK)
return
continue
if(!isnull(processing_client.movingmob))
LAZYREMOVE(processing_client.movingmob.client_mobs_in_contents, processing_client.mob)
LAZYADD(movable_eye.client_mobs_in_contents, processing_client.mob)
processing_client.movingmob = movable_eye
if (MC_TICK_CHECK)
return
currentrun = null
/**
* Gets parallax type for zlevel.
*/
/datum/controller/subsystem/parallax/proc/get_parallax_type(z)
return /datum/parallax/space
/**
* Gets parallax for zlevel.
*/
/datum/controller/subsystem/parallax/proc/get_parallax_datum(z)
var/datum_type = get_parallax_type(z)
return new datum_type
/**
* Gets parallax added vis contents for zlevel
*/
/datum/controller/subsystem/parallax/proc/get_parallax_vis_contents(z)
return list()
/**
* Gets parallax motion for a zlevel
*
* Returns null or list(speed, dir deg clockwise from north, windup, turnrate)
* THE RETURNED LIST MUST BE A 4-TUPLE, OR PARALLAX WILL CRASH.
* DO NOT SCREW WITH THIS UNLESS YOU KNOW WHAT YOU ARE DOING.
*
* This will override area motion
*/
/datum/controller/subsystem/parallax/proc/get_parallax_motion(z)
return null
/**
* updates all parallax for clients on a z
*/
/datum/controller/subsystem/parallax/proc/update_clients_on_z(z)
for(var/client/C in GLOB.clients)
if(C.mob.z == z)
C.parallax_holder?.Update()
/**
* resets all parallax for clients on a z
*/
/datum/controller/subsystem/parallax/proc/reset_clients_on_z(z)
for(var/client/C in GLOB.clients)
if(C.mob.z == z)
C.parallax_holder?.Reset()
/**
* updates motion of all clients on z
*/
/datum/controller/subsystem/parallax/proc/update_z_motion(z)
for(var/client/C in GLOB.clients)
if(C.mob.z == z)
C.parallax_holder?.UpdateMotion()
+2 -2
View File
@@ -31,12 +31,12 @@ SUBSYSTEM_DEF(pathfinder)
while(flow[free])
CHECK_TICK
free = (free % lcount) + 1
var/t = addtimer(CALLBACK(src, /datum/flowcache.proc/toolong, free), 150, TIMER_STOPPABLE)
var/t = addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/flowcache, toolong), free), 150, TIMER_STOPPABLE)
flow[free] = t
flow[t] = M
return free
else
return 0
return FALSE
/datum/flowcache/proc/toolong(l)
log_game("Pathfinder route took longer than 150 ticks, src bot [flow[flow[l]]]")
@@ -335,7 +335,7 @@ SUBSYSTEM_DEF(persistence)
if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars)
continue
var/mob/living/carbon/human/original_human = ending_human.mind.original_character
var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve()
if(!original_human || original_human.stat == DEAD || !original_human.all_scars || !(original_human == ending_human))
if(ending_human.client) // i was told if i don't check this every step of the way byond might decide a client ceases to exist mid proc so here we go
ending_human.client.prefs.scars_list["[ending_human.client.prefs.scars_index]"] = ""
@@ -356,7 +356,7 @@ SUBSYSTEM_DEF(persistence)
if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.tcg_cards)
continue
var/mob/living/carbon/human/original_human = ending_human.mind.original_character
var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve()
if(!original_human || original_human.stat == DEAD || !(original_human == ending_human))
continue
@@ -0,0 +1,11 @@
/**
* Persists polly messages across rounds
*/
/datum/controller/subsystem/persistence/LoadGamePersistence()
. = ..()
LoadPolly()
/datum/controller/subsystem/persistence/proc/LoadPolly()
for(var/mob/living/simple_animal/parrot/Polly/P in GLOB.alive_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
@@ -1,11 +0,0 @@
/**
* Persists poly messages across rounds
*/
/datum/controller/subsystem/persistence/LoadGamePersistence()
. = ..()
LoadPoly()
/datum/controller/subsystem/persistence/proc/LoadPoly()
for(var/mob/living/simple_animal/parrot/Poly/P in GLOB.alive_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
@@ -5,24 +5,18 @@
var/list/saved_modes = list(1,2,3)
var/list/saved_chaos = list(5,5,5)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_storytellers = list("foo","bar","baz")
var/average_threat = 50
var/list/saved_maps
/datum/controller/subsystem/persistence/SaveServerPersistence()
. = ..()
CollectRoundtype()
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
CollectStoryteller(mode)
CollectRulesets(mode)
RecordMaps()
/datum/controller/subsystem/persistence/LoadServerPersistence()
. = ..()
LoadRecentModes()
LoadRecentChaos()
LoadRecentStorytellers()
LoadRecentRulesets()
LoadRecentMaps()
@@ -45,30 +39,6 @@
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
saved_storytellers.len = 3
saved_storytellers[3] = saved_storytellers[2]
saved_storytellers[2] = saved_storytellers[1]
saved_storytellers[1] = mode.storyteller.name
var/json_file = file("data/RecentStorytellers.json")
var/list/file_data = list()
file_data["data"] = saved_storytellers
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectRulesets(var/datum/game_mode/dynamic/mode)
saved_dynamic_rules[3] = saved_dynamic_rules[2]
saved_dynamic_rules[2] = saved_dynamic_rules[1]
saved_dynamic_rules[1] = list()
for(var/r in mode.executed_rules)
var/datum/dynamic_ruleset/rule = r
saved_dynamic_rules[1] += rule.config_tag
var/json_file = file("data/RecentRulesets.json")
var/list/file_data = list()
file_data["data"] = saved_dynamic_rules
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/RecordMaps()
saved_maps = saved_maps?.len ? list("[SSmapping.config.map_name]") | saved_maps : list("[SSmapping.config.map_name]")
var/json_file = file("data/RecentMaps.json")
@@ -107,15 +77,6 @@
return
saved_dynamic_rules = json["data"]
/datum/controller/subsystem/persistence/proc/LoadRecentStorytellers()
var/json_file = file("data/RecentStorytellers.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_storytellers = json["data"]
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
var/json_file = file("data/RecentMaps.json")
if(!fexists(json_file))
@@ -17,7 +17,6 @@ SUBSYSTEM_DEF(processing)
/datum/controller/subsystem/processing/fire(resumed = FALSE)
if (!resumed)
currentrun = processing.Copy()
var/delta_time = (flags & SS_TICKER)? (wait * world.tick_lag * 0.1) : (wait * 0.1)
//cache for sanic speed (lists are references anyways)
var/list/current_run = currentrun
@@ -26,7 +25,7 @@ SUBSYSTEM_DEF(processing)
current_run.len--
if(QDELETED(thing))
processing -= thing
else if(thing.process(delta_time) == PROCESS_KILL)
else if(thing.process(wait * 0.1) == PROCESS_KILL)
// fully stop so that a future START_PROCESSING will work
STOP_PROCESSING(src, thing)
if (MC_TICK_CHECK)
@@ -47,5 +46,5 @@ SUBSYSTEM_DEF(processing)
* If you override this do not call parent, as it will return PROCESS_KILL. This is done to prevent objects that dont override process() from staying in the processing list
*/
/datum/proc/process(delta_time)
// SHOULD_NOT_SLEEP(TRUE)
set waitfor = FALSE
return PROCESS_KILL
@@ -22,7 +22,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
/datum/controller/subsystem/processing/quirks/proc/SetupQuirks()
// Sort by Positive, Negative, Neutral; and then by name
var/list/quirk_list = sortList(subtypesof(/datum/quirk), /proc/cmp_quirk_asc)
var/list/quirk_list = sort_list(subtypesof(/datum/quirk), GLOBAL_PROC_REF(cmp_quirk_asc))
for(var/V in quirk_list)
var/datum/quirk/T = V
@@ -49,6 +49,13 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
if (!silent && LAZYLEN(cut))
to_chat(to_chat_target || user, "<span class='boldwarning'>Some quirks have been cut from your character because of these quirks conflicting with your job assignment: [english_list(cut)].</span>")
var/mob/living/carbon/human/H = user
if(istype(H) && H.dna?.species)
var/datum/species/S = H.dna.species
if(S.remove_blacklisted_quirks(H))
to_chat(to_chat_target || user, "<span class='boldwarning'>Some quirks have been cut from your character due to them conflicting with your species: [english_list(S.removed_quirks)]</span>")
/datum/controller/subsystem/processing/quirks/proc/quirk_path_by_name(name)
return quirks[name]
@@ -0,0 +1,84 @@
PROCESSING_SUBSYSTEM_DEF(station)
name = "Station"
init_order = INIT_ORDER_STATION
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME
wait = 5 SECONDS
///A list of currently active station traits
var/list/station_traits = list()
///Assoc list of trait type || assoc list of traits with weighted value. Used for picking traits from a specific category.
var/list/selectable_traits_by_types = list(STATION_TRAIT_POSITIVE = list(), STATION_TRAIT_NEUTRAL = list(), STATION_TRAIT_NEGATIVE = list())
///Currently active announcer. Starts as a type but gets initialized after traits are selected
var/datum/centcom_announcer/announcer = /datum/centcom_announcer/default
/datum/controller/subsystem/processing/station/Initialize(timeofday)
//If doing unit tests we don't do none of that trait shit ya know?
#ifndef UNIT_TESTS
SetupTraits()
#endif
announcer = new announcer() //Initialize the station's announcer datum
return ..()
///Rolls for the amount of traits and adds them to the traits list
/datum/controller/subsystem/processing/station/proc/SetupTraits()
if (fexists(FUTURE_STATION_TRAITS_FILE))
var/forced_traits_contents = file2text(FUTURE_STATION_TRAITS_FILE)
fdel(FUTURE_STATION_TRAITS_FILE)
var/list/forced_traits_text_paths = json_decode(forced_traits_contents)
forced_traits_text_paths = SANITIZE_LIST(forced_traits_text_paths)
for (var/trait_text_path in forced_traits_text_paths)
var/station_trait_path = text2path(trait_text_path)
if (!ispath(station_trait_path, /datum/station_trait) || station_trait_path == /datum/station_trait)
var/message = "Invalid station trait path [station_trait_path] was requested in the future station traits!"
log_game(message)
message_admins(message)
continue
setup_trait(station_trait_path)
return
for(var/i in subtypesof(/datum/station_trait))
var/datum/station_trait/trait_typepath = i
// If forced, (probably debugging), just set it up now, keep it out of the pool.
if(initial(trait_typepath.force))
setup_trait(trait_typepath)
continue
if(initial(trait_typepath.trait_flags) & STATION_TRAIT_ABSTRACT || initial(trait_typepath.weight) == 0)
continue //Dont add abstract ones to it
selectable_traits_by_types[initial(trait_typepath.trait_type)][trait_typepath] = initial(trait_typepath.weight)
var/positive_trait_count = pick(20;0, 5;1, 1;2)
var/neutral_trait_count = pick(10;0, 10;1, 3;2)
var/negative_trait_count = pick(20;0, 5;1, 1;2)
can_fire = FALSE
pick_traits(STATION_TRAIT_POSITIVE, positive_trait_count)
pick_traits(STATION_TRAIT_NEUTRAL, neutral_trait_count)
pick_traits(STATION_TRAIT_NEGATIVE, negative_trait_count)
///Picks traits of a specific category (e.g. bad or good) and a specified amount, then initializes them and adds them to the list of traits.
/datum/controller/subsystem/processing/station/proc/pick_traits(trait_sign, amount)
if(!amount)
return
for(var/iterator in 1 to amount)
var/datum/station_trait/trait_type = pickweight(selectable_traits_by_types[trait_sign]) //Rolls from the table for the specific trait type
setup_trait(trait_type)
///Creates a given trait of a specific type, while also removing any blacklisted ones from the future pool.
/datum/controller/subsystem/processing/station/proc/setup_trait(datum/station_trait/trait_type)
var/datum/station_trait/trait_instance = new trait_type()
can_fire = can_fire || trait_instance.trait_processes
station_traits += trait_instance
if(!trait_instance.blacklist)
return
for(var/i in trait_instance.blacklist)
var/datum/station_trait/trait_to_remove = i
selectable_traits_by_types[initial(trait_to_remove.trait_type)] -= trait_to_remove
@@ -22,7 +22,7 @@ PROCESSING_SUBSYSTEM_DEF(weather)
run_weather(W, list(text2num(z)))
eligible_zlevels -= z
var/randTime = rand(3000, 6000)
addtimer(CALLBACK(src, .proc/make_eligible, z, possible_weather), randTime + initial(W.weather_duration_upper), TIMER_UNIQUE) //Around 5-10 minutes between weathers
addtimer(CALLBACK(src, PROC_REF(make_eligible), z, possible_weather), randTime + initial(W.weather_duration_upper), TIMER_UNIQUE) //Around 5-10 minutes between weathers
next_hit_by_zlevel["[z]"] = world.time + randTime + initial(W.telegraph_duration)
/datum/controller/subsystem/processing/weather/Initialize(start_timeofday)
+1 -1
View File
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(radio)
if(frequency)
frequency.remove_listener(device)
// let's don't delete frequencies in case a non-listener keeps a reference
return 1
return TRUE
/datum/controller/subsystem/radio/proc/return_frequency(new_frequency as num)
var/f_text = num2text(new_frequency)
+25 -14
View File
@@ -343,16 +343,22 @@ SUBSYSTEM_DEF(research)
break //Just need one to work.
if (!isnull(last_income))
var/income_time_difference = world.time - last_income
science_tech.last_bitcoins = bitcoins // Doesn't take tick drift into account
for(var/i in bitcoins)
bitcoins[i] *= income_time_difference / 10
science_tech.add_point_list(bitcoins)
var/list/income = science_tech.commit_income()
for(var/i in income)
var/old_weighted = science_tech.last_bitcoins[i] * (1 MINUTES - income_time_difference)
var/new_weighted = income[i] * income_time_difference
science_tech.last_bitcoins[i] = (old_weighted + new_weighted) / (1 MINUTES)
else
science_tech.last_bitcoins = bitcoins.Copy()
last_income = world.time
/datum/controller/subsystem/research/proc/calculate_server_coefficient() //Diminishing returns.
var/amt = servers.len
if(!amt)
return 0
return FALSE
var/coeff = 100
coeff = sqrt(coeff / amt)
return coeff
@@ -366,10 +372,16 @@ SUBSYSTEM_DEF(research)
techweb_categories[I.category] = list(I.id = TRUE)
/datum/controller/subsystem/research/proc/techweb_node_by_id(id)
return techweb_nodes[id] || error_node
if(techweb_nodes[id])
return techweb_nodes[id]
stack_trace("Attempted to access node ID [id] which didn't exist")
return error_node
/datum/controller/subsystem/research/proc/techweb_design_by_id(id)
return techweb_designs[id] || error_design
if(techweb_designs[id])
return techweb_designs[id]
stack_trace("Attempted to access design ID [id] which didn't exist")
return error_design
/datum/controller/subsystem/research/proc/on_design_deletion(datum/design/D)
for(var/i in techweb_nodes)
@@ -409,8 +421,7 @@ SUBSYSTEM_DEF(research)
var/datum/techweb_node/TN = techweb_nodes[id]
TN.Initialize()
techweb_nodes = returned
if (!verify_techweb_nodes()) //Verify all nodes have ids and such.
stack_trace("Invalid techweb nodes detected")
verify_techweb_nodes()
calculate_techweb_nodes()
calculate_techweb_boost_list()
if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly.
@@ -442,52 +453,52 @@ SUBSYSTEM_DEF(research)
for(var/n in techweb_nodes)
var/datum/techweb_node/N = techweb_nodes[n]
if(!istype(N))
WARNING("Invalid research node with ID [n] detected and removed.")
stack_trace("Invalid research node with ID [n] detected and removed.")
techweb_nodes -= n
research_node_id_error(n)
. = FALSE
for(var/p in N.prereq_ids)
var/datum/techweb_node/P = techweb_nodes[p]
if(!istype(P))
WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.")
stack_trace("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.")
N.prereq_ids -= p
research_node_id_error(p)
. = FALSE
for(var/d in N.design_ids)
var/datum/design/D = techweb_designs[d]
if(!istype(D))
WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.")
stack_trace("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.")
N.design_ids -= d
design_id_error(d)
. = FALSE
for(var/u in N.unlock_ids)
var/datum/techweb_node/U = techweb_nodes[u]
if(!istype(U))
WARNING("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.")
stack_trace("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.")
N.unlock_ids -= u
research_node_id_error(u)
. = FALSE
for(var/p in N.boost_item_paths)
if(!ispath(p))
N.boost_item_paths -= p
WARNING("[p] is not a valid path.")
stack_trace("[p] is not a valid path.")
node_boost_error(N.id, "[p] is not a valid path.")
. = FALSE
var/list/points = N.boost_item_paths[p]
if(islist(points))
for(var/i in points)
if(!isnum(points[i]))
WARNING("[points[i]] is not a valid number.")
stack_trace("[points[i]] is not a valid number.")
node_boost_error(N.id, "[points[i]] is not a valid number.")
. = FALSE
else if(!point_types[i])
WARNING("[i] is not a valid point type.")
stack_trace("[i] is not a valid point type.")
node_boost_error(N.id, "[i] is not a valid point type.")
. = FALSE
else if(!isnull(points))
N.boost_item_paths -= p
node_boost_error(N.id, "No valid list.")
WARNING("No valid list.")
stack_trace("No valid list.")
. = FALSE
CHECK_TICK
@@ -0,0 +1,114 @@
// NOT THE SAME AS TG! THIS IS BAREMETAL JUST TO MAKE COMSIGS WORK!
SUBSYSTEM_DEF(security_level)
name = "Security Level"
can_fire = FALSE // We will control when we fire in this subsystem
init_order = INIT_ORDER_SECURITY_LEVEL
/**
* Sets a new security level as our current level
*
* This is how everything should change the security level.
*
* Arguments:
* * new_level - The new security level that will become our current level
*/
/datum/controller/subsystem/security_level/proc/set_level(new_level)
if(!isnum(new_level))
new_level = SECLEVEL2NUM(new_level)
//Will not be announced if you try to set to the same level as it already is
if(new_level >= SEC_LEVEL_GREEN && new_level <= SEC_LEVEL_DELTA && new_level != GLOB.security_level)
switch(new_level)
if(SEC_LEVEL_GREEN)
minor_announce(CONFIG_GET(string/alert_green), "Attention! Security level lowered to green:")
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
if(GLOB.security_level >= SEC_LEVEL_RED)
SSshuttle.emergency.modTimer(4)
else if(GLOB.security_level == SEC_LEVEL_AMBER)
SSshuttle.emergency.modTimer(2.5)
else
SSshuttle.emergency.modTimer(1.66)
GLOB.security_level = SEC_LEVEL_GREEN
for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z))
FA.update_icon()
if(SEC_LEVEL_BLUE)
if(GLOB.security_level < SEC_LEVEL_BLUE)
minor_announce(CONFIG_GET(string/alert_blue_upto), "Attention! Security level elevated to blue:",1)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
SSshuttle.emergency.modTimer(0.6)
else
minor_announce(CONFIG_GET(string/alert_blue_downto), "Attention! Security level lowered to blue:")
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
if(GLOB.security_level >= SEC_LEVEL_RED)
SSshuttle.emergency.modTimer(2.4)
else
SSshuttle.emergency.modTimer(1.5)
GLOB.security_level = SEC_LEVEL_BLUE
sound_to_playing_players('sound/misc/voybluealert.ogg', volume = 50) // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z))
FA.update_icon()
if(SEC_LEVEL_AMBER)
if(GLOB.security_level < SEC_LEVEL_AMBER)
minor_announce(CONFIG_GET(string/alert_amber_upto), "Attention! Security level elevated to amber:",1)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
if(GLOB.security_level == SEC_LEVEL_GREEN)
SSshuttle.emergency.modTimer(0.4)
else
SSshuttle.emergency.modTimer(0.66)
else
minor_announce(CONFIG_GET(string/alert_amber_downto), "Attention! Security level lowered to amber:")
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
SSshuttle.emergency.modTimer(1.6)
GLOB.security_level = SEC_LEVEL_AMBER
sound_to_playing_players('sound/effects/alert.ogg', volume = 50) // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z))
FA.update_icon()
if(SEC_LEVEL_RED)
if(GLOB.security_level < SEC_LEVEL_RED)
minor_announce(CONFIG_GET(string/alert_red_upto), "Attention! Code red!",1)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
if(GLOB.security_level == SEC_LEVEL_GREEN)
SSshuttle.emergency.modTimer(0.25)
else if(GLOB.security_level == SEC_LEVEL_BLUE)
SSshuttle.emergency.modTimer(0.416)
else
SSshuttle.emergency.modTimer(0.625)
else
minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code red!")
GLOB.security_level = SEC_LEVEL_RED
sound_to_playing_players('sound/misc/voyalert.ogg', volume = 50) // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z))
FA.update_icon()
for(var/obj/machinery/computer/shuttle/pod/pod in GLOB.machines)
pod.admin_controlled = FALSE
if(SEC_LEVEL_DELTA)
minor_announce(CONFIG_GET(string/alert_delta), "Attention! Delta security level reached!",1)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
if(GLOB.security_level < SEC_LEVEL_BLUE)
SSshuttle.emergency.modTimer(0.25)
else if(GLOB.security_level == SEC_LEVEL_BLUE)
SSshuttle.emergency.modTimer(0.416)
else
SSshuttle.emergency.modTimer(0.625)
GLOB.security_level = SEC_LEVEL_DELTA
sound_to_playing_players('sound/misc/deltakalaxon.ogg') // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z))
FA.update_icon()
for(var/obj/machinery/computer/shuttle/pod/pod in GLOB.machines)
pod.admin_controlled = FALSE
if(new_level >= SEC_LEVEL_RED)
for(var/obj/machinery/door/D in GLOB.machines)
if(D.red_alert_access)
D.visible_message("<span class='notice'>[D] whirrs as it automatically lifts access requirements!</span>")
playsound(D, 'sound/machines/boltsup.ogg', 50, TRUE)
SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGED, new_level)
SSblackbox.record_feedback("tally", "security_level_changes", 1, NUM2SECLEVEL(GLOB.security_level))
SSnightshift.check_nightshift()
else
return
+14 -10
View File
@@ -169,7 +169,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/block_recall(lockout_timer)
emergencyNoRecall = TRUE
addtimer(CALLBACK(src, .proc/unblock_recall), lockout_timer)
addtimer(CALLBACK(src, PROC_REF(unblock_recall)), lockout_timer)
/datum/controller/subsystem/shuttle/proc/unblock_recall()
emergencyNoRecall = FALSE
@@ -254,7 +254,7 @@ SUBSYSTEM_DEF(shuttle)
var/area/A = get_area(user)
log_shuttle("[key_name(user)] has called the emergency shuttle.")
deadchat_broadcast(" has called the shuttle at <span class='name'>[A.name]</span>.", "<span class='name'>[user.real_name]</span>", user) //, message_type=DEADCHAT_ANNOUNCEMENT)
deadchat_broadcast(" has called the shuttle at [span_name("[A.name]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
if(call_reason)
SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
log_shuttle("Shuttle call reason: [call_reason]")
@@ -293,8 +293,8 @@ SUBSYSTEM_DEF(shuttle)
emergency.cancel(get_area(user))
log_shuttle("[key_name(user)] has recalled the shuttle.")
message_admins("[ADMIN_LOOKUPFLW(user)] has recalled the shuttle.")
deadchat_broadcast(" has recalled the shuttle from <span class='name'>[get_area_name(user, TRUE)]</span>.", "<span class='name'>[user.real_name]</span>", user) //, message_type=DEADCHAT_ANNOUNCEMENT)
return 1
deadchat_broadcast(" has recalled the shuttle from [span_name("[get_area_name(user, TRUE)]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
return TRUE
/datum/controller/subsystem/shuttle/proc/canRecall()
if(!emergency || emergency.mode != SHUTTLE_CALL || emergencyNoRecall || SSticker.mode.name == "meteor")
@@ -400,7 +400,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
if(!M)
return 1
return TRUE
var/obj/docking_port/stationary/dockedAt = M.get_docked()
var/destination = dockHome
if(dockedAt && dockedAt.id == dockHome)
@@ -411,7 +411,7 @@ SUBSYSTEM_DEF(shuttle)
else
if(M.initiate_docking(getDock(destination)) != DOCKING_SUCCESS)
return 2
return 0 //dock successful
return FALSE //dock successful
/datum/controller/subsystem/shuttle/proc/moveShuttle(shuttleId, dockId, timed)
@@ -419,14 +419,14 @@ SUBSYSTEM_DEF(shuttle)
var/obj/docking_port/stationary/D = getDock(dockId)
if(!M)
return 1
return TRUE
if(timed)
if(M.request(D))
return 2
else
if(M.initiate_docking(D) != DOCKING_SUCCESS)
return 2
return 0 //dock successful
return FALSE //dock successful
/datum/controller/subsystem/shuttle/proc/request_transit_dock(obj/docking_port/mobile/M)
if(!istype(M))
@@ -513,7 +513,9 @@ SUBSYSTEM_DEF(shuttle)
if(!midpoint)
return FALSE
var/area/shuttle/transit/A = new()
A.parallax_movedir = travel_dir
A.parallax_moving = TRUE
A.parallax_move_angle = dir2angle(travel_dir)
A.parallax_move_speed = M.parallax_speed
A.contents = proposal.reserved_turfs
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
new_transit_dock.reserved_area = proposal
@@ -726,6 +728,8 @@ SUBSYSTEM_DEF(shuttle)
preview_shuttle.register(replace)
preview_shuttle.reset_air()
// TODO indicate to the user that success happened, rather than just
// blanking the modification tab
preview_shuttle = null
@@ -821,7 +825,7 @@ SUBSYSTEM_DEF(shuttle)
templates[S.port_id]["templates"] += list(L)
data["templates_tabs"] = sortList(data["templates_tabs"])
data["templates_tabs"] = sort_list(data["templates_tabs"])
data["existing_shuttle"] = null
@@ -0,0 +1,3 @@
PROCESSING_SUBSYSTEM_DEF(sound_loops)
name = "Sound Loops"
priority = FIRE_PRIORITY_SOUND_LOOPS
+6 -6
View File
@@ -4,7 +4,7 @@ SUBSYSTEM_DEF(sounds)
name = "Sounds"
flags = SS_NO_FIRE
init_order = INIT_ORDER_SOUNDS
var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
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
@@ -42,7 +42,7 @@ SUBSYSTEM_DEF(sounds)
var/text_channel = num2text(channel)
var/using = using_channels[text_channel]
using_channels -= text_channel
if(using != TRUE) // datum channel
if(using != TRUE) // datum channel
using_channels_by_datum[using] -= channel
if(!length(using_channels_by_datum[using]))
using_channels_by_datum -= using
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(sounds)
/// NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
. = reserve_channel()
if(!.) //oh no..
if(!.) //oh no..
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = DATUMLESS
@@ -74,7 +74,7 @@ SUBSYSTEM_DEF(sounds)
/// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
if(!D) //i don't like typechecks but someone will fuck it up
if(!D) //i don't like typechecks but someone will fuck it up
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
.= reserve_channel()
if(!.)
@@ -89,7 +89,7 @@ SUBSYSTEM_DEF(sounds)
*/
/datum/controller/subsystem/sounds/proc/reserve_channel()
PRIVATE_PROC(TRUE)
if(channel_reserve_high <= random_channels_min) // out of channels
if(channel_reserve_high <= random_channels_min) // out of channels
return
var/channel = channel_list[channel_reserve_high]
reserved_channels[num2text(channel)] = channel_reserve_high--
@@ -112,7 +112,7 @@ SUBSYSTEM_DEF(sounds)
// now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
// get it, and update position.
var/text_reserved = num2text(channel_list[index])
if(!reserved_channels[text_reserved]) //if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
if(!reserved_channels[text_reserved]) //if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
return
reserved_channels[text_reserved] = index
+2 -2
View File
@@ -18,7 +18,7 @@ SUBSYSTEM_DEF(statpanels)
cached ? "Next Map: [cached.map_name]" : null,
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
"Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]",
"Round Time: [round_time > MIDNIGHT_ROLLOVER ? "[round(round_time/MIDNIGHT_ROLLOVER)]:[worldtime2text()]" : worldtime2text()]",
"Round Time: [GAMETIMESTAMP("hh:mm:ss", round_time)]",
"Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]",
"Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)"
)
@@ -137,7 +137,7 @@ SUBSYSTEM_DEF(statpanels)
if(length(turfitems) < 30) // only create images for the first 30 items on the turf, for performance reasons
if(!(REF(turf_content) in cached_images))
cached_images += REF(turf_content)
turf_content.RegisterSignal(turf_content, COMSIG_PARENT_QDELETING, /atom/.proc/remove_from_cache) // we reset cache if anything in it gets deleted
turf_content.RegisterSignal(turf_content, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/atom/, remove_from_cache)) // we reset cache if anything in it gets deleted
if(ismob(turf_content) || length(turf_content.overlays) > 2)
turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content), costly_icon2html(turf_content, target, sourceonly=TRUE))
else
-2
View File
@@ -59,5 +59,3 @@ SUBSYSTEM_DEF(sun)
/datum/controller/subsystem/sun/proc/complete_movement()
SEND_SIGNAL(src, COMSIG_SUN_MOVED, primary_sun, suns)
#undef OCCLUSION_DISTANCE
+12 -9
View File
@@ -1,10 +1,13 @@
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
/**
* tgui subsystem
*
* Contains all tgui state and subsystem code.
*
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
SUBSYSTEM_DEF(tgui)
@@ -42,8 +45,8 @@ SUBSYSTEM_DEF(tgui)
var/datum/tgui/ui = current_run[current_run.len]
current_run.len--
// TODO: Move user/src_object check to process()
if(ui && ui.user && ui.src_object)
ui.process()
if(ui?.user && ui.src_object)
ui.process(wait * 0.1)
else
open_uis.Remove(ui)
if(MC_TICK_CHECK)
@@ -191,8 +194,8 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(force = 1)
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(wait * 0.1, force = 1)
count++
return count
@@ -213,7 +216,7 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
@@ -230,7 +233,7 @@ SUBSYSTEM_DEF(tgui)
for(var/key in open_uis_by_src)
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
@@ -251,7 +254,7 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in user.tgui_open_uis)
if(isnull(src_object) || ui.src_object == src_object)
ui.process(force = 1)
ui.process(wait * 0.1, force = 1)
count++
return count
+60 -37
View File
@@ -41,37 +41,61 @@ SUBSYSTEM_DEF(throwing)
currentrun = null
/datum/thrownthing
///Defines the atom that has been thrown (Objects and Mobs, mostly.)
var/atom/movable/thrownthing
var/atom/target
///Weakref to the original intended target of the throw, to prevent hardDels
var/datum/weakref/initial_target
///The turf that the target was on, if it's not a turf itself.
var/turf/target_turf
///If the target happens to be a carbon and that carbon has a body zone aimed at, this is carried on here.
var/target_zone
///The initial direction of the thrower of the thrownthing for building the trajectory of the throw.
var/init_dir
///The maximum number of turfs that the thrownthing will travel to reach it's target.
var/maxrange
///The speed of the projectile thrownthing being thrown.
var/speed
///If a mob is the one who has thrown the object, then it's moved here.
var/mob/thrower
///A variable that helps in describing objects thrown at an angle, if it should be moved diagonally first or last.
var/diagonals_first
var/dist_travelled = 0
var/start_time
var/dist_x
var/dist_y
var/dx
var/dy
var/force = MOVE_FORCE_DEFAULT
var/gentle = FALSE
///Set to TRUE if the throw is exclusively diagonal (45 Degree angle throws for example)
var/pure_diagonal
///Tracks how far a thrownthing has traveled mid-throw for the purposes of maxrange
var/dist_travelled = 0
///The start_time obtained via world.time for the purposes of tiles moved/tick.
var/start_time
///Distance to travel in the X axis/direction.
var/dist_x
///Distance to travel in the y axis/direction.
var/dist_y
///The Horizontal direction we're traveling (EAST or WEST)
var/dx
///The VERTICAL direction we're traveling (NORTH or SOUTH)
var/dy
///The movement force provided to a given object in transit. More info on these in move_force.dm
var/force = MOVE_FORCE_DEFAULT
///If the throw is gentle, then the thrownthing is harmless on impact.
var/gentle = FALSE
///How many tiles that need to be moved in order to travel to the target.
var/diagonal_error
///If a thrown thing has a callback, it can be invoked here within thrownthing.
var/datum/callback/callback
///Mainly exists for things that would freeze a thrown object in place, like a timestop'd tile. Or a Tractor Beam.
var/paused = FALSE
///How long an object has been paused for, to be added to the travel time.
var/delayed_time = 0
///The last world.time value stored when the thrownthing was moving.
var/last_move = 0
/datum/thrownthing/New(thrownthing, target, target_turf, init_dir, maxrange, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
/datum/thrownthing/New(thrownthing, target, init_dir, maxrange, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
. = ..()
src.thrownthing = thrownthing
RegisterSignal(thrownthing, COMSIG_PARENT_QDELETING, .proc/on_thrownthing_qdel)
src.target = target
src.target_turf = target_turf
RegisterSignal(thrownthing, COMSIG_PARENT_QDELETING, PROC_REF(on_thrownthing_qdel))
src.target_turf = get_turf(target)
if(target_turf != target)
src.initial_target = WEAKREF(target)
src.init_dir = init_dir
src.maxrange = maxrange
src.speed = speed
@@ -87,10 +111,11 @@ SUBSYSTEM_DEF(throwing)
if(HAS_TRAIT_FROM(thrownthing, TRAIT_SPOOKY_THROW, "revenant"))
REMOVE_TRAIT(thrownthing, TRAIT_SPOOKY_THROW, "revenant")
SSthrowing.processing -= thrownthing
SSthrowing.currentrun -= thrownthing
thrownthing.throwing = null
thrownthing = null
target = null
thrower = null
initial_target = null
if(callback)
QDEL_NULL(callback) //It stores a reference to the thrownthing, its source. Let's clean that.
return ..()
@@ -113,9 +138,17 @@ SUBSYSTEM_DEF(throwing)
delayed_time += world.time - last_move
return
if (dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
finalize()
return
var/atom/movable/actual_target = initial_target?.resolve()
if(dist_travelled) //to catch sneaky things moving on our tile while we slept
for(var/atom/movable/obstacle as anything in get_turf(thrownthing))
if (obstacle == thrownthing || (obstacle == thrower && !ismob(thrownthing)))
continue
if(obstacle.pass_flags_self & LETPASSTHROW)
continue
if (obstacle == actual_target || (obstacle.density && !(obstacle.flags_1 & ON_BORDER_1)))
finalize(TRUE, obstacle)
return
var/atom/step
@@ -142,14 +175,17 @@ SUBSYSTEM_DEF(throwing)
finalize()
return
AM.Move(step, get_dir(AM, step), DELAY_TO_GLIDE_SIZE(1 / speed))
if (!AM.throwing) // we hit something during our move
finalize(hit = TRUE)
if(!AM.Move(step, get_dir(AM, step), DELAY_TO_GLIDE_SIZE(1 / speed))) // we hit something during our move...
if(AM.throwing) // ...but finalize() wasn't called on Bump() because of a higher level definition that doesn't always call parent.
finalize()
return
dist_travelled++
if(actual_target && !(actual_target.pass_flags_self & LETPASSTHROW) && actual_target.loc == AM.loc) // we crossed a movable with no density (e.g. a mouse or APC) we intend to hit anyway.
finalize(TRUE, actual_target)
return
if (dist_travelled > MAX_THROWING_DIST)
finalize()
return
@@ -161,11 +197,10 @@ SUBSYSTEM_DEF(throwing)
return
thrownthing.throwing = null
if (!hit)
for (var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on.
var/atom/A = thing
if (A == target)
for (var/atom/movable/obstacle as anything in get_turf(thrownthing)) //looking for our target on the turf we land on.
if (obstacle == target)
hit = TRUE
thrownthing.throw_impact(A, src)
thrownthing.throw_impact(obstacle, src)
if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
return //deletion should already be handled by on_thrownthing_qdel()
break
@@ -191,15 +226,3 @@ SUBSYSTEM_DEF(throwing)
T.zFall(thrownthing)
qdel(src)
/datum/thrownthing/proc/hit_atom(atom/A)
finalize(hit=TRUE, target=A)
/datum/thrownthing/proc/hitcheck()
for (var/thing in get_turf(thrownthing))
var/atom/movable/AM = thing
if (AM == thrownthing || (AM == thrower && !ismob(thrownthing)))
continue
if (AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags_1 & ON_BORDER_1))
finalize(hit=TRUE, target=AM)
return TRUE
+50 -52
View File
@@ -71,6 +71,9 @@ SUBSYSTEM_DEF(ticker)
var/station_integrity = 100 // stored at roundend for use in some antag goals
var/emergency_reason
/// If the gamemode fails to be run too many times, we swap to a preset gamemode, this should give admins time to set their preferred one
var/emergency_swap = 0
/datum/controller/subsystem/ticker/Initialize(timeofday)
load_mode()
@@ -161,8 +164,8 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "<span class='boldnotice'>Welcome to [station_name()]!</span>")
send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game))
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME)
fire()
if(GAME_STATE_PREGAME)
//lobby stats for statpanels
@@ -179,7 +182,11 @@ SUBSYSTEM_DEF(ticker)
timeLeft = 0
if(!modevoted)
send_gamemode_vote()
var/forcemode = CONFIG_GET(string/force_gamemode)
if(forcemode)
force_gamemode(forcemode)
if(!forcemode || (GLOB.master_mode == "dynamic" && CONFIG_GET(flag/dynamic_voting)))
send_gamemode_vote()
//countdown
if(timeLeft < 0)
return
@@ -196,6 +203,7 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in SSvote.voting)
C << browse(null, "window=vote;can_close=0")
SSvote.reset()
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_SETTING_UP)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
if(start_immediately)
@@ -208,6 +216,7 @@ SUBSYSTEM_DEF(ticker)
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)
mode.process(wait * 0.1)
@@ -227,36 +236,16 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, "<span class='boldannounce'>Starting game...</span>")
var/init_start = world.timeofday
//Create and announce mode
var/list/datum/game_mode/runnable_modes
if(GLOB.master_mode == "random" || GLOB.master_mode == "secret")
runnable_modes = config.get_runnable_modes()
if(GLOB.master_mode == "secret")
hide_mode = 1
if(GLOB.secret_force_mode != "secret")
var/datum/game_mode/smode = config.pick_mode(GLOB.secret_force_mode)
if(!smode.can_start())
message_admins("<span class='notice'>Unable to force secret [GLOB.secret_force_mode]. [smode.required_players] players and [smode.required_enemies] eligible antagonists needed.</span>")
else
mode = smode
if(!mode)
if(!runnable_modes.len)
to_chat(world, "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby.")
return 0
mode = pickweight(runnable_modes)
if(!mode) //too few roundtypes all run too recently
mode = pick(runnable_modes)
else
mode = config.pick_mode(GLOB.master_mode)
if(!mode.can_start())
to_chat(world, "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players and [mode.required_enemies] eligible antagonists needed. Reverting to pre-game lobby.")
qdel(mode)
mode = null
SSjob.ResetOccupations()
return 0
if(emergency_swap >= 10)
force_gamemode("extended") // If everything fails extended does not have hard requirements for starting, could be changed if needed.
mode = config.pick_mode(GLOB.master_mode)
if(!mode.can_start())
to_chat(world, "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players and [mode.required_enemies] eligible antagonists needed. Reverting to pre-game lobby.")
qdel(mode)
mode = null
SSjob.ResetOccupations()
emergency_swap++
return FALSE
CHECK_TICK
//Configure mode and assign player to special mode stuff
@@ -274,7 +263,8 @@ SUBSYSTEM_DEF(ticker)
QDEL_NULL(mode)
to_chat(world, "<B>Error setting up [GLOB.master_mode].</B> Reverting to pre-game lobby.")
SSjob.ResetOccupations()
return 0
emergency_swap++
return FALSE
else
message_admins("<span class='notice'>DEBUG: Bypassing prestart checks...</span>")
@@ -283,7 +273,7 @@ SUBSYSTEM_DEF(ticker)
var/list/modes = new
for (var/datum/game_mode/M in runnable_modes)
modes += M.name
modes = sortList(modes)
modes = sort_list(modes)
to_chat(world, "<b>The gamemode is: secret!\nPossibilities:</B> [english_list(modes)]")
else
mode.announce()*/
@@ -306,12 +296,14 @@ SUBSYSTEM_DEF(ticker)
cb.InvokeAsync()
LAZYCLEARLIST(round_start_events)
SEND_SIGNAL(src, COMSIG_TICKER_ROUND_STARTING)
log_world("Game start took [(world.timeofday - init_start)/10]s")
round_start_time = world.time
SSdbcore.SetRoundStart()
to_chat(world, "<span class='notice'><B>Welcome to [station_name()], enjoy your stay!</B></span>")
SEND_SOUND(world, sound(get_announcer_sound("welcome")))
SEND_SOUND(world, sound(SSstation.announcer.get_rand_welcome_sound()))
current_state = GAME_STATE_PLAYING
Master.SetRunLevel(RUNLEVEL_GAME)
@@ -327,6 +319,19 @@ SUBSYSTEM_DEF(ticker)
return TRUE
/datum/controller/subsystem/ticker/proc/force_gamemode(gamemode)
if(gamemode)
if(!modevoted)
modevoted = TRUE
if(gamemode in config.modes)
GLOB.master_mode = gamemode
SSticker.save_mode(gamemode)
message_admins("The gamemode has been set to [gamemode].")
else
GLOB.master_mode = "extended"
SSticker.save_mode("extended")
message_admins("force_gamemode proc received an invalid gamemode, defaulting to extended.")
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
mode.post_setup()
@@ -377,8 +382,6 @@ SUBSYSTEM_DEF(ticker)
LAZYOR(player.client.prefs.characters_joined_as, player.new_character.real_name)
else
stack_trace("WARNING: Either a player did not have a new_character, did not have a client, or did not have preferences. This is VERY bad.")
else
player.new_player_panel()
CHECK_TICK
/datum/controller/subsystem/ticker/proc/collect_minds()
@@ -421,12 +424,12 @@ SUBSYSTEM_DEF(ticker)
if (living.client.prefs && living.client.prefs.auto_ooc)
if (living.client.prefs.chat_toggles & CHAT_OOC)
living.client.prefs.chat_toggles ^= CHAT_OOC
var/obj/screen/splash/S = new(living.client, TRUE)
var/atom/movable/screen/splash/S = new(living.client, TRUE)
S.Fade(TRUE)
living.client.init_verbs()
livings += living
if(livings.len)
addtimer(CALLBACK(src, .proc/release_characters, livings), 30, TIMER_CLIENT_TIME)
addtimer(CALLBACK(src, PROC_REF(release_characters), livings), 30, TIMER_CLIENT_TIME)
/datum/controller/subsystem/ticker/proc/release_characters(list/livings)
for(var/I in livings)
@@ -485,7 +488,7 @@ SUBSYSTEM_DEF(ticker)
if (!prob((world.time/600)*CONFIG_GET(number/maprotatechancedelta)) && CONFIG_GET(flag/tgstyle_maprotation))
return
if(CONFIG_GET(flag/tgstyle_maprotation))
INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/.proc/maprotate)
INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping/, maprotate))
else
var/vote_type = CONFIG_GET(string/map_vote_type)
SSvote.initiate_vote("map","server", display = SHOW_RESULTS, votesystem = vote_type)
@@ -602,14 +605,16 @@ SUBSYSTEM_DEF(ticker)
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(GANG_VICTORY)
news_message = "Company officials reaffirmed that sudden deployments of special forces are not in any way connected to rumors of [station_name()] being covered in graffiti."
if(GANG_OPERATING)
news_message = "The company would like to state that any rumors of criminal organizing on board stations such as [station_name()] are falsehoods, and not to be emulated."
if(GANG_DESTROYED)
news_message = "The crew of [station_name()] would like to thank the Spinward Stellar Coalition Police Department for quickly resolving a minor terror threat to the station."
if(SSblackbox.first_death)
var/list/ded = SSblackbox.first_death
if(ded.len)
var/last_words = ded["last_words"] ? " Their last words were: \"[ded["last_words"]]\"" : ""
news_message += "\nNT Sanctioned Psykers picked up faint traces of someone near the station, allegedly having had died. Their name was: [ded["name"]], [ded["role"]], at [ded["area"]].[last_words]"
news_message += "\nNT Sanctioned Psykers picked up faint traces of someone near the station, allegedly having had died.\nTheir name was: [ded["name"]], [ded["role"]], at [ded["area"]].[last_words]"
else
news_message += "\nNT Sanctioned Psykers proudly confirm reports that nobody died this shift!"
@@ -630,19 +635,12 @@ SUBSYSTEM_DEF(ticker)
else
timeLeft = newtime
//Everyone who wanted to be an observer gets made one now
/datum/controller/subsystem/ticker/proc/create_observers()
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
//Break chain since this has a sleep input in it
addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1)
/datum/controller/subsystem/ticker/proc/load_mode()
var/mode = trim(file2text("data/mode.txt"))
if(mode)
GLOB.master_mode = mode
else
GLOB.master_mode = "extended"
GLOB.master_mode = GLOB.dynamic_forced_extended
log_game("Saved mode is '[GLOB.master_mode]'")
/datum/controller/subsystem/ticker/proc/save_mode(the_mode)
+12 -8
View File
@@ -31,18 +31,20 @@ SUBSYSTEM_DEF(time_track)
"maptick",
"num_timers",
"air_turf_cost",
"air_turf_thread_time",
"air_equalize_cost",
"air_post_process_cost",
"air_eg_cost",
"air_highpressure_cost",
"air_hotspots_cost",
"air_superconductivity_cost",
"air_heat_spread_cost",
"air_pipenets_cost",
"air_rebuilds_cost",
"air_turf_count",
"air_eg_count",
"air_amt_gas_mixes",
"air_alloc_gas_mixes",
"air_hotspot_count",
"air_network_count",
"air_delta_count",
"air_superconductive_count"
)
)
@@ -81,17 +83,19 @@ SUBSYSTEM_DEF(time_track)
MAPTICK_LAST_INTERNAL_TICK_USAGE,
length(SStimer.timer_id_dict),
SSair.cost_turfs,
SSair.turf_process_time(),
SSair.cost_equalize,
SSair.cost_post_process,
SSair.cost_groups,
SSair.cost_highpressure,
SSair.cost_hotspots,
SSair.cost_superconductivity,
SSair.cost_pipenets,
SSair.cost_rebuilds,
SSair.get_active_turfs(), //does not return a list, which is what we want
SSair.get_amt_excited_groups(),
SSair.get_amt_gas_mixes(),
SSair.get_max_gas_mixes(),
length(SSair.hotspots),
length(SSair.networks),
length(SSair.high_pressure_delta),
length(SSair.active_super_conductivity)
length(SSair.high_pressure_delta)
)
)
+1 -1
View File
@@ -257,7 +257,7 @@ SUBSYSTEM_DEF(timer)
return
// Sort all timers by time to run
sortTim(alltimers, .proc/cmp_timer)
sortTim(alltimers, PROC_REF(cmp_timer))
// Get the earliest timer, and if the TTR is earlier than the current world.time,
// then set the head offset appropriately to be the earliest time tracked by the
+3 -2
View File
@@ -32,7 +32,7 @@ SUBSYSTEM_DEF(title)
file_path = "[global.config.directory]/title_screens/images/[pick(title_screens)]"
if(!file_path)
file_path = "icons/default_title.dmi"
file_path = "icons/runtime/default_title.dmi"
ASSERT(fexists(file_path))
@@ -40,6 +40,7 @@ SUBSYSTEM_DEF(title)
if(splash_turf)
splash_turf.icon = icon
splash_turf.handle_generic_titlescreen_sizes()
return ..()
@@ -59,7 +60,7 @@ SUBSYSTEM_DEF(title)
for(var/thing in GLOB.clients)
if(!thing)
continue
var/obj/screen/splash/S = new(thing, FALSE)
var/atom/movable/screen/splash/S = new(thing, FALSE)
S.Fade(FALSE,FALSE)
/datum/controller/subsystem/title/Recover()
+5 -5
View File
@@ -100,14 +100,14 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/suit/space/hardsuit/ert/engi, /obj/item/clothing/suit/space/hardsuit/ert/med,
/obj/item/clothing/suit/space/hardsuit/deathsquad, /obj/item/clothing/head/helmet/space/hardsuit/deathsquad,
/obj/machinery/door/airlock/centcom)),
"robots" = typecacheof(list(/obj/machinery/computer/upload, /obj/item/aiModule/, /obj/machinery/recharge_station,
"robots" = typecacheof(list(/obj/machinery/computer/upload, /obj/item/ai_module/, /obj/machinery/recharge_station,
/obj/item/aicard, /obj/item/deactivated_swarmer, /obj/effect/mob_spawn/swarmer)),
"doctors" = typecacheof(list(/obj/item/clothing/under/rank/medical/doctor, /obj/item/clothing/under/rank/medical/chemist,
/obj/item/clothing/under/rank/medical/doctor/nurse, /obj/item/clothing/under/rank/medical/chief_medical_officer,
/obj/item/reagent_containers/syringe, /obj/item/reagent_containers/pill/, /obj/item/reagent_containers/hypospray,
/obj/item/storage/firstaid, /obj/item/storage/pill_bottle, /obj/item/healthanalyzer,
/obj/structure/sign/departments/medbay, /obj/machinery/door/airlock/medical, /obj/machinery/sleeper,
/obj/structure/sign/departments/medbay, /obj/machinery/door/airlock/medical, /obj/machinery/sleeper, /obj/machinery/stasis,
/obj/machinery/dna_scannernew, /obj/machinery/atmospherics/components/unary/cryo_cell, /obj/item/surgical_drapes,
/obj/item/retractor, /obj/item/hemostat, /obj/item/cautery, /obj/item/surgicaldrill, /obj/item/scalpel, /obj/item/circular_saw,
/obj/item/clothing/suit/bio_suit/plaguedoctorsuit, /obj/item/clothing/head/plaguedoctorhat, /obj/item/clothing/mask/gas/plaguedoctor)),
@@ -159,7 +159,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/storage/backpack/mime, /obj/item/reagent_containers/food/snacks/grown/banana/mime,
/obj/item/grown/bananapeel/mimanapeel, /obj/item/cartridge/virus/mime, /obj/item/clothing/shoes/sneakers/mime,
/obj/item/bedsheet/mime, /obj/item/reagent_containers/food/snacks/burger/mime, /obj/item/clothing/head/beret,
/obj/item/toy/figure/mime, /obj/item/toy/crayon/mime, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced, /obj/mecha/combat/reticence)),
/obj/item/toy/figure/mime, /obj/item/toy/crayon/mime, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced, /obj/vehicle/sealed/mecha/combat/reticence)),
"cats" = typecacheof(list(/obj/item/organ/ears/cat, /obj/item/organ/tail/cat, /obj/item/laser_pointer, /obj/item/toy/cattoy, /obj/item/clothing/head/kitty,
/obj/item/clothing/head/collectable/kitty, /obj/item/melee/chainofcommand/tailwhip/kitty, /obj/item/stack/sheet/animalhide/cat)),
@@ -169,8 +169,8 @@ SUBSYSTEM_DEF(traumas)
/obj/structure/fluff/empty_sleeper/syndicate, /obj/item/implant/radio/syndicate, /obj/item/clothing/head/helmet/space/syndicate, /obj/machinery/nuclearbomb/syndicate, /obj/item/grenade/syndieminibomb, /obj/item/storage/backpack/duffelbag/syndie, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver,
/obj/item/gun/ballistic/automatic/shotgun/bulldog, /obj/item/gun/ballistic/automatic/c20r, /obj/item/gun/ballistic/automatic/m90, /obj/item/gun/ballistic/automatic/l6_saw, /obj/item/storage/belt/grenade/full, /obj/item/gun/ballistic/automatic/sniper_rifle/syndicate, /obj/item/gun/energy/kinetic_accelerator/crossbow,
/obj/item/melee/transforming/energy/sword/saber, /obj/item/dualsaber, /obj/item/melee/powerfist, /obj/item/storage/box/syndie_kit, /obj/item/grenade/spawnergrenade/manhacks, /obj/item/grenade/chem_grenade/bioterrorfoam, /obj/item/reagent_containers/spray/chemsprayer/bioterror, /obj/item/ammo_box/magazine/m10mm,
/obj/item/ammo_box/magazine/pistolm9mm, /obj/item/ammo_box/a357, /obj/item/ammo_box/magazine/m12g, /obj/item/ammo_box/magazine/mm195x129, /obj/item/antag_spawner/nuke_ops, /obj/mecha/combat/gygax/dark, /obj/mecha/combat/marauder/mauler, /obj/item/soap/syndie, /obj/item/gun/syringe/syndicate, /obj/item/cartridge/virus/syndicate,
/obj/item/cartridge/virus/frame, /obj/item/chameleon, /obj/item/storage/box/syndie_kit/cutouts, /obj/item/clothing/suit/space/hardsuit/syndi, /obj/item/card/emag, /obj/item/storage/toolbox/syndicate, /obj/item/storage/book/bible/syndicate, /obj/item/encryptionkey/binary, /obj/item/encryptionkey/syndicate, /obj/item/aiModule/syndicate,
/obj/item/ammo_box/magazine/pistolm9mm, /obj/item/ammo_box/a357, /obj/item/ammo_box/magazine/m12g, /obj/item/ammo_box/magazine/mm712x82, /obj/item/antag_spawner/nuke_ops, /obj/vehicle/sealed/mecha/combat/gygax/dark, /obj/vehicle/sealed/mecha/combat/marauder/mauler, /obj/item/soap/syndie, /obj/item/gun/syringe/syndicate, /obj/item/cartridge/virus/syndicate,
/obj/item/cartridge/virus/frame, /obj/item/chameleon, /obj/item/storage/box/syndie_kit/cutouts, /obj/item/clothing/suit/space/hardsuit/syndi, /obj/item/card/emag, /obj/item/storage/toolbox/syndicate, /obj/item/storage/book/bible/syndicate, /obj/item/encryptionkey/binary, /obj/item/encryptionkey/syndicate, /obj/item/ai_module/syndicate,
/obj/item/clothing/shoes/magboots/syndie, /obj/item/powersink, /obj/item/sbeacondrop, /obj/item/sbeacondrop/bomb, /obj/item/syndicatedetonator, /obj/item/shield/energy, /obj/item/assault_pod, /obj/item/slimepotion/slime/sentience/nuclear, /obj/item/stack/telecrystal, /obj/item/jammer, /obj/item/codespeak_manual/unlimited,
/obj/item/toy/cards/deck/syndicate, /obj/item/storage/secure/briefcase/syndie, /obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/toy/syndicateballoon, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/paper/fluff/ruins/thederelict/syndie_mission, /obj/item/organ/cyberimp/eyes/hud/security/syndicate, /obj/item/clothing/head/HoS/syndicate,
/obj/machinery/computer/pod/old/syndicate, /obj/machinery/vending/medical/syndicate_access, /obj/item/mmi/syndie, /obj/item/target/syndicate, /obj/machinery/vending/cigarette/syndicate, /obj/item/robot_module/syndicate, /obj/item/clothing/mask/gas/syndicate, /obj/machinery/power/singularity_beacon/syndicate, /obj/item/clothing/head/syndicatefake,
+1 -4
View File
@@ -5,12 +5,10 @@ SUBSYSTEM_DEF(vis_overlays)
init_order = INIT_ORDER_VIS
var/list/vis_overlay_cache
var/list/unique_vis_overlays
var/list/currentrun
/datum/controller/subsystem/vis_overlays/Initialize()
vis_overlay_cache = list()
unique_vis_overlays = list()
return ..()
/datum/controller/subsystem/vis_overlays/fire(resumed = FALSE)
@@ -30,7 +28,7 @@ SUBSYSTEM_DEF(vis_overlays)
if(MC_TICK_CHECK)
return
//the "thing" var can be anything with vis_contents which includes images
//the "thing" var can be anything with vis_contents which includes images - in the future someone should totally allow vis overlays to be passed in as an arg instead of all this bullshit
/datum/controller/subsystem/vis_overlays/proc/add_vis_overlay(atom/movable/thing, icon, iconstate, layer, plane, dir, alpha = 255, add_appearance_flags = NONE, unique = FALSE)
var/obj/effect/overlay/vis/overlay
if(!unique)
@@ -45,7 +43,6 @@ SUBSYSTEM_DEF(vis_overlays)
overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
overlay.cache_expiration = -1
var/cache_id = "\ref[overlay]@{[world.time]}"
unique_vis_overlays += overlay
vis_overlay_cache[cache_id] = overlay
. = overlay
thing.vis_contents += overlay
+37 -77
View File
@@ -32,6 +32,8 @@ SUBSYSTEM_DEF(vote)
var/list/stored_modetier_results = list() // The aggregated tier list of the modes available in secret.
var/transfer_votes_done = 0
/datum/controller/subsystem/vote/fire() //called by master_controller
if(mode)
if(end_time < world.time)
@@ -68,7 +70,7 @@ SUBSYSTEM_DEF(vote)
//get the highest number of votes
var/greatest_votes = 0
var/total_votes = 0
if(mode == "gamemode" && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
choices[choices[voted[P.ckey]]]--
@@ -104,8 +106,7 @@ SUBSYSTEM_DEF(vote)
return .
/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text)
// https://en.wikipedia.org/wiki/Schulze_method#Implementation
if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
voted -= P.ckey
@@ -155,7 +156,7 @@ SUBSYSTEM_DEF(vote)
for(var/choice in choices)
scores_by_choice += "[choice]"
scores_by_choice["[choice]"] = list()
if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
voted -= P.ckey
@@ -164,7 +165,7 @@ SUBSYSTEM_DEF(vote)
var/list/pretty_vote = list()
for(var/choice in choices)
if(("[choice]" in this_vote) && ("[choice]" in scores_by_choice))
sorted_insert(scores_by_choice["[choice]"],this_vote["[choice]"],/proc/cmp_numeric_asc)
sorted_insert(scores_by_choice["[choice]"],this_vote["[choice]"],GLOBAL_PROC_REF(cmp_numeric_asc))
// START BALLOT GATHERING
pretty_vote += "[choice]"
if(this_vote["[choice]"] in GLOB.vote_score_options)
@@ -249,8 +250,18 @@ SUBSYSTEM_DEF(vote)
if(vote_system == SCORE_VOTING)
calculate_scores(vote_title_text)
if(vote_system == HIGHEST_MEDIAN_VOTING)
calculate_highest_median(vote_title_text) // nothing uses this at the moment
var/list/winners = vote_system == INSTANT_RUNOFF_VOTING ? get_runoff_results() : get_result()
calculate_highest_median(vote_title_text)
var/list/winners = list()
if(mode == "transfer")
var/amount_required = 1 + transfer_votes_done
transfer_votes_done += 1
text += "\nExtending requires at least [amount_required] votes to win."
if(choices[VOTE_CONTINUE] < amount_required || choices[VOTE_TRANSFER] >= choices[VOTE_CONTINUE])
winners = list(VOTE_TRANSFER)
else
winners = list(VOTE_CONTINUE)
else
winners = vote_system == INSTANT_RUNOFF_VOTING ? get_runoff_results() : get_result()
var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic"
if(winners.len > 0)
if(was_roundtype_vote)
@@ -306,14 +317,14 @@ SUBSYSTEM_DEF(vote)
if(vote_system == SCHULZE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
else if(vote_system == HIGHEST_MEDIAN_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!"
admintext += "\nIt should be noted that this is not a raw tally of votes but rather the median score plus a tiebreaker!"
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
admintext += "\n<b>[choices[i]]:</b> [votes]"
admintext += "\n<b>[choices[i]]:</b> [votes ? votes : "0"]" //This is raw data, but the raw data is null by default. If ya don't compensate for it, then it'll look weird!
else
for(var/i=1,i<=scores.len,i++)
var/score = scores[scores[i]]
admintext += "\n<b>[scores[i]]:</b> [score]"
admintext += "\n<b>[scores[i]]:</b> [score ? score : "0"]"
message_admins(admintext)
return .
@@ -322,51 +333,17 @@ SUBSYSTEM_DEF(vote)
var/restart = 0
if(.)
switch(mode)
if("roundtype") //CIT CHANGE - adds the roundstart extended/secret vote
if("roundtype") //CIT CHANGE - adds the roundstart extended/dynamic vote
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
GLOB.master_mode = .
SSticker.save_mode(.)
GLOB.master_mode = "dynamic"
if(. == "extended")
GLOB.dynamic_extended = TRUE
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
if(CONFIG_GET(flag/modetier_voting))
reset()
started_time = 0
initiate_vote("mode tiers","server", votesystem=SCORE_VOTING, forced=TRUE, vote_time = 30 MINUTES)
to_chat(world,"<b>The vote will end right as the round starts.</b>")
return .
if("restart")
if(. == "Restart Round")
restart = 1
if("gamemode")
if(GLOB.master_mode != .)
SSticker.save_mode(.)
if(SSticker.HasRoundStarted())
restart = 1
else
GLOB.master_mode = .
if("mode tiers")
var/list/raw_score_numbers = list()
for(var/score_name in scores)
sorted_insert(raw_score_numbers,scores[score_name],/proc/cmp_numeric_asc)
stored_modetier_results = scores.Copy()
for(var/score_name in stored_modetier_results)
if(stored_modetier_results[score_name] <= raw_score_numbers[CONFIG_GET(number/dropped_modes)])
stored_modetier_results -= score_name
stored_modetier_results += "traitor"
if("dynamic")
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
var/list/runnable_storytellers = config.get_runnable_storytellers()
var/datum/dynamic_storyteller/picked
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
if(stored_gamemode_votes[initial(S.name)] == 1 && CHECK_BITFIELD(initial(S.flags), FORCE_IF_WON))
picked = S
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
if(!picked)
picked = pickweight(runnable_storytellers, 0)
GLOB.dynamic_storyteller_type = picked
if("map")
var/datum/map_config/VM = config.maplist[.]
message_admins("The map has been voted for and will change to: [VM.map_name]")
@@ -374,7 +351,7 @@ SUBSYSTEM_DEF(vote)
if(SSmapping.changemap(config.maplist[.]))
to_chat(world, "<span class='boldannounce'>The map vote has chosen [VM.map_name] for next round!</span>")
if("transfer") // austation begin -- Crew autotransfer vote
if(. == "Initiate Crew Transfer")
if(. == VOTE_TRANSFER)
SSshuttle.autoEnd()
var/obj/machinery/computer/communications/C = locate() in GLOB.machines
if(C)
@@ -396,7 +373,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/submit_vote(vote, score = 0)
if(mode)
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
return 0
return FALSE
if(vote && ISINRANGE(vote, 1, choices.len))
switch(vote_system)
if(PLURALITY_VOTING)
@@ -438,7 +415,7 @@ SUBSYSTEM_DEF(vote)
voted[usr.ckey] = list()
voted[usr.ckey][choices[vote]] = score
saved -= usr.ckey
return 0
return FALSE
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, display = display_votes, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds display argument to votes to allow for obfuscated votes
vote_system = votesystem
@@ -447,7 +424,7 @@ SUBSYSTEM_DEF(vote)
var/next_allowed_time = (started_time + CONFIG_GET(number/vote_delay))
if(mode)
to_chat(usr, "<span class='warning'>There is already a vote in progress! please wait for it to finish.</span>")
return 0
return FALSE
var/admin = FALSE
var/ckey = ckey(initiator_key)
@@ -456,7 +433,7 @@ SUBSYSTEM_DEF(vote)
if(next_allowed_time > world.time && !admin)
to_chat(usr, "<span class='warning'>A vote was initiated recently, you must wait [DisplayTimeText(next_allowed_time-world.time)] before a new vote can be started!</span>")
return 0
return FALSE
SEND_SOUND(world, sound('sound/misc/notice2.ogg'))
reset()
@@ -481,30 +458,13 @@ SUBSYSTEM_DEF(vote)
continue
choices |= M
if("transfer") // austation begin -- Crew autotranfer vote
choices.Add("Initiate Crew Transfer","Continue Playing") // austation end
choices.Add(VOTE_TRANSFER,VOTE_CONTINUE) // austation end
if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote
choices.Add("secret", "extended")
if("mode tiers")
var/list/modes_to_add = config.votable_modes
var/list/probabilities = CONFIG_GET(keyed_list/probability)
for(var/tag in modes_to_add)
if(probabilities[tag] <= 0)
modes_to_add -= tag
modes_to_add -= "traitor" // makes it so that traitor is always available
choices.Add(modes_to_add)
if("dynamic")
GLOB.master_mode = "dynamic"
var/list/probabilities = CONFIG_GET(keyed_list/storyteller_weight)
for(var/T in config.get_runnable_storytellers())
var/datum/dynamic_storyteller/S = T
var/probability = ((initial(S.config_tag) in probabilities) ? probabilities[initial(S.config_tag)] : initial(S.weight))
if(probability > 0)
choices.Add(initial(S.name))
choice_descs.Add(initial(S.desc))
choices.Add("dynamic", "extended")
if("custom")
question = stripped_input(usr,"What is the vote for?")
if(!question)
return 0
return FALSE
var/system_string = input(usr,"Which voting type?",GLOB.vote_type_names[1]) in GLOB.vote_type_names
vote_system = GLOB.vote_type_names[system_string]
for(var/i=1,i<=10,i++)
@@ -528,7 +488,7 @@ SUBSYSTEM_DEF(vote)
toggles ^= choices[chosen]
display_votes = toggles
else
return 0
return FALSE
mode = vote_type
initiator = initiator_key ? initiator_key : "the Server" // austation -- Crew autotransfer vote
started_time = world.time
@@ -561,8 +521,8 @@ SUBSYSTEM_DEF(vote)
popup.set_window_options("can_close=0")
popup.set_content(SSvote.interface(C))
popup.open(0)
return 1
return 0
return TRUE
return FALSE
/datum/controller/subsystem/vote/proc/interface(client/C)
if(!C)
@@ -776,7 +736,7 @@ SUBSYSTEM_DEF(vote)
Remove(owner)
/datum/action/vote/IsAvailable(silent = FALSE)
return 1
return TRUE
/datum/action/vote/proc/remove_from_client()
if(!owner)