diff --git a/byond-extools.dll b/byond-extools.dll
index ea5491a741..c404bf1be5 100644
Binary files a/byond-extools.dll and b/byond-extools.dll differ
diff --git a/byond-extools.pdb b/byond-extools.pdb
new file mode 100644
index 0000000000..f89dc06441
Binary files /dev/null and b/byond-extools.pdb differ
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index a3fdf762c2..9deddd4b46 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -122,6 +122,7 @@
#define FIRE_PRIORITY_PROJECTILES 200
#define FIRE_PRIORITY_TICKER 200
#define FIRE_PRIORITY_ATMOS_ADJACENCY 300
+#define FIRE_PRIORITY_STATPANEL 390
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_OVERLAYS 500
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 0171057286..3efb50ef65 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -553,12 +553,6 @@
if(D.vars[varname] == value)
return D
-//remove all nulls from a list
-/proc/removeNullsFromList(list/L)
- while(L.Remove(null))
- continue
- return L
-
//Copies a list, and all lists inside it recusively
//Does not copy any other reference type
/proc/deepCopyList(list/l)
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 2bd477d98c..4723577fad 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1223,7 +1223,7 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
return "
"
//Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
-/proc/costly_icon2html(thing, target)
+/proc/costly_icon2html(thing, target, sourceonly = FALSE)
if (!thing)
return
@@ -1231,4 +1231,5 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
return icon2html(thing, target)
var/icon/I = getFlatIcon(thing)
- return icon2html(I, target)
+ return icon2html(I, target, sourceonly = sourceonly)
+
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index dfd8d4c873..e207fe838a 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -16,7 +16,6 @@ SUBSYSTEM_DEF(air)
var/cost_atmos_machinery = 0
var/cost_equalize = 0
- var/list/active_turfs = list()
var/list/hotspots = list()
var/list/networks = list()
var/list/pipenets_needing_rebuilt = list()
@@ -54,13 +53,16 @@ SUBSYSTEM_DEF(air)
msg += "PN:[round(cost_pipenets,1)]|"
msg += "AM:[round(cost_atmos_machinery,1)]"
msg += "} "
- msg += "AT:[active_turfs.len]|"
+ var/active_turfs_len = get_amt_active_turfs()
+ msg += "AT:[active_turfs_len]|"
msg += "EG:[get_amt_excited_groups()]|"
msg += "HS:[hotspots.len]|"
msg += "PN:[networks.len]|"
msg += "HP:[high_pressure_delta.len]|"
msg += "AS:[active_super_conductivity.len]|"
- msg += "AT/MS:[round((cost ? active_turfs.len/cost : 0),0.1)]"
+ msg += "GA:[get_amt_gas_mixes()]|"
+ msg += "MG:[get_max_gas_mixes()]|"
+ msg += "AT/MS:[round((cost ? active_turfs_len/cost : 0),0.1)]"
return ..()
/datum/controller/subsystem/air/Initialize(timeofday)
@@ -70,9 +72,11 @@ SUBSYSTEM_DEF(air)
setup_atmos_machinery()
setup_pipenets()
gas_reactions = init_gas_reactions()
+ extools_update_reactions()
return ..()
/datum/controller/subsystem/air/proc/extools_update_ssair()
+/datum/controller/subsystem/air/proc/extools_update_reactions()
/datum/controller/subsystem/air/fire(resumed = 0)
var/timer = TICK_USAGE_REAL
@@ -237,6 +241,8 @@ SUBSYSTEM_DEF(air)
return
/datum/controller/subsystem/air/proc/process_turf_equalize(resumed = 0)
+ return process_turf_equalize_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
+ /*
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
@@ -251,8 +257,11 @@ SUBSYSTEM_DEF(air)
//equalize_pressure_in_zone(T, fire_count)
if (MC_TICK_CHECK)
return
+ */
/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
+ return process_active_turfs_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
+ /*
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
@@ -266,10 +275,10 @@ SUBSYSTEM_DEF(air)
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))
- sleep()
+ return process_excited_groups_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
/*
if (!resumed)
src.currentrun = excited_groups.Copy()
@@ -288,13 +297,20 @@ SUBSYSTEM_DEF(air)
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/remove_from_active(turf/open/T)
- active_turfs -= T
- if(currentpart == SSAIR_ACTIVETURFS)
- currentrun -= T
+ remove_from_active_extools(T)
#ifdef VISUALIZE_ACTIVE_TURFS
T.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#00ff00")
#endif
@@ -308,9 +324,7 @@ SUBSYSTEM_DEF(air)
T.add_atom_colour("#00ff00", TEMPORARY_COLOUR_PRIORITY)
#endif
T.set_excited(TRUE)
- active_turfs |= T
- if(currentpart == SSAIR_ACTIVETURFS)
- currentrun |= T
+ add_to_active_extools(T)
if(blockchanges)
T.eg_garbage_collect()
else if(T.flags_1 & INITIALIZED_1)
@@ -335,12 +349,11 @@ SUBSYSTEM_DEF(air)
/datum/controller/subsystem/air/proc/setup_allturfs()
var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))
- var/list/active_turfs = src.active_turfs
var/times_fired = ++src.times_fired
// Clear active turfs - faster than removing every single turf in the world
// one-by-one, and Initalize_Atmos only ever adds `src` back in.
- active_turfs.Cut()
+ clear_active_turfs()
for(var/thing in turfs_to_init)
var/turf/T = thing
@@ -348,28 +361,27 @@ SUBSYSTEM_DEF(air)
continue
T.Initalize_Atmos(times_fired)
CHECK_TICK
-
- if(active_turfs.len)
- var/starting_ats = active_turfs.len
+ 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).")
- for(var/turf/T in active_turfs)
+ var/list/turfs_to_check = get_amt_active_turfs()
+ for(var/T in turfs_to_check)
GLOB.active_turfs_startlist += T
//now lets clear out these active turfs
- var/list/turfs_to_check = active_turfs.Copy()
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
-
- active_turfs += new_turfs_to_check
+ 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 = active_turfs.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)
@@ -445,6 +457,8 @@ SUBSYSTEM_DEF(air)
return pipe_init_dirs_cache[type]["[dir]"]
+/proc/get_extools_benchmarks()
+
#undef SSAIR_PIPENETS
#undef SSAIR_ATMOSMACHINERY
#undef SSAIR_ACTIVETURFS
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 4731940f41..cf7d4ba533 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -1,7 +1,8 @@
SUBSYSTEM_DEF(statpanels)
name = "Stat Panels"
wait = 4
- init_order = INIT_ORDER_STATPANELS //Really early so we can debug MC roundstart
+ init_order = INIT_ORDER_STATPANELS
+ priority = FIRE_PRIORITY_STATPANEL
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/list/currentrun = list()
var/encoded_global_data
@@ -9,13 +10,15 @@ SUBSYSTEM_DEF(statpanels)
var/list/cached_images = list()
/datum/controller/subsystem/statpanels/fire(resumed = FALSE)
- if(!resumed)
+ if (!resumed)
var/datum/map_config/cached = SSmapping.next_map_config
+ var/round_time = world.time - SSticker.round_start_time
var/list/global_data = list(
"Map: [SSmapping.config?.map_name || "Loading..."]",
cached ? "Next Map: [cached.map_name]" : null,
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
- "[SStime_track.stat_time_text]",
+ "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]",
+ "Round Time: [round_time > MIDNIGHT_ROLLOVER ? "[round(round_time/MIDNIGHT_ROLLOVER)]:[worldtime2text()]" : worldtime2text()]",
"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)]%)"
)
@@ -24,96 +27,169 @@ SUBSYSTEM_DEF(statpanels)
var/ETA = SSshuttle.emergency.getModeStr()
if(ETA)
global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
+/* Please fix
+ if(SSvote.mode)
+ var/static/list/supported = list(PLURALITY_VOTING, APPROVAL_VOTING)
+ global_data += "Vote active!, There is currently a vote running. Question: [SSvote.question]"
+ if(!(SSvote.vote_system in supported))
+ global_data += ", The current vote system is not supported by statpanel rendering. Please vote manually by opening the vote popup using the action button or chat link."
+ return
+ global_data += "Time Left:, [round(SSvote.end_time - world.time)] seconds"
+
+ global_data += "Choices:"
+ for(var/i in 1 to SSvote.choice_statclicks.len)
+ var/choice = SSvote.choice_statclicks[i]
+ var/ivotedforthis = FALSE
+ if(usr.ckey)
+ switch(SSvote.vote_system)
+ if(APPROVAL_VOTING)
+ ivotedforthis = SSvote.voted[usr.ckey] && (i in SSvote.voted[usr.ckey])
+ if(PLURALITY_VOTING)
+ ivotedforthis = SSvote.voted[usr.ckey] == i
+
+ global_data += (ivotedforthis? "\[X\]" : "\[ \]", SSvote.choice_statclicks[choice])
+*/
+
encoded_global_data = url_encode(json_encode(global_data))
-
- var/list/mc_data = list(
- list("CPU:", world.cpu),
- list("Instances:", "[num2text(world.contents.len, 10)]"),
- list("World Time:", "[world.time]"),
- list("Globals:", GLOB.stat_entry(), "\ref[GLOB]"),
- list("[config]:", config.stat_entry(), "\ref[config]"),
- list("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
- list("Master Controller:", Master.stat_entry(), "\ref[Master]"),
- list("Failsafe Controller:", Failsafe.stat_entry(), "\ref[Failsafe]"),
- list("","")
- )
- for(var/ss in Master.subsystems)
- var/datum/controller/subsystem/sub_system = ss
- mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
- mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]")
- mc_data_encoded = url_encode(json_encode(mc_data))
src.currentrun = GLOB.clients.Copy()
-
+ mc_data_encoded = null
var/list/currentrun = src.currentrun
while(length(currentrun))
var/client/target = currentrun[length(currentrun)]
currentrun.len--
- var/ping_str = url_encode("Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)")
- var/other_str = url_encode(json_encode(target.mob.get_status_tab_items()))
- target << output("[encoded_global_data];[ping_str];[other_str]", "statbrowser:update")
+ if(!target.statbrowser_ready)
+ continue
+ if(target.stat_tab == "Status")
+ var/ping_str = url_encode("Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)")
+ var/other_str = url_encode(json_encode(target.mob.get_status_tab_items()))
+ target << output("[encoded_global_data];[ping_str];[other_str]", "statbrowser:update")
if(!target.holder)
target << output("", "statbrowser:remove_admin_tabs")
else
- var/turf/eye_turf = get_turf(target.eye)
- var/coord_entry = url_encode(COORD(eye_turf))
- target << output("[mc_data_encoded];[coord_entry];[url_encode(target.holder.href_token)]", "statbrowser:update_mc")
- var/list/ahelp_tickets = GLOB.ahelp_tickets.stat_entry()
- target << output("[url_encode(json_encode(ahelp_tickets))];", "statbrowser:update_tickets")
- if(!length(GLOB.sdql2_queries))
+ if(!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs))
+ target << output("[url_encode(target.holder.href_token)]", "statbrowser:add_admin_tabs")
+ if(target.stat_tab == "MC")
+ var/turf/eye_turf = get_turf(target.eye)
+ var/coord_entry = url_encode(COORD(eye_turf))
+ if(!mc_data_encoded)
+ generate_mc_data()
+ target << output("[mc_data_encoded];[coord_entry]", "statbrowser:update_mc")
+ if(target.stat_tab == "Tickets")
+ var/list/ahelp_tickets = GLOB.ahelp_tickets.stat_entry()
+ target << output("[url_encode(json_encode(ahelp_tickets))];", "statbrowser:update_tickets")
+ if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target << output("", "statbrowser:remove_sdql2")
- else
- var/list/sqdl2A = list()
- sqdl2A[++sqdl2A.len] = list("", "Access Global SDQL2 List", REF(GLOB.sdql2_vv_statobj))
- var/list/sqdl2B = list()
+ else if(length(GLOB.sdql2_queries) && (target.stat_tab == "SDQL2" || !("SDQL2" in target.panel_tabs)))
+ var/list/sdql2A = list()
+ sdql2A[++sdql2A.len] = list("", "Access Global SDQL2 List", REF(GLOB.sdql2_vv_statobj))
+ var/list/sdql2B = list()
for(var/i in GLOB.sdql2_queries)
var/datum/SDQL2_query/Q = i
- sqdl2B = Q.generate_stat()
- sqdl2A += sqdl2B
- target << output(url_encode(json_encode(sqdl2A)), "statbrowser:update_sdql2")
- var/list/proc_holders = target.mob.get_proc_holders()
- target.spell_tabs.Cut()
- for(var/phl in proc_holders)
- var/list/proc_holder_list = phl
- target.spell_tabs |= proc_holder_list[1]
- var/proc_holders_encoded = ""
- if(length(proc_holders))
- proc_holders_encoded = url_encode(json_encode(proc_holders))
- target << output("[url_encode(json_encode(target.spell_tabs))];[proc_holders_encoded]", "statbrowser:update_spells")
- if(target.mob?.listed_turf)
- var/mob/target_mob = target.mob
- if(!target_mob.TurfAdjacent(target_mob.listed_turf))
- target << output("", "statbrowser:remove_listedturf")
- target_mob.listed_turf = null
- else
- var/list/overrides = list()
- var/list/turfitems = list()
- for(var/img in target.images)
- var/image/target_image = img
- if(!target_image.loc || target_image.loc.loc != target_mob.listed_turf || !target_image.override)
- continue
- overrides += target_image.loc
- if(!(REF(target_mob.listed_turf) in cached_images))
- target << browse_rsc(getFlatIcon(target_mob.listed_turf, no_anim = TRUE), "[REF(target_mob.listed_turf)].png")
- cached_images += REF(target_mob.listed_turf)
- turfitems[++turfitems.len] = list("[target_mob.listed_turf]", REF(target_mob.listed_turf), "[REF(target_mob.listed_turf)].png")
- for(var/tc in target_mob.listed_turf)
- var/atom/movable/turf_content = tc
- if(turf_content.mouse_opacity == MOUSE_OPACITY_TRANSPARENT)
- continue
- if(turf_content.invisibility > target_mob.see_invisible)
- continue
- if(turf_content in overrides)
- continue
- if(turf_content.IsObscured())
- continue
- 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))
- target << browse_rsc(getFlatIcon(turf_content, no_anim = TRUE), "[REF(turf_content)].png")
- cached_images += REF(turf_content)
- turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content), "[REF(turf_content)].png")
- else
- turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content))
- turfitems = url_encode(json_encode(turfitems))
- target << output("[turfitems];", "statbrowser:update_listedturf")
+ sdql2B = Q.generate_stat()
+ sdql2A += sdql2B
+ target << output(url_encode(json_encode(sdql2A)), "statbrowser:update_sdql2")
+ if(target.mob)
+ var/mob/M = target.mob
+ if((target.stat_tab in target.spell_tabs) || !length(target.spell_tabs) && (length(M.mob_spell_list) || length(M.mind?.spell_list)))
+ var/list/proc_holders = M.get_proc_holders()
+ target.spell_tabs.Cut()
+ for(var/phl in proc_holders)
+ var/list/proc_holder_list = phl
+ target.spell_tabs |= proc_holder_list[1]
+ var/proc_holders_encoded = ""
+ if(length(proc_holders))
+ proc_holders_encoded = url_encode(json_encode(proc_holders))
+ target << output("[url_encode(json_encode(target.spell_tabs))];[proc_holders_encoded]", "statbrowser:update_spells")
+ if(M?.listed_turf)
+ var/mob/target_mob = M
+ if(!target_mob.TurfAdjacent(target_mob.listed_turf))
+ target << output("", "statbrowser:remove_listedturf")
+ target_mob.listed_turf = null
+ else if(target.stat_tab == M?.listed_turf.name || !(M?.listed_turf.name in target.panel_tabs))
+ var/list/overrides = list()
+ var/list/turfitems = list()
+ for(var/img in target.images)
+ var/image/target_image = img
+ if(!target_image.loc || target_image.loc.loc != target_mob.listed_turf || !target_image.override)
+ continue
+ overrides += target_image.loc
+ turfitems[++turfitems.len] = list("[target_mob.listed_turf]", REF(target_mob.listed_turf), icon2html(target_mob.listed_turf, target, sourceonly=TRUE))
+ for(var/tc in target_mob.listed_turf)
+ var/atom/movable/turf_content = tc
+ if(turf_content.mouse_opacity == MOUSE_OPACITY_TRANSPARENT)
+ continue
+ if(turf_content.invisibility > target_mob.see_invisible)
+ continue
+ if(turf_content in overrides)
+ continue
+ if(turf_content.IsObscured())
+ continue
+ 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
+ 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
+ turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content), icon2html(turf_content, target, sourceonly=TRUE))
+ else
+ turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content))
+ else
+ turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content))
+ turfitems = url_encode(json_encode(turfitems))
+ target << output("[turfitems];", "statbrowser:update_listedturf")
if(MC_TICK_CHECK)
return
+
+
+/datum/controller/subsystem/statpanels/proc/generate_mc_data()
+ var/list/mc_data = list(
+ list("CPU:", world.cpu),
+ list("Instances:", "[num2text(world.contents.len, 10)]"),
+ list("World Time:", "[world.time]"),
+ list("Globals:", GLOB.stat_entry(), "\ref[GLOB]"),
+ list("[config]:", config.stat_entry(), "\ref[config]"),
+ list("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
+ list("Master Controller:", Master.stat_entry(), "\ref[Master]"),
+ list("Failsafe Controller:", Failsafe.stat_entry(), "\ref[Failsafe]"),
+ list("","")
+ )
+ for(var/ss in Master.subsystems)
+ var/datum/controller/subsystem/sub_system = ss
+ mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
+ mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]")
+ mc_data_encoded = url_encode(json_encode(mc_data))
+
+/atom/proc/remove_from_cache()
+ SSstatpanels.cached_images -= REF(src)
+
+/// verbs that send information from the browser UI
+/client/verb/set_tab(tab as text|null)
+ set name = "Set Tab"
+ set hidden = TRUE
+
+ stat_tab = tab
+
+/client/verb/send_tabs(tabs as text|null)
+ set name = "Send Tabs"
+ set hidden = TRUE
+
+ panel_tabs |= tabs
+
+/client/verb/remove_tabs(tabs as text|null)
+ set name = "Remove Tabs"
+ set hidden = TRUE
+
+ panel_tabs -= tabs
+
+/client/verb/reset_tabs()
+ set name = "Reset Tabs"
+ set hidden = TRUE
+
+ panel_tabs = list()
+
+/client/verb/panel_ready()
+ set name = "Panel Ready"
+ set hidden = TRUE
+
+ statbrowser_ready = TRUE
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 8fdeaa0f9f..db29614f20 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -718,8 +718,9 @@ GENETICS SCANNER
to_chat(user, "Pressure: [round(pressure,0.01)] kPa")
for(var/id in air_contents.get_gases())
- var/gas_concentration = air_contents.get_moles(id)/total_moles
- to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)")
+ if(air_contents.get_moles(id) >= 0.005)
+ var/gas_concentration = air_contents.get_moles(id)/total_moles
+ to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)")
to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)")
else
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 6552e29246..1ff056c572 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -210,7 +210,7 @@
if(!get_excited() && air.compare(enemy_air))
//testing("Active turf found. Return value of compare(): [is_active]")
set_excited(TRUE)
- SSair.active_turfs |= src
+ SSair.add_to_active_extools(src)
/turf/open/proc/GetHeatCapacity()
. = air.heat_capacity()
diff --git a/code/game/world.dm b/code/game/world.dm
index 121d51136d..42c90d8be1 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -273,12 +273,6 @@ GLOBAL_LIST(topic_status_cache)
..()
/world/Del()
- // memory leaks bad
- var/num_deleted = 0
- for(var/datum/gas_mixture/GM)
- GM.__gasmixture_unregister()
- num_deleted++
- log_world("Deallocated [num_deleted] gas mixtures")
shutdown_logging() // makes sure the thread is closed before end, else we terminate
..()
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index e498ff05c9..3cc597ec31 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -196,7 +196,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
//Compares sample to self to see if within acceptable ranges that group processing may be enabled
//Returns: a string indicating what check failed, or "" if check passes
-/datum/gas_mixture/proc/react(turf/open/dump_location)
+/datum/gas_mixture/proc/react(datum/holder)
//Performs various reactions such as combustion or fusion (LOL)
//Returns: 1 if any reaction took place; 0 otherwise
@@ -243,7 +243,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
set_moles(path, text2num(gas[id]))
archive()
return 1
-
+/*
/datum/gas_mixture/react(datum/holder)
. = NO_REACTION
if(!total_moles())
@@ -291,7 +291,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
. |= reaction.react(src, holder)
if (. & STOP_REACTIONS)
break
-
+*/
//Takes the amount of the gas you want to PP as an argument
//So I don't have to do some hacky switches/defines/magic strings
//eg:
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index bb65bdcaa4..7f073567c5 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -275,7 +275,7 @@
for (var/gas_id in air.get_gases())
gas_power += (gas_fusion_powers[gas_id]*air.get_moles(gas_id))
var/instability = MODULUS((gas_power*INSTABILITY_GAS_POWER_FACTOR)**2,toroidal_size) //Instability effects how chaotic the behavior of the reaction is
- cached_scan_results[id] = instability//used for analyzer feedback
+ cached_scan_results["fusion"] = instability//used for analyzer feedback
var/plasma = (initial_plasma-FUSION_MOLE_THRESHOLD)/(scale_factor) //We have to scale the amounts of carbon and plasma down a significant amount in order to show the chaotic dynamics we want
var/carbon = (initial_carbon-FUSION_MOLE_THRESHOLD)/(scale_factor) //We also subtract out the threshold amount to make it harder for fusion to burn itself out.
diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm
index 098df67321..6c732e3ee1 100644
--- a/code/modules/atmospherics/machinery/datum_pipeline.dm
+++ b/code/modules/atmospherics/machinery/datum_pipeline.dm
@@ -206,7 +206,7 @@
. = other_airs + air
if(null in .)
stack_trace("[src]([REF(src)]) has one or more null gas mixtures, which may cause bugs. Null mixtures will not be considered in reconcile_air().")
- return removeNullsFromList(.)
+ return listclearnulls(.)
/datum/pipeline/proc/reconcile_air()
var/list/datum/gas_mixture/GL = list()
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index d4f8547c34..755665583a 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -130,6 +130,15 @@
/// datum wrapper for client view
var/datum/view_data/view_size
+ /// our current tab
+ var/stat_tab
+
+ /// whether our browser is ready or not yet
+ var/statbrowser_ready = FALSE
+
+ /// list of all tabs
+ var/list/panel_tabs = list()
+
/// list of tabs containing spells and abilities
var/list/spell_tabs = list()
/// list of tabs containing verbs
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index f0e4bd4a53..7c55bbfcd1 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -64,7 +64,7 @@
originMachine.visible_message("[originMachine] beeps and seems lifeless.")
kill()
return
- vendingMachines = removeNullsFromList(vendingMachines)
+ vendingMachines = listclearnulls(vendingMachines)
if(!vendingMachines.len) //if every machine is infected
for(var/obj/machinery/vending/upriser in infectedMachines)
if(prob(70) && !QDELETED(upriser))
diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm
index 806782a8b2..b675925aeb 100644
--- a/code/modules/mob/living/carbon/human/species_types/ipc.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm
@@ -5,7 +5,8 @@
default_color = "00FF00"
blacklisted = 0
sexes = 0
- species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE)
+ species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE,HAIR)
+ hair_alpha = 210
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
mutant_bodyparts = list("ipc_screen" = "Blank", "ipc_antenna" = "None")
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
diff --git a/html/changelog.html b/html/changelog.html
index 4ca8081fa6..4dffb85ae0 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,18 @@
-->
+
25 September 2020
+
Putnam3145 updated:
+
+ - Removed a non-working proc that already had its functionality implemented in another proc in the same file.
+
+
+
24 September 2020
+
Putnam3145 updated:
+
+
22 September 2020
Arturlang updated:
@@ -1232,84 +1244,6 @@
- beepsky replaces the word THREAT_LEVEL with the actual threat level
-
- 24 July 2020
- EmeraldSundisk updated:
-
- - Adds a CMO office, along with Virology and Genetics labs to Omega Station
- - Adds a second chemistry station to the chemistry lab
- - Adjusts the locations of some objects in medical to accommodate these new additions
- - Relocates the morgue
- - Relocates items in impacted areas of maintenance as well as the library
- - Fixes an air line Bartholomew somehow knocked out
-
- Linzolle updated:
-
- - wounds now have a description on examine
-
- Owai-Seek updated:
-
- - Meatball Sub, Meatloaf + Meatloaf Slices, Bear Chili, Mashed Potatoes
- - Buttered Potatoes, Fancy Cracker Pack, Spiral Soup, Sweet and Sour Chicken
- - Organised the Food DMI a bit.
- - Deleted some stray pixels on some sprites.
- - Nuggie boxes are now centered correctly.
- - Icons for the the food items in this PR.
-
- Sneakyrat6 updated:
-
- - Fixes dressers not giving you undies
- - Fixes Snaxi not loading properly because of typos
- - You can now burn photos
-
- Zandario updated:
-
- - Murdered **tipes** and gave birth to **is**.
-
- silicons updated:
-
- - sanitization now doesn't cut off 15.7 or something million possible colors from character preferences (instead of only allowing 16 values for R G and B, it now allows 256 each)
- - projectiles are by default 17.5 tiles per second instead of 12.5
-
- timothyteakettle updated:
-
- - due to recent innovative research in the medical field, you now have bones
- - zombie claws are now sharp and do less damage, but can destroy non-lifeforms far faster
- - zombies now take less stamina damage
- - beepsky can now wear hats
-
- zeroisthebiggay updated:
-
- - rad and kravglove sprites
-
-
- 23 July 2020
- DeltaFire15 updated:
-
- - Traits are no longer fucked
-
- Putnam3145 updated:
-
- - Slight optimization in chat code.
-
- kappa-sama updated:
-
- - tendril chests being empty
-
- zeroisthebiggay updated:
-
-
- 22 July 2020
- Ludox updated:
-
- - You can no longer be brainwashed into giving birth to a fake baby
-
- kappa-sama updated:
-
- - brainwashing disk has lost its cost buffs (3->5) and is role restricted once more (medical doctor/roboticist)
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 1c89f98a85..b5492c9d68 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -27361,3 +27361,10 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- imageadd: Mechsuits, robotics jumpsuits added to RoboDrobe
timothyteakettle:
- tweak: character previews should be more consistent now
+2020-09-24:
+ Putnam3145:
+ - refactor: Atmos is free.
+2020-09-25:
+ Putnam3145:
+ - bugfix: Removed a non-working proc that already had its functionality implemented
+ in another proc in the same file.
diff --git a/html/changelogs/AutoChangeLog-pr-13426.yml b/html/changelogs/AutoChangeLog-pr-13426.yml
new file mode 100644
index 0000000000..fcce56a747
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13426.yml
@@ -0,0 +1,4 @@
+author: "CoreFlare"
+delete-after: True
+changes:
+ - rscadd: "IPC's can have hair. Why wasn't this added earlier. Use the bald hairstyle for no hair."
diff --git a/libbyond-extools.so b/libbyond-extools.so
index 052a8cb037..bdae36893f 100644
Binary files a/libbyond-extools.so and b/libbyond-extools.so differ