diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index f58bba4bd90..c0de1b973a6 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -353,6 +353,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list()) var/statspage = CONFIG_GET(string/roundstatsurl) var/info = statspage ? "[GLOB.round_id]" : GLOB.round_id parts += "[FOURSPACES]Round ID: [info]" + parts += "[FOURSPACES]Map: [SSmapping.current_map?.return_map_name()]" parts += "[FOURSPACES]Shift Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]" parts += "[FOURSPACES]Station Integrity: [GLOB.station_was_nuked ? span_redtext("Destroyed") : "[popcount["station_integrity"]]%"]" var/total_players = GLOB.joined_player_list.len diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 6a43e92ae88..90263bb6ecd 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -434,6 +434,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]'") diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 739877fe125..d244c539f6d 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -788,3 +788,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 = "" diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index f29b5853465..d9e947f7c2a 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -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() @@ -958,3 +961,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 = "[map_name]" + else + text = map_name + if(webmap_included && !isnull(SSmapping.current_map.mapping_url)) + text += " | (Show Map)" + return text diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index ffec9bb8703..2494afd2281 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -22,14 +22,25 @@ SUBSYSTEM_DEF(statpanels) num_fires++ var/datum/map_config/cached = SSmap_vote.next_map_config /* SKYRAT EDIT CHANGE - 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)]%)", ) */ var/round_time = world.time - SSticker.round_start_time @@ -123,6 +134,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 diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm index ce4685b3808..1cde722361a 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -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_skyrat", "ferry" = "ferry_fancy", "whiteship" = "whiteship_meta", - "emergency" = "emergency_skyrat") //SKYRAT EDIT CHANGE + "emergency" = "emergency_skyrat", //SKYRAT EDIT CHANGE + ) /// 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; } diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index e10e7bea96a..dd9dff55f0e 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -144,6 +144,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)) diff --git a/code/modules/escape_menu/details.dm b/code/modules/escape_menu/details.dm index 8f35781b8ab..00267ecba5d 100644 --- a/code/modules/escape_menu/details.dm +++ b/code/modules/escape_menu/details.dm @@ -35,7 +35,7 @@ GLOBAL_DATUM(escape_menu_details, /atom/movable/screen/escape_menu/details) Round ID: [GLOB.round_id || "Unset"]
Round Time: [ROUND_TIME()]
- Map: [SSmapping.current_map.map_name || "Loading..."]
+ Map: [SSmapping.current_map.return_map_name(webmap_included = TRUE) || "Loading..."]
Time Dilation: [round(SStime_track.time_dilation_current,1)]%
"} diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 3136f568572..7b95a643648 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -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.")) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dc5b53322ae..8da5076ec1a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -889,6 +889,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 diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm index 94b6f38067a..f77d327d114 100644 --- a/code/modules/pai/pai.dm +++ b/code/modules/pai/pai.dm @@ -174,7 +174,7 @@ //SKYRAT EDIT ADDITION END /mob/living/silicon/pai/get_status_tab_items() - . += ..() + . = ..() if(!stat) . += "Emitter Integrity: [holochassis_health * (100 / HOLOCHASSIS_MAX_HEALTH)]." else diff --git a/config/maps.txt b/config/maps.txt index b3da0791147..3b32d58cbc9 100644 --- a/config/maps.txt +++ b/config/maps.txt @@ -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. @@ -33,6 +34,7 @@ map metastation minplayers 25 #voteweight 0.5 votable + #feedbacklink https://www.youtube.com/watch?v=XG8b7WhANNA endmap map tramstation diff --git a/html/statbrowser.js b/html/statbrowser.js index d814f44d8b3..2e6a3b1b42e 100644 --- a/html/statbrowser.js +++ b/html/statbrowser.js @@ -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();