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