mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 01:34:01 +00:00
Adds map feedback thread support (AI stat panel buff) (#90506)
## About The Pull Request This PR started with the idea of adding support for map feedback threads, which I added to the roundend report, escape menu, and stat panel. To do this though I had to make pretty annoying changes to the stat panel and had to touch every single time something to the stat panel was added, so since we now have a way to have links in the stat panel I thought of taking full advantage of it and add some QOL. AIs can now track their borgs by clicking their status on the stat panel https://github.com/user-attachments/assets/1789dc46-5d12-48e9-bb8d-d3278aa19639 With Melbert's comment, I added another stat panel entry that directs you to the Webmap page, which currently seems to be a little messed up (https://github.com/AffectedArc07/SS13WebMap/issues/41 & https://github.com/AffectedArc07/SS13WebMap/issues/42) but if they get fixed this would be a swag asf feature ##### Code bounty for Ezel/Improvedname ## Why It's Good For The Game Feedback threads was a suggestion from a player and is fully in control of admins as an optional thing, and while we still have stat panel I think it's nice to be able to take advantage of its features. ## Changelog 🆑 admin: Admins can now link a URL for maps, used to give feedback on said maps. Accessible through the roundend report, escape menu, and stat panel. qol: AIs can track their borgs by clicking on them in the stat panel. qol: You can now directly go to the webmap of maps from the stat panel (assuming it's set in config). /🆑
This commit is contained in:
@@ -341,6 +341,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
|
||||
var/statspage = CONFIG_GET(string/roundstatsurl)
|
||||
var/info = statspage ? "<a href='byond://?action=openLink&link=[url_encode(statspage)][GLOB.round_id]'>[GLOB.round_id]</a>" : GLOB.round_id
|
||||
parts += "[FOURSPACES]Round ID: <b>[info]</b>"
|
||||
parts += "[FOURSPACES]Map: [SSmapping.current_map?.return_map_name()]"
|
||||
parts += "[FOURSPACES]Shift Duration: <B>[DisplayTimeText(world.time - SSticker.round_start_time)]</B>"
|
||||
parts += "[FOURSPACES]Station Integrity: <B>[GLOB.station_was_nuked ? span_redtext("Destroyed") : "[popcount["station_integrity"]]%"]</B>"
|
||||
var/total_players = GLOB.joined_player_list.len
|
||||
|
||||
@@ -432,6 +432,9 @@ Example config:
|
||||
currentmap = null
|
||||
if ("disabled")
|
||||
currentmap = null
|
||||
if("feedbacklink")
|
||||
if(currentmap.map_name == SSmapping.current_map.map_name)
|
||||
SSmapping.current_map.feedback_link = data
|
||||
else
|
||||
log_config("Unknown command in map vote config: '[command]'")
|
||||
|
||||
|
||||
@@ -785,3 +785,7 @@
|
||||
|
||||
// If set, enables the "Link forum account" OOC verb
|
||||
/datum/config_entry/string/forum_link_uri
|
||||
|
||||
/datum/config_entry/string/webmap_url
|
||||
//ex: "https://webmap.affectedarc07.co.uk/maps/tgstation/"
|
||||
default = ""
|
||||
|
||||
@@ -106,6 +106,9 @@ SUBSYSTEM_DEF(mapping)
|
||||
if(!current_map || current_map.defaulted)
|
||||
to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to [old_config.map_name]."))
|
||||
current_map = old_config
|
||||
var/mapping_url = config.Get(/datum/config_entry/string/webmap_url)
|
||||
if(mapping_url != "")
|
||||
current_map.mapping_url = mapping_url
|
||||
plane_offset_to_true = list()
|
||||
true_to_offset_planes = list()
|
||||
plane_to_offset = list()
|
||||
@@ -950,3 +953,14 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away
|
||||
var/number_of_remaining_levels = length(checkable_levels)
|
||||
if(number_of_remaining_levels > 0)
|
||||
CRASH("The following [number_of_remaining_levels] away mission(s) were not loaded: [checkable_levels.Join("\n")]")
|
||||
|
||||
///Returns the map name, with an openlink action tied to it (if one exists) for the map.
|
||||
/datum/map_config/proc/return_map_name(webmap_included)
|
||||
var/text
|
||||
if(feedback_link)
|
||||
text = "<a href='byond://?action=openLink&link=[url_encode(feedback_link)]'>[map_name]</a>"
|
||||
else
|
||||
text = map_name
|
||||
if(webmap_included && !isnull(SSmapping.current_map.mapping_url))
|
||||
text += " | <a href='byond://?action=openWebMap'>(Show Map)</a>"
|
||||
return text
|
||||
|
||||
@@ -21,14 +21,26 @@ SUBSYSTEM_DEF(statpanels)
|
||||
if (!resumed)
|
||||
num_fires++
|
||||
var/datum/map_config/cached = SSmap_vote.next_map_config
|
||||
global_data = list(
|
||||
"Map: [SSmapping.current_map?.map_name || "Loading..."]",
|
||||
cached ? "Next Map: [cached.map_name]" : null,
|
||||
|
||||
if(isnull(SSmapping.current_map))
|
||||
global_data = list("Loading")
|
||||
else if(SSmapping.current_map.feedback_link)
|
||||
global_data = list(list("Map: [SSmapping.current_map.map_name]", " (Feedback)", "action=openLink&link=[SSmapping.current_map.feedback_link]"))
|
||||
else
|
||||
global_data = list("Map: [SSmapping.current_map?.map_name]")
|
||||
|
||||
if(SSmapping.current_map?.mapping_url)
|
||||
global_data += list(list("same_line", " | (View in Browser)", "action=openWebMap"))
|
||||
|
||||
if(cached)
|
||||
global_data += "Next Map: [cached.map_name]"
|
||||
|
||||
global_data += list(
|
||||
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
|
||||
"Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss", world.timezone)]",
|
||||
"Round Time: [ROUND_TIME()]",
|
||||
"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)]%)"
|
||||
"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)
|
||||
@@ -98,6 +110,15 @@ SUBSYSTEM_DEF(statpanels)
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/*
|
||||
* send_message for the stat panel can be sent 1 of 4 things:
|
||||
* 1- A string entry, to show up as plain text.
|
||||
* 2- An empty string (""), which will translate to a new line, to for a break between lines.
|
||||
* 3- a list, in which the first entry is plain text, the second entry is highlighted text, and the third entry is a link
|
||||
* that clicking the second entry will take you to.
|
||||
* 4- a list with "same_line" as the first entry, which will automatically put it on the line above it,
|
||||
* with the second/third entry matching #3 (text & url), allowing you to have 2 clickable links on one line.
|
||||
*/
|
||||
/datum/controller/subsystem/statpanels/proc/set_status_tab(client/target)
|
||||
if(!global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data()
|
||||
return
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
var/voteweight = 1
|
||||
var/votable = FALSE
|
||||
|
||||
///A URL linking to a place for people to send feedback about this map.
|
||||
var/feedback_link
|
||||
|
||||
/// The URL given by config directing you to the webmap.
|
||||
var/mapping_url
|
||||
|
||||
// Config actually from the JSON - should default to Meta
|
||||
var/map_name = "MetaStation"
|
||||
var/map_path = "map_files/MetaStation"
|
||||
@@ -34,7 +40,8 @@
|
||||
"cargo" = "cargo_box",
|
||||
"ferry" = "ferry_fancy",
|
||||
"whiteship" = "whiteship_meta",
|
||||
"emergency" = "emergency_meta")
|
||||
"emergency" = "emergency_meta",
|
||||
)
|
||||
|
||||
/// Dictionary of job sub-typepath to template changes dictionary
|
||||
var/job_changes = list()
|
||||
@@ -66,7 +73,7 @@
|
||||
* Returns the config for the map to load.
|
||||
*/
|
||||
/proc/load_map_config(filename = null, directory = null, error_if_missing = TRUE)
|
||||
var/datum/map_config/config = load_default_map_config()
|
||||
var/datum/map_config/configuring_map = load_default_map_config()
|
||||
|
||||
if(filename) // If none is specified, then go to look for next_map.json, for map rotation purposes.
|
||||
|
||||
@@ -74,7 +81,7 @@
|
||||
if(directory)
|
||||
if(!(directory in MAP_DIRECTORY_WHITELIST))
|
||||
log_world("map directory not in whitelist: [directory] for map [filename]")
|
||||
return config
|
||||
return configuring_map
|
||||
else
|
||||
directory = MAP_DIRECTORY_MAPS
|
||||
|
||||
@@ -83,10 +90,10 @@
|
||||
filename = PATH_TO_NEXT_MAP_JSON
|
||||
|
||||
|
||||
if (!config.LoadConfig(filename, error_if_missing))
|
||||
qdel(config)
|
||||
if (!configuring_map.LoadConfig(filename, error_if_missing))
|
||||
qdel(configuring_map)
|
||||
return load_default_map_config()
|
||||
return config
|
||||
return configuring_map
|
||||
|
||||
|
||||
#define CHECK_EXISTS(X) if(!istext(json[X])) { log_world("[##X] missing from json!"); return; }
|
||||
|
||||
@@ -135,6 +135,13 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
switch(href_list["action"])
|
||||
if("openLink")
|
||||
src << link(href_list["link"])
|
||||
if("openWebMap")
|
||||
if(!SSmapping.current_map.mapping_url)
|
||||
return
|
||||
if(is_station_level(mob.z))
|
||||
src << link("[SSmapping.current_map.mapping_url][LOWER_TEXT(sanitize_css_class_name(SSmapping.current_map.map_name))]/?x=[mob.x]&y=[mob.y]&zoom=6")
|
||||
else
|
||||
src << link("[SSmapping.current_map.mapping_url][LOWER_TEXT(sanitize_css_class_name(SSmapping.current_map.map_name))]")
|
||||
if (hsrc)
|
||||
var/datum/real_src = hsrc
|
||||
if(QDELETED(real_src))
|
||||
|
||||
@@ -35,7 +35,7 @@ GLOBAL_DATUM(escape_menu_details, /atom/movable/screen/escape_menu/details)
|
||||
<span style='text-align: right; line-height: 0.7'>
|
||||
Round ID: [GLOB.round_id || "Unset"]<br />
|
||||
Round Time: [ROUND_TIME()]<br />
|
||||
Map: [SSmapping.current_map.map_name || "Loading..."]<br />
|
||||
Map: [SSmapping.current_map.return_map_name(webmap_included = TRUE) || "Loading..."]<br />
|
||||
Time Dilation: [round(SStime_track.time_dilation_current,1)]%<br />
|
||||
</span>
|
||||
"}
|
||||
|
||||
@@ -234,8 +234,13 @@
|
||||
else if(!connected_robot.cell || connected_robot.cell.charge <= 0)
|
||||
robot_status = "DEPOWERED"
|
||||
//Name, Health, Battery, Model, Area, and Status! Everything an AI wants to know about its borgies!
|
||||
. += "[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[display_energy(connected_robot.cell.charge)]/[display_energy(connected_robot.cell.maxcharge)]" : "Empty"] | \
|
||||
Model: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]"
|
||||
. += list(list("[connected_robot.name]: ",
|
||||
"S.Integrity: [connected_robot.health]% | \
|
||||
Cell: [connected_robot.cell ? "[display_energy(connected_robot.cell.charge)]/[display_energy(connected_robot.cell.maxcharge)]" : "Empty"] | \
|
||||
Model: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | \
|
||||
Status: [robot_status]",
|
||||
"src=[REF(src)];track_cyborg=[text_ref(connected_robot)]",
|
||||
))
|
||||
. += "AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]" //Count of total AI shells
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_call_shuttle()
|
||||
@@ -390,6 +395,12 @@
|
||||
if(usr != src)
|
||||
return
|
||||
|
||||
if(href_list["track_cyborg"])
|
||||
var/mob/living/silicon/robot/cyborg = locate(href_list["track_cyborg"]) in connected_robots
|
||||
if(!cyborg)
|
||||
return
|
||||
ai_tracking_tool.set_tracked_mob(cyborg)
|
||||
|
||||
if(href_list["emergencyAPC"]) //This check comes before incapacitated because the only time it would be useful is when we have no power.
|
||||
if(!apc_override)
|
||||
to_chat(src, span_notice("APC backdoor is no longer available."))
|
||||
|
||||
@@ -838,6 +838,7 @@
|
||||
/mob/proc/get_status_tab_items()
|
||||
. = list("") //we want to offset unique stuff from standard stuff
|
||||
SEND_SIGNAL(src, COMSIG_MOB_GET_STATUS_TAB_ITEMS, .)
|
||||
return .
|
||||
|
||||
/**
|
||||
* Convert a list of spells into a displyable list for the statpanel
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
. += "Its master ID string seems to be [(!master_name || emagged) ? "empty" : master_name]."
|
||||
|
||||
/mob/living/silicon/pai/get_status_tab_items()
|
||||
. += ..()
|
||||
. = ..()
|
||||
if(!stat)
|
||||
. += "Emitter Integrity: [holochassis_health * (100 / HOLOCHASSIS_MAX_HEALTH)]."
|
||||
else
|
||||
|
||||
@@ -11,6 +11,7 @@ Format:
|
||||
voteweight [number] (How much to count each player vote as, defaults to 1, setting to 0.5 counts each vote as half a vote, 2 as double, etc, Setting to 0 disables the map but allows players to still pick it)
|
||||
disabled (disables the map)
|
||||
votable (is this map votable)
|
||||
feedbackurl (link in-game shown to players to leave feedback for the map)
|
||||
endmap
|
||||
|
||||
# Production-level maps.
|
||||
@@ -35,6 +36,7 @@ map metastation
|
||||
minplayers 25
|
||||
#voteweight 0.5
|
||||
votable
|
||||
#feedbacklink https://www.youtube.com/watch?v=XG8b7WhANNA
|
||||
endmap
|
||||
|
||||
map tramstation
|
||||
|
||||
@@ -15,9 +15,13 @@ if (!String.prototype.trim) {
|
||||
}
|
||||
|
||||
// Status panel implementation ------------------------------------------------
|
||||
var status_tab_parts = ["Loading..."];
|
||||
//status_tab_parts expects a list to be returned, to which we'll send a list within a list
|
||||
//with just "loading" to not appear broken.
|
||||
var status_tab_parts = [["Loading..."]];
|
||||
var current_tab = null;
|
||||
var mc_tab_parts = [["Loading...", ""]];
|
||||
//mc_tab_parts expects a list to be returned, to which we'll send a list within a list
|
||||
//with just "loading" to not appear broken.
|
||||
var mc_tab_parts = [["Loading..."]];
|
||||
var href_token = null;
|
||||
var spells = [];
|
||||
var spell_tabs = [];
|
||||
@@ -346,16 +350,42 @@ function draw_status() {
|
||||
current_tab = "Status";
|
||||
}
|
||||
statcontentdiv.textContent = '';
|
||||
var table = document.createElement("table");
|
||||
for (var i = 0; i < status_tab_parts.length; i++) {
|
||||
if (status_tab_parts[i].trim() == "") {
|
||||
document.getElementById("statcontent").appendChild(document.createElement("br"));
|
||||
} else {
|
||||
var part = status_tab_parts[i];
|
||||
if(!Array.isArray(part)) {
|
||||
var div = document.createElement("div");
|
||||
div.textContent = status_tab_parts[i];
|
||||
div.className = "status-info";
|
||||
document.getElementById("statcontent").appendChild(div);
|
||||
if (part.trim() == "") {
|
||||
table.appendChild(document.createElement("br"));
|
||||
} else {
|
||||
div.textContent = part;
|
||||
table.appendChild(div);
|
||||
}
|
||||
} else {
|
||||
var div
|
||||
if (part[0].trim() == "same_line") {
|
||||
var a = document.createElement("a");
|
||||
a.href = "byond://?" + part[2];
|
||||
a.textContent = part[1];
|
||||
div.appendChild(a);
|
||||
} else {
|
||||
div = document.createElement("div");
|
||||
if (part[0].trim() == "") {
|
||||
table.appendChild(document.createElement("br"));
|
||||
} else {
|
||||
div.textContent = part[0];
|
||||
if (part[2]) {
|
||||
var a = document.createElement("a");
|
||||
a.href = "byond://?" + part[2];
|
||||
a.textContent = part[1];
|
||||
div.appendChild(a);
|
||||
}
|
||||
table.appendChild(div);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById("statcontent").appendChild(table);
|
||||
if (verb_tabs.length == 0 || !verbs) {
|
||||
Byond.command("Fix-Stat-Panel");
|
||||
}
|
||||
@@ -876,13 +906,18 @@ Byond.subscribeTo('init_verbs', function (payload) {
|
||||
|
||||
Byond.subscribeTo('update_stat', function (payload) {
|
||||
status_tab_parts = [payload.ping_str];
|
||||
|
||||
var parsed = payload.global_data;
|
||||
|
||||
for (var i = 0; i < parsed.length; i++) if (parsed[i] != null) status_tab_parts.push(parsed[i]);
|
||||
for (var i = 0; i < parsed.length; i++)
|
||||
if (parsed[i] != null)
|
||||
status_tab_parts.push(parsed[i]);
|
||||
|
||||
parsed = payload.other_str;
|
||||
|
||||
for (var i = 0; i < parsed.length; i++) if (parsed[i] != null) status_tab_parts.push(parsed[i]);
|
||||
for (var i = 0; i < parsed.length; i++)
|
||||
if (parsed[i] != null)
|
||||
status_tab_parts.push(parsed[i]);
|
||||
|
||||
if (current_tab == "Status") {
|
||||
draw_status();
|
||||
|
||||
Reference in New Issue
Block a user