diff --git a/code/__DEFINES/procpath.dm b/code/__DEFINES/procpath.dm
index 11800583588..642ca3eab6c 100644
--- a/code/__DEFINES/procpath.dm
+++ b/code/__DEFINES/procpath.dm
@@ -22,3 +22,5 @@
var/category as text
/// Only clients/mobs with `see_invisibility` higher can use the verb.
var/invisibility as num
+ /// Whether or not the verb appears in statpanel and commandbar when you press space
+ var/hidden as num
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index aa9b4a29534..975a7011753 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -138,6 +138,7 @@
#define INIT_ORDER_DISCORD -60
#define INIT_ORDER_EXPLOSIONS -69
#define INIT_ORDER_PERSISTENCE -95
+#define INIT_ORDER_STATPANELS -98
#define INIT_ORDER_DEMO -99 // o avoid a bunch of changes related to initialization being written, do this last
#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init.
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 2c6005f7e98..9bce07bba7e 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -368,7 +368,7 @@
if(!previous)
var/list/report_parts = list(personal_report(C), GLOB.common_report)
content = report_parts.Join()
- C.verbs -= /client/proc/show_previous_roundend_report
+ remove_verb(C, /client/proc/show_previous_roundend_report)
fdel(filename)
text2file(content, filename)
else
diff --git a/code/__HELPERS/verbs.dm b/code/__HELPERS/verbs.dm
new file mode 100644
index 00000000000..3606c7d918a
--- /dev/null
+++ b/code/__HELPERS/verbs.dm
@@ -0,0 +1,96 @@
+/**
+ * handles adding verbs and updating the stat panel browser
+ *
+ * pass the verb type path to this instead of adding it directly to verbs so the statpanel can update
+ * Arguments:
+ * * target - Who the verb is being added to, client or mob typepath
+ * * verb - typepath to a verb, or a list of verbs, supports lists of lists
+ */
+/proc/add_verb(client/target, verb_or_list_to_add)
+ if(!target)
+ CRASH("add_verb called without a target")
+ if(IsAdminAdvancedProcCall())
+ return
+ var/mob/mob_target = null
+
+ if(ismob(target))
+ mob_target = target
+ target = mob_target.client
+ else if(!istype(target, /client))
+ CRASH("add_verb called on a non-mob and non-client")
+ var/list/verbs_list = list()
+ if(!islist(verb_or_list_to_add))
+ verbs_list += verb_or_list_to_add
+ else
+ var/list/verb_listref = verb_or_list_to_add
+ var/list/elements_to_process = verb_listref.Copy()
+ while(length(elements_to_process))
+ var/element_or_list = elements_to_process[length(elements_to_process)] //Last element
+ elements_to_process.len--
+ if(islist(element_or_list))
+ elements_to_process += element_or_list //list/a += list/b adds the contents of b into a, not the reference to the list itself
+ else
+ verbs_list += element_or_list
+
+ if(mob_target)
+ mob_target.verbs += verbs_list
+ if(!target)
+ return //Our work is done.
+ else
+ target.verbs += verbs_list
+
+ var/list/output_list = list()
+ for(var/thing in verbs_list)
+ var/procpath/verb_to_add = thing
+ output_list[++output_list.len] = list(verb_to_add.category, verb_to_add.name)
+ output_list = url_encode(json_encode(output_list))
+
+ target << output("[output_list];", "statbrowser:add_verb_list")
+
+/**
+ * handles removing verb and sending it to browser to update, use this for removing verbs
+ *
+ * pass the verb type path to this instead of removing it from verbs so the statpanel can update
+ * Arguments:
+ * * target - Who the verb is being removed from, client or mob typepath
+ * * verb - typepath to a verb, or a list of verbs, supports lists of lists
+ */
+/proc/remove_verb(client/target, verb_or_list_to_remove)
+ if(IsAdminAdvancedProcCall())
+ return
+
+ var/mob/mob_target = null
+ if(ismob(target))
+ mob_target = target
+ target = mob_target.client
+ else if(!istype(target, /client))
+ CRASH("remove_verb called on a non-mob and non-client")
+
+ var/list/verbs_list = list()
+ if(!islist(verb_or_list_to_remove))
+ verbs_list += verb_or_list_to_remove
+ else
+ var/list/verb_listref = verb_or_list_to_remove
+ var/list/elements_to_process = verb_listref.Copy()
+ while(length(elements_to_process))
+ var/element_or_list = elements_to_process[length(elements_to_process)] //Last element
+ elements_to_process.len--
+ if(islist(element_or_list))
+ elements_to_process += element_or_list //list/a += list/b adds the contents of b into a, not the reference to the list itself
+ else
+ verbs_list += element_or_list
+
+ if(mob_target)
+ mob_target.verbs -= verbs_list
+ if(!target)
+ return //Our work is done.
+ else
+ target.verbs -= verbs_list
+
+ var/list/output_list = list()
+ for(var/thing in verbs_list)
+ var/procpath/verb_to_remove = thing
+ output_list[++output_list.len] = list(verb_to_remove.category, verb_to_remove.name)
+ output_list = url_encode(json_encode(output_list))
+
+ target << output("[output_list];", "statbrowser:remove_verb_list")
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 47ea7db10df..1a6820170b9 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -349,14 +349,14 @@
var/turf/T = get_turf(src)
if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T))
user.listed_turf = T
- user.client.statpanel = T.name
+ user.client << output("[url_encode(json_encode(T.name))];", "statbrowser:create_listedturf")
/// Use this instead of [/mob/proc/AltClickOn] where you only want turf content listing without additional atom alt-click interaction
/atom/proc/AltClickNoInteract(mob/user, atom/A)
var/turf/T = get_turf(A)
if(T && user.TurfAdjacent(T))
user.listed_turf = T
- user.client.statpanel = T.name
+ user.client << output("[url_encode(json_encode(T.name))];", "statbrowser:create_listedturf")
/mob/proc/TurfAdjacent(turf/T)
return T.Adjacent(src)
diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm
index 2cd0ddedaa1..31ea453d4a6 100644
--- a/code/_onclick/hud/credits.dm
+++ b/code/_onclick/hud/credits.dm
@@ -11,7 +11,7 @@
var/icon/credits_icon = new(CREDITS_PATH)
LAZYINITLIST(credits)
var/list/_credits = credits
- verbs += /client/proc/ClearCredits
+ add_verb(src, /client/proc/ClearCredits)
var/static/list/credit_order_for_this_round
if(isnull(credit_order_for_this_round))
credit_order_for_this_round = list("Thanks for playing!") + (shuffle(icon_states(credits_icon)) - "Thanks for playing!")
@@ -21,13 +21,13 @@
_credits += new /obj/screen/credit(null, I, src, credits_icon)
sleep(CREDIT_SPAWN_SPEED)
sleep(CREDIT_ROLL_SPEED - CREDIT_SPAWN_SPEED)
- verbs -= /client/proc/ClearCredits
+ remove_verb(src, /client/proc/ClearCredits)
qdel(credits_icon)
/client/proc/ClearCredits()
set name = "Hide Credits"
set category = "OOC"
- verbs -= /client/proc/ClearCredits
+ remove_verb(src, /client/proc/ClearCredits)
QDEL_LIST(credits)
credits = null
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index d9b16624232..16558f5870d 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -220,23 +220,12 @@
log_world(msg)
return time
-//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
/datum/controller/subsystem/stat_entry(msg)
- if(!statclick)
- statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
-
-
-
if(can_fire && !(SS_NO_FIRE & flags))
msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
-
- var/title = name
- if (can_fire)
- title = "\[[state_letter()]][title]"
-
- stat(title, statclick.update(msg))
+ return msg
/datum/controller/subsystem/proc/state_letter()
switch (state)
diff --git a/code/controllers/subsystem/acid.dm b/code/controllers/subsystem/acid.dm
index 7c9e7634abc..25d080a1b10 100644
--- a/code/controllers/subsystem/acid.dm
+++ b/code/controllers/subsystem/acid.dm
@@ -7,8 +7,9 @@ SUBSYSTEM_DEF(acid)
var/list/currentrun = list()
var/list/processing = list()
-/datum/controller/subsystem/acid/stat_entry()
- ..("P:[processing.len]")
+/datum/controller/subsystem/acid/stat_entry(msg)
+ msg = "P:[length(processing)]"
+ return ..()
/datum/controller/subsystem/acid/fire(resumed = 0)
diff --git a/code/controllers/subsystem/adjacent_air.dm b/code/controllers/subsystem/adjacent_air.dm
index 4254bfb83d8..e93db07775e 100644
--- a/code/controllers/subsystem/adjacent_air.dm
+++ b/code/controllers/subsystem/adjacent_air.dm
@@ -6,12 +6,13 @@ SUBSYSTEM_DEF(adjacent_air)
priority = FIRE_PRIORITY_ATMOS_ADJACENCY
var/list/queue = list()
-/datum/controller/subsystem/adjacent_air/stat_entry()
+/datum/controller/subsystem/adjacent_air/stat_entry(msg)
#ifdef TESTING
- ..("P:[length(queue)], S:[GLOB.atmos_adjacent_savings[1]], T:[GLOB.atmos_adjacent_savings[2]]")
+ msg = "P:[length(queue)], S:[GLOB.atmos_adjacent_savings[1]], T:[GLOB.atmos_adjacent_savings[2]]"
#else
- ..("P:[length(queue)]")
+ msg = "P:[length(queue)]"
#endif
+ return ..()
/datum/controller/subsystem/adjacent_air/Initialize()
while(length(queue))
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index 9e47f256317..71b82973c6b 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -57,7 +57,7 @@ SUBSYSTEM_DEF(air)
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)
+ return ..()
/datum/controller/subsystem/air/Initialize(timeofday)
diff --git a/code/controllers/subsystem/augury.dm b/code/controllers/subsystem/augury.dm
index 1b1c7bc3b7b..53c86004a6a 100644
--- a/code/controllers/subsystem/augury.dm
+++ b/code/controllers/subsystem/augury.dm
@@ -9,7 +9,8 @@ SUBSYSTEM_DEF(augury)
var/list/observers_given_action = list()
/datum/controller/subsystem/augury/stat_entry(msg)
- ..("W:[watchers.len]|D:[doombringers.len]")
+ msg = "W:[watchers.len]|D:[length(doombringers)]"
+ return ..()
/datum/controller/subsystem/augury/proc/register_doom(atom/A, severity)
doombringers[A] = severity
diff --git a/code/controllers/subsystem/disease.dm b/code/controllers/subsystem/disease.dm
index 9be1d8d90c0..4fe5533e161 100644
--- a/code/controllers/subsystem/disease.dm
+++ b/code/controllers/subsystem/disease.dm
@@ -20,7 +20,8 @@ SUBSYSTEM_DEF(disease)
return ..()
/datum/controller/subsystem/disease/stat_entry(msg)
- ..("P:[active_diseases.len]")
+ msg = "P:[length(active_diseases)]"
+ return ..()
/datum/controller/subsystem/disease/proc/get_disease_name(id)
var/datum/disease/advance/A = archive_diseases[id]
diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm
index 445aa183388..600f8645de8 100644
--- a/code/controllers/subsystem/explosions.dm
+++ b/code/controllers/subsystem/explosions.dm
@@ -64,7 +64,7 @@ SUBSYSTEM_DEF(explosions)
msg += "TO:[throwturf.len]"
msg += "} "
- ..(msg)
+ return ..()
#define SSEX_TURF "turf"
diff --git a/code/controllers/subsystem/fire_burning.dm b/code/controllers/subsystem/fire_burning.dm
index d976baec33d..f2ae4890c0a 100644
--- a/code/controllers/subsystem/fire_burning.dm
+++ b/code/controllers/subsystem/fire_burning.dm
@@ -7,8 +7,9 @@ SUBSYSTEM_DEF(fire_burning)
var/list/currentrun = list()
var/list/processing = list()
-/datum/controller/subsystem/fire_burning/stat_entry()
- ..("P:[processing.len]")
+/datum/controller/subsystem/fire_burning/stat_entry(msg)
+ msg = "P:[length(processing)]"
+ return ..()
/datum/controller/subsystem/fire_burning/fire(resumed = 0)
@@ -36,4 +37,3 @@ SUBSYSTEM_DEF(fire_burning)
if (MC_TICK_CHECK)
return
-
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index 11731b79c78..2260a1fa7a8 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -56,7 +56,7 @@ SUBSYSTEM_DEF(garbage)
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
msg += " P:[pass_counts.Join(",")]"
msg += "|F:[fail_counts.Join(",")]"
- ..(msg)
+ return ..()
/datum/controller/subsystem/garbage/Shutdown()
//Adds the del() log to the qdel log file
diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm
index 7b20defe498..ccdaa555a25 100644
--- a/code/controllers/subsystem/idlenpcpool.dm
+++ b/code/controllers/subsystem/idlenpcpool.dm
@@ -8,10 +8,11 @@ SUBSYSTEM_DEF(idlenpcpool)
var/list/currentrun = list()
var/static/list/idle_mobs_by_zlevel[][]
-/datum/controller/subsystem/idlenpcpool/stat_entry()
+/datum/controller/subsystem/idlenpcpool/stat_entry(msg)
var/list/idlelist = GLOB.simple_animals[AI_IDLE]
var/list/zlist = GLOB.simple_animals[AI_Z_OFF]
- ..("IdleNPCS:[idlelist.len]|Z:[zlist.len]")
+ msg = "IdleNPCS:[length(idlelist)]|Z:[length(zlist)]"
+ return ..()
/datum/controller/subsystem/idlenpcpool/proc/MaxZChanged()
if (!islist(idle_mobs_by_zlevel))
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index 3383db4e579..992b1d12c92 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -7,8 +7,9 @@ SUBSYSTEM_DEF(lighting)
var/static/list/corners_queue = list() // List of lighting corners queued for update.
var/static/list/objects_queue = list() // List of lighting objects queued for update.
-/datum/controller/subsystem/lighting/stat_entry()
- ..("L:[length(sources_queue)]|C:[length(corners_queue)]|O:[length(objects_queue)]")
+/datum/controller/subsystem/lighting/stat_entry(msg)
+ msg = "L:[length(sources_queue)]|C:[length(corners_queue)]|O:[length(objects_queue)]"
+ return ..()
/datum/controller/subsystem/lighting/Initialize(timeofday)
diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm
index c4b09d1b874..f356009569e 100644
--- a/code/controllers/subsystem/machines.dm
+++ b/code/controllers/subsystem/machines.dm
@@ -22,8 +22,9 @@ SUBSYSTEM_DEF(machines)
NewPN.add_cable(PC)
propagate_network(PC,PC.powernet)
-/datum/controller/subsystem/machines/stat_entry()
- ..("M:[processing.len]|PN:[powernets.len]")
+/datum/controller/subsystem/machines/stat_entry(msg)
+ msg = "M:[length(processing)]|PN:[length(powernets)]"
+ return ..()
/datum/controller/subsystem/machines/fire(resumed = 0)
diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm
index 885587ff554..5a7dd30ce3b 100644
--- a/code/controllers/subsystem/mobs.dm
+++ b/code/controllers/subsystem/mobs.dm
@@ -10,8 +10,9 @@ SUBSYSTEM_DEF(mobs)
var/static/list/cubemonkeys = list()
var/static/list/cheeserats = list()
-/datum/controller/subsystem/mobs/stat_entry()
- ..("P:[GLOB.mob_living_list.len]")
+/datum/controller/subsystem/mobs/stat_entry(msg)
+ msg = "P:[length(GLOB.mob_living_list)]"
+ return ..()
/datum/controller/subsystem/mobs/proc/MaxZChanged()
if (!islist(clients_by_zlevel))
diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm
index aca46c366ff..80d63c91d67 100644
--- a/code/controllers/subsystem/npcpool.dm
+++ b/code/controllers/subsystem/npcpool.dm
@@ -6,9 +6,10 @@ SUBSYSTEM_DEF(npcpool)
var/list/currentrun = list()
-/datum/controller/subsystem/npcpool/stat_entry()
+/datum/controller/subsystem/npcpool/stat_entry(msg)
var/list/activelist = GLOB.simple_animals[AI_ON]
- ..("NPCS:[activelist.len]")
+ msg = "NPCS:[length(activelist)]"
+ return ..()
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm
index 3c676309af6..6376025eb1c 100644
--- a/code/controllers/subsystem/overlays.dm
+++ b/code/controllers/subsystem/overlays.dm
@@ -22,8 +22,9 @@ SUBSYSTEM_DEF(overlays)
return ..()
-/datum/controller/subsystem/overlays/stat_entry()
- ..("Ov:[length(queue)]")
+/datum/controller/subsystem/overlays/stat_entry(msg)
+ msg = "Ov:[length(queue)]"
+ return ..()
/datum/controller/subsystem/overlays/Shutdown()
diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm
index f81a7d3df61..454709e1d52 100644
--- a/code/controllers/subsystem/processing/processing.dm
+++ b/code/controllers/subsystem/processing/processing.dm
@@ -10,8 +10,9 @@ SUBSYSTEM_DEF(processing)
var/list/processing = list()
var/list/currentrun = list()
-/datum/controller/subsystem/processing/stat_entry()
- ..("[stat_tag]:[processing.len]")
+/datum/controller/subsystem/processing/stat_entry(msg)
+ msg = "[stat_tag]:[length(processing)]"
+ return ..()
/datum/controller/subsystem/processing/fire(resumed = 0)
if (!resumed)
diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm
index c251492227a..c3261df3041 100644
--- a/code/controllers/subsystem/spacedrift.dm
+++ b/code/controllers/subsystem/spacedrift.dm
@@ -8,8 +8,9 @@ SUBSYSTEM_DEF(spacedrift)
var/list/currentrun = list()
var/list/processing = list()
-/datum/controller/subsystem/spacedrift/stat_entry()
- ..("P:[processing.len]")
+/datum/controller/subsystem/spacedrift/stat_entry(msg)
+ msg = "P:[length(processing)]"
+ return ..()
/datum/controller/subsystem/spacedrift/fire(resumed = 0)
@@ -56,4 +57,3 @@ SUBSYSTEM_DEF(spacedrift)
AM.inertia_last_loc = AM.loc
if (MC_TICK_CHECK)
return
-
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
new file mode 100644
index 00000000000..60ab051456e
--- /dev/null
+++ b/code/controllers/subsystem/statpanel.dm
@@ -0,0 +1,117 @@
+SUBSYSTEM_DEF(statpanels)
+ name = "Stat Panels"
+ wait = 4
+ init_order = INIT_ORDER_STATPANELS
+ runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
+ var/list/currentrun = list()
+ var/encoded_global_data
+ var/mc_data_encoded
+ var/list/cached_images = list()
+
+/datum/controller/subsystem/statpanels/fire(resumed = FALSE)
+ 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"]",
+ "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()]",
+ "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)]%)"
+ )
+
+ if(SSshuttle.emergency)
+ var/ETA = SSshuttle.emergency.getModeStr()
+ if(ETA)
+ global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
+ 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:", "Edit", "\ref[GLOB]"),
+ list("[config]:", "Edit", "\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)]%))"),
+ list("Master Controller:", Master ? "(TickRate:[Master.processing]) (Iteration:[Master.iteration])" : "ERROR", "\ref[Master]"),
+ list("Failsafe Controller:", Failsafe ? "Defcon: [Failsafe.defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])" : "ERROR", "\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()
+
+ 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.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))
+ target << output("", "statbrowser:remove_sqdl2")
+ else
+ var/list/sqdl2A = list()
+ sqdl2A[++sqdl2A.len] = list("", "Access Global SDQL2 List", REF(GLOB.sdql2_vv_statobj))
+ var/list/sqdl2B = 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_sqdl2")
+ 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
+ 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")
+ if(MC_TICK_CHECK)
+ return
diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm
index 93ea5ca3109..2fe7c64c72d 100644
--- a/code/controllers/subsystem/tgui.dm
+++ b/code/controllers/subsystem/tgui.dm
@@ -29,8 +29,9 @@ SUBSYSTEM_DEF(tgui)
/datum/controller/subsystem/tgui/Shutdown()
close_all_uis()
-/datum/controller/subsystem/tgui/stat_entry()
- ..("P:[open_uis.len]")
+/datum/controller/subsystem/tgui/stat_entry(msg)
+ msg = "P:[length(open_uis)]"
+ return ..()
/datum/controller/subsystem/tgui/fire(resumed = 0)
if(!resumed)
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 39e4c304110..70aa9c23098 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -11,8 +11,9 @@ SUBSYSTEM_DEF(throwing)
var/list/currentrun
var/list/processing = list()
-/datum/controller/subsystem/throwing/stat_entry()
- ..("P:[processing.len]")
+/datum/controller/subsystem/throwing/stat_entry(msg)
+ msg = "P:[length(processing)]"
+ return ..()
/datum/controller/subsystem/throwing/fire(resumed = 0)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index c0bdafac78c..98578c9134c 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -407,6 +407,7 @@ SUBSYSTEM_DEF(ticker)
if(living.client)
var/obj/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)
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index f4ea741857d..414c8b3c071 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -54,7 +54,8 @@ SUBSYSTEM_DEF(timer)
bucket_resolution = world.tick_lag
/datum/controller/subsystem/timer/stat_entry(msg)
- ..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]")
+ msg = "B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]"
+ return ..()
/datum/controller/subsystem/timer/fire(resumed = FALSE)
// Store local references to datum vars as it is faster to access them
diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm
index 2396a2f8a67..84ad5467d36 100644
--- a/code/datums/martial/_martial.dm
+++ b/code/datums/martial/_martial.dm
@@ -46,7 +46,7 @@
else if(make_temporary)
base = H.mind.default_martial_art
if(help_verb)
- H.verbs += help_verb
+ add_verb(H, help_verb)
H.mind.martial_art = src
return TRUE
@@ -69,7 +69,7 @@
/datum/martial_art/proc/on_remove(mob/living/carbon/human/H)
if(help_verb)
- H.verbs -= help_verb
+ remove_verb(H, help_verb)
return
///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob.
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index f08f477f277..a6bc5a3a3ff 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -135,6 +135,7 @@
new_character.key = key //now transfer the key to link the client to our new body
if(new_character.client)
LAZYCLEARLIST(new_character.client.recent_examines)
+ new_character.client.init_verbs() // re-initialize character specific verbs
current.update_atom_languages()
/datum/mind/proc/init_known_skills()
@@ -720,6 +721,7 @@
if(istype(S, spell))
spell_list -= S
qdel(S)
+ current?.client << output(null, "statbrowser:check_spells")
/datum/mind/proc/RemoveAllSpells()
for(var/obj/effect/proc_holder/S in spell_list)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 6fdfbaeac04..40fb1eef94e 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1534,3 +1534,21 @@
*/
/atom/proc/attempt_charge(atom/sender, atom/target, extra_fees = 0)
return SEND_SIGNAL(sender, COMSIG_OBJ_ATTEMPT_CHARGE, target, extra_fees)
+
+///Passes Stat Browser Panel clicks to the game and calls client click on an atom
+/atom/Topic(href, list/href_list)
+ . = ..()
+ if(!usr?.client)
+ return
+ var/client/usr_client = usr.client
+ var/list/paramslist = list()
+ if(href_list["statpanel_item_shiftclick"])
+ paramslist["shift"] = "1"
+ if(href_list["statpanel_item_ctrlclick"])
+ paramslist["ctrl"] = "1"
+ if(href_list["statpanel_item_altclick"])
+ paramslist["alt"] = "1"
+ if(href_list["statpanel_item_click"])
+ // first of all make sure we valid
+ var/mouseparams = list2params(paramslist)
+ usr_client.Click(src, loc, null, mouseparams)
diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm
index 9d41e451adf..952b1093ba7 100644
--- a/code/game/gamemodes/sandbox/h_sandbox.dm
+++ b/code/game/gamemodes/sandbox/h_sandbox.dm
@@ -5,7 +5,8 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE)
sandbox.owner = src.ckey
if(src.client.holder)
sandbox.admin = 1
- verbs += new/mob/proc/sandbox_panel
+ add_verb(src, /mob/proc/sandbox_panel)
+
/mob/proc/sandbox_panel()
set name = "Sandbox Panel"
if(sandbox)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 21420d9ed02..39de5098c53 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -18,6 +18,7 @@ GLOBAL_PROTECT(admin_verbs_default)
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/stop_sounds,
/client/proc/mark_datum_mapview,
+ /client/proc/debugstatpanel,
/client/proc/fix_air /*resets air in designated radius to its default atmos composition*/
)
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
@@ -259,36 +260,36 @@ GLOBAL_PROTECT(admin_verbs_hideable)
control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
var/rights = holder.rank.rights
- verbs += GLOB.admin_verbs_default
+ add_verb(src, GLOB.admin_verbs_default)
if(rights & R_BUILD)
- verbs += /client/proc/togglebuildmodeself
+ add_verb(src, /client/proc/togglebuildmodeself)
if(rights & R_ADMIN)
- verbs += GLOB.admin_verbs_admin
+ add_verb(src, GLOB.admin_verbs_admin)
if(rights & R_BAN)
- verbs += GLOB.admin_verbs_ban
+ add_verb(src, GLOB.admin_verbs_ban)
if(rights & R_FUN)
- verbs += GLOB.admin_verbs_fun
+ add_verb(src, GLOB.admin_verbs_fun)
if(rights & R_SERVER)
- verbs += GLOB.admin_verbs_server
+ add_verb(src, GLOB.admin_verbs_server)
if(rights & R_DEBUG)
- verbs += GLOB.admin_verbs_debug
+ add_verb(src, GLOB.admin_verbs_debug)
if(rights & R_POSSESS)
- verbs += GLOB.admin_verbs_possess
+ add_verb(src, GLOB.admin_verbs_possess)
if(rights & R_PERMISSIONS)
- verbs += GLOB.admin_verbs_permissions
+ add_verb(src, GLOB.admin_verbs_permissions)
if(rights & R_STEALTH)
- verbs += /client/proc/stealth
+ add_verb(src, /client/proc/stealth)
if(rights & R_ADMIN)
- verbs += GLOB.admin_verbs_poll
+ add_verb(src, GLOB.admin_verbs_poll)
if(rights & R_SOUND)
- verbs += GLOB.admin_verbs_sounds
+ add_verb(src, GLOB.admin_verbs_sounds)
if(CONFIG_GET(string/invoke_youtubedl))
- verbs += /client/proc/play_web_sound
+ add_verb(src, /client/proc/play_web_sound)
if(rights & R_SPAWN)
- verbs += GLOB.admin_verbs_spawn
+ add_verb(src, GLOB.admin_verbs_spawn)
/client/proc/remove_admin_verbs()
- verbs.Remove(
+ remove_verb(src, list(
GLOB.admin_verbs_default,
/client/proc/togglebuildmodeself,
GLOB.admin_verbs_admin,
@@ -307,14 +308,14 @@ GLOBAL_PROTECT(admin_verbs_hideable)
GLOB.admin_verbs_debug_mapping,
/client/proc/disable_debug_verbs,
/client/proc/readmin
- )
+ ))
/client/proc/hide_verbs()
set name = "Adminverbs - Hide All"
set category = "Admin"
remove_admin_verbs()
- verbs += /client/proc/show_verbs
+ add_verb(src, /client/proc/show_verbs)
to_chat(src, "Almost all of your adminverbs have been hidden.", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -324,7 +325,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set name = "Adminverbs - Show"
set category = "Admin"
- verbs -= /client/proc/show_verbs
+ remove_verb(src, /client/proc/show_verbs)
add_admin_verbs()
to_chat(src, "All of your adminverbs are now visible.", confidential = TRUE)
@@ -359,6 +360,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1)
+ init_verbs()
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -703,7 +705,6 @@ GLOBAL_PROTECT(admin_verbs_hideable)
holder.deactivate()
-
to_chat(src, "You are now a normal player.")
log_admin("[src] deadminned themselves.")
message_admins("[src] deadminned themselves.")
@@ -779,3 +780,9 @@ GLOBAL_PROTECT(admin_verbs_hideable)
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
+
+/client/proc/debugstatpanel()
+ set name = "Debug Stat Panel"
+ set category = "Debug"
+
+ src << output("", "statbrowser:create_debug")
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index b8ae48a8940..e10c8dc15e1 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -91,7 +91,7 @@ GLOBAL_PROTECT(href_token)
var/client/C
if ((C = owner) || (C = GLOB.directory[target]))
disassociate()
- C.verbs += /client/proc/readmin
+ add_verb(C, /client/proc/readmin)
/datum/admins/proc/associate(client/C)
if(IsAdminAdvancedProcCall())
@@ -111,7 +111,8 @@ GLOBAL_PROTECT(href_token)
owner = C
owner.holder = src
owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system
- owner.verbs -= /client/proc/readmin
+ remove_verb(owner, /client/proc/readmin)
+ owner.init_verbs() //re-initialize the verb list
GLOB.admins |= C
/datum/admins/proc/disassociate()
@@ -123,6 +124,7 @@ GLOBAL_PROTECT(href_token)
if(owner)
GLOB.admins -= owner
owner.remove_admin_verbs()
+ owner.init_verbs()
owner.holder = null
owner = null
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 0d9d3423645..63868583ac4 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -430,11 +430,13 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
delete_click = new(null, "INITIALIZING", src)
if(!action_click)
action_click = new(null, "INITIALIZNG", src)
- stat("[id] ", delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
+ var/list/L = list()
+ L[++L.len] = list("[id] ", "[delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
[islist(obj_count_all)? length(obj_count_all) : (isnull(obj_count_all)? "0" : obj_count_all)]/\
[islist(obj_count_eligible)? length(obj_count_eligible) : (isnull(obj_count_eligible)? "0" : obj_count_eligible)]/\
- [islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]"))
- stat(" ", action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]"))
+ [islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]")]", REF(delete_click))
+ L[++L.len] = list(" ", "[action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]")]", REF(action_click))
+ return L
/datum/sdql2_query/proc/delete_click()
admin_del(usr)
@@ -1204,10 +1206,18 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
return istype(thing, /datum) || istype(thing, /client)
/obj/effect/statclick/SDQL2_delete/Click()
+ if(!usr.client?.holder)
+ message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
+ log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
+ return
var/datum/sdql2_query/Q = target
Q.delete_click()
/obj/effect/statclick/SDQL2_action/Click()
+ if(!usr.client?.holder)
+ message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
+ log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
+ return
var/datum/sdql2_query/Q = target
Q.action_click()
@@ -1215,4 +1225,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
name = "VIEW VARIABLES"
/obj/effect/statclick/sdql2_vv_all/Click()
+ if(!usr.client?.holder)
+ message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
+ log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
+ return
usr.client.debug_variables(GLOB.sdql2_queries)
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index f0f9d1728d3..ae10d77657c 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -89,18 +89,23 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Tickets statpanel
/datum/admin_help_tickets/proc/stat_entry()
+ SHOULD_CALL_PARENT(TRUE)
+ SHOULD_NOT_SLEEP(TRUE)
+ var/list/L = list()
var/num_disconnected = 0
- stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
+ L[++L.len] = list("Active Tickets:", "[astatclick.update("[active_tickets.len]")]", null, REF(astatclick))
+ astatclick.update("[active_tickets.len]")
for(var/I in active_tickets)
var/datum/admin_help/AH = I
if(AH.initiator)
- stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update())
+ L[++L.len] = list("#[AH.id]. [AH.initiator_key_name]:", "[AH.statclick.update()]", REF(AH))
else
++num_disconnected
if(num_disconnected)
- stat("Disconnected:", astatclick.update("[num_disconnected]"))
- stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]"))
- stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
+ L[++L.len] = list("Disconnected:", "[astatclick.update("[num_disconnected]")]", null, REF(astatclick))
+ L[++L.len] = list("Closed Tickets:", "[cstatclick.update("[closed_tickets.len]")]", null, REF(cstatclick))
+ L[++L.len] = list("Resolved Tickets:", "[rstatclick.update("[resolved_tickets.len]")]", null, REF(rstatclick))
+ return L
//Reassociate still open ticket if one exists
/datum/admin_help_tickets/proc/ClientLogin(client/C)
@@ -139,6 +144,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/obj/effect/statclick/ticket_list/Click()
GLOB.ahelp_tickets.BrowseTickets(current_state)
+//called by admin topic
+/obj/effect/statclick/ticket_list/proc/Action()
+ Click()
+
//
//TICKET DATUM
//
@@ -219,7 +228,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Removes the ahelp verb and returns it after 2 minutes
/datum/admin_help/proc/TimeoutVerb()
- initiator.verbs -= /client/verb/adminhelp
+ remove_verb(initiator, /client/verb/adminhelp)
initiator.adminhelptimerid = addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 1200, TIMER_STOPPABLE) //2 minute cooldown of admin helps
//private
@@ -485,7 +494,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//
/client/proc/giveadminhelpverb()
- src.verbs |= /client/verb/adminhelp
+ add_verb(src, /client/verb/adminhelp)
deltimer(adminhelptimerid)
adminhelptimerid = 0
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 9b1426b91d9..b187de8f6bc 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -205,15 +205,15 @@ GLOBAL_LIST_EMPTY(dirty_vars)
set name = "Debug verbs - Enable"
if(!check_rights(R_DEBUG))
return
- verbs -= /client/proc/enable_debug_verbs
- verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
+ remove_verb(src, /client/proc/enable_debug_verbs)
+ add_verb(src, list(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping))
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/disable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Disable"
- verbs.Remove(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
- verbs += /client/proc/enable_debug_verbs
+ remove_verb(src, list(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping))
+ add_verb(src, /client/proc/enable_debug_verbs)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_on_z_level()
diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm
index 8fee260c903..bfdc8e00809 100644
--- a/code/modules/admin/verbs/possess.dm
+++ b/code/modules/admin/verbs/possess.dm
@@ -48,6 +48,6 @@
set desc = "Give this guy possess/release verbs"
set category = "Debug"
set name = "Give Possessing Verbs"
- M.verbs += /proc/possess
- M.verbs += /proc/release
+ add_verb(M, /proc/possess)
+ add_verb(M, /proc/release)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Possessing Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm
index 73e154c26f7..93aea93739a 100644
--- a/code/modules/antagonists/blob/blob_mobs.dm
+++ b/code/modules/antagonists/blob/blob_mobs.dm
@@ -31,7 +31,7 @@
/mob/living/simple_animal/hostile/blob/Initialize()
. = ..()
if(!independent) //no pulling people deep into the blob
- verbs -= /mob/living/verb/pulled
+ remove_verb(src, /mob/living/verb/pulled)
else
pass_flags &= ~PASSBLOB
@@ -40,10 +40,10 @@
overmind.blob_mobs -= src
return ..()
-/mob/living/simple_animal/hostile/blob/Stat()
- ..()
- if(overmind && statpanel("Status"))
- stat(null, "Blobs to Win: [overmind.blobs_legit.len]/[overmind.blobwincount]")
+/mob/living/simple_animal/hostile/blob/get_status_tab_items()
+ . = ..()
+ if(overmind)
+ . += "Blobs to Win: [overmind.blobs_legit.len]/[overmind.blobwincount]"
/mob/living/simple_animal/hostile/blob/blob_act(obj/structure/blob/B)
if(stat != DEAD && health < maxHealth)
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 583a9fb0067..e1c09d065b7 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -252,19 +252,18 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/mob/camera/blob/blob_act(obj/structure/blob/B)
return
-/mob/camera/blob/Stat()
- ..()
- if(statpanel("Status"))
- if(blob_core)
- stat(null, "Core Health: [blob_core.obj_integrity]")
- stat(null, "Power Stored: [blob_points]/[max_blob_points]")
- stat(null, "Blobs to Win: [blobs_legit.len]/[blobwincount]")
- if(free_strain_rerolls)
- stat(null, "You have [free_strain_rerolls] Free Strain Reroll\s Remaining")
- if(!placed)
- if(manualplace_min_time)
- stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
- stat(null, "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]")
+/mob/camera/blob/get_status_tab_items()
+ . = ..()
+ if(blob_core)
+ . += "Core Health: [blob_core.obj_integrity]"
+ . += "Power Stored: [blob_points]/[max_blob_points]"
+ . += "Blobs to Win: [blobs_legit.len]/[blobwincount]"
+ if(free_strain_rerolls)
+ . += "You have [free_strain_rerolls] Free Strain Reroll\s Remaining"
+ if(!placed)
+ if(manualplace_min_time)
+ . += "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]"
+ . += "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]"
/mob/camera/blob/Move(NewLoc, Dir = 0)
if(placed)
diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm
index 6ae5b1796dc..028435043c5 100644
--- a/code/modules/antagonists/disease/disease_mob.dm
+++ b/code/modules/antagonists/disease/disease_mob.dm
@@ -87,17 +87,16 @@ the new instance inside the host to be updated to the template's stats.
to_chat(src, "You have [DisplayTimeText(freemove_end - world.time)] to select your first host. Click on a human to select your host.")
-/mob/camera/disease/Stat()
+/mob/camera/disease/get_status_tab_items()
..()
- if(statpanel("Status"))
- if(freemove)
- stat("Host Selection Time: [round((freemove_end - world.time)/10)]s")
- else
- stat("Adaptation Points: [points]/[total_points]")
- stat("Hosts: [disease_instances.len]")
- var/adapt_ready = next_adaptation_time - world.time
- if(adapt_ready > 0)
- stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s")
+ if(freemove)
+ . += "Host Selection Time: [round((freemove_end - world.time)/10)]s"
+ else
+ . += "Adaptation Points: [points]/[total_points]"
+ . += "Hosts: [disease_instances.len]"
+ var/adapt_ready = next_adaptation_time - world.time
+ if(adapt_ready > 0)
+ . += "Adaptation Ready: [round(adapt_ready/10, 0.1)]s"
/mob/camera/disease/examine(mob/user)
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index d24c9c7d21c..07a8f400276 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -135,13 +135,12 @@
update_health_hud()
..()
-/mob/living/simple_animal/revenant/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Current essence: [essence]/[essence_regen_cap]E")
- stat(null, "Stolen essence: [essence_accumulated]E")
- stat(null, "Unused stolen essence: [essence_excess]E")
- stat(null, "Stolen perfect souls: [perfectsouls]")
+/mob/living/simple_animal/revenant/get_status_tab_items()
+ . = ..()
+ . += "Current essence: [essence]/[essence_regen_cap]E"
+ . += "Stolen essence: [essence_accumulated]E"
+ . += "Unused stolen essence: [essence_excess]E"
+ . += "Stolen perfect souls: [perfectsouls]"
/mob/living/simple_animal/revenant/update_health_hud()
if(hud_used)
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index 9f11f149961..76511ad3195 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -105,7 +105,7 @@
if("Immortality")
to_chat(user, "Your wish is granted, but at a terrible cost...")
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
- user.verbs += /mob/living/carbon/proc/immortality
+ add_verb(user, /mob/living/carbon/proc/immortality)
user.set_species(/datum/species/shadow)
if("Peace")
to_chat(user, "Whatever alien sentience that the Wish Granter possesses is satisfied with your wish. There is a distant wailing as the last of the Faithless begin to die, then silence.")
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index dad02f6c93b..9c551b13b43 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -7,6 +7,8 @@
////////////////
//ADMIN THINGS//
////////////////
+ /// hides the byond verb panel as we use our own custom version
+ show_verb_panel = FALSE
///Contains admin info. Null if client is not an admin.
var/datum/admins/holder = null
///Needs to implement InterceptClickOn(user,params,atom) proc
@@ -138,7 +140,14 @@
/// Messages currently seen by this client
var/list/seen_messages
+
+ /// datum wrapper for client view
var/datum/view_data/view_size
+
+ /// list of tabs containing spells and abilities
+ var/list/spell_tabs = list()
+ /// list of tabs containing verbs
+ var/list/verb_tabs = list()
///A lazy list of atoms we've examined in the last EXAMINE_MORE_TIME (default 1.5) seconds, so that we will call [atom/proc/examine_more()] instead of [atom/proc/examine()] on them when examining
var/list/recent_examines
@@ -174,3 +183,4 @@
/// rate limiting for the crew manifest
var/crew_manifest_delay
+
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index f5e0f97fe05..03fa982b2a1 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -212,7 +212,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
holder.owner = src
connecting_admin = TRUE
else if(GLOB.deadmins[ckey])
- verbs += /client/proc/readmin
+ add_verb(src, /client/proc/readmin)
connecting_admin = TRUE
if(CONFIG_GET(flag/autoadmin))
if(!GLOB.admin_datums[ckey])
@@ -242,7 +242,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
fps = (prefs.clientfps < 0) ? RECOMMENDED_FPS : prefs.clientfps
if(fexists(roundend_report_file()))
- verbs += /client/proc/show_previous_roundend_report
+ add_verb(src, /client/proc/show_previous_roundend_report)
var/full_version = "[byond_version].[byond_build ? byond_build : "xxx"]"
log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]")
@@ -309,6 +309,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
// Initialize tgui panel
tgui_panel.initialize()
+ src << browse(file('html/statbrowser.html'), "window=statbrowser")
+
if(alert_mob_dupe_login)
spawn()
@@ -845,9 +847,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
/client/proc/add_verbs_from_config()
if(CONFIG_GET(flag/see_own_notes))
- verbs += /client/proc/self_notes
+ add_verb(src, /client/proc/self_notes)
if(CONFIG_GET(flag/use_exp_tracking))
- verbs += /client/proc/self_playtime
+ add_verb(src, /client/proc/self_playtime)
#undef UPLOAD_LIMIT
@@ -990,3 +992,21 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!src)
return
prefs.save_preferences()
+
+/// compiles a full list of verbs and sends it to the browser
+/client/proc/init_verbs()
+ if(IsAdminAdvancedProcCall())
+ return
+ var/list/verblist = list()
+ verb_tabs.Cut()
+ for(var/thing in (verbs + mob?.verbs))
+ var/procpath/verb_to_init = thing
+ if(!verb_to_init)
+ continue
+ if(verb_to_init.hidden)
+ continue
+ if(!istext(verb_to_init.category))
+ continue
+ verb_tabs |= verb_to_init.category
+ verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
+ src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 291befebe0f..bb3581ef127 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -385,3 +385,9 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
policytext += "No related rules found."
usr << browse(policytext.Join(""),"window=policy")
+
+/client/verb/fix_stat_panel()
+ set name = "Fix Stat Panel"
+ set hidden = TRUE
+
+ init_verbs()
diff --git a/code/modules/events/wizard/ghost.dm b/code/modules/events/wizard/ghost.dm
index d5366c57697..c288953efb0 100644
--- a/code/modules/events/wizard/ghost.dm
+++ b/code/modules/events/wizard/ghost.dm
@@ -21,6 +21,6 @@
/datum/round_event/wizard/possession/start()
for(var/mob/dead/observer/G in GLOB.player_list)
- G.verbs += /mob/dead/observer/verb/boo
- G.verbs += /mob/dead/observer/verb/possess
+ add_verb(G, /mob/dead/observer/verb/boo)
+ add_verb(G, /mob/dead/observer/verb/possess)
to_chat(G, "You suddenly feel a welling of new spooky powers...")
diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm
index 2076cb3a193..2a51822c5c1 100644
--- a/code/modules/mob/dead/dead.dm
+++ b/code/modules/mob/dead/dead.dm
@@ -18,7 +18,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
prepare_huds()
if(length(CONFIG_GET(keyed_list/cross_server)))
- verbs += /mob/dead/proc/server_hop
+ add_verb(src, /mob/dead/proc/server_hop)
set_focus(src)
return INITIALIZE_HINT_NORMAL
@@ -43,27 +43,25 @@ INITIALIZE_IMMEDIATE(/mob/dead)
loc = destination
Moved(oldloc, NONE, TRUE)
-/mob/dead/Stat()
- ..()
-
- if(!statpanel("Status"))
- return
- stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]")
+/mob/dead/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]"
if(SSticker.HasRoundStarted())
return
var/time_remaining = SSticker.GetTimeLeft()
if(time_remaining > 0)
- stat(null, "Time To Start: [round(time_remaining/10)]s")
+ . += "Time To Start: [round(time_remaining/10)]s"
else if(time_remaining == -10)
- stat(null, "Time To Start: DELAYED")
+ . += "Time To Start: DELAYED"
else
- stat(null, "Time To Start: SOON")
+ . += "Time To Start: SOON"
- stat(null, "Players: [SSticker.totalPlayers]")
+ . += "Players: [SSticker.totalPlayers]"
if(client.holder)
- stat(null, "Players Ready: [SSticker.totalPlayersReady]")
+ . += "Players Ready: [SSticker.totalPlayersReady]"
/mob/dead/proc/server_hop()
set category = "OOC"
@@ -76,7 +74,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
var/pick
switch(csa.len)
if(0)
- verbs -= /mob/dead/proc/server_hop
+ remove_verb(src, /mob/dead/proc/server_hop)
to_chat(src, "Server Hop has been disabled.")
if(1)
pick = csa[1]
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 7f327708c6f..527f0df3ae4 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -233,6 +233,7 @@
if(observer.client && observer.client.prefs)
observer.real_name = observer.client.prefs.real_name
observer.name = observer.real_name
+ observer.client.init_verbs()
observer.update_icon()
observer.stop_sound_channel(CHANNEL_LOBBYMUSIC)
QDEL_NULL(mind)
@@ -324,7 +325,7 @@
character.update_parallax_teleport()
SSticker.minds += character.mind
-
+ character.client.init_verbs() // init verbs for the late join
var/mob/living/carbon/human/humanc
if(ishuman(character))
humanc = character //Let's retypecast the var to be human,
@@ -455,7 +456,7 @@
mind.original_character = H
H.name = real_name
-
+ client.init_verbs()
. = H
new_character = .
if(transfer_after)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index af009daf377..3624277ebe9 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -65,10 +65,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer/Initialize()
set_invisibility(GLOB.observer_default_invisibility)
- verbs += list(
+ add_verb(src, list(
/mob/dead/observer/proc/dead_tele,
/mob/dead/observer/proc/open_spawners_menu,
- /mob/dead/observer/proc/tray_view)
+ /mob/dead/observer/proc/tray_view))
if(icon_state in GLOB.ghost_forms_with_directions_list)
ghostimage_default = image(src.icon,src,src.icon_state + "_nodir")
@@ -129,8 +129,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
real_name = name
if(!fun_verbs)
- verbs -= /mob/dead/observer/verb/boo
- verbs -= /mob/dead/observer/verb/possess
+ remove_verb(src, /mob/dead/observer/verb/boo)
+ remove_verb(src, /mob/dead/observer/verb/possess)
animate(src, pixel_y = 2, time = 10, loop = -1)
@@ -280,6 +280,7 @@ Works together with spawning an observer, noted above.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
ghost.key = key
+ ghost.client.init_verbs()
if(!can_reenter_corpse) // Disassociates observer mind from the body mind
ghost.mind = null
return ghost
@@ -357,6 +358,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
client.view_size.setDefault(getScreenSize(client.prefs.widescreenpref))//Let's reset so people can't become allseeing gods
SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
mind.current.key = key
+ mind.current.client.init_verbs()
return TRUE
/mob/dead/observer/verb/stay_dead()
@@ -815,11 +817,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
ghostimage_simple.icon_state = icon_state
if(NAMEOF(src, fun_verbs))
if(fun_verbs)
- verbs += /mob/dead/observer/verb/boo
- verbs += /mob/dead/observer/verb/possess
+ add_verb(src, /mob/dead/observer/verb/boo)
+ add_verb(src, /mob/dead/observer/verb/possess)
else
- verbs -= /mob/dead/observer/verb/boo
- verbs -= /mob/dead/observer/verb/possess
+ remove_verb(src, /mob/dead/observer/verb/boo)
+ remove_verb(src, /mob/dead/observer/verb/possess)
/mob/dead/observer/reset_perspective(atom/A)
if(client)
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index 4deaa1ee8f6..8bc8f9eb40b 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -26,8 +26,8 @@
var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?")
/mob/living/carbon/alien/Initialize()
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
create_bodyparts() //initialize bodyparts
@@ -74,11 +74,9 @@
/mob/living/carbon/alien/IsAdvancedToolUser()
return has_fine_manipulation
-/mob/living/carbon/alien/Stat()
- ..()
-
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
+/mob/living/carbon/alien/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
/mob/living/carbon/alien/getTrail()
if(getBruteLoss() < 200)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
index f61744a8853..1fc9e2853ea 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
@@ -149,10 +149,10 @@ Doesn't work on other aliens/AI.*/
action_icon_state = "alien_acid"
/obj/effect/proc_holder/alien/acid/on_gain(mob/living/carbon/user)
- user.verbs.Add(/mob/living/carbon/proc/corrosive_acid)
+ add_verb(user, /mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/on_lose(mob/living/carbon/user)
- user.verbs.Remove(/mob/living/carbon/proc/corrosive_acid)
+ remove_verb(user, /mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/proc/corrode(atom/target,mob/living/carbon/user = usr)
if(target in oview(1,user))
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index de5eafc5b28..6b4097e6faf 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -40,10 +40,9 @@
..()
//This needs to be fixed
-/mob/living/carbon/alien/larva/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Progress: [amount_grown]/[max_grown]")
+/mob/living/carbon/alien/larva/get_status_tab_items()
+ . = ..()
+ . += "Progress: [amount_grown]/[max_grown]"
/mob/living/carbon/alien/larva/Login()
. = ..()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index f6e28875190..444746b6476 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -441,16 +441,17 @@
var/turf/target = get_turf(loc)
I.safe_throw_at(target,I.throw_range,I.throw_speed,src, force = move_force)
-/mob/living/carbon/Stat()
- ..()
- if(statpanel("Status"))
- var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
- if(vessel)
- stat(null, "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]")
- if(locate(/obj/item/assembly/health) in src)
- stat(null, "Health: [health]")
+/mob/living/carbon/get_status_tab_items()
+ . = ..()
+ var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
+ if(vessel)
+ . += "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]"
+ if(locate(/obj/item/assembly/health) in src)
+ . += "Health: [health]"
- add_abilities_to_panel()
+/mob/living/carbon/get_proc_holders()
+ . = ..()
+ . += add_abilities_to_panel()
/mob/living/carbon/attack_ui(slot)
if(!has_hand_for_held_index(active_hand_index))
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f64a8816161..2159a1847b8 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1,6 +1,6 @@
/mob/living/carbon/human/Initialize()
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
icon_state = "" //Remove the inherent human icon that is visible on the map editor. We're rendering ourselves limb by limb, having it still be there results in a bug where the basic human icon appears below as south in all directions and generally looks nasty.
@@ -53,58 +53,55 @@
//...and display them.
add_to_all_human_data_huds()
-/mob/living/carbon/human/Stat()
- ..()
-
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
- stat(null, "Move Mode: [m_intent]")
- if (internal)
- if (!internal.air_contents)
- qdel(internal)
- else
- stat(null, "Internal Atmosphere Info: [internal.name]")
- stat(null, "Tank Pressure: [internal.air_contents.return_pressure()]")
- stat(null, "Distribution Pressure: [internal.distribute_pressure]")
- if(istype(wear_suit, /obj/item/clothing/suit/space))
- var/obj/item/clothing/suit/space/S = wear_suit
- stat(null, "Thermal Regulator: [S.thermal_on ? "on" : "off"]")
- stat(null, "Cell Charge: [S.cell ? "[round(S.cell.percent(), 0.1)]%" : "!invalid!"]")
-
- if(mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- stat(null, "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]")
- stat(null, "Absorbed DNA: [changeling.absorbedcount]")
-
+/mob/living/carbon/human/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
+ . += "Move Mode: [m_intent]"
+ if (internal)
+ if (!internal.air_contents)
+ qdel(internal)
+ else
+ . += ""
+ . += "Internal Atmosphere Info: [internal.name]"
+ . += "Tank Pressure: [internal.air_contents.return_pressure()]"
+ . += "Distribution Pressure: [internal.distribute_pressure]"
+ if(istype(wear_suit, /obj/item/clothing/suit/space))
+ var/obj/item/clothing/suit/space/S = wear_suit
+ . += "Thermal Regulator: [S.thermal_on ? "on" : "off"]"
+ . += "Cell Charge: [S.cell ? "[round(S.cell.percent(), 0.1)]%" : "!invalid!"]"
+ if(mind)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ . += ""
+ . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
+ . += "Absorbed DNA: [changeling.absorbedcount]"
//NINJACODE
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
- if(statpanel("SpiderOS"))
- stat("SpiderOS Status:","[SN.s_initialized ? "Initialized" : "Disabled"]")
- stat("Current Time:", "[station_time_timestamp()]")
- if(SN.s_initialized)
- //Suit gear
- stat("Energy Charge:", "[round(SN.cell.charge/100)]%")
- stat("Smoke Bombs:", "\Roman [SN.s_bombs]")
- //Ninja status
- stat("Fingerprints:", "[md5(dna.uni_identity)]")
- stat("Unique Identity:", "[dna.unique_enzymes]")
- stat("Overall Status:", "[stat > 1 ? "dead" : "[health]% healthy"]")
- stat("Nutrition Status:", "[nutrition]")
- stat("Oxygen Loss:", "[getOxyLoss()]")
- stat("Toxin Levels:", "[getToxLoss()]")
- stat("Burn Severity:", "[getFireLoss()]")
- stat("Brute Trauma:", "[getBruteLoss()]")
- stat("Radiation Levels:","[radiation] rad")
- stat("Body Temperature:","[bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)")
+ . += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]"
+ . += "Current Time: [station_time_timestamp()]"
+ if(SN.s_initialized)
+ //Suit gear
+ . += "Energy Charge: [round(SN.cell.charge/100)]%"
+ . += "Smoke Bombs: \Roman [SN.s_bombs]"
+ //Ninja status
+ . += "Fingerprints: [md5(dna.uni_identity)]"
+ . += "Unique Identity: [dna.unique_enzymes]"
+ . += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]"
+ . += "Nutrition Status: [nutrition]"
+ . += "Oxygen Loss: [getOxyLoss()]"
+ . += "Toxin Levels: [getToxLoss()]"
+ . += "Burn Severity: [getFireLoss()]"
+ . += "Brute Trauma: [getBruteLoss()]"
+ . += "Radiation Levels: [radiation] rad"
+ . += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)"
- //Diseases
- if(diseases.len)
- stat("Viruses:", null)
- for(var/thing in diseases)
- var/datum/disease/D = thing
- stat("*", "[D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]")
+ //Diseases
+ if(length(diseases))
+ . += "Viruses:"
+ for(var/thing in diseases)
+ var/datum/disease/D = thing
+ . += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]"
/mob/living/carbon/human/show_inv(mob/user)
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index e1ece348605..9e6ac3a9e12 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -121,13 +121,11 @@
#undef VAMP_DRAIN_AMOUNT
-/mob/living/carbon/Stat()
- ..()
- if(statpanel("Status"))
- var/obj/item/organ/heart/vampire/darkheart = getorgan(/obj/item/organ/heart/vampire)
- if(darkheart)
- stat(null, "Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM].")
- return 1
+/mob/living/carbon/get_status_tab_items()
+ . = ..()
+ var/obj/item/organ/heart/vampire/darkheart = getorgan(/obj/item/organ/heart/vampire)
+ if(darkheart)
+ . += "Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM]."
/obj/item/organ/heart/vampire
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index 1978de956cb..8f0fb6d5c53 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -25,8 +25,8 @@
hud_type = /datum/hud/monkey
/mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner)
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
if(unique_name) //used to exclude pun pun
gender = pick(MALE, FEMALE)
@@ -93,16 +93,16 @@
slow += ((283.222 - bodytemperature) / 10) * 1.75
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_temperature_speedmod, TRUE, slow)
-/mob/living/carbon/monkey/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
- stat(null, "Move Mode: [m_intent]")
- if(client && mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
- stat("Absorbed DNA", changeling.absorbedcount)
+/mob/living/carbon/monkey/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
+ . += "Move Mode: [m_intent]"
+ if(client && mind)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ . += ""
+ . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
+ . += "Absorbed DNA: [changeling.absorbedcount]"
/mob/living/carbon/monkey/verb/removeinternal()
set name = "Remove Internals"
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9166fbc8a24..c579971db6f 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1274,8 +1274,10 @@
A.action.Remove(src)
/mob/living/proc/add_abilities_to_panel()
+ var/list/L = list()
for(var/obj/effect/proc_holder/A in abilities)
- statpanel("[A.panel]",A.get_panel_text(),A)
+ L[++L.len] = list("[A.panel]",A.get_panel_text(),A.name,"[REF(A)]")
+ return L
/mob/living/lingcheck()
if(mind)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index bf2d0bcdc0a..5dfdc397cae 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -145,7 +145,7 @@
spark_system.set_up(5, 0, src)
spark_system.attach(src)
- verbs += /mob/living/silicon/ai/proc/show_laws_verb
+ add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb)
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = real_name
@@ -158,10 +158,10 @@
deploy_action.Grant(src)
if(isturf(loc))
- verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
+ add_verb(src, list(/mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, \
- /mob/living/silicon/ai/proc/set_automatic_say_channel)
+ /mob/living/silicon/ai/proc/set_automatic_say_channel))
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
@@ -240,28 +240,28 @@
display_icon_override = ai_core_icon
set_core_display_icon(ai_core_icon)
-/mob/living/silicon/ai/Stat()
- ..()
- if(statpanel("Status"))
- if(!stat)
- stat(null, text("System integrity: [(health+100)/2]%"))
- if(isturf(loc)) //only show if we're "in" a core
- stat(null, text("Backup Power: [battery/2]%"))
- stat(null, text("Connected cyborgs: [connected_robots.len]"))
- for(var/mob/living/silicon/robot/R in connected_robots)
- var/robot_status = "Nominal"
- if(R.shell)
- robot_status = "AI SHELL"
- else if(R.stat || !R.client)
- robot_status = "OFFLINE"
- else if(!R.cell || R.cell.charge <= 0)
- robot_status = "DEPOWERED"
- //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
- stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
- Module: [R.designation] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
- stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
- else
- stat(null, text("Systems nonfunctional"))
+/mob/living/silicon/ai/get_status_tab_items()
+ . = ..()
+ if(stat != CONSCIOUS)
+ . += text("Systems nonfunctional")
+ return
+ . += text("System integrity: [(health + 100) * 0.5]%")
+ if(isturf(loc)) //only show if we're "in" a core
+ . += text("Backup Power: [battery * 0.5]%")
+ . += text("Connected cyborgs: [length(connected_robots)]")
+ for(var/r in connected_robots)
+ var/mob/living/silicon/robot/connected_robot = r
+ var/robot_status = "Nominal"
+ if(connected_robot.shell)
+ robot_status = "AI SHELL"
+ else if(connected_robot.stat != CONSCIOUS || !connected_robot.client)
+ robot_status = "OFFLINE"
+ else if(!connected_robot.cell || connected_robot.cell.charge <= 0)
+ robot_status = "DEPOWERED"
+ //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
+ . += text("[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \
+ Module: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]")
+ . += text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]") //Count of total AI shells
/mob/living/silicon/ai/proc/ai_alerts()
var/dat = "
Current Station Alerts\n"
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 4485e86e6a2..539bfb7376d 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -193,13 +193,12 @@
else
client.eye = card
-/mob/living/silicon/pai/Stat()
- ..()
- if(statpanel("Status"))
- if(!stat)
- stat(null, text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]"))
- else
- stat(null, text("Systems nonfunctional"))
+/mob/living/silicon/pai/get_status_tab_items()
+ . += ..()
+ if(!stat)
+ . += text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]")
+ else
+ . += text("Systems nonfunctional")
/mob/living/silicon/pai/restrained(ignore_grab)
. = FALSE
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 775910e580c..36225bdedf0 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -309,19 +309,19 @@
if(thruster_button)
thruster_button.icon_state = "ionpulse[ionpulse_on]"
-/mob/living/silicon/robot/Stat()
- ..()
- if(statpanel("Status"))
- if(cell)
- stat("Charge Left:", "[cell.charge]/[cell.maxcharge]")
- else
- stat(null, text("No Cell Inserted!"))
+/mob/living/silicon/robot/get_status_tab_items()
+ . = ..()
+ . += ""
+ if(cell)
+ . += "Charge Left: [cell.charge]/[cell.maxcharge]"
+ else
+ . += text("No Cell Inserted!")
- if(module)
- for(var/datum/robot_energy_storage/st in module.storages)
- stat("[st.name]:", "[st.energy]/[st.max_energy]")
- if(connected_ai)
- stat("Master AI:", connected_ai.name)
+ if(module)
+ for(var/datum/robot_energy_storage/st in module.storages)
+ . += "[st.name]: [st.energy]/[st.max_energy]"
+ if(connected_ai)
+ . += "Master AI: [connected_ai.name]"
/mob/living/silicon/robot/restrained(ignore_grab)
. = 0
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 9074221a63d..7912004bc0c 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -503,15 +503,15 @@
update_icon()
-/mob/living/simple_animal/bot/mulebot/Stat()
- ..()
- if(statpanel("Status"))
- if(cell)
- stat("Charge Left:", "[cell.charge]/[cell.maxcharge]")
- else
- stat(null, text("No Cell Inserted!"))
- if(load)
- stat("Current Load:", get_load_name())
+/mob/living/simple_animal/bot/mulebot/get_status_tab_items()
+ . = ..()
+ if(cell)
+ . += "Charge Left: [cell.charge]/[cell.maxcharge]"
+ else
+ . += text("No Cell Inserted!")
+ if(load)
+ . += "Current Load: [get_load_name()]"
+
/mob/living/simple_animal/bot/mulebot/call_bot()
..()
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 9ccb93591ba..b9cad100f66 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -45,7 +45,7 @@
/mob/living/simple_animal/pet/cat/Initialize()
. = ..()
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/lay_down)
add_cell_sample()
/mob/living/simple_animal/pet/cat/add_cell_sample()
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 23ac9bfe57c..acf2f89fc03 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -193,18 +193,17 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
qdel(src)
snapback()
-/mob/living/simple_animal/hostile/guardian/Stat()
- ..()
- if(statpanel("Status"))
- if(summoner)
- var/resulthealth
- if(iscarbon(summoner))
- resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100)
- else
- resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5)
- stat(null, "Summoner Health: [resulthealth]%")
- if(cooldown >= world.time)
- stat(null, "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/get_status_tab_items()
+ . += ..()
+ if(summoner)
+ var/resulthealth
+ if(iscarbon(summoner))
+ resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100)
+ else
+ resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5)
+ . += "Summoner Health: [resulthealth]%"
+ if(cooldown >= world.time)
+ . += "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/Move() //Returns to summoner if they move out of range
. = ..()
@@ -481,13 +480,13 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
to_chat(src, "[G.real_name] has appeared!")
guardians -= G
if(!guardians.len)
- verbs -= /mob/living/proc/guardian_reset
+ remove_verb(src, /mob/living/proc/guardian_reset)
else
to_chat(src, "There were no ghosts willing to take control of [G.real_name]. Looks like you're stuck with it for now.")
else
to_chat(src, "You decide not to reset [guardians.len > 1 ? "any of your guardians":"your guardian"].")
else
- verbs -= /mob/living/proc/guardian_reset
+ remove_verb(src, /mob/living/proc/guardian_reset)
////////parasite tracking/finding procs
@@ -618,9 +617,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
if("miner")
to_chat(user, "[G.miner_fluff_string]")
to_chat(user, "[G.real_name] has appeared!")
- user.verbs += /mob/living/proc/guardian_comm
- user.verbs += /mob/living/proc/guardian_recall
- user.verbs += /mob/living/proc/guardian_reset
+ add_verb(user, list(/mob/living/proc/guardian_comm, \
+ /mob/living/proc/guardian_recall, \
+ /mob/living/proc/guardian_reset))
/obj/item/guardiancreator/choose
random = FALSE
diff --git a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
index d4a1bbdac1d..62cf875ea93 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
@@ -27,11 +27,10 @@
if(loc == summoner && toggle)
ToggleMode(0)
-/mob/living/simple_animal/hostile/guardian/assassin/Stat()
- ..()
- if(statpanel("Status"))
- if(stealthcooldown >= world.time)
- stat(null, "Stealth Cooldown Remaining: [DisplayTimeText(stealthcooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/assassin/get_status_tab_items()
+ . = ..()
+ if(stealthcooldown >= world.time)
+ . += "Stealth Cooldown Remaining: [DisplayTimeText(stealthcooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/assassin/AttackingTarget()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
index 3a317b4143d..11795f7ffaf 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
@@ -18,11 +18,10 @@
var/bomb_cooldown = 0
var/static/list/boom_signals = list(COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_BUMPED, COMSIG_ATOM_ATTACK_HAND)
-/mob/living/simple_animal/hostile/guardian/bomb/Stat()
- ..()
- if(statpanel("Status"))
- if(bomb_cooldown >= world.time)
- stat(null, "Bomb Cooldown Remaining: [DisplayTimeText(bomb_cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/bomb/get_status_tab_items()
+ . = ..()
+ if(bomb_cooldown >= world.time)
+ . += "Bomb Cooldown Remaining: [DisplayTimeText(bomb_cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/bomb/AttackingTarget()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/support.dm b/code/modules/mob/living/simple_animal/guardian/types/support.dm
index 16001fa6b4f..4ec8a5c4680 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/support.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/support.dm
@@ -22,11 +22,10 @@
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
-/mob/living/simple_animal/hostile/guardian/healer/Stat()
- ..()
- if(statpanel("Status"))
- if(beacon_cooldown >= world.time)
- stat(null, "Beacon Cooldown Remaining: [DisplayTimeText(beacon_cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/healer/get_status_tab_items()
+ . = ..()
+ if(beacon_cooldown >= world.time)
+ . += "Beacon Cooldown Remaining: [DisplayTimeText(beacon_cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/healer/AttackingTarget()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index 540b61b3333..7a367ff3dc1 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -128,7 +128,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
. = ..()
- verbs -= /mob/living/verb/pulled
+ remove_verb(src, /mob/living/verb/pulled)
/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
face_atom(A)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index d398caf6e74..aa799eb0032 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -691,8 +691,8 @@ Difficulty: Very Hard
/mob/living/simple_animal/hostile/lightgeist/Initialize()
. = ..()
- verbs -= /mob/living/verb/pulled
- verbs -= /mob/verb/me_verb
+ remove_verb(src, /mob/living/verb/pulled)
+ remove_verb(src, /mob/verb/me_verb)
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
@@ -781,7 +781,7 @@ Difficulty: Very Hard
L.mind.transfer_to(holder_animal)
var/obj/effect/proc_holder/spell/targeted/exit_possession/P = new /obj/effect/proc_holder/spell/targeted/exit_possession
holder_animal.mind.AddSpell(P)
- holder_animal.verbs -= /mob/living/verb/pulled
+ remove_verb(holder_animal, /mob/living/verb/pulled)
/obj/structure/closet/stasis/dump_contents(kill = 1)
STOP_PROCESSING(SSobj, src)
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 696634c0cae..940faac7909 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -114,12 +114,12 @@
parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var
- verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
+ add_verb(src, list(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
/mob/living/simple_animal/parrot/proc/steal_from_mob, \
/mob/living/simple_animal/parrot/verb/drop_held_item_player, \
/mob/living/simple_animal/parrot/proc/perch_player, \
/mob/living/simple_animal/parrot/proc/toggle_mode,
- /mob/living/simple_animal/parrot/proc/perch_mob_player)
+ /mob/living/simple_animal/parrot/proc/perch_mob_player))
/mob/living/simple_animal/parrot/examine(mob/user)
@@ -141,11 +141,11 @@
..(gibbed)
-/mob/living/simple_animal/parrot/Stat()
- ..()
- if(statpanel("Status"))
- stat("Held Item", held_item)
- stat("Mode",a_intent)
+/mob/living/simple_animal/parrot/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Held Item: [held_item]"
+ . += "Mode: [a_intent]"
/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list())
. = ..()
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 58e8f8e0aea..84fa8932571 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -403,11 +403,10 @@
remove_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed, multiplicative_slowdown = speed)
-/mob/living/simple_animal/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Health: [round((health / maxHealth) * 100)]%")
- return 1
+/mob/living/simple_animal/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Health: [round((health / maxHealth) * 100)]%"
/mob/living/simple_animal/proc/drop_loot()
if(loot.len)
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 2ceb68a275c..97fba146527 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -201,25 +201,21 @@
/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0)
return 2
-
-/mob/living/simple_animal/slime/Stat()
+/mob/living/simple_animal/slime/get_status_tab_items()
. = ..()
- if(!.)
- return
-
if(!docile)
- stat(null, "Nutrition: [nutrition]/[get_max_nutrition()]")
+ . += "Nutrition: [nutrition]/[get_max_nutrition()]"
if(amount_grown >= SLIME_EVOLUTION_THRESHOLD)
if(is_adult)
- stat(null, "You can reproduce!")
+ . += "You can reproduce!"
else
- stat(null, "You can evolve!")
+ . += "You can evolve!"
switch(stat)
if(HARD_CRIT, UNCONSCIOUS)
- stat(null,"You are knocked out by high levels of BZ!")
+ . += "You are knocked out by high levels of BZ!"
else
- stat(null,"Power Level: [powerlevel]")
+ . += "Power Level: [powerlevel]"
/mob/living/simple_animal/slime/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 8d9c98f7001..225d84a7222 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -803,111 +803,34 @@
/mob/proc/is_muzzled()
return 0
-/**
- * Output an update to the stat panel for the client
- *
- * calculates client ping, round id, server time, time dilation and other data about the round
- * and puts it in the mob status panel on a regular loop
- */
-/mob/Stat()
- ..()
-
- if(statpanel("Status"))
- if (client)
- stat(null, "Ping: [round(client.lastping, 1)]ms (Average: [round(client.avgping, 1)]ms)")
- stat(null, "Map: [SSmapping.config?.map_name || "Loading..."]")
- var/datum/map_config/cached = SSmapping.next_map_config
- if(cached)
- stat(null, "Next Map: [cached.map_name]")
- stat(null, "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]")
- stat(null, "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]")
- if (SSticker.round_start_time)
- var/round_time = world.time - SSticker.round_start_time
- if(round_time > MIDNIGHT_ROLLOVER)
- stat(null, "Round Time: [round(round_time/MIDNIGHT_ROLLOVER)]:[gameTimestamp("hh:mm:ss", round_time)]")
- else
- stat(null, "Round Time: [gameTimestamp("hh:mm:ss", round_time)]")
- else
- stat(null, "Lobby Time: [gameTimestamp("hh:mm:ss", 0)]")
- stat(null, "Station Time: [station_time_timestamp()]")
- stat(null, "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)]%)")
- if(SSshuttle.emergency)
- var/ETA = SSshuttle.emergency.getModeStr()
- if(ETA)
- stat(null, "[ETA] [SSshuttle.emergency.getTimerStr()]")
-
- if(client && client.holder)
- if(statpanel("MC"))
- var/turf/T = get_turf(client.eye)
- stat("Location:", COORD(T))
- stat("CPU:", "[world.cpu]")
- stat("Instances:", "[num2text(world.contents.len, 10)]")
- stat("World Time:", "[world.time]")
- GLOB.stat_entry()
- config.stat_entry()
- stat(null)
- if(Master)
- Master.stat_entry()
- else
- stat("Master Controller:", "ERROR")
- if(Failsafe)
- Failsafe.stat_entry()
- else
- stat("Failsafe Controller:", "ERROR")
- if(Master)
- stat(null)
- for(var/datum/controller/subsystem/SS in Master.subsystems)
- SS.stat_entry()
- GLOB.cameranet.stat_entry()
- if(statpanel("Tickets"))
- GLOB.ahelp_tickets.stat_entry()
- if(length(GLOB.sdql2_queries))
- if(statpanel("SDQL2"))
- stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
- for(var/i in GLOB.sdql2_queries)
- var/datum/sdql2_query/Q = i
- Q.generate_stat()
-
- if(listed_turf && client)
- if(!TurfAdjacent(listed_turf))
- listed_turf = null
- else
- statpanel(listed_turf.name, null, listed_turf)
- var/list/overrides = list()
- for(var/image/I in client.images)
- if(I.loc && I.loc.loc == listed_turf && I.override)
- overrides += I.loc
- for(var/atom/A in listed_turf)
- if(!A.mouse_opacity)
- continue
- if(A.invisibility > see_invisible)
- continue
- if(overrides.len && (A in overrides))
- continue
- if(A.IsObscured())
- continue
- statpanel(listed_turf.name, null, A)
-
+/// Adds this list to the output to the stat browser
+/mob/proc/get_status_tab_items()
+ . = list()
+/// Gets all relevant proc holders for the browser statpenl
+/mob/proc/get_proc_holders()
+ . = list()
if(mind)
- add_spells_to_statpanel(mind.spell_list)
- add_spells_to_statpanel(mob_spell_list)
+ . += get_spells_for_statpanel(mind.spell_list)
+ . += get_spells_for_statpanel(mob_spell_list)
/**
* Convert a list of spells into a displyable list for the statpanel
*
* Shows charge and other important info
*/
-/mob/proc/add_spells_to_statpanel(list/spells)
+/mob/proc/get_spells_for_statpanel(list/spells)
+ var/list/L = list()
for(var/obj/effect/proc_holder/spell/S in spells)
if(S.can_be_cast_by(src))
switch(S.charge_type)
if("recharge")
- statpanel("[S.panel]","[S.charge_counter/10.0]/[S.charge_max/10]",S)
+ L[++L.len] = list("[S.panel]", "[S.charge_counter/10.0]/[S.charge_max/10]", S.name, REF(S))
if("charges")
- statpanel("[S.panel]","[S.charge_counter]/[S.charge_max]",S)
+ L[++L.len] = list("[S.panel]", "[S.charge_counter]/[S.charge_max]", S.name, REF(S))
if("holdervar")
- statpanel("[S.panel]","[S.holder_var_type] [S.holder_var_amount]",S)
+ L[++L.len] = list("[S.panel]", "[S.holder_var_type] [S.holder_var_amount]", S.name, REF(S))
+ return L
#define MOB_FACE_DIRECTION_DELAY 1
@@ -1028,6 +951,8 @@
if(istype(S, spell))
LAZYREMOVE(mob_spell_list, S)
qdel(S)
+ if(client)
+ client << output(null, "statbrowser:check_spells")
///Return any anti magic atom on this mob that matches the magic type
/mob/proc/anti_magic_check(magic = TRUE, holy = FALSE, tinfoil = FALSE, chargecost = 1, self = FALSE)
diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm
index 6bf28ab0fb4..eeff8509de1 100644
--- a/code/modules/swarmers/swarmer.dm
+++ b/code/modules/swarmers/swarmer.dm
@@ -77,7 +77,7 @@
/mob/living/simple_animal/hostile/swarmer/Initialize()
. = ..()
- verbs -= /mob/living/verb/pulled
+ remove_verb(src, /mob/living/verb/pulled)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
@@ -94,10 +94,9 @@
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "hudstat"
-/mob/living/simple_animal/hostile/swarmer/Stat()
- ..()
- if(statpanel("Status"))
- stat("Resources:",resources)
+/mob/living/simple_animal/hostile/swarmer/get_status_tab_items()
+ . = ..()
+ . += "Resources: [resources]"
/mob/living/simple_animal/hostile/swarmer/emp_act()
. = ..()
diff --git a/html/statbrowser.html b/html/statbrowser.html
new file mode 100644
index 00000000000..f8d6df48dea
--- /dev/null
+++ b/html/statbrowser.html
@@ -0,0 +1,882 @@
+
+
+
+Stat Browser
+
+
+
+
+
+
+
+
+
+
+
diff --git a/interface/skin.dmf b/interface/skin.dmf
index 3453cf00140..653d2ed7f5d 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -2,21 +2,21 @@ macro "default"
menu "menu"
- elem
+ elem
name = "&File"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Quick screenshot\tF2"
command = ".screenshot auto"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Save screenshot as...\tShift+F2"
command = ".screenshot"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = ""
command = ""
category = "&File"
@@ -26,26 +26,27 @@ menu "menu"
command = ".reconnect"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Quit\tAlt-F4"
command = ".quit"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Help"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Admin Help\tF1"
command = "adminhelp"
category = "&Help"
saved-params = "is-checked"
- elem
+ elem
name = "&Hotkeys"
command = "hotkeys-help"
category = "&Help"
saved-params = "is-checked"
+
window "mainwindow"
elem "mainwindow"
type = MAIN
@@ -115,7 +116,6 @@ window "mainwindow"
anchor1 = none
anchor2 = none
is-visible = false
- auto-format = false
saved-params = ""
elem "tooltip"
type = BROWSER
@@ -145,7 +145,8 @@ window "mapwindow"
font-size = 7
text-color = none
is-default = true
- style=".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 6px; }"
+ saved-params = "zoom;letterbox;zoom-mode"
+ style = ".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold;\tfont-size: 8px; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 6px; }"
window "infowindow"
elem "infowindow"
@@ -240,7 +241,6 @@ window "outputwindow"
is-visible = false
is-disabled = true
saved-params = ""
- auto-format = false
elem "output"
type = OUTPUT
pos = 0,0
@@ -257,7 +257,6 @@ window "popupwindow"
size = 120x120
anchor1 = none
anchor2 = none
- background-color = none
is-visible = false
saved-params = "pos;size;is-minimized;is-maximized"
statusbar = false
@@ -292,18 +291,18 @@ window "preferences_window"
window "statwindow"
elem "statwindow"
type = MAIN
- pos = 281,0
+ pos = 372,0
size = 640x480
anchor1 = none
anchor2 = none
saved-params = "pos;size;is-minimized;is-maximized"
is-pane = true
- elem "stat"
- type = INFO
+ elem "statbrowser"
+ type = BROWSER
pos = 0,0
size = 640x480
anchor1 = 0,0
anchor2 = 100,100
- is-default = true
+ is-visible = false
saved-params = ""
diff --git a/tgstation.dme b/tgstation.dme
index 0c374144bde..df710f6dd48 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -167,6 +167,7 @@
#include "code\__HELPERS\type_processing.dm"
#include "code\__HELPERS\typelists.dm"
#include "code\__HELPERS\unsorted.dm"
+#include "code\__HELPERS\verbs.dm"
#include "code\__HELPERS\view.dm"
#include "code\__HELPERS\sorts\__main.dm"
#include "code\__HELPERS\sorts\InsertSort.dm"
@@ -305,6 +306,7 @@
#include "code\controllers\subsystem\skills.dm"
#include "code\controllers\subsystem\sounds.dm"
#include "code\controllers\subsystem\spacedrift.dm"
+#include "code\controllers\subsystem\statpanel.dm"
#include "code\controllers\subsystem\stickyban.dm"
#include "code\controllers\subsystem\sun.dm"
#include "code\controllers\subsystem\tcgsetup.dm"
diff --git a/tgui/packages/tgui-panel/themes.js b/tgui/packages/tgui-panel/themes.js
index c378a786683..1ef6caaf459 100644
--- a/tgui/packages/tgui-panel/themes.js
+++ b/tgui/packages/tgui-panel/themes.js
@@ -10,6 +10,8 @@ const COLOR_DARK_BG = '#202020';
const COLOR_DARK_BG_DARKER = '#171717';
const COLOR_DARK_TEXT = '#a4bad6';
+let setClientThemeTimer = null;
+
/**
* Darkmode preference, originally by Kmc2000.
*
@@ -21,6 +23,14 @@ const COLOR_DARK_TEXT = '#a4bad6';
* It's painful but it works, and is the way Lummox suggested.
*/
export const setClientTheme = name => {
+ // Transmit once for fast updates and again in a little while in case we won
+ // the race against statbrowser init.
+ clearInterval(setClientThemeTimer);
+ Byond.command(`.output statbrowser:set_theme ${name}`);
+ setClientThemeTimer = setTimeout(() => {
+ Byond.command(`.output statbrowser:set_theme ${name}`);
+ }, 1500);
+
if (name === 'light') {
return Byond.winset({
// Main windows
diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js
index ea82bf701d1..0c9a6d29068 100644
--- a/tgui/public/tgui-panel.bundle.js
+++ b/tgui/public/tgui-panel.bundle.js
@@ -1 +1 @@
-!function(e){function t(t){for(var o,i,c=t[0],s=t[1],d=t[2],u=0,g=[];u=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n=i.IMAGE_RETRY_LIMIT)u.error("failed to load an image after "+n+" attempts");else{var o=t.src;t.src=null,t.src=o+"#"+n,t.setAttribute("data-reload-n",n+1)}}),i.IMAGE_RETRY_DELAY)},f=function(e){var t=e.node,n=e.times;if(t&&n){var o=t.querySelector(".Chat__badge"),a=o||document.createElement("div");a.textContent=n,a.className=(0,r.classes)(["Chat__badge","Chat__badge--animate"]),requestAnimationFrame((function(){a.className="Chat__badge"})),o||t.appendChild(a)}},v=function(){function t(){var e=this;this.loaded=!1,this.rootNode=null,this.queue=[],this.messages=[],this.visibleMessages=[],this.page=null,this.events=new o.EventEmitter,this.scrollNode=null,this.scrollTracking=!0,this.handleScroll=function(t){var n=e.scrollNode,o=n.scrollHeight,r=n.scrollTop+n.offsetHeight,a=Math.abs(o-r)<24;a!==e.scrollTracking&&(e.scrollTracking=a,e.events.emit("scrollTrackingChanged",a),u.debug("tracking",e.scrollTracking))},this.ensureScrollTracking=function(){e.scrollTracking&&e.scrollToBottom()},setInterval((function(){return e.pruneMessages()}),i.MESSAGE_PRUNE_INTERVAL)}var n=t.prototype;return n.isReady=function(){return this.loaded&&this.rootNode&&this.page},n.mount=function(t){var n=this;this.rootNode?t.appendChild(this.rootNode):this.rootNode=t,this.scrollNode=function(e){for(var t=document.body,n=e;n&&n!==t;){if(n.scrollWidth0&&(this.processBatch(this.queue),this.queue=[])},n.assignStyle=function(e){void 0===e&&(e={});for(var t=0,n=Object.keys(e);t1&&n.test(e)}));if(0===o.length)return this.highlightRegex=null,void(this.highlightColor=null);this.highlightRegex=new RegExp("("+o.join("|")+")","gi"),this.highlightColor=t},n.scrollToBottom=function(){this.scrollNode.scrollTop=this.scrollNode.scrollHeight},n.changePage=function(e){if(!this.isReady())return this.page=e,void this.tryFlushQueue();this.page=e,this.rootNode.textContent="",this.visibleMessages=[];for(var t,n,o=document.createDocumentFragment(),r=d(this.messages);!(n=r()).done;){var a=n.value;(0,c.canPageAcceptType)(e,a.type)&&(t=a.node,o.appendChild(t),this.visibleMessages.push(a))}t&&(this.rootNode.appendChild(o),t.scrollIntoView())},n.getCombinableMessage=function(e){for(var t=Date.now(),n=this.visibleMessages.length,o=n-1,r=Math.max(0,n-i.COMBINE_MAX_MESSAGES),a=o;a>=r;a--){var s=this.visibleMessages[a];if(!s.type.startsWith(i.MESSAGE_TYPE_INTERNAL)&&(0,c.isSameMessage)(s,e)&&t0){this.visibleMessages=e.slice(t);for(var n=0;n0&&(this.messages=this.messages.slice(r),u.log("pruned "+r+" stored messages"))}else u.debug("pruning delayed")},n.rebuildChat=function(){if(this.isReady()){for(var e,t=Math.max(0,this.messages.length-i.MAX_PERSISTED_MESSAGES),n=this.messages.slice(t),o=d(n);!(e=o()).done;)e.value.node=undefined;this.rootNode.textContent="",this.messages=[],this.visibleMessages=[],this.processBatch(n,{notifyListeners:!1})}},n.saveToDisk=function(){if(!Byond.IS_LTE_IE10){for(var e="",t=document.styleSheets,n=0;n\n\n\nSS13 Chat Log\n\n\n\n\n'+i+"
\n\n\n"]),u=(new Date).toISOString().substring(0,19).replace(/[-:]/g,"").replace("T","-");window.navigator.msSaveBlob(l,"ss13-chatlog-"+u+".html")}},t}();window.__chatRenderer__||(window.__chatRenderer__=new v);var y=window.__chatRenderer__;t.chatRenderer=y}).call(this,n(101).setImmediate)},216:function(e,t,n){"use strict";t.__esModule=!0,t.gameReducer=t.gameMiddleware=t.useGame=void 0;var o=n(680);t.useGame=o.useGame;var r=n(681);t.gameMiddleware=r.gameMiddleware;var a=n(683);t.gameReducer=a.gameReducer},217:function(e,t,n){"use strict";t.__esModule=!0,t.selectGame=void 0;t.selectGame=function(e){return e.game}},218:function(e,t,n){"use strict";t.__esModule=!0,t.connectionRestored=t.connectionLost=t.roundRestarted=void 0;var o=n(22),r=(0,o.createAction)("roundrestart");t.roundRestarted=r;var a=(0,o.createAction)("game/connectionLost");t.connectionLost=a;var i=(0,o.createAction)("game/connectionRestored");t.connectionRestored=i},219:function(e,t,n){"use strict";t.__esModule=!0,t.pingReducer=t.PingIndicator=t.pingMiddleware=void 0;var o=n(685);t.pingMiddleware=o.pingMiddleware;var r=n(686);t.PingIndicator=r.PingIndicator;var a=n(689);t.pingReducer=a.pingReducer},220:function(e,t,n){"use strict";t.__esModule=!0,t.PING_ROUNDTRIP_WORST=t.PING_ROUNDTRIP_BEST=t.PING_QUEUE_SIZE=t.PING_MAX_FAILS=t.PING_TIMEOUT=t.PING_INTERVAL=void 0;t.PING_INTERVAL=2500;t.PING_TIMEOUT=2e3;t.PING_MAX_FAILS=3;t.PING_QUEUE_SIZE=8;t.PING_ROUNDTRIP_BEST=50;t.PING_ROUNDTRIP_WORST=200},65:function(e,t,n){"use strict";t.__esModule=!0,t.openChatSettings=t.toggleSettings=t.changeSettingsTab=t.loadSettings=t.updateSettings=void 0;var o=n(22),r=(0,o.createAction)("settings/update");t.updateSettings=r;var a=(0,o.createAction)("settings/load");t.loadSettings=a;var i=(0,o.createAction)("settings/changeTab");t.changeSettingsTab=i;var c=(0,o.createAction)("settings/toggle");t.toggleSettings=c;var s=(0,o.createAction)("settings/openChatTab");t.openChatSettings=s},660:function(e,t,n){n(148),e.exports=n(661)},661:function(e,t,n){"use strict";var o=n(0);n(662),n(663);var r,a,i=n(99),c=n(22),s=(n(100),n(57)),d=n(186),l=n(136),u=n(187),g=n(211),p=n(145),h=n(216),m=n(684),f=n(219),v=n(144),y=n(690);i.perf.mark("inception",null==(r=window.performance)||null==(a=r.timing)?void 0:a.navigationStart),i.perf.mark("init");var b=(0,u.configureStore)({reducer:(0,c.combineReducers)({audio:g.audioReducer,chat:p.chatReducer,game:h.gameReducer,ping:f.pingReducer,settings:v.settingsReducer}),middleware:{pre:[p.chatMiddleware,f.pingMiddleware,y.telemetryMiddleware,v.settingsMiddleware,g.audioMiddleware,h.gameMiddleware]}}),S=(0,l.createRenderer)((function(){var e=n(691).Panel;return(0,o.createComponentVNode)(2,u.StoreProvider,{store:b,children:(0,o.createComponentVNode)(2,e)})}));!function _(){if("loading"!==document.readyState){for((0,s.setupGlobalEvents)({ignoreWindowFocus:!0}),(0,m.setupPanelFocusHacks)(),(0,d.captureExternalLinks)(),b.subscribe(S),window.update=function(e){return b.dispatch(Byond.parseJson(e))};;){var e=window.__updateQueue__.shift();if(!e)break;window.update(e)}Byond.winset("output",{"is-visible":!1}),Byond.winset("browseroutput",{"is-visible":!0,"is-disabled":!1,pos:"0x0",size:"0x0"})}else document.addEventListener("DOMContentLoaded",_)}()},662:function(e,t,n){},663:function(e,t,n){},664:function(e,t,n){"use strict";t.__esModule=!0,t.useAudio=void 0;var o=n(22),r=n(212);t.useAudio=function(e){var t=(0,o.useSelector)(e,r.selectAudio),n=(0,o.useDispatch)(e);return Object.assign({},t,{toggle:function(){return n({type:"audio/toggle"})}})}},665:function(e,t,n){"use strict";t.__esModule=!0,t.audioMiddleware=void 0;var o=n(666);t.audioMiddleware=function(e){var t=new o.AudioPlayer;return t.onPlay((function(){e.dispatch({type:"audio/playing"})})),t.onStop((function(){e.dispatch({type:"audio/stopped"})})),function(e){return function(n){var o=n.type,r=n.payload;if("audio/playMusic"===o){var a=r.url,i=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o=0||(r[n]=e[n]);return r}(r,["url"]);return t.play(a,i),e(n)}if("audio/stopMusic"===o)return t.stop(),e(n);if("settings/update"===o||"settings/load"===o){var c=null==r?void 0:r.adminMusicVolume;return"number"==typeof c&&t.setVolume(c),e(n)}return e(n)}}}},666:function(e,t,n){"use strict";function o(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0&&e.node.currentTime>=e.options.end&&e.stop())}),1e3))}var t=e.prototype;return t.destroy=function(){this.node&&(this.node.stop(),document.removeChild(this.node),clearInterval(this.playbackInterval))},t.play=function(e,t){void 0===t&&(t={}),this.node&&(a.log("playing",e,t),this.options=t,this.node.src=e)},t.stop=function(){if(this.node){if(this.playing)for(var e,t=o(this.onStopSubscribers);!(e=t()).done;)(0,e.value)();a.log("stopping"),this.playing=!1,this.node.src=""}},t.setVolume=function(e){this.node&&(this.volume=e,this.node.volume=e)},t.onPlay=function(e){this.node&&this.onPlaySubscribers.push(e)},t.onStop=function(e){this.node&&this.onStopSubscribers.push(e)},e}();t.AudioPlayer=i},667:function(e,t,n){"use strict";t.__esModule=!0,t.NowPlayingWidget=void 0;var o=n(0),r=n(11),a=n(22),i=n(1),c=n(144),s=n(212);t.NowPlayingWidget=function(e,t){var n,d=(0,a.useSelector)(t,s.selectAudio),l=(0,a.useDispatch)(t),u=(0,c.useSettings)(t),g=null==(n=d.meta)?void 0:n.title;return(0,o.createComponentVNode)(2,i.Flex,{align:"center",children:[d.playing&&(0,o.createFragment)([(0,o.createComponentVNode)(2,i.Flex.Item,{shrink:0,mx:.5,color:"label",children:"Now playing:"}),(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,grow:1,style:{"white-space":"nowrap",overflow:"hidden","text-overflow":"ellipsis"},children:g||"Unknown Track"})],4)||(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,color:"label",children:"Nothing to play."}),d.playing&&(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,fontSize:"0.9em",children:(0,o.createComponentVNode)(2,i.Button,{tooltip:"Stop",icon:"stop",onClick:function(){return l({type:"audio/stopMusic"})}})}),(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,fontSize:"0.9em",children:(0,o.createComponentVNode)(2,i.Knob,{minValue:0,maxValue:1,value:u.adminMusicVolume,step:.0025,stepPixelSize:1,format:function(e){return(0,r.toFixed)(100*e)+"%"},onDrag:function(e,t){return u.update({adminMusicVolume:t})}})})]})}},668:function(e,t,n){"use strict";t.__esModule=!0,t.useSettings=void 0;var o=n(22),r=n(65),a=n(105);t.useSettings=function(e){var t=(0,o.useSelector)(e,a.selectSettings),n=(0,o.useDispatch)(e);return Object.assign({},t,{visible:t.view.visible,toggle:function(){return n((0,r.toggleSettings)())},update:function(e){return n((0,r.updateSettings)(e))}})}},669:function(e,t,n){"use strict";t.__esModule=!0,t.settingsMiddleware=void 0;var o=n(102),r=n(213),a=n(65),i=n(105);t.settingsMiddleware=function(e){var t=!1;return function(n){return function(c){var s,d=c.type,l=c.payload;if(t||(t=!0,o.storage.get("panel-settings").then((function(t){e.dispatch((0,a.loadSettings)(t))}))),d===a.updateSettings.type||d===a.loadSettings.type){var u=null==l?void 0:l.theme;u&&(0,r.setClientTheme)(u),n(c);var g=(0,i.selectSettings)(e.getState());return s=g.fontSize,document.documentElement.style.setProperty("font-size",s+"px"),document.body.style.setProperty("font-size",s+"px"),void o.storage.set("panel-settings",g)}return n(c)}}}},670:function(e,t,n){"use strict";t.__esModule=!0,t.settingsReducer=void 0;var o=n(65),r={version:1,fontSize:13,lineHeight:1.2,theme:"light",adminMusicVolume:.5,highlightText:"",highlightColor:"#ffdd44",view:{visible:!1,activeTab:n(214).SETTINGS_TABS[0].id}};t.settingsReducer=function(e,t){void 0===e&&(e=r);var n=t.type,a=t.payload;if(n===o.updateSettings.type)return Object.assign({},e,a);if(n===o.loadSettings.type)return(null==a?void 0:a.version)?(delete a.view,Object.assign({},e,a)):e;if(n===o.toggleSettings.type)return Object.assign({},e,{view:Object.assign({},e.view,{visible:!e.view.visible})});if(n===o.openChatSettings.type)return Object.assign({},e,{view:Object.assign({},e.view,{visible:!0,activeTab:"chatPage"})});if(n===o.changeSettingsTab.type){var i=a.tabId;return Object.assign({},e,{view:Object.assign({},e.view,{activeTab:i})})}return e}},671:function(e,t,n){"use strict";t.__esModule=!0,t.SettingsGeneral=t.SettingsPanel=void 0;var o=n(0),r=n(11),a=n(22),i=n(1),c=n(145),s=n(79),d=n(213),l=n(65),u=n(214),g=n(105);t.SettingsPanel=function(e,t){var n=(0,a.useSelector)(t,g.selectActiveTab),r=(0,a.useDispatch)(t);return(0,o.createComponentVNode)(2,i.Flex,{children:[(0,o.createComponentVNode)(2,i.Flex.Item,{mr:1,children:(0,o.createComponentVNode)(2,i.Section,{fitted:!0,fill:!0,minHeight:"8em",children:(0,o.createComponentVNode)(2,i.Tabs,{vertical:!0,children:u.SETTINGS_TABS.map((function(e){return(0,o.createComponentVNode)(2,i.Tabs.Tab,{selected:e.id===n,onClick:function(){return r((0,l.changeSettingsTab)({tabId:e.id}))},children:e.name},e.id)}))})})}),(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,basis:0,children:["general"===n&&(0,o.createComponentVNode)(2,p),"chatPage"===n&&(0,o.createComponentVNode)(2,c.ChatPageSettings)]})]})};var p=function(e,t){var n=(0,a.useSelector)(t,g.selectSettings),c=n.theme,u=n.fontSize,p=n.lineHeight,h=n.highlightText,m=n.highlightColor,f=(0,a.useDispatch)(t);return(0,o.createComponentVNode)(2,i.Section,{fill:!0,children:[(0,o.createComponentVNode)(2,i.LabeledList,{children:[(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Theme",children:(0,o.createComponentVNode)(2,i.Dropdown,{selected:c,options:d.THEMES,onSelected:function(e){return f((0,l.updateSettings)({theme:e}))}})}),(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Font size",children:(0,o.createComponentVNode)(2,i.NumberInput,{width:"4em",step:1,stepPixelSize:10,minValue:8,maxValue:32,value:u,unit:"px",format:function(e){return(0,r.toFixed)(e)},onChange:function(e,t){return f((0,l.updateSettings)({fontSize:t}))}})}),(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Line height",children:(0,o.createComponentVNode)(2,i.NumberInput,{width:"4em",step:.01,stepPixelSize:2,minValue:.8,maxValue:5,value:p,format:function(e){return(0,r.toFixed)(e,2)},onDrag:function(e,t){return f((0,l.updateSettings)({lineHeight:t}))}})})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Flex,{mb:1,color:"label",align:"baseline",children:[(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,children:"Highlight words (comma separated):"}),(0,o.createComponentVNode)(2,i.Flex.Item,{shrink:0,children:[(0,o.createComponentVNode)(2,i.ColorBox,{mr:1,color:m}),(0,o.createComponentVNode)(2,i.Input,{width:"5em",monospace:!0,placeholder:"#ffffff",value:m,onInput:function(e,t){return f((0,l.updateSettings)({highlightColor:t}))}})]})]}),(0,o.createComponentVNode)(2,i.TextArea,{height:"3em",value:h,onChange:function(e,t){return f((0,l.updateSettings)({highlightText:t}))}})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Button,{icon:"check",onClick:function(){return f((0,s.rebuildChat)())},children:"Apply now"}),(0,o.createComponentVNode)(2,i.Box,{inline:!0,fontSize:"0.9em",ml:1,color:"label",children:"Can freeze the chat for a while."})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Button,{icon:"save",onClick:function(){return f((0,s.saveChatToDisk)())},children:"Save chat log"})]})};t.SettingsGeneral=p},672:function(e,t,n){"use strict";t.__esModule=!0,t.ChatPageSettings=void 0;var o=n(0),r=n(22),a=n(1),i=n(79),c=n(107),s=n(146);t.ChatPageSettings=function(e,t){var n=(0,r.useSelector)(t,s.selectCurrentChatPage),d=(0,r.useDispatch)(t);return(0,o.createComponentVNode)(2,a.Section,{fill:!0,children:[(0,o.createComponentVNode)(2,a.Flex,{mx:-.5,align:"center",children:[(0,o.createComponentVNode)(2,a.Flex.Item,{mx:.5,grow:1,children:(0,o.createComponentVNode)(2,a.Input,{fluid:!0,value:n.name,onChange:function(e,t){return d((0,i.updateChatPage)({pageId:n.id,name:t}))}})}),(0,o.createComponentVNode)(2,a.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,a.Button,{icon:"times",color:"red",onClick:function(){return d((0,i.removeChatPage)({pageId:n.id}))},children:"Remove"})})]}),(0,o.createComponentVNode)(2,a.Divider),(0,o.createComponentVNode)(2,a.Section,{title:"Messages to display",level:2,children:[c.MESSAGE_TYPES.filter((function(e){return!e.important&&!e.admin})).map((function(e){return(0,o.createComponentVNode)(2,a.Button.Checkbox,{checked:n.acceptedTypes[e.type],onClick:function(){return d((0,i.toggleAcceptedType)({pageId:n.id,type:e.type}))},children:e.name},e.type)})),(0,o.createComponentVNode)(2,a.Collapsible,{mt:1,color:"transparent",title:"Admin stuff",children:c.MESSAGE_TYPES.filter((function(e){return!e.important&&e.admin})).map((function(e){return(0,o.createComponentVNode)(2,a.Button.Checkbox,{checked:n.acceptedTypes[e.type],onClick:function(){return d((0,i.toggleAcceptedType)({pageId:n.id,type:e.type}))},children:e.name},e.type)}))})]})]})}},673:function(e,t,n){"use strict";t.__esModule=!0,t.createUuid=void 0;t.createUuid=function(){var e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var n=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"===t?n:3&n|8).toString(16)}))}},674:function(e,t,n){"use strict";t.__esModule=!0,t.ChatPanel=void 0;var o=n(0),r=n(6),a=n(1),i=n(215);var c=function(e){var t,n;function c(){var t;return(t=e.call(this)||this).ref=(0,o.createRef)(),t.state={scrollTracking:!0},t.handleScrollTrackingChange=function(e){return t.setState({scrollTracking:e})},t}n=e,(t=c).prototype=Object.create(n.prototype),t.prototype.constructor=t,t.__proto__=n;var s=c.prototype;return s.componentDidMount=function(){i.chatRenderer.mount(this.ref.current),i.chatRenderer.events.on("scrollTrackingChanged",this.handleScrollTrackingChange),this.componentDidUpdate()},s.componentWillUnmount=function(){i.chatRenderer.events.off("scrollTrackingChanged",this.handleScrollTrackingChange)},s.componentDidUpdate=function(e){requestAnimationFrame((function(){i.chatRenderer.ensureScrollTracking()})),(!e||(0,r.shallowDiffers)(this.props,e))&&i.chatRenderer.assignStyle({width:"100%","white-space":"pre-wrap","font-size":this.props.fontSize,"line-height":this.props.lineHeight})},s.render=function(){var e=this.state.scrollTracking;return(0,o.createFragment)([(0,o.createVNode)(1,"div","Chat",null,1,null,null,this.ref),!e&&(0,o.createComponentVNode)(2,a.Button,{className:"Chat__scrollButton",icon:"arrow-down",onClick:function(){return i.chatRenderer.scrollToBottom()},children:"Scroll to bottom"})],0)},c}(o.Component);t.ChatPanel=c},675:function(e,t,n){"use strict";t.__esModule=!0,t.linkifyNode=t.highlightNode=t.replaceInTextNode=void 0;var o=function(e,t){return function(n){for(var o,r,a=n.textContent,i=a.length,c=0,s=0;o=e.exec(a);){s+=1,r||(r=document.createDocumentFragment());var d=o[0],l=d.length,u=o.index;c0&&(0,o.createComponentVNode)(2,d,{value:e.unreadCount}),onClick:function(){return u((0,i.changeChatPage)({pageId:e.id}))},children:e.name},e.id)}))})}),(0,o.createComponentVNode)(2,a.Flex.Item,{ml:1,children:(0,o.createComponentVNode)(2,a.Button,{color:"transparent",icon:"plus",onClick:function(){u((0,i.addChatPage)()),u((0,s.openChatSettings)())}})})]})}},677:function(e,t,n){"use strict";t.__esModule=!0,t.chatMiddleware=void 0;var o=n(102),r=n(65),a=n(105),i=n(79),c=n(107),s=n(106),d=n(215),l=n(146);n(25);function u(e,t,n,o,r,a,i){try{var c=e[a](i),s=c.value}catch(d){return void n(d)}c.done?t(s):Promise.resolve(s).then(o,r)}function g(e){return function(){var t=this,n=arguments;return new Promise((function(o,r){var a=e.apply(t,n);function i(e){u(a,o,r,i,c,"next",e)}function c(e){u(a,o,r,i,c,"throw",e)}i(undefined)}))}}var p=function(){var e=g(regeneratorRuntime.mark((function t(e){var n,r,a;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=(0,l.selectChat)(e.getState()),r=Math.max(0,d.chatRenderer.messages.length-c.MAX_PERSISTED_MESSAGES),a=d.chatRenderer.messages.slice(r).map((function(e){return(0,s.serializeMessage)(e)})),o.storage.set("chat-state",n),o.storage.set("chat-messages",a);case 5:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}(),h=function(){var e=g(regeneratorRuntime.mark((function t(e){var n,r,a,c;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,Promise.all([o.storage.get("chat-state"),o.storage.get("chat-messages")]);case 2:if(n=t.sent,r=n[0],a=n[1],!(r&&r.version<=4)){t.next=8;break}return e.dispatch((0,i.loadChat)()),t.abrupt("return");case 8:a&&(c=[].concat(a,[(0,s.createMessage)({type:"internal/reconnected"})]),d.chatRenderer.processBatch(c,{prepend:!0})),e.dispatch((0,i.loadChat)(r));case 10:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}();t.chatMiddleware=function(e){var t=!1,n=!1;return d.chatRenderer.events.on("batchProcessed",(function(t){n&&e.dispatch((0,i.updateMessageCount)(t))})),d.chatRenderer.events.on("scrollTrackingChanged",(function(t){e.dispatch((0,i.changeScrollTracking)(t))})),setInterval((function(){return p(e)}),c.MESSAGE_SAVE_INTERVAL),function(o){return function(c){var s=c.type,u=c.payload;if(t||(t=!0,h(e)),"chat/message"!==s){if(s===i.loadChat.type){o(c);var g=(0,l.selectCurrentChatPage)(e.getState());return d.chatRenderer.changePage(g),d.chatRenderer.onStateLoaded(),void(n=!0)}if(s!==i.changeChatPage.type&&s!==i.addChatPage.type&&s!==i.removeChatPage.type&&s!==i.toggleAcceptedType.type){if(s===i.rebuildChat.type)return d.chatRenderer.rebuildChat(),o(c);if(s!==r.updateSettings.type&&s!==r.loadSettings.type){if("roundrestart"===s)return p(e),o(c);if(s!==i.saveChatToDisk.type)return o(c);d.chatRenderer.saveToDisk()}else{o(c);var m=(0,a.selectSettings)(e.getState());d.chatRenderer.setHighlight(m.highlightText,m.highlightColor)}}else{o(c);var f=(0,l.selectCurrentChatPage)(e.getState());d.chatRenderer.changePage(f)}}else{var v=Array.isArray(u)?u:[u];d.chatRenderer.processBatch(v)}}}}},678:function(e,t,n){"use strict";t.__esModule=!0,t.chatReducer=t.initialState=void 0;var o,r=n(79),a=n(106);function i(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return c(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return c(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0&&(C[M.id]=Object.assign({},M,{unreadCount:M.unreadCount+A}))}return Object.assign({},e,{pageById:C})}if(o===r.addChatPage.type)return Object.assign({},e,{currentPageId:c.id,pages:[].concat(e.pages,[c.id]),pageById:Object.assign({},e.pageById,(n={},n[c.id]=c,n))});if(o===r.changeChatPage.type){var w,P=c.pageId,x=Object.assign({},e.pageById[P],{unreadCount:0});return Object.assign({},e,{currentPageId:P,pageById:Object.assign({},e.pageById,(w={},w[P]=x,w))})}if(o===r.updateChatPage.type){var k,R=c.pageId,O=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o=0||(r[n]=e[n]);return r}(c,["pageId"]),V=Object.assign({},e.pageById[R],O);return Object.assign({},e,{pageById:Object.assign({},e.pageById,(k={},k[R]=V,k))})}if(o===r.toggleAcceptedType.type){var G,B=c.pageId,L=c.type,D=Object.assign({},e.pageById[B]);return D.acceptedTypes=Object.assign({},D.acceptedTypes),D.acceptedTypes[L]=!D.acceptedTypes[L],Object.assign({},e,{pageById:Object.assign({},e.pageById,(G={},G[B]=D,G))})}if(o===r.removeChatPage.type){var j=c.pageId,F=Object.assign({},e,{pages:[].concat(e.pages),pageById:Object.assign({},e.pageById)});return delete F.pageById[j],F.pages=F.pages.filter((function(e){return e!==j})),0===F.pages.length&&(F.pages.push(s.id),F.pageById[s.id]=s,F.currentPageId=s.id),F.currentPageId&&F.currentPageId!==j||(F.currentPageId=F.pages[0]),F}return e}},679:function(e,t,n){"use strict";t.__esModule=!0,t.audioReducer=void 0;var o={visible:!1,playing:!1,track:null};t.audioReducer=function(e,t){void 0===e&&(e=o);var n=t.type,r=t.payload;return"audio/playing"===n?Object.assign({},e,{visible:!0,playing:!0}):"audio/stopped"===n?Object.assign({},e,{visible:!1,playing:!1}):"audio/playMusic"===n?Object.assign({},e,{meta:r}):"audio/stopMusic"===n?Object.assign({},e,{visible:!1,playing:!1,meta:null}):"audio/toggle"===n?Object.assign({},e,{visible:!e.visible}):e}},680:function(e,t,n){"use strict";t.__esModule=!0,t.useGame=void 0;var o=n(22),r=n(217);t.useGame=function(e){return(0,o.useSelector)(e,r.selectGame)}},681:function(e,t,n){"use strict";t.__esModule=!0,t.gameMiddleware=void 0;var o=n(147),r=n(218),a=n(217),i=n(682),c=function(e){return Object.assign({},e,{meta:Object.assign({},e.meta,{now:Date.now()})})};t.gameMiddleware=function(e){var t;return setInterval((function(){var n=e.getState();if(n){var o=(0,a.selectGame)(n),s=t&&Date.now()>=t+i.CONNECTION_LOST_AFTER;!o.connectionLostAt&&s&&e.dispatch(c((0,r.connectionLost)())),o.connectionLostAt&&!s&&e.dispatch(c((0,r.connectionRestored)()))}}),1e3),function(e){return function(n){var a=n.type,i=(n.payload,n.meta);return a===o.pingSuccess.type?(t=i.now,e(n)):a===r.roundRestarted.type?e(c(n)):e(n)}}}},682:function(e,t,n){"use strict";t.__esModule=!0,t.CONNECTION_LOST_AFTER=void 0;t.CONNECTION_LOST_AFTER=15e3},683:function(e,t,n){"use strict";t.__esModule=!0,t.gameReducer=void 0;var o=n(218),r={roundId:null,roundTime:null,roundRestartedAt:null,connectionLostAt:null};t.gameReducer=function(e,t){void 0===e&&(e=r);var n=t.type,a=(t.payload,t.meta);return"roundrestart"===n?Object.assign({},e,{roundRestartedAt:a.now}):n===o.connectionLost.type?Object.assign({},e,{connectionLostAt:a.now}):n===o.connectionRestored.type?Object.assign({},e,{connectionLostAt:null}):e}},684:function(e,t,n){"use strict";(function(e){t.__esModule=!0,t.setupPanelFocusHacks=void 0;var o=n(103),r=n(57),a=n(189),i=function(){return e((function(){return(0,a.focusMap)()}))};t.setupPanelFocusHacks=function(){var e=!1,t=null;window.addEventListener("focusin",(function(t){e=(0,r.canStealFocus)(t.target)})),window.addEventListener("mousedown",(function(e){t=[e.screenX,e.screenY]})),window.addEventListener("mouseup",(function(n){if(t){var r=[n.screenX,n.screenY];(0,o.vecLength)((0,o.vecSubtract)(r,t))>=10&&(e=!0)}e||i()})),r.globalEvents.on("keydown",(function(e){e.isModifierKey()||i()}))}}).call(this,n(101).setImmediate)},685:function(e,t,n){"use strict";t.__esModule=!0,t.pingMiddleware=void 0;var o=n(2),r=n(147),a=n(220);t.pingMiddleware=function(e){var t=!1,n=0,i=[],c=function(){for(var t=0;ta.PING_TIMEOUT&&(i[t]=null,e.dispatch((0,r.pingFail)()))}var s={index:n,sentAt:Date.now()};i[n]=s,(0,o.sendMessage)({type:"ping",payload:{index:n}}),n=(n+1)%a.PING_QUEUE_SIZE};return function(e){return function(n){var o=n.type,s=n.payload;if(t||(t=!0,setInterval(c,a.PING_INTERVAL),c()),"pingReply"===o){var d=s.index,l=i[d];if(!l)return;return i[d]=null,e((0,r.pingSuccess)(l))}return e(n)}}}},686:function(e,t,n){"use strict";t.__esModule=!0,t.PingIndicator=void 0;var o=n(0),r=n(687),a=n(11),i=n(22),c=n(1),s=n(688);t.PingIndicator=function(e,t){var n=(0,i.useSelector)(t,s.selectPing),d=r.Color.lookup(n.networkQuality,[new r.Color(220,40,40),new r.Color(220,200,40),new r.Color(60,220,40)]),l=n.roundtrip?(0,a.toFixed)(n.roundtrip):"--";return(0,o.createVNode)(1,"div","Ping",[(0,o.createComponentVNode)(2,c.Box,{className:"Ping__indicator",backgroundColor:d}),l],0)}},687:function(e,t,n){"use strict";t.__esModule=!0,t.Color=void 0;var o=function(){function e(e,t,n,o){void 0===e&&(e=0),void 0===t&&(t=0),void 0===n&&(n=0),void 0===o&&(o=1),this.r=e,this.g=t,this.b=n,this.a=o}return e.prototype.toString=function(){return"rgba("+(0|this.r)+", "+(0|this.g)+", "+(0|this.b)+", "+(0|this.a)+")"},e}();t.Color=o,o.fromHex=function(e){return new o(parseInt(e.substr(1,2),16),parseInt(e.substr(3,2),16),parseInt(e.substr(5,2),16))},o.lerp=function(e,t,n){return new o((t.r-e.r)*n+e.r,(t.g-e.g)*n+e.g,(t.b-e.b)*n+e.b,(t.a-e.a)*n+e.a)},o.lookup=function(e,t){void 0===t&&(t=[]);var n=t.length;if(n<2)throw new Error("Needs at least two colors!");var r=e*(n-1);if(e<1e-4)return t[0];if(e>=.9999)return t[n-1];var a=r%1,i=0|r;return o.lerp(t[i],t[i+1],a)}},688:function(e,t,n){"use strict";t.__esModule=!0,t.selectPing=void 0;t.selectPing=function(e){return e.ping}},689:function(e,t,n){"use strict";t.__esModule=!0,t.pingReducer=void 0;var o=n(11),r=n(147),a=n(220);t.pingReducer=function(e,t){void 0===e&&(e={});var n=t.type,i=t.payload;if(n===r.pingSuccess.type){var c=i.roundtrip,s=e.roundtripAvg||c,d=Math.round(.4*s+.6*c);return{roundtrip:c,roundtripAvg:d,failCount:0,networkQuality:1-(0,o.scale)(d,a.PING_ROUNDTRIP_BEST,a.PING_ROUNDTRIP_WORST)}}if(n===r.pingFail.type){var l=e.failCount,u=void 0===l?0:l,g=(0,o.clamp01)(e.networkQuality-u/a.PING_MAX_FAILS),p=Object.assign({},e,{failCount:u+1,networkQuality:g});return u>a.PING_MAX_FAILS&&(p.roundtrip=undefined,p.roundtripAvg=undefined),p}return e}},690:function(e,t,n){"use strict";t.__esModule=!0,t.telemetryMiddleware=void 0;var o=n(2),r=n(102);function a(e,t,n,o,r,a,i){try{var c=e[a](i),s=c.value}catch(d){return void n(d)}c.done?t(s):Promise.resolve(s).then(o,r)}var i=(0,n(25).createLogger)("telemetry");t.telemetryMiddleware=function(e){var t,n;return function(c){return function(s){var d,l=s.type,u=s.payload;if("telemetry/request"!==l)return"backend/update"===l?(c(s),void(d=regeneratorRuntime.mark((function h(){var o,a,c,s;return regeneratorRuntime.wrap((function(d){for(;;)switch(d.prev=d.next){case 0:if(a=null==u||null==(o=u.config)?void 0:o.client){d.next=4;break}return i.error("backend/update payload is missing client data!"),d.abrupt("return");case 4:if(t){d.next=13;break}return d.next=7,r.storage.get("telemetry");case 7:if(d.t0=d.sent,d.t0){d.next=10;break}d.t0={};case 10:(t=d.t0).connections||(t.connections=[]),i.debug("retrieved telemetry from storage",t);case 13:c=!1,t.connections.find((function(e){return n=a,(t=e).ckey===n.ckey&&t.address===n.address&&t.computer_id===n.computer_id;var t,n}))||(c=!0,t.connections.unshift(a),t.connections.length>10&&t.connections.pop()),c&&(i.debug("saving telemetry to storage",t),r.storage.set("telemetry",t)),n&&(s=n,n=null,e.dispatch({type:"telemetry/request",payload:s}));case 18:case"end":return d.stop()}}),h)})),function(){var e=this,t=arguments;return new Promise((function(n,o){var r=d.apply(e,t);function i(e){a(r,n,o,i,c,"next",e)}function c(e){a(r,n,o,i,c,"throw",e)}i(undefined)}))})()):c(s);if(!t)return i.debug("deferred"),void(n=u);i.debug("sending");var g=(null==u?void 0:u.limits)||{},p=t.connections.slice(0,g.connections);(0,o.sendMessage)({type:"telemetry",payload:{connections:p}})}}}},691:function(e,t,n){"use strict";t.__esModule=!0,t.Panel=void 0;var o=n(0),r=n(1),a=n(3),i=n(211),c=n(145),s=n(216),d=n(692),l=n(219),u=n(144);t.Panel=function(e,t){if(Byond.IS_LTE_IE10)return(0,o.createComponentVNode)(2,g);var n=(0,i.useAudio)(t),p=(0,u.useSettings)(t),h=(0,s.useGame)(t);return(0,o.createComponentVNode)(2,a.Pane,{theme:p.theme,children:(0,o.createComponentVNode)(2,r.Flex,{direction:"column",height:"100%",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{children:(0,o.createComponentVNode)(2,r.Section,{fitted:!0,children:(0,o.createComponentVNode)(2,r.Flex,{mx:.5,align:"center",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,grow:1,overflowX:"auto",children:(0,o.createComponentVNode)(2,c.ChatTabs)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,l.PingIndicator)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,r.Button,{color:"grey",selected:n.visible,icon:"music",tooltip:"Music player",tooltipPosition:"bottom-left",onClick:function(){return n.toggle()}})}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,r.Button,{icon:p.visible?"times":"cog",selected:p.visible,tooltip:p.visible?"Close settings":"Open settings",tooltipPosition:"bottom-left",onClick:function(){return p.toggle()}})})]})})}),n.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,r.Section,{children:(0,o.createComponentVNode)(2,i.NowPlayingWidget)})}),p.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,u.SettingsPanel)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,grow:1,children:(0,o.createComponentVNode)(2,r.Section,{fill:!0,fitted:!0,position:"relative",children:[(0,o.createComponentVNode)(2,a.Pane.Content,{scrollable:!0,children:(0,o.createComponentVNode)(2,c.ChatPanel,{lineHeight:p.lineHeight})}),(0,o.createComponentVNode)(2,d.Notifications,{children:[h.connectionLostAt&&(0,o.createComponentVNode)(2,d.Notifications.Item,{rightSlot:(0,o.createComponentVNode)(2,r.Button,{color:"white",onClick:function(){return Byond.command(".reconnect")},children:"Reconnect"}),children:"You are either AFK, experiencing lag or the connection has closed."}),h.roundRestartedAt&&(0,o.createComponentVNode)(2,d.Notifications.Item,{children:"The connection has been closed because the server is restarting. Please wait while you automatically reconnect."})]})]})})]})})};var g=function(e,t){var n=(0,u.useSettings)(t);return(0,o.createComponentVNode)(2,a.Pane,{theme:n.theme,children:(0,o.createComponentVNode)(2,a.Pane.Content,{scrollable:!0,children:[(0,o.createComponentVNode)(2,r.Button,{style:{position:"fixed",top:"1em",right:"2em","z-index":1e3},selected:n.visible,onClick:function(){return n.toggle()},children:"Settings"}),n.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,u.SettingsPanel)})||(0,o.createComponentVNode)(2,c.ChatPanel,{lineHeight:n.lineHeight})]})})}},692:function(e,t,n){"use strict";t.__esModule=!0,t.Notifications=void 0;var o=n(0),r=n(1),a=function(e){var t=e.children;return(0,o.createVNode)(1,"div","Notifications",t,0)};t.Notifications=a;a.Item=function(e){var t=e.rightSlot,n=e.children;return(0,o.createComponentVNode)(2,r.Flex,{align:"center",className:"Notification",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{className:"Notification__content",grow:1,children:n}),t&&(0,o.createComponentVNode)(2,r.Flex.Item,{className:"Notification__rightSlot",children:t})]})}},79:function(e,t,n){"use strict";t.__esModule=!0,t.saveChatToDisk=t.changeScrollTracking=t.removeChatPage=t.toggleAcceptedType=t.updateChatPage=t.changeChatPage=t.addChatPage=t.updateMessageCount=t.rebuildChat=t.loadChat=void 0;var o=n(22),r=n(106),a=(0,o.createAction)("chat/load");t.loadChat=a;var i=(0,o.createAction)("chat/rebuild");t.rebuildChat=i;var c=(0,o.createAction)("chat/updateMessageCount");t.updateMessageCount=c;var s=(0,o.createAction)("chat/addPage",(function(){return{payload:(0,r.createPage)()}}));t.addChatPage=s;var d=(0,o.createAction)("chat/changePage");t.changeChatPage=d;var l=(0,o.createAction)("chat/updatePage");t.updateChatPage=l;var u=(0,o.createAction)("chat/toggleAcceptedType");t.toggleAcceptedType=u;var g=(0,o.createAction)("chat/removePage");t.removeChatPage=g;var p=(0,o.createAction)("chat/changeScrollTracking");t.changeScrollTracking=p;var h=(0,o.createAction)("chat/saveToDisk");t.saveChatToDisk=h}});
\ No newline at end of file
+!function(e){function t(t){for(var o,i,c=t[0],s=t[1],d=t[2],u=0,g=[];u=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n=i.IMAGE_RETRY_LIMIT)u.error("failed to load an image after "+n+" attempts");else{var o=t.src;t.src=null,t.src=o+"#"+n,t.setAttribute("data-reload-n",n+1)}}),i.IMAGE_RETRY_DELAY)},f=function(e){var t=e.node,n=e.times;if(t&&n){var o=t.querySelector(".Chat__badge"),a=o||document.createElement("div");a.textContent=n,a.className=(0,r.classes)(["Chat__badge","Chat__badge--animate"]),requestAnimationFrame((function(){a.className="Chat__badge"})),o||t.appendChild(a)}},v=function(){function t(){var e=this;this.loaded=!1,this.rootNode=null,this.queue=[],this.messages=[],this.visibleMessages=[],this.page=null,this.events=new o.EventEmitter,this.scrollNode=null,this.scrollTracking=!0,this.handleScroll=function(t){var n=e.scrollNode,o=n.scrollHeight,r=n.scrollTop+n.offsetHeight,a=Math.abs(o-r)<24;a!==e.scrollTracking&&(e.scrollTracking=a,e.events.emit("scrollTrackingChanged",a),u.debug("tracking",e.scrollTracking))},this.ensureScrollTracking=function(){e.scrollTracking&&e.scrollToBottom()},setInterval((function(){return e.pruneMessages()}),i.MESSAGE_PRUNE_INTERVAL)}var n=t.prototype;return n.isReady=function(){return this.loaded&&this.rootNode&&this.page},n.mount=function(t){var n=this;this.rootNode?t.appendChild(this.rootNode):this.rootNode=t,this.scrollNode=function(e){for(var t=document.body,n=e;n&&n!==t;){if(n.scrollWidth0&&(this.processBatch(this.queue),this.queue=[])},n.assignStyle=function(e){void 0===e&&(e={});for(var t=0,n=Object.keys(e);t1&&n.test(e)}));if(0===o.length)return this.highlightRegex=null,void(this.highlightColor=null);this.highlightRegex=new RegExp("("+o.join("|")+")","gi"),this.highlightColor=t},n.scrollToBottom=function(){this.scrollNode.scrollTop=this.scrollNode.scrollHeight},n.changePage=function(e){if(!this.isReady())return this.page=e,void this.tryFlushQueue();this.page=e,this.rootNode.textContent="",this.visibleMessages=[];for(var t,n,o=document.createDocumentFragment(),r=d(this.messages);!(n=r()).done;){var a=n.value;(0,c.canPageAcceptType)(e,a.type)&&(t=a.node,o.appendChild(t),this.visibleMessages.push(a))}t&&(this.rootNode.appendChild(o),t.scrollIntoView())},n.getCombinableMessage=function(e){for(var t=Date.now(),n=this.visibleMessages.length,o=n-1,r=Math.max(0,n-i.COMBINE_MAX_MESSAGES),a=o;a>=r;a--){var s=this.visibleMessages[a];if(!s.type.startsWith(i.MESSAGE_TYPE_INTERNAL)&&(0,c.isSameMessage)(s,e)&&t0){this.visibleMessages=e.slice(t);for(var n=0;n0&&(this.messages=this.messages.slice(r),u.log("pruned "+r+" stored messages"))}else u.debug("pruning delayed")},n.rebuildChat=function(){if(this.isReady()){for(var e,t=Math.max(0,this.messages.length-i.MAX_PERSISTED_MESSAGES),n=this.messages.slice(t),o=d(n);!(e=o()).done;)e.value.node=undefined;this.rootNode.textContent="",this.messages=[],this.visibleMessages=[],this.processBatch(n,{notifyListeners:!1})}},n.saveToDisk=function(){if(!Byond.IS_LTE_IE10){for(var e="",t=document.styleSheets,n=0;n\n\n\nSS13 Chat Log\n\n\n\n\n'+i+"
\n\n\n"]),u=(new Date).toISOString().substring(0,19).replace(/[-:]/g,"").replace("T","-");window.navigator.msSaveBlob(l,"ss13-chatlog-"+u+".html")}},t}();window.__chatRenderer__||(window.__chatRenderer__=new v);var y=window.__chatRenderer__;t.chatRenderer=y}).call(this,n(101).setImmediate)},216:function(e,t,n){"use strict";t.__esModule=!0,t.gameReducer=t.gameMiddleware=t.useGame=void 0;var o=n(680);t.useGame=o.useGame;var r=n(681);t.gameMiddleware=r.gameMiddleware;var a=n(683);t.gameReducer=a.gameReducer},217:function(e,t,n){"use strict";t.__esModule=!0,t.selectGame=void 0;t.selectGame=function(e){return e.game}},218:function(e,t,n){"use strict";t.__esModule=!0,t.connectionRestored=t.connectionLost=t.roundRestarted=void 0;var o=n(22),r=(0,o.createAction)("roundrestart");t.roundRestarted=r;var a=(0,o.createAction)("game/connectionLost");t.connectionLost=a;var i=(0,o.createAction)("game/connectionRestored");t.connectionRestored=i},219:function(e,t,n){"use strict";t.__esModule=!0,t.pingReducer=t.PingIndicator=t.pingMiddleware=void 0;var o=n(685);t.pingMiddleware=o.pingMiddleware;var r=n(686);t.PingIndicator=r.PingIndicator;var a=n(689);t.pingReducer=a.pingReducer},220:function(e,t,n){"use strict";t.__esModule=!0,t.PING_ROUNDTRIP_WORST=t.PING_ROUNDTRIP_BEST=t.PING_QUEUE_SIZE=t.PING_MAX_FAILS=t.PING_TIMEOUT=t.PING_INTERVAL=void 0;t.PING_INTERVAL=2500;t.PING_TIMEOUT=2e3;t.PING_MAX_FAILS=3;t.PING_QUEUE_SIZE=8;t.PING_ROUNDTRIP_BEST=50;t.PING_ROUNDTRIP_WORST=200},65:function(e,t,n){"use strict";t.__esModule=!0,t.openChatSettings=t.toggleSettings=t.changeSettingsTab=t.loadSettings=t.updateSettings=void 0;var o=n(22),r=(0,o.createAction)("settings/update");t.updateSettings=r;var a=(0,o.createAction)("settings/load");t.loadSettings=a;var i=(0,o.createAction)("settings/changeTab");t.changeSettingsTab=i;var c=(0,o.createAction)("settings/toggle");t.toggleSettings=c;var s=(0,o.createAction)("settings/openChatTab");t.openChatSettings=s},660:function(e,t,n){n(148),e.exports=n(661)},661:function(e,t,n){"use strict";var o=n(0);n(662),n(663);var r,a,i=n(99),c=n(22),s=(n(100),n(57)),d=n(186),l=n(136),u=n(187),g=n(211),p=n(145),h=n(216),m=n(684),f=n(219),v=n(144),y=n(690);i.perf.mark("inception",null==(r=window.performance)||null==(a=r.timing)?void 0:a.navigationStart),i.perf.mark("init");var b=(0,u.configureStore)({reducer:(0,c.combineReducers)({audio:g.audioReducer,chat:p.chatReducer,game:h.gameReducer,ping:f.pingReducer,settings:v.settingsReducer}),middleware:{pre:[p.chatMiddleware,f.pingMiddleware,y.telemetryMiddleware,v.settingsMiddleware,g.audioMiddleware,h.gameMiddleware]}}),S=(0,l.createRenderer)((function(){var e=n(691).Panel;return(0,o.createComponentVNode)(2,u.StoreProvider,{store:b,children:(0,o.createComponentVNode)(2,e)})}));!function _(){if("loading"!==document.readyState){for((0,s.setupGlobalEvents)({ignoreWindowFocus:!0}),(0,m.setupPanelFocusHacks)(),(0,d.captureExternalLinks)(),b.subscribe(S),window.update=function(e){return b.dispatch(Byond.parseJson(e))};;){var e=window.__updateQueue__.shift();if(!e)break;window.update(e)}Byond.winset("output",{"is-visible":!1}),Byond.winset("browseroutput",{"is-visible":!0,"is-disabled":!1,pos:"0x0",size:"0x0"})}else document.addEventListener("DOMContentLoaded",_)}()},662:function(e,t,n){},663:function(e,t,n){},664:function(e,t,n){"use strict";t.__esModule=!0,t.useAudio=void 0;var o=n(22),r=n(212);t.useAudio=function(e){var t=(0,o.useSelector)(e,r.selectAudio),n=(0,o.useDispatch)(e);return Object.assign({},t,{toggle:function(){return n({type:"audio/toggle"})}})}},665:function(e,t,n){"use strict";t.__esModule=!0,t.audioMiddleware=void 0;var o=n(666);t.audioMiddleware=function(e){var t=new o.AudioPlayer;return t.onPlay((function(){e.dispatch({type:"audio/playing"})})),t.onStop((function(){e.dispatch({type:"audio/stopped"})})),function(e){return function(n){var o=n.type,r=n.payload;if("audio/playMusic"===o){var a=r.url,i=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o=0||(r[n]=e[n]);return r}(r,["url"]);return t.play(a,i),e(n)}if("audio/stopMusic"===o)return t.stop(),e(n);if("settings/update"===o||"settings/load"===o){var c=null==r?void 0:r.adminMusicVolume;return"number"==typeof c&&t.setVolume(c),e(n)}return e(n)}}}},666:function(e,t,n){"use strict";function o(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0&&e.node.currentTime>=e.options.end&&e.stop())}),1e3))}var t=e.prototype;return t.destroy=function(){this.node&&(this.node.stop(),document.removeChild(this.node),clearInterval(this.playbackInterval))},t.play=function(e,t){void 0===t&&(t={}),this.node&&(a.log("playing",e,t),this.options=t,this.node.src=e)},t.stop=function(){if(this.node){if(this.playing)for(var e,t=o(this.onStopSubscribers);!(e=t()).done;)(0,e.value)();a.log("stopping"),this.playing=!1,this.node.src=""}},t.setVolume=function(e){this.node&&(this.volume=e,this.node.volume=e)},t.onPlay=function(e){this.node&&this.onPlaySubscribers.push(e)},t.onStop=function(e){this.node&&this.onStopSubscribers.push(e)},e}();t.AudioPlayer=i},667:function(e,t,n){"use strict";t.__esModule=!0,t.NowPlayingWidget=void 0;var o=n(0),r=n(11),a=n(22),i=n(1),c=n(144),s=n(212);t.NowPlayingWidget=function(e,t){var n,d=(0,a.useSelector)(t,s.selectAudio),l=(0,a.useDispatch)(t),u=(0,c.useSettings)(t),g=null==(n=d.meta)?void 0:n.title;return(0,o.createComponentVNode)(2,i.Flex,{align:"center",children:[d.playing&&(0,o.createFragment)([(0,o.createComponentVNode)(2,i.Flex.Item,{shrink:0,mx:.5,color:"label",children:"Now playing:"}),(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,grow:1,style:{"white-space":"nowrap",overflow:"hidden","text-overflow":"ellipsis"},children:g||"Unknown Track"})],4)||(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,color:"label",children:"Nothing to play."}),d.playing&&(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,fontSize:"0.9em",children:(0,o.createComponentVNode)(2,i.Button,{tooltip:"Stop",icon:"stop",onClick:function(){return l({type:"audio/stopMusic"})}})}),(0,o.createComponentVNode)(2,i.Flex.Item,{mx:.5,fontSize:"0.9em",children:(0,o.createComponentVNode)(2,i.Knob,{minValue:0,maxValue:1,value:u.adminMusicVolume,step:.0025,stepPixelSize:1,format:function(e){return(0,r.toFixed)(100*e)+"%"},onDrag:function(e,t){return u.update({adminMusicVolume:t})}})})]})}},668:function(e,t,n){"use strict";t.__esModule=!0,t.useSettings=void 0;var o=n(22),r=n(65),a=n(105);t.useSettings=function(e){var t=(0,o.useSelector)(e,a.selectSettings),n=(0,o.useDispatch)(e);return Object.assign({},t,{visible:t.view.visible,toggle:function(){return n((0,r.toggleSettings)())},update:function(e){return n((0,r.updateSettings)(e))}})}},669:function(e,t,n){"use strict";t.__esModule=!0,t.settingsMiddleware=void 0;var o=n(102),r=n(213),a=n(65),i=n(105);t.settingsMiddleware=function(e){var t=!1;return function(n){return function(c){var s,d=c.type,l=c.payload;if(t||(t=!0,o.storage.get("panel-settings").then((function(t){e.dispatch((0,a.loadSettings)(t))}))),d===a.updateSettings.type||d===a.loadSettings.type){var u=null==l?void 0:l.theme;u&&(0,r.setClientTheme)(u),n(c);var g=(0,i.selectSettings)(e.getState());return s=g.fontSize,document.documentElement.style.setProperty("font-size",s+"px"),document.body.style.setProperty("font-size",s+"px"),void o.storage.set("panel-settings",g)}return n(c)}}}},670:function(e,t,n){"use strict";t.__esModule=!0,t.settingsReducer=void 0;var o=n(65),r={version:1,fontSize:13,lineHeight:1.2,theme:"light",adminMusicVolume:.5,highlightText:"",highlightColor:"#ffdd44",view:{visible:!1,activeTab:n(214).SETTINGS_TABS[0].id}};t.settingsReducer=function(e,t){void 0===e&&(e=r);var n=t.type,a=t.payload;if(n===o.updateSettings.type)return Object.assign({},e,a);if(n===o.loadSettings.type)return(null==a?void 0:a.version)?(delete a.view,Object.assign({},e,a)):e;if(n===o.toggleSettings.type)return Object.assign({},e,{view:Object.assign({},e.view,{visible:!e.view.visible})});if(n===o.openChatSettings.type)return Object.assign({},e,{view:Object.assign({},e.view,{visible:!0,activeTab:"chatPage"})});if(n===o.changeSettingsTab.type){var i=a.tabId;return Object.assign({},e,{view:Object.assign({},e.view,{activeTab:i})})}return e}},671:function(e,t,n){"use strict";t.__esModule=!0,t.SettingsGeneral=t.SettingsPanel=void 0;var o=n(0),r=n(11),a=n(22),i=n(1),c=n(145),s=n(79),d=n(213),l=n(65),u=n(214),g=n(105);t.SettingsPanel=function(e,t){var n=(0,a.useSelector)(t,g.selectActiveTab),r=(0,a.useDispatch)(t);return(0,o.createComponentVNode)(2,i.Flex,{children:[(0,o.createComponentVNode)(2,i.Flex.Item,{mr:1,children:(0,o.createComponentVNode)(2,i.Section,{fitted:!0,fill:!0,minHeight:"8em",children:(0,o.createComponentVNode)(2,i.Tabs,{vertical:!0,children:u.SETTINGS_TABS.map((function(e){return(0,o.createComponentVNode)(2,i.Tabs.Tab,{selected:e.id===n,onClick:function(){return r((0,l.changeSettingsTab)({tabId:e.id}))},children:e.name},e.id)}))})})}),(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,basis:0,children:["general"===n&&(0,o.createComponentVNode)(2,p),"chatPage"===n&&(0,o.createComponentVNode)(2,c.ChatPageSettings)]})]})};var p=function(e,t){var n=(0,a.useSelector)(t,g.selectSettings),c=n.theme,u=n.fontSize,p=n.lineHeight,h=n.highlightText,m=n.highlightColor,f=(0,a.useDispatch)(t);return(0,o.createComponentVNode)(2,i.Section,{fill:!0,children:[(0,o.createComponentVNode)(2,i.LabeledList,{children:[(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Theme",children:(0,o.createComponentVNode)(2,i.Dropdown,{selected:c,options:d.THEMES,onSelected:function(e){return f((0,l.updateSettings)({theme:e}))}})}),(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Font size",children:(0,o.createComponentVNode)(2,i.NumberInput,{width:"4em",step:1,stepPixelSize:10,minValue:8,maxValue:32,value:u,unit:"px",format:function(e){return(0,r.toFixed)(e)},onChange:function(e,t){return f((0,l.updateSettings)({fontSize:t}))}})}),(0,o.createComponentVNode)(2,i.LabeledList.Item,{label:"Line height",children:(0,o.createComponentVNode)(2,i.NumberInput,{width:"4em",step:.01,stepPixelSize:2,minValue:.8,maxValue:5,value:p,format:function(e){return(0,r.toFixed)(e,2)},onDrag:function(e,t){return f((0,l.updateSettings)({lineHeight:t}))}})})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Flex,{mb:1,color:"label",align:"baseline",children:[(0,o.createComponentVNode)(2,i.Flex.Item,{grow:1,children:"Highlight words (comma separated):"}),(0,o.createComponentVNode)(2,i.Flex.Item,{shrink:0,children:[(0,o.createComponentVNode)(2,i.ColorBox,{mr:1,color:m}),(0,o.createComponentVNode)(2,i.Input,{width:"5em",monospace:!0,placeholder:"#ffffff",value:m,onInput:function(e,t){return f((0,l.updateSettings)({highlightColor:t}))}})]})]}),(0,o.createComponentVNode)(2,i.TextArea,{height:"3em",value:h,onChange:function(e,t){return f((0,l.updateSettings)({highlightText:t}))}})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Button,{icon:"check",onClick:function(){return f((0,s.rebuildChat)())},children:"Apply now"}),(0,o.createComponentVNode)(2,i.Box,{inline:!0,fontSize:"0.9em",ml:1,color:"label",children:"Can freeze the chat for a while."})]}),(0,o.createComponentVNode)(2,i.Divider),(0,o.createComponentVNode)(2,i.Button,{icon:"save",onClick:function(){return f((0,s.saveChatToDisk)())},children:"Save chat log"})]})};t.SettingsGeneral=p},672:function(e,t,n){"use strict";t.__esModule=!0,t.ChatPageSettings=void 0;var o=n(0),r=n(22),a=n(1),i=n(79),c=n(107),s=n(146);t.ChatPageSettings=function(e,t){var n=(0,r.useSelector)(t,s.selectCurrentChatPage),d=(0,r.useDispatch)(t);return(0,o.createComponentVNode)(2,a.Section,{fill:!0,children:[(0,o.createComponentVNode)(2,a.Flex,{mx:-.5,align:"center",children:[(0,o.createComponentVNode)(2,a.Flex.Item,{mx:.5,grow:1,children:(0,o.createComponentVNode)(2,a.Input,{fluid:!0,value:n.name,onChange:function(e,t){return d((0,i.updateChatPage)({pageId:n.id,name:t}))}})}),(0,o.createComponentVNode)(2,a.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,a.Button,{icon:"times",color:"red",onClick:function(){return d((0,i.removeChatPage)({pageId:n.id}))},children:"Remove"})})]}),(0,o.createComponentVNode)(2,a.Divider),(0,o.createComponentVNode)(2,a.Section,{title:"Messages to display",level:2,children:[c.MESSAGE_TYPES.filter((function(e){return!e.important&&!e.admin})).map((function(e){return(0,o.createComponentVNode)(2,a.Button.Checkbox,{checked:n.acceptedTypes[e.type],onClick:function(){return d((0,i.toggleAcceptedType)({pageId:n.id,type:e.type}))},children:e.name},e.type)})),(0,o.createComponentVNode)(2,a.Collapsible,{mt:1,color:"transparent",title:"Admin stuff",children:c.MESSAGE_TYPES.filter((function(e){return!e.important&&e.admin})).map((function(e){return(0,o.createComponentVNode)(2,a.Button.Checkbox,{checked:n.acceptedTypes[e.type],onClick:function(){return d((0,i.toggleAcceptedType)({pageId:n.id,type:e.type}))},children:e.name},e.type)}))})]})]})}},673:function(e,t,n){"use strict";t.__esModule=!0,t.createUuid=void 0;t.createUuid=function(){var e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var n=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"===t?n:3&n|8).toString(16)}))}},674:function(e,t,n){"use strict";t.__esModule=!0,t.ChatPanel=void 0;var o=n(0),r=n(6),a=n(1),i=n(215);var c=function(e){var t,n;function c(){var t;return(t=e.call(this)||this).ref=(0,o.createRef)(),t.state={scrollTracking:!0},t.handleScrollTrackingChange=function(e){return t.setState({scrollTracking:e})},t}n=e,(t=c).prototype=Object.create(n.prototype),t.prototype.constructor=t,t.__proto__=n;var s=c.prototype;return s.componentDidMount=function(){i.chatRenderer.mount(this.ref.current),i.chatRenderer.events.on("scrollTrackingChanged",this.handleScrollTrackingChange),this.componentDidUpdate()},s.componentWillUnmount=function(){i.chatRenderer.events.off("scrollTrackingChanged",this.handleScrollTrackingChange)},s.componentDidUpdate=function(e){requestAnimationFrame((function(){i.chatRenderer.ensureScrollTracking()})),(!e||(0,r.shallowDiffers)(this.props,e))&&i.chatRenderer.assignStyle({width:"100%","white-space":"pre-wrap","font-size":this.props.fontSize,"line-height":this.props.lineHeight})},s.render=function(){var e=this.state.scrollTracking;return(0,o.createFragment)([(0,o.createVNode)(1,"div","Chat",null,1,null,null,this.ref),!e&&(0,o.createComponentVNode)(2,a.Button,{className:"Chat__scrollButton",icon:"arrow-down",onClick:function(){return i.chatRenderer.scrollToBottom()},children:"Scroll to bottom"})],0)},c}(o.Component);t.ChatPanel=c},675:function(e,t,n){"use strict";t.__esModule=!0,t.linkifyNode=t.highlightNode=t.replaceInTextNode=void 0;var o=function(e,t){return function(n){for(var o,r,a=n.textContent,i=a.length,c=0,s=0;o=e.exec(a);){s+=1,r||(r=document.createDocumentFragment());var d=o[0],l=d.length,u=o.index;c0&&(0,o.createComponentVNode)(2,d,{value:e.unreadCount}),onClick:function(){return u((0,i.changeChatPage)({pageId:e.id}))},children:e.name},e.id)}))})}),(0,o.createComponentVNode)(2,a.Flex.Item,{ml:1,children:(0,o.createComponentVNode)(2,a.Button,{color:"transparent",icon:"plus",onClick:function(){u((0,i.addChatPage)()),u((0,s.openChatSettings)())}})})]})}},677:function(e,t,n){"use strict";t.__esModule=!0,t.chatMiddleware=void 0;var o=n(102),r=n(65),a=n(105),i=n(79),c=n(107),s=n(106),d=n(215),l=n(146);n(25);function u(e,t,n,o,r,a,i){try{var c=e[a](i),s=c.value}catch(d){return void n(d)}c.done?t(s):Promise.resolve(s).then(o,r)}function g(e){return function(){var t=this,n=arguments;return new Promise((function(o,r){var a=e.apply(t,n);function i(e){u(a,o,r,i,c,"next",e)}function c(e){u(a,o,r,i,c,"throw",e)}i(undefined)}))}}var p=function(){var e=g(regeneratorRuntime.mark((function t(e){var n,r,a;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=(0,l.selectChat)(e.getState()),r=Math.max(0,d.chatRenderer.messages.length-c.MAX_PERSISTED_MESSAGES),a=d.chatRenderer.messages.slice(r).map((function(e){return(0,s.serializeMessage)(e)})),o.storage.set("chat-state",n),o.storage.set("chat-messages",a);case 5:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}(),h=function(){var e=g(regeneratorRuntime.mark((function t(e){var n,r,a,c;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,Promise.all([o.storage.get("chat-state"),o.storage.get("chat-messages")]);case 2:if(n=t.sent,r=n[0],a=n[1],!(r&&r.version<=4)){t.next=8;break}return e.dispatch((0,i.loadChat)()),t.abrupt("return");case 8:a&&(c=[].concat(a,[(0,s.createMessage)({type:"internal/reconnected"})]),d.chatRenderer.processBatch(c,{prepend:!0})),e.dispatch((0,i.loadChat)(r));case 10:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}();t.chatMiddleware=function(e){var t=!1,n=!1;return d.chatRenderer.events.on("batchProcessed",(function(t){n&&e.dispatch((0,i.updateMessageCount)(t))})),d.chatRenderer.events.on("scrollTrackingChanged",(function(t){e.dispatch((0,i.changeScrollTracking)(t))})),setInterval((function(){return p(e)}),c.MESSAGE_SAVE_INTERVAL),function(o){return function(c){var s=c.type,u=c.payload;if(t||(t=!0,h(e)),"chat/message"!==s){if(s===i.loadChat.type){o(c);var g=(0,l.selectCurrentChatPage)(e.getState());return d.chatRenderer.changePage(g),d.chatRenderer.onStateLoaded(),void(n=!0)}if(s!==i.changeChatPage.type&&s!==i.addChatPage.type&&s!==i.removeChatPage.type&&s!==i.toggleAcceptedType.type){if(s===i.rebuildChat.type)return d.chatRenderer.rebuildChat(),o(c);if(s!==r.updateSettings.type&&s!==r.loadSettings.type){if("roundrestart"===s)return p(e),o(c);if(s!==i.saveChatToDisk.type)return o(c);d.chatRenderer.saveToDisk()}else{o(c);var m=(0,a.selectSettings)(e.getState());d.chatRenderer.setHighlight(m.highlightText,m.highlightColor)}}else{o(c);var f=(0,l.selectCurrentChatPage)(e.getState());d.chatRenderer.changePage(f)}}else{var v=Array.isArray(u)?u:[u];d.chatRenderer.processBatch(v)}}}}},678:function(e,t,n){"use strict";t.__esModule=!0,t.chatReducer=t.initialState=void 0;var o,r=n(79),a=n(106);function i(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return c(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return c(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0&&(C[M.id]=Object.assign({},M,{unreadCount:M.unreadCount+A}))}return Object.assign({},e,{pageById:C})}if(o===r.addChatPage.type)return Object.assign({},e,{currentPageId:c.id,pages:[].concat(e.pages,[c.id]),pageById:Object.assign({},e.pageById,(n={},n[c.id]=c,n))});if(o===r.changeChatPage.type){var w,P=c.pageId,x=Object.assign({},e.pageById[P],{unreadCount:0});return Object.assign({},e,{currentPageId:P,pageById:Object.assign({},e.pageById,(w={},w[P]=x,w))})}if(o===r.updateChatPage.type){var k,R=c.pageId,O=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o=0||(r[n]=e[n]);return r}(c,["pageId"]),V=Object.assign({},e.pageById[R],O);return Object.assign({},e,{pageById:Object.assign({},e.pageById,(k={},k[R]=V,k))})}if(o===r.toggleAcceptedType.type){var G,B=c.pageId,L=c.type,D=Object.assign({},e.pageById[B]);return D.acceptedTypes=Object.assign({},D.acceptedTypes),D.acceptedTypes[L]=!D.acceptedTypes[L],Object.assign({},e,{pageById:Object.assign({},e.pageById,(G={},G[B]=D,G))})}if(o===r.removeChatPage.type){var j=c.pageId,F=Object.assign({},e,{pages:[].concat(e.pages),pageById:Object.assign({},e.pageById)});return delete F.pageById[j],F.pages=F.pages.filter((function(e){return e!==j})),0===F.pages.length&&(F.pages.push(s.id),F.pageById[s.id]=s,F.currentPageId=s.id),F.currentPageId&&F.currentPageId!==j||(F.currentPageId=F.pages[0]),F}return e}},679:function(e,t,n){"use strict";t.__esModule=!0,t.audioReducer=void 0;var o={visible:!1,playing:!1,track:null};t.audioReducer=function(e,t){void 0===e&&(e=o);var n=t.type,r=t.payload;return"audio/playing"===n?Object.assign({},e,{visible:!0,playing:!0}):"audio/stopped"===n?Object.assign({},e,{visible:!1,playing:!1}):"audio/playMusic"===n?Object.assign({},e,{meta:r}):"audio/stopMusic"===n?Object.assign({},e,{visible:!1,playing:!1,meta:null}):"audio/toggle"===n?Object.assign({},e,{visible:!e.visible}):e}},680:function(e,t,n){"use strict";t.__esModule=!0,t.useGame=void 0;var o=n(22),r=n(217);t.useGame=function(e){return(0,o.useSelector)(e,r.selectGame)}},681:function(e,t,n){"use strict";t.__esModule=!0,t.gameMiddleware=void 0;var o=n(147),r=n(218),a=n(217),i=n(682),c=function(e){return Object.assign({},e,{meta:Object.assign({},e.meta,{now:Date.now()})})};t.gameMiddleware=function(e){var t;return setInterval((function(){var n=e.getState();if(n){var o=(0,a.selectGame)(n),s=t&&Date.now()>=t+i.CONNECTION_LOST_AFTER;!o.connectionLostAt&&s&&e.dispatch(c((0,r.connectionLost)())),o.connectionLostAt&&!s&&e.dispatch(c((0,r.connectionRestored)()))}}),1e3),function(e){return function(n){var a=n.type,i=(n.payload,n.meta);return a===o.pingSuccess.type?(t=i.now,e(n)):a===r.roundRestarted.type?e(c(n)):e(n)}}}},682:function(e,t,n){"use strict";t.__esModule=!0,t.CONNECTION_LOST_AFTER=void 0;t.CONNECTION_LOST_AFTER=15e3},683:function(e,t,n){"use strict";t.__esModule=!0,t.gameReducer=void 0;var o=n(218),r={roundId:null,roundTime:null,roundRestartedAt:null,connectionLostAt:null};t.gameReducer=function(e,t){void 0===e&&(e=r);var n=t.type,a=(t.payload,t.meta);return"roundrestart"===n?Object.assign({},e,{roundRestartedAt:a.now}):n===o.connectionLost.type?Object.assign({},e,{connectionLostAt:a.now}):n===o.connectionRestored.type?Object.assign({},e,{connectionLostAt:null}):e}},684:function(e,t,n){"use strict";(function(e){t.__esModule=!0,t.setupPanelFocusHacks=void 0;var o=n(103),r=n(57),a=n(189),i=function(){return e((function(){return(0,a.focusMap)()}))};t.setupPanelFocusHacks=function(){var e=!1,t=null;window.addEventListener("focusin",(function(t){e=(0,r.canStealFocus)(t.target)})),window.addEventListener("mousedown",(function(e){t=[e.screenX,e.screenY]})),window.addEventListener("mouseup",(function(n){if(t){var r=[n.screenX,n.screenY];(0,o.vecLength)((0,o.vecSubtract)(r,t))>=10&&(e=!0)}e||i()})),r.globalEvents.on("keydown",(function(e){e.isModifierKey()||i()}))}}).call(this,n(101).setImmediate)},685:function(e,t,n){"use strict";t.__esModule=!0,t.pingMiddleware=void 0;var o=n(2),r=n(147),a=n(220);t.pingMiddleware=function(e){var t=!1,n=0,i=[],c=function(){for(var t=0;ta.PING_TIMEOUT&&(i[t]=null,e.dispatch((0,r.pingFail)()))}var s={index:n,sentAt:Date.now()};i[n]=s,(0,o.sendMessage)({type:"ping",payload:{index:n}}),n=(n+1)%a.PING_QUEUE_SIZE};return function(e){return function(n){var o=n.type,s=n.payload;if(t||(t=!0,setInterval(c,a.PING_INTERVAL),c()),"pingReply"===o){var d=s.index,l=i[d];if(!l)return;return i[d]=null,e((0,r.pingSuccess)(l))}return e(n)}}}},686:function(e,t,n){"use strict";t.__esModule=!0,t.PingIndicator=void 0;var o=n(0),r=n(687),a=n(11),i=n(22),c=n(1),s=n(688);t.PingIndicator=function(e,t){var n=(0,i.useSelector)(t,s.selectPing),d=r.Color.lookup(n.networkQuality,[new r.Color(220,40,40),new r.Color(220,200,40),new r.Color(60,220,40)]),l=n.roundtrip?(0,a.toFixed)(n.roundtrip):"--";return(0,o.createVNode)(1,"div","Ping",[(0,o.createComponentVNode)(2,c.Box,{className:"Ping__indicator",backgroundColor:d}),l],0)}},687:function(e,t,n){"use strict";t.__esModule=!0,t.Color=void 0;var o=function(){function e(e,t,n,o){void 0===e&&(e=0),void 0===t&&(t=0),void 0===n&&(n=0),void 0===o&&(o=1),this.r=e,this.g=t,this.b=n,this.a=o}return e.prototype.toString=function(){return"rgba("+(0|this.r)+", "+(0|this.g)+", "+(0|this.b)+", "+(0|this.a)+")"},e}();t.Color=o,o.fromHex=function(e){return new o(parseInt(e.substr(1,2),16),parseInt(e.substr(3,2),16),parseInt(e.substr(5,2),16))},o.lerp=function(e,t,n){return new o((t.r-e.r)*n+e.r,(t.g-e.g)*n+e.g,(t.b-e.b)*n+e.b,(t.a-e.a)*n+e.a)},o.lookup=function(e,t){void 0===t&&(t=[]);var n=t.length;if(n<2)throw new Error("Needs at least two colors!");var r=e*(n-1);if(e<1e-4)return t[0];if(e>=.9999)return t[n-1];var a=r%1,i=0|r;return o.lerp(t[i],t[i+1],a)}},688:function(e,t,n){"use strict";t.__esModule=!0,t.selectPing=void 0;t.selectPing=function(e){return e.ping}},689:function(e,t,n){"use strict";t.__esModule=!0,t.pingReducer=void 0;var o=n(11),r=n(147),a=n(220);t.pingReducer=function(e,t){void 0===e&&(e={});var n=t.type,i=t.payload;if(n===r.pingSuccess.type){var c=i.roundtrip,s=e.roundtripAvg||c,d=Math.round(.4*s+.6*c);return{roundtrip:c,roundtripAvg:d,failCount:0,networkQuality:1-(0,o.scale)(d,a.PING_ROUNDTRIP_BEST,a.PING_ROUNDTRIP_WORST)}}if(n===r.pingFail.type){var l=e.failCount,u=void 0===l?0:l,g=(0,o.clamp01)(e.networkQuality-u/a.PING_MAX_FAILS),p=Object.assign({},e,{failCount:u+1,networkQuality:g});return u>a.PING_MAX_FAILS&&(p.roundtrip=undefined,p.roundtripAvg=undefined),p}return e}},690:function(e,t,n){"use strict";t.__esModule=!0,t.telemetryMiddleware=void 0;var o=n(2),r=n(102);function a(e,t,n,o,r,a,i){try{var c=e[a](i),s=c.value}catch(d){return void n(d)}c.done?t(s):Promise.resolve(s).then(o,r)}var i=(0,n(25).createLogger)("telemetry");t.telemetryMiddleware=function(e){var t,n;return function(c){return function(s){var d,l=s.type,u=s.payload;if("telemetry/request"!==l)return"backend/update"===l?(c(s),void(d=regeneratorRuntime.mark((function h(){var o,a,c,s;return regeneratorRuntime.wrap((function(d){for(;;)switch(d.prev=d.next){case 0:if(a=null==u||null==(o=u.config)?void 0:o.client){d.next=4;break}return i.error("backend/update payload is missing client data!"),d.abrupt("return");case 4:if(t){d.next=13;break}return d.next=7,r.storage.get("telemetry");case 7:if(d.t0=d.sent,d.t0){d.next=10;break}d.t0={};case 10:(t=d.t0).connections||(t.connections=[]),i.debug("retrieved telemetry from storage",t);case 13:c=!1,t.connections.find((function(e){return n=a,(t=e).ckey===n.ckey&&t.address===n.address&&t.computer_id===n.computer_id;var t,n}))||(c=!0,t.connections.unshift(a),t.connections.length>10&&t.connections.pop()),c&&(i.debug("saving telemetry to storage",t),r.storage.set("telemetry",t)),n&&(s=n,n=null,e.dispatch({type:"telemetry/request",payload:s}));case 18:case"end":return d.stop()}}),h)})),function(){var e=this,t=arguments;return new Promise((function(n,o){var r=d.apply(e,t);function i(e){a(r,n,o,i,c,"next",e)}function c(e){a(r,n,o,i,c,"throw",e)}i(undefined)}))})()):c(s);if(!t)return i.debug("deferred"),void(n=u);i.debug("sending");var g=(null==u?void 0:u.limits)||{},p=t.connections.slice(0,g.connections);(0,o.sendMessage)({type:"telemetry",payload:{connections:p}})}}}},691:function(e,t,n){"use strict";t.__esModule=!0,t.Panel=void 0;var o=n(0),r=n(1),a=n(3),i=n(211),c=n(145),s=n(216),d=n(692),l=n(219),u=n(144);t.Panel=function(e,t){if(Byond.IS_LTE_IE10)return(0,o.createComponentVNode)(2,g);var n=(0,i.useAudio)(t),p=(0,u.useSettings)(t),h=(0,s.useGame)(t);return(0,o.createComponentVNode)(2,a.Pane,{theme:p.theme,children:(0,o.createComponentVNode)(2,r.Flex,{direction:"column",height:"100%",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{children:(0,o.createComponentVNode)(2,r.Section,{fitted:!0,children:(0,o.createComponentVNode)(2,r.Flex,{mx:.5,align:"center",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,grow:1,overflowX:"auto",children:(0,o.createComponentVNode)(2,c.ChatTabs)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,l.PingIndicator)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,r.Button,{color:"grey",selected:n.visible,icon:"music",tooltip:"Music player",tooltipPosition:"bottom-left",onClick:function(){return n.toggle()}})}),(0,o.createComponentVNode)(2,r.Flex.Item,{mx:.5,children:(0,o.createComponentVNode)(2,r.Button,{icon:p.visible?"times":"cog",selected:p.visible,tooltip:p.visible?"Close settings":"Open settings",tooltipPosition:"bottom-left",onClick:function(){return p.toggle()}})})]})})}),n.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,r.Section,{children:(0,o.createComponentVNode)(2,i.NowPlayingWidget)})}),p.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,u.SettingsPanel)}),(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,grow:1,children:(0,o.createComponentVNode)(2,r.Section,{fill:!0,fitted:!0,position:"relative",children:[(0,o.createComponentVNode)(2,a.Pane.Content,{scrollable:!0,children:(0,o.createComponentVNode)(2,c.ChatPanel,{lineHeight:p.lineHeight})}),(0,o.createComponentVNode)(2,d.Notifications,{children:[h.connectionLostAt&&(0,o.createComponentVNode)(2,d.Notifications.Item,{rightSlot:(0,o.createComponentVNode)(2,r.Button,{color:"white",onClick:function(){return Byond.command(".reconnect")},children:"Reconnect"}),children:"You are either AFK, experiencing lag or the connection has closed."}),h.roundRestartedAt&&(0,o.createComponentVNode)(2,d.Notifications.Item,{children:"The connection has been closed because the server is restarting. Please wait while you automatically reconnect."})]})]})})]})})};var g=function(e,t){var n=(0,u.useSettings)(t);return(0,o.createComponentVNode)(2,a.Pane,{theme:n.theme,children:(0,o.createComponentVNode)(2,a.Pane.Content,{scrollable:!0,children:[(0,o.createComponentVNode)(2,r.Button,{style:{position:"fixed",top:"1em",right:"2em","z-index":1e3},selected:n.visible,onClick:function(){return n.toggle()},children:"Settings"}),n.visible&&(0,o.createComponentVNode)(2,r.Flex.Item,{mt:1,children:(0,o.createComponentVNode)(2,u.SettingsPanel)})||(0,o.createComponentVNode)(2,c.ChatPanel,{lineHeight:n.lineHeight})]})})}},692:function(e,t,n){"use strict";t.__esModule=!0,t.Notifications=void 0;var o=n(0),r=n(1),a=function(e){var t=e.children;return(0,o.createVNode)(1,"div","Notifications",t,0)};t.Notifications=a;a.Item=function(e){var t=e.rightSlot,n=e.children;return(0,o.createComponentVNode)(2,r.Flex,{align:"center",className:"Notification",children:[(0,o.createComponentVNode)(2,r.Flex.Item,{className:"Notification__content",grow:1,children:n}),t&&(0,o.createComponentVNode)(2,r.Flex.Item,{className:"Notification__rightSlot",children:t})]})}},79:function(e,t,n){"use strict";t.__esModule=!0,t.saveChatToDisk=t.changeScrollTracking=t.removeChatPage=t.toggleAcceptedType=t.updateChatPage=t.changeChatPage=t.addChatPage=t.updateMessageCount=t.rebuildChat=t.loadChat=void 0;var o=n(22),r=n(106),a=(0,o.createAction)("chat/load");t.loadChat=a;var i=(0,o.createAction)("chat/rebuild");t.rebuildChat=i;var c=(0,o.createAction)("chat/updateMessageCount");t.updateMessageCount=c;var s=(0,o.createAction)("chat/addPage",(function(){return{payload:(0,r.createPage)()}}));t.addChatPage=s;var d=(0,o.createAction)("chat/changePage");t.changeChatPage=d;var l=(0,o.createAction)("chat/updatePage");t.updateChatPage=l;var u=(0,o.createAction)("chat/toggleAcceptedType");t.toggleAcceptedType=u;var g=(0,o.createAction)("chat/removePage");t.removeChatPage=g;var p=(0,o.createAction)("chat/changeScrollTracking");t.changeScrollTracking=p;var h=(0,o.createAction)("chat/saveToDisk");t.saveChatToDisk=h}});
\ No newline at end of file