From 14b6339d5c7b83ad06814f6110dc10b04519c0f1 Mon Sep 17 00:00:00 2001 From: Arrow768 <1331699+Arrow768@users.noreply.github.com> Date: Tue, 26 May 2026 19:57:26 +0200 Subject: [PATCH] TGUI Migration (for everything remaining except the hardsuit) (#22404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` - refactor: "Updates the Computer Fabricator to TGUI" - refactor: "Updates the Instruments to TGUI" - refactor: "Updates the Geo Scanner to TGUI" - refactor: "Updates the Stacking Machine to TGUI" - refactor: "Updates the Robotics Console to TGUI" - refactor: "Updates the Requests Console to TGUI" - refactor: "Updates the Synthesizer/Instrument main panel to TGUI" - refactor: "Updates the Nuclear Bomb control panel to TGUI" - rscadd: "Requests Console messages are now typed (Assistance Request, Supply Request, Information, Reply); the type is shown in the recipient's log, audible alert, and PDA notification." - rscadd: "Reconstructs the Synthesizer's Virtual Environment Editor (was broken since added — its NanoUI template never existed). The Custom environment preset that depends on it is gated behind a new musical_config.can_use_custom flag (default off, matching prior behavior)." - bugfix: "Fixes the Autolathe UI re-opening after every print job." - bugfix: "Fixes the Song Editor's currently-playing line indicator not advancing during playback." - bugfix: "Fixes a class of bugs where TGUI windows for the Air Alarm, Portable Turret, Mining Vendor, Mineral Processor, Tank Dispenser, Helm Console, all Chemistry machines, and Suit Cycler would force themselves back open after every action, even when the player had closed them." - bugfix: "Fixes the spatial sound system silently dropping every sound that used the 'None' (-1) environment id or a 23-element custom environment list. The validator was rejecting both as invalid, so synthesizer notes played with Custom or None mode produced no audible output at all." - admin: "Adds two requests consoles and two fax machines to the bridge on the runtime test map." ``` Claude Opus 4.7 has been used during the creation of this PR --------- Signed-off-by: Arrow768 <1331699+Arrow768@users.noreply.github.com> Co-authored-by: Werner Co-authored-by: Claude Opus 4.7 Co-authored-by: Batrachophreno --- aurorastation.dme | 1 + code/datums/sound_player.dm | 10 +- code/game/machinery/alarm.dm | 2 +- code/game/machinery/computer/robot.dm | 371 ++++++------ code/game/machinery/nuclear_bomb.dm | 201 +++--- code/game/machinery/portable_turret.dm | 2 +- code/game/machinery/requests_console.dm | 556 +++++++++-------- code/game/machinery/suit_cycler.dm | 18 +- .../telecomms/machines/message_server.dm | 83 ++- .../game/objects/structures/tank_dispenser.dm | 4 +- code/modules/fabrication/fabricator_build.dm | 2 +- code/modules/mining/machine_processing.dm | 2 +- code/modules/mining/machine_stacking.dm | 80 +-- code/modules/mining/machine_vending.dm | 2 +- .../modular_computers/laptop_vendor.dm | 252 ++++---- code/modules/overmap/ships/computers/helm.dm | 11 +- code/modules/reagents/Chemistry-Machinery.dm | 14 +- .../machinery/geosample_scanner.dm | 326 +++++----- .../synthesized_instruments/echo_editor.dm | 85 ++- .../synthesized_instruments/env_editor.dm | 97 +-- .../synthesized_instruments/globals.dm | 3 + .../synthesized_instruments/instrument_ui.dm | 15 + .../real_instruments.dm | 371 ++++++------ code/modules/synthesized_instruments/song.dm | 2 +- .../synthesized_instruments/song_editor.dm | 116 ++-- .../synthesized_instruments/usage_info.dm | 35 +- html/changelogs/arrow768-tgui-batch-2.yml | 72 +++ maps/runtime/runtime.dmm | 151 +++-- nano/templates/computer_fabricator.tmpl | 69 --- nano/templates/echo_editor.tmpl | 21 - nano/templates/geoscanner.tmpl | 185 ------ nano/templates/nuclear_bomb.tmpl | 68 --- nano/templates/requests_console.tmpl | 169 ------ nano/templates/robot_control.tmpl | 108 ---- nano/templates/song_editor.tmpl | 43 -- nano/templates/song_usage_info.tmpl | 16 - nano/templates/stacking_machine.tmpl | 18 - nano/templates/synthesizer.tmpl | 168 ----- .../tgui/interfaces/ComputerFabricator.tsx | 297 +++++++++ tgui/packages/tgui/interfaces/EchoEditor.tsx | 70 +++ tgui/packages/tgui/interfaces/EnvEditor.tsx | 77 +++ tgui/packages/tgui/interfaces/GeoScanner.tsx | 251 ++++++++ tgui/packages/tgui/interfaces/NuclearBomb.tsx | 157 +++++ .../tgui/interfaces/RequestsConsole.tsx | 572 ++++++++++++++++++ .../tgui/interfaces/RoboticsControl.tsx | 151 +++++ tgui/packages/tgui/interfaces/SongEditor.tsx | 124 ++++ .../tgui/interfaces/SongUsageInfo.tsx | 49 ++ .../tgui/interfaces/StackingMachine.tsx | 59 ++ tgui/packages/tgui/interfaces/Synthesizer.tsx | 314 ++++++++++ 49 files changed, 3685 insertions(+), 2185 deletions(-) create mode 100644 code/modules/synthesized_instruments/instrument_ui.dm create mode 100644 html/changelogs/arrow768-tgui-batch-2.yml delete mode 100644 nano/templates/computer_fabricator.tmpl delete mode 100644 nano/templates/echo_editor.tmpl delete mode 100644 nano/templates/geoscanner.tmpl delete mode 100644 nano/templates/nuclear_bomb.tmpl delete mode 100644 nano/templates/requests_console.tmpl delete mode 100644 nano/templates/robot_control.tmpl delete mode 100644 nano/templates/song_editor.tmpl delete mode 100644 nano/templates/song_usage_info.tmpl delete mode 100644 nano/templates/stacking_machine.tmpl delete mode 100644 nano/templates/synthesizer.tmpl create mode 100644 tgui/packages/tgui/interfaces/ComputerFabricator.tsx create mode 100644 tgui/packages/tgui/interfaces/EchoEditor.tsx create mode 100644 tgui/packages/tgui/interfaces/EnvEditor.tsx create mode 100644 tgui/packages/tgui/interfaces/GeoScanner.tsx create mode 100644 tgui/packages/tgui/interfaces/NuclearBomb.tsx create mode 100644 tgui/packages/tgui/interfaces/RequestsConsole.tsx create mode 100644 tgui/packages/tgui/interfaces/RoboticsControl.tsx create mode 100644 tgui/packages/tgui/interfaces/SongEditor.tsx create mode 100644 tgui/packages/tgui/interfaces/SongUsageInfo.tsx create mode 100644 tgui/packages/tgui/interfaces/StackingMachine.tsx create mode 100644 tgui/packages/tgui/interfaces/Synthesizer.tsx diff --git a/aurorastation.dme b/aurorastation.dme index 12456425071..7fb9137bae1 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -3890,6 +3890,7 @@ #include "code\modules\synthesized_instruments\env_editor.dm" #include "code\modules\synthesized_instruments\event_manager.dm" #include "code\modules\synthesized_instruments\globals.dm" +#include "code\modules\synthesized_instruments\instrument_ui.dm" #include "code\modules\synthesized_instruments\instruments.dm" #include "code\modules\synthesized_instruments\real_instruments.dm" #include "code\modules\synthesized_instruments\song.dm" diff --git a/code/datums/sound_player.dm b/code/datums/sound_player.dm index d3952f54dce..32dd34d7f4d 100644 --- a/code/datums/sound_player.dm +++ b/code/datums/sound_player.dm @@ -280,11 +280,11 @@ GLOBAL_DATUM_INIT(sound_player, /singleton/sound_player, new) return A && PrivIsValidEnvironment(A.sound_environment) ? A.sound_environment : sound.environment /datum/sound_token/proc/PrivIsValidEnvironment(environment) - if(islist(environment) && length(environment) != 23) - return FALSE - if(!isnum(environment) || environment < 0 || environment > 25) - return FALSE - return TRUE + if(islist(environment)) + return length(environment) == 23 + if(isnum(environment)) + return environment >= -1 && environment <= 25 + return FALSE /datum/sound_token/static_environment/PrivGetEnvironment() return sound.environment diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 39d91e40472..de00ec52c98 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1050,7 +1050,7 @@ pixel_x = 10; else playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE) balloon_alert(user, locked ? "locked" : "unlocked") - updateUsrDialog() + SStgui.update_uis(src) else to_chat(user, SPAN_NOTICE("Access denied.")) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 18a833f5a5c..abdfd2f4cae 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -11,243 +11,218 @@ req_one_access = list(ACCESS_RD, ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/robotics - var/safety = 1 + var/safety = TRUE -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) +/obj/machinery/computer/robotics/attack_ai(mob/user) if(!ai_can_interact(user)) return ui_interact(user) -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) +/obj/machinery/computer/robotics/attack_hand(mob/user) ui_interact(user) -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["robots"] = get_cyborgs(user) - data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) +/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) - ui.set_initial_data(data) + ui = new(user, src, "RoboticsControl", "Robotic Control Console") ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/robotics/Topic(href, href_list) - if(..()) +/obj/machinery/computer/robotics/ui_data(mob/user) + return list( + "robots" = get_cyborgs(user), + "safety" = safety, + "is_ai" = issilicon(user) + ) + +/obj/machinery/computer/robotics/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) return var/mob/user = usr if(!src.allowed(user)) to_chat(user, "Access denied.") return - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if(isrobot(user) && user != target) - to_chat(user, "Access denied.") - return - var/choice = tgui_alert(usr, "Really detonate [target.name]?", "Robotics Control", list("Yes", "No")) - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - return - - if(target.emagged) - to_chat(user, "Access denied. Safety protocols are disabled.") - return - - else - message_admins("[key_name_admin(usr)] detonated [target.name]!") - log_game("[key_name(usr)] detonated [target.name]!") + switch(action) + // Destroys the cyborg + if("detonate") + var/mob/living/silicon/robot/target = get_cyborg_by_name(params["name"]) + if(!target || !istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access denied. This robot is not linked to you.") + return + // Cyborgs may blow up themselves via the console + if(isrobot(user) && user != target) + to_chat(user, "Access denied.") + return + var/choice = tgui_alert(user, "Really detonate [target.name]?", "Robotics Control", list("Yes", "No")) + if(choice != "Yes") + return TRUE + if(!target || !istype(target)) + return TRUE + // Antagonistic cyborgs? Left here for downstream + if(target.mind && target.mind.special_role && target.emagged) + to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + target.ResetSecurityCodes() + return TRUE + if(target.emagged) + to_chat(user, "Access denied. Safety protocols are disabled.") + return TRUE + message_admins("[key_name_admin(user)] detonated [target.name]!") + log_game("[key_name(user)] detonated [target.name]!") to_chat(target, SPAN_DANGER("Self-destruct command received.")) addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/silicon/robot, self_destruct)), 1 SECONDS) + return TRUE + + // Locks or unlocks the cyborg + if("lockdown") + var/mob/living/silicon/robot/target = get_cyborg_by_name(params["name"]) + if(!target || !istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access denied. This robot is not linked to you.") + return + if(isrobot(user)) + to_chat(user, "Access denied.") + return + if(target.emagged) + return TRUE + var/choice = tgui_alert(user, "Really [target.lock_charge ? "unlock" : "lockdown"] [target.name]?", "Robotics Control", list("Yes", "No")) + if(choice != "Yes") + return TRUE + if(!target || !istype(target)) + return TRUE + target.SetLockdown(!target.lock_charge) + message_admins("[key_name_admin(user)] [target.lock_charge ? "locked down" : "released"] [target.name]!") + log_game("[key_name(user)] [target.lock_charge ? "locked down" : "released"] [target.name]!") + to_chat(target, (target.lock_charge ? "You have been locked down!" : "Your lockdown has been lifted!")) + return TRUE + + // Changes borg's access + if("access") + var/mob/living/silicon/robot/target = get_cyborg_by_name(params["name"]) + if(!istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access denied. This robot is not linked to you.") + return + if(isrobot(user) || target.emagged) + to_chat(user, "Access denied.") + return + if(!target.module) + to_chat(user, "\The [src]\s access protocols are immutable.") + return TRUE + target.module.all_access = !target.module.all_access + target.update_access() + var/log_message = "[key_name_admin(user)] changed [target.name] access to [target.module.all_access ? "all access" : "role specific"]." + message_admins(log_message) + log_game(log_message) + to_chat(target, "Your access was changed to: [target.module.all_access ? "all access" : "role specific"].") + return TRUE + + // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. + if("hack") + var/mob/living/silicon/robot/target = get_cyborg_by_name(params["name"]) + if(!target || !istype(target)) + return + if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) + to_chat(user, "Access denied.") + return + if(target.emagged) + to_chat(user, "Robot is already hacked.") + return TRUE + var/choice = tgui_alert(user, "Really hack [target.name]? This cannot be undone.", "Robotics Control", list("Yes", "No")) + if(choice != "Yes") + return TRUE + if(!target || !istype(target)) + return TRUE + message_admins("[key_name_admin(user)] emagged [target.name] using robotic console!") + log_game("[key_name(user)] emagged [target.name] using robotic console!") + target.emagged = TRUE + to_chat(target, SPAN_NOTICE("Failsafe protocols overriden. New tools available.")) + return TRUE + + // Arms/disarms the emergency self-destruct system + if("arm") + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access denied.") + return + safety = !safety + to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct") + return TRUE + + // Destroys all accessible cyborgs if safety is disabled + if("nuke") + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access denied.") + return + if(safety) + to_chat(user, "Self-destruct aborted - safety active") + return TRUE + message_admins("[key_name_admin(user)] detonated all cyborgs!") + log_game("[key_name(user)] detonated all cyborgs!") + for(var/mob/living/silicon/robot/cyborg in GLOB.mob_list) + if(istype(cyborg, /mob/living/silicon/robot/drone)) + continue + if(cyborg.scrambled_codes) + continue + if(cyborg.emagged) + continue + to_chat(cyborg, SPAN_DANGER("Self-destruct command received.")) + addtimer(CALLBACK(cyborg, TYPE_PROC_REF(/mob/living/silicon/robot, self_destruct)), 1 SECONDS) + return TRUE - - // Locks or unlocks the cyborg - else if(href_list["lockdown"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) - if(!target || !istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access denied. This robot is not linked to you.") - return - - if(isrobot(user)) - to_chat(user, "Access denied.") - return - - if(target.emagged) - return - - var/choice = tgui_alert(usr, "Really [target.lock_charge ? "unlock" : "lockdown"] [target.name] ?", "Robotics Control", list("Yes", "No")) - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - target.SetLockdown(!target.lock_charge) // Toggle. - message_admins("[key_name_admin(usr)] [target.lock_charge ? "locked down" : "released"] [target.name]!") - log_game("[key_name(usr)] [target.lock_charge ? "locked down" : "released"] [target.name]!") - to_chat(target, (target.lock_charge ? "You have been locked down!" : "Your lockdown has been lifted!")) - - // Changes borg's access - else if(href_list["access"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["access"]) - if(!istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access denied. This robot is not linked to you.") - return - - if(isrobot(user) || target.emagged) - to_chat(user, "Access denied.") - return - - if(!istype(target)) - return - - if(!target.module) - to_chat(user, "\The [src]\s access protocols are immutable.") - return - - target.module.all_access = !target.module.all_access - target.update_access() - - var/log_message = "[key_name_admin(usr)] changed [target.name] access to [target.module.all_access ? "all access" : "role specific"]." - message_admins(log_message) - log_game(log_message) - to_chat(target, ("Your access was changed to: [target.module.all_access ? "all access" : "role specific"].")) - - // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. - else if(href_list["hack"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) - if(!target || !istype(target)) - return - - // Antag AI checks - if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) - to_chat(user, "Access denied.") - return - - if(target.emagged) - to_chat(user, "Robot is already hacked.") - return - - var/choice = tgui_alert(usr, "Really hack [target.name]? This cannot be undone.", list("Yes", "No")) - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] emagged [target.name] using robotic console!") - log_game("[key_name(usr)] emagged [target.name] using robotic console!") - target.emagged = 1 - to_chat(target, SPAN_NOTICE("Failsafe protocols overriden. New tools available.")) - - // Arms the emergency self-destruct system - else if(href_list["arm"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access denied.") - return - - safety = !safety - to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct") - - // Destroys all accessible cyborgs if safety is disabled - else if(href_list["nuke"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access denied.") - return - if(safety) - to_chat(user, "Self-destruct aborted - safety active") - return - - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("[key_name(usr)] detonated all cyborgs!") - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambled_codes) - continue - if(R.emagged) - continue - to_chat(R, SPAN_DANGER("Self-destruct command received.")) - spawn(10) - R.self_destruct() - - -// Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) -// Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) +/** + * Returns a list of accessible cyborgs for the TGUI interface. + * + * * operator - The mob operating the console; determines AI-specific hackable status visibility + */ +/obj/machinery/computer/robotics/proc/get_cyborgs(mob/operator) var/list/robots = list() - for(var/mob/living/silicon/robot/R in GLOB.mob_list) + for(var/mob/living/silicon/robot/cyborg in GLOB.mob_list) // Ignore drones - if(istype(R, /mob/living/silicon/robot/drone)) + if(istype(cyborg, /mob/living/silicon/robot/drone)) continue // Ignore antagonistic cyborgs - if(R.scrambled_codes) + if(cyborg.scrambled_codes) continue var/list/robot = list() - robot["name"] = R.name - if(R.stat) + robot["name"] = cyborg.name + if(cyborg.stat) robot["status"] = "Not Responding" - else if(R.lock_charge) // changed this from !R.canmove to R.lock_charge because of issues with lockdown and chairs + else if(cyborg.lock_charge) // changed this from !cyborg.canmove to cyborg.lock_charge because of issues with lockdown and chairs robot["status"] = "Lockdown" else robot["status"] = "Operational" - if(R.cell) - robot["cell"] = 1 - robot["cell_capacity"] = R.cell.maxcharge - robot["cell_current"] = R.cell.charge - robot["cell_percentage"] = round(R.cell.percent()) + if(cyborg.cell) + robot["cell"] = TRUE + robot["cell_capacity"] = cyborg.cell.maxcharge + robot["cell_current"] = cyborg.cell.charge + robot["cell_percentage"] = round(cyborg.cell.percent()) else - robot["cell"] = 0 + robot["cell"] = FALSE - robot["module"] = R.module ? R.module.name : "None" - robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" - robot["hackable"] = 0 - robot["access"] = R.module ? R.module.all_access : FALSE + robot["module"] = cyborg.module ? cyborg.module.name : "None" + robot["master_ai"] = cyborg.connected_ai ? cyborg.connected_ai.name : "None" + robot["hackable"] = FALSE + robot["access"] = cyborg.module ? cyborg.module.all_access : FALSE // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) - robot["hacked"] = R.emagged ? 1 : 0 - robot["hackable"] = R.emagged? 0 : 1 + if(operator && istype(operator, /mob/living/silicon/ai) && (cyborg.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) + robot["hacked"] = cyborg.emagged ? TRUE : FALSE + robot["hackable"] = cyborg.emagged ? FALSE : TRUE robots.Add(list(robot)) return robots -// Proc: get_cyborg_by_name() -// Parameters: 1 (name - Cyborg we are trying to find) -// Description: Helper proc for finding cyborg by name -/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) +/// Finds a cyborg mob by name in the global mob list. Returns null if not found. +/obj/machinery/computer/robotics/proc/get_cyborg_by_name(name) if(!name) return - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.name == name) - return R + for(var/mob/living/silicon/robot/cyborg in GLOB.mob_list) + if(cyborg.name == name) + return cyborg diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index ecf933ddae3..a3b16e7f9a8 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -37,12 +37,12 @@ GLOBAL_VAR(bomb_set) return ..() /obj/machinery/nuclearbomb/process() - if (src.timing) + if(src.timing) src.timeleft = max(timeleft - 2, 0) // 2 seconds per process() - if (timeleft <= 0) + if(timeleft <= 0) spawn explode() - SSnanoui.update_uis(src) + SStgui.update_uis(src) return /obj/machinery/nuclearbomb/attackby(obj/item/attacking_item, mob/user, params) @@ -166,20 +166,22 @@ GLOBAL_VAR(bomb_set) update_icon() return -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["hacking"] = 0 +/obj/machinery/nuclearbomb/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "NuclearBomb", "Nuke Control Panel") + ui.open() + +/obj/machinery/nuclearbomb/ui_data(mob/user) + var/list/data = list() data["auth"] = is_auth(user) - if (is_auth(user)) - if (yes_code) + if(is_auth(user)) + if(yes_code) data["authstatus"] = timing ? "Functional/Set" : "Functional" else data["authstatus"] = "Auth. S2" else - if (timing) - data["authstatus"] = "Set" - else - data["authstatus"] = "Auth. S1" + data["authstatus"] = timing ? "Set" : "Auth. S1" data["safe"] = safety ? "Safe" : "Engaged" data["time"] = timeleft data["timer"] = timing @@ -187,17 +189,11 @@ GLOBAL_VAR(bomb_set) data["anchored"] = anchored data["yescode"] = yes_code data["message"] = "AUTH" - if (is_auth(user)) + if(is_auth(user)) data["message"] = code - if (yes_code) + if(yes_code) data["message"] = "*****" - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 300, 510) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data /obj/machinery/nuclearbomb/verb/toggle_deployable() set category = "Object" @@ -222,102 +218,107 @@ GLOBAL_VAR(bomb_set) return 1 return 0 -/obj/machinery/nuclearbomb/Topic(href, href_list) - if(..()) - return 1 +/obj/machinery/nuclearbomb/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return - if (href_list["auth"]) - if (auth) + if(action == "auth") + if(auth) auth.forceMove(loc) yes_code = 0 auth = null else var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/disk/nuclear)) - usr.drop_from_inventory(I,src) + if(istype(I, /obj/item/disk/nuclear)) + usr.drop_from_inventory(I, src) auth = I - if (is_auth(usr)) - if (href_list["type"]) - if (href_list["type"] == "E") - if (code == r_code) + return TRUE + + if(!is_auth(usr)) + return TRUE + + switch(action) + if("type") + var/key = params["value"] + if(key == "E") + if(code == r_code) yes_code = 1 code = null else code = "ERROR" + else if(key == "R") + yes_code = 0 + code = null else - if (href_list["type"] == "R") - yes_code = 0 - code = null + lastentered = "[key]" + if(text2num(lastentered) == null) + var/turf/LOC = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) + log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") else - lastentered = "[href_list["type"]]" - if (text2num(lastentered) == null) - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") - else - code += lastentered - if (length(code) > 5) - code = "ERROR" - if (yes_code) - if (href_list["time"]) - var/time = text2num(href_list["time"]) - timeleft += time - timeleft = clamp(timeleft, 120, 600) - if (href_list["timer"]) - if (timing == -1) - SSnanoui.update_uis(src) - return - if (!anchored) - to_chat(usr, SPAN_WARNING("\The [src] needs to be anchored.")) - SSnanoui.update_uis(src) - return - if (safety) - to_chat(usr, SPAN_WARNING("The safety is still on.")) - SSnanoui.update_uis(src) - return - if (wires.is_cut(WIRE_TIMING)) - to_chat(usr, SPAN_WARNING("Nothing happens, something might be wrong with the wiring.")) - SSnanoui.update_uis(src) - return + code += lastentered + if(length(code) > 5) + code = "ERROR" + return TRUE - if (!timing && !safety) - timing = 1 - log_and_message_admins("engaged a nuclear bomb") - GLOB.bomb_set++ //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N - update_icon() - else - secure_device() + if(!yes_code) + return TRUE - if(alerted == 0) - set_security_level(SEC_LEVEL_DELTA) - alerted = 1 - if (href_list["safety"]) - if (wires.is_cut(WIRE_SAFETY)) - to_chat(usr, SPAN_WARNING("Nothing happens, something might be wrong with the wiring.")) - SSnanoui.update_uis(src) - return - safety = !safety - if(safety) - secure_device() + switch(action) + if("time") + timeleft += text2num(params["value"]) + timeleft = clamp(timeleft, 120, 600) + return TRUE + if("timer") + if(timing == -1) + return TRUE + if(!anchored) + to_chat(usr, SPAN_WARNING("\The [src] needs to be anchored.")) + return TRUE + if(safety) + to_chat(usr, SPAN_WARNING("The safety is still on.")) + return TRUE + if(wires.is_cut(WIRE_TIMING)) + to_chat(usr, SPAN_WARNING("Nothing happens, something might be wrong with the wiring.")) + return TRUE + if(!timing && !safety) + timing = 1 + log_and_message_admins("engaged a nuclear bomb") + GLOB.bomb_set++ //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N update_icon() - if (href_list["anchor"]) - if(removal_stage == 5) - anchored = 0 - visible_message(SPAN_WARNING("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")) - SSnanoui.update_uis(src) - return - - if(!isinspace()) - anchored = !anchored - if(anchored) - visible_message(SPAN_WARNING("With a steely snap, bolts slide out of [src] and anchor it to the flooring.")) - else - secure_device() - visible_message(SPAN_WARNING("The anchoring bolts slide back into the depths of [src].")) + else + secure_device() + if(alerted == 0) + set_security_level(SEC_LEVEL_DELTA) + alerted = 1 + return TRUE + if("safety") + if(wires.is_cut(WIRE_SAFETY)) + to_chat(usr, SPAN_WARNING("Nothing happens, something might be wrong with the wiring.")) + return TRUE + safety = !safety + if(safety) + secure_device() + update_icon() + return TRUE + if("anchor") + if(removal_stage == 5) + anchored = 0 + visible_message(SPAN_WARNING("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")) + return TRUE + if(!isinspace()) + anchored = !anchored + if(anchored) + visible_message(SPAN_WARNING("With a steely snap, bolts slide out of [src] and anchor it to the flooring.")) + playsound(src, 'sound/machines/boltsdown.ogg', 30, 0, extrarange = SILENCED_SOUND_EXTRARANGE) else - to_chat(usr, SPAN_WARNING("There is nothing to anchor to!")) - - SSnanoui.update_uis(src) + secure_device() + visible_message(SPAN_WARNING("The anchoring bolts slide back into the depths of [src].")) + playsound(src, 'sound/machines/boltsup.ogg', 30, 0, extrarange = SILENCED_SOUND_EXTRARANGE) + else + to_chat(usr, SPAN_WARNING("There is nothing to anchor to!")) + return TRUE /obj/machinery/nuclearbomb/proc/secure_device() if(timing <= 0) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 3deb4a3cbe0..28a602bd8a9 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -452,7 +452,7 @@ else playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE) balloon_alert(user, locked ? "locked" : "unlocked") - updateUsrDialog() + SStgui.update_uis(src) else to_chat(user, SPAN_NOTICE("Access denied.")) playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index d37ff72603b..4931d117835 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -109,15 +109,18 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) ///Priority of the message being sent var/priority = -1 ; + /// Category of the message being composed: "assist", "supply", "info", or "reply". + var/message_type = "info" + //Form intregration - var/SQLquery + var/sql_filter_dept = "" var/paperstock = 20 var/lid = 0 //End Form Integration var/datum/announcement/announcement = new ///List of PDAs we alert upon a request receipt - var/list/obj/item/modular_computer/alert_pdas = list() + var/list/datum/weakref/alert_pdas = list() /obj/machinery/requests_console/north PRESET_NORTH @@ -137,7 +140,7 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) /obj/machinery/requests_console/update_icon() ClearOverlays() - var/mutable_appearance/screen = overlay_image(icon, "req_comp-idle") + var/mutable_appearance/screen_overlay = overlay_image(icon, "req_comp-idle") var/mutable_appearance/screen_hologram = overlay_image(icon, "req_comp-idle") var/mutable_appearance/screen_emis = emissive_appearance(icon, "req_comp-idle") screen_hologram.filters += filter(type="color", color=list( @@ -146,33 +149,33 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) 0, 0, 0, 0, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_OPACITY )) - screen.filters += filter(type="color", color=list( + screen_overlay.filters += filter(type="color", color=list( HOLOSCREEN_ADDITION_OPACITY, 0, 0, 0, 0, HOLOSCREEN_ADDITION_OPACITY, 0, 0, 0, 0, HOLOSCREEN_ADDITION_OPACITY, 0, 0, 0, 0, 1 )) screen_hologram.blend_mode = BLEND_MULTIPLY - screen.blend_mode = BLEND_ADD + screen_overlay.blend_mode = BLEND_ADD if(stat & NOPOWER) icon_state = initial(icon_state) set_light(FALSE) else switch(newmessagepriority) if(0) - screen = overlay_image(icon, "req_comp-idle") + screen_overlay = overlay_image(icon, "req_comp-idle") set_light(L_WALLMOUNT_RANGE, L_WALLMOUNT_POWER, COLOR_CYAN) if(1) - screen = overlay_image(icon, "req_comp-alert") + screen_overlay = overlay_image(icon, "req_comp-alert") set_light(L_WALLMOUNT_RANGE, L_WALLMOUNT_POWER, COLOR_CYAN) if(2) - screen = overlay_image(icon, "req_comp-redalert") + screen_overlay = overlay_image(icon, "req_comp-redalert") set_light(L_WALLMOUNT_RANGE,L_WALLMOUNT_POWER, COLOR_ORANGE) if(3) - screen = overlay_image(icon, "req_comp-yellowalert") + screen_overlay = overlay_image(icon, "req_comp-yellowalert") set_light(L_WALLMOUNT_RANGE, L_WALLMOUNT_POWER, COLOR_ORANGE) AddOverlays(screen_hologram) - AddOverlays(screen) + AddOverlays(screen_overlay) AddOverlays(screen_emis) AddOverlays(overlay_image(icon, "req_comp-scanline")) @@ -200,11 +203,11 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) name = "[department] requests console" GLOB.allConsoles += src - if (departmentType & RC_ASSIST) + if(departmentType & RC_ASSIST) GLOB.req_console_assistance |= department - if (departmentType & RC_SUPPLY) + if(departmentType & RC_SUPPLY) GLOB.req_console_supplies |= department - if (departmentType & RC_INFO) + if(departmentType & RC_INFO) GLOB.req_console_information |= department update_icon() @@ -218,16 +221,16 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) /obj/machinery/requests_console/Destroy() GLOB.allConsoles -= src var/lastDeptRC = 1 - for (var/obj/machinery/requests_console/Console in GLOB.allConsoles) - if (Console.department == department) + for(var/obj/machinery/requests_console/console in GLOB.allConsoles) + if(console.department == department) lastDeptRC = 0 break if(lastDeptRC) - if (departmentType & RC_ASSIST) + if(departmentType & RC_ASSIST) GLOB.req_console_assistance -= department - if (departmentType & RC_SUPPLY) + if(departmentType & RC_SUPPLY) GLOB.req_console_supplies -= department - if (departmentType & RC_INFO) + if(departmentType & RC_INFO) GLOB.req_console_information -= department alert_pdas.Cut() @@ -238,266 +241,303 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) return ui_interact(user) -/obj/machinery/requests_console/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["department"] = department - data["screen"] = screen - data["message_log"] = message_log - data["newmessagepriority"] = newmessagepriority - data["silent"] = silent - data["announcementConsole"] = announcementConsole - - data["assist_dept"] = GLOB.req_console_assistance - data["supply_dept"] = GLOB.req_console_supplies - data["info_dept"] = GLOB.req_console_information - - data["message"] = message - data["recipient"] = recipient - data["priortiy"] = priority - data["msgStamped"] = msgStamped - data["msgVerified"] = msgVerified - data["announceAuth"] = announceAuth - - if (screen == RCS_FORMS) - if (!establish_db_connection(GLOB.dbcon)) - data["sql_error"] = 1 - else - if (!SQLquery) - SQLquery = "SELECT id, name, department FROM ss13_forms ORDER BY id" - - var/datum/db_query/query = SSdbcore.NewQuery(SQLquery) - query.Execute() - - var/list/forms = list() - while (query.NextRow()) - forms += list(list("id" = query.item[1], "name" = query.item[2], "department" = query.item[3])) - - qdel(query) - - if (!forms.len) - data["sql_error"] = 1 - - data["forms"] = forms - - data["pda_list"] = list() - - for (var/A in alert_pdas) - var/obj/item/modular_computer/pda = A - data["pda_list"] += list(list("name" = alert_pdas[pda], "pda" = "[REF(pda)]")) - - data["lid"] = lid - data["paper"] = paperstock - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "requests_console.tmpl", "[department] Requests Console", 520, 410) - ui.set_initial_data(data) +/obj/machinery/requests_console/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RequestsConsole", "[department] Requests Console") ui.open() -/obj/machinery/requests_console/Topic(href, href_list) - if(..()) return - usr.set_machine(src) +/obj/machinery/requests_console/ui_data(mob/user) + var/list/data = list( + "department" = department, + "screen" = screen, + "message_log" = message_log, + "newmessagepriority" = newmessagepriority, + "silent" = silent, + "announcementConsole" = announcementConsole, + "assist_dept" = GLOB.req_console_assistance, + "supply_dept" = GLOB.req_console_supplies, + "info_dept" = GLOB.req_console_information, + "message" = message, + "recipient" = recipient, + "priority" = priority, + "msgStamped" = msgStamped, + "msgVerified" = msgVerified, + "announceAuth" = announceAuth, + "lid" = lid, + "paper" = paperstock + ) + + if(screen == RCS_FORMS) + if(!SSdbcore.Connect()) + data["sql_error"] = 1 + else + var/datum/db_query/query + if(sql_filter_dept) + query = SSdbcore.NewQuery( + "SELECT id, name, department FROM ss13_forms WHERE department LIKE :filter ORDER BY id", + list("filter" = "%[sql_filter_dept]%")) + else + query = SSdbcore.NewQuery("SELECT id, name, department FROM ss13_forms ORDER BY id") + if(!query.Execute()) + data["sql_error"] = 1 + else + var/list/forms = list() + while(query.NextRow()) + forms += list(list("id" = query.item[1], "name" = query.item[2], "department" = query.item[3])) + if(!forms.len) + data["sql_error"] = 1 + data["forms"] = forms + qdel(query) + + var/list/pda_list = list() + for(var/datum/weakref/ref in alert_pdas) + var/obj/item/modular_computer/pda = ref.resolve() + if(!pda) + alert_pdas -= ref + continue + pda_list += list(list("name" = alert_pdas[ref], "pda" = "[REF(pda)]")) + data["pda_list"] = pda_list + + return data + +/obj/machinery/requests_console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return add_fingerprint(usr) - if(reject_bad_text(href_list["write"])) - recipient = href_list["write"] //write contains the string of the receiving department's name + switch(action) + // Begin composing a message: gets text via input dialog then shows auth screen + if("compose") + var/rcpt = params["recipient"] + if(!reject_bad_text(rcpt)) + return TRUE + recipient = rcpt + switch(screen) + if(RCS_RQASSIST) + message_type = "assist" + if(RCS_RQSUPPLY) + message_type = "supply" + if(RCS_SENDINFO) + message_type = "info" + else + message_type = "reply" + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Compose Message", encode = FALSE)) + if(new_message && !use_check_and_message(usr)) + message = new_message + screen = RCS_MESSAUTH + switch(text2num(params["priority"])) + if(1) priority = 1 + if(2) priority = 2 + else priority = 0 + else + reset_message(1) + return TRUE - var/new_message = sanitize(input("Write your message:", "Awaiting Input", null) as null|text) - if(new_message && !use_check_and_message(usr)) - message = new_message - screen = RCS_MESSAUTH - switch(href_list["priority"]) - if("1") priority = 1 - if("2") priority = 2 - else priority = 0 - else + if("write_announcement") + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Announcement", encode = FALSE)) + if(new_message && !use_check_and_message(usr)) + message = new_message + else + reset_message(1) + return TRUE + + if("send_announcement") + if(!announcementConsole) + return TRUE + announcement.Announce(message, msg_sanitized = 1) reset_message(1) + return TRUE - if(href_list["writeAnnouncement"]) - var/new_message = sanitize(input("Write your message:", "Awaiting Input", null) as null|text) - if(new_message && !use_check_and_message(usr)) - message = new_message - else - reset_message(1) + // Send the composed+authenticated message + if("send_message") + if(!message) + return TRUE + screen = RCS_SENTFAIL + var/pass = FALSE + for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms) + if(MS.use_power) + MS.send_rc_message(recipient, department, message, msgStamped, msgVerified, priority, message_type) + pass = TRUE + if(pass) + screen = RCS_SENTPASS + message_log += list(list( + "type" = "sent", + "category" = message_type, + "recipient" = recipient, + "body" = message + )) + else + audible_message("The Requests Console beeps, [SPAN_WARNING("NOTICE: No server detected!")]") + return TRUE - if(href_list["sendAnnouncement"]) - if(!announcementConsole) return - announcement.Announce(message, msg_sanitized = 1) - reset_message(1) + if("set_screen") + var/new_screen = text2num(params["screen"]) + if(new_screen == RCS_ANNOUNCE && !announcementConsole) + return TRUE + if(new_screen == RCS_VIEWMSGS) + for(var/obj/machinery/requests_console/console in GLOB.allConsoles) + if(console.department == department) + console.newmessagepriority = 0 + console.update_icon() + if(new_screen == RCS_MAINMENU) + reset_message() + screen = new_screen + return TRUE - if( href_list["department"] && message ) - var/log_msg = message - screen = RCS_SENTFAIL - var/pass = FALSE - var/datum/data_rc_msg/log = new(href_list["department"], department, log_msg, msgStamped, msgVerified, priority) - for (var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms) - if (MS.use_power) - MS.rc_msgs += log - pass = TRUE - if(pass) - screen = RCS_SENTPASS - message_log += "Message sent to [recipient]
[message]" - else - var/msg = "NOTICE: No server detected!" - audible_message("The Requests Console beeps, [SPAN_WARNING(msg)]") + if("toggle_silent") + silent = !silent + return TRUE - //Handle screen switching - if(href_list["setScreen"]) - var/tempScreen = text2num(href_list["setScreen"]) - if(tempScreen == RCS_ANNOUNCE && !announcementConsole) - return - if(tempScreen == RCS_VIEWMSGS) - for (var/obj/machinery/requests_console/Console in GLOB.allConsoles) - if (Console.department == department) - Console.newmessagepriority = 0 - Console.icon_state = "req_comp0" - Console.set_light(0) - if(tempScreen == RCS_MAINMENU) - reset_message() - screen = tempScreen + if("link_pda") + var/obj/item/modular_computer/pda = usr.get_active_hand() + if(!pda || !istype(pda)) + to_chat(usr, SPAN_WARNING("You need to be holding a handheld computer to link it.")) + else + var/datum/weakref/ref = find_pda_ref(pda) + if(ref) + to_chat(usr, SPAN_NOTICE("\The [pda] appears to be already linked.")) + alert_pdas[ref] = pda.name + else + ref = WEAKREF(pda) + alert_pdas += ref + alert_pdas[ref] = pda.name + to_chat(usr, SPAN_NOTICE("You link \the [pda] to \the [src]. It will now ping upon the arrival of a request to this machine.")) + return TRUE - //Handle silencing the console - if(href_list["toggleSilent"]) - silent = !silent + if("unlink_pda") + var/obj/item/modular_computer/pda = locate(params["pda"]) + if(pda && istype(pda)) + var/datum/weakref/ref = find_pda_ref(pda) + if(ref) + to_chat(usr, SPAN_NOTICE("You unlink [alert_pdas[ref]] from \the [src]. It will no longer be notified of new requests.")) + alert_pdas -= ref + return TRUE - // Link a PDA - if(href_list["linkpda"]) - var/obj/item/modular_computer/pda = usr.get_active_hand() - if (!pda || !istype(pda)) - to_chat(usr, SPAN_WARNING("You need to be holding a handheld computer to link it.")) - else if (pda in alert_pdas) - to_chat(usr, SPAN_NOTICE("\The [pda] appears to be already linked.")) - //Update the name real quick. - alert_pdas[pda] = pda.name - else - alert_pdas += pda - alert_pdas[pda] = pda.name - to_chat(usr, SPAN_NOTICE("You link \the [pda] to \the [src]. It will now ping upon the arrival of a request to this machine.")) + if("sort_forms") + sql_filter_dept = sanitize(params["department"]) + return TRUE - // Unlink a PDA. - if(href_list["unlink"]) - var/obj/item/modular_computer/pda = locate(href_list["unlink"]) - if (pda && istype(pda)) - if (pda in alert_pdas) - to_chat(usr, SPAN_NOTICE("You unlink [alert_pdas[pda]] from \the [src]. It will no longer be notified of new requests.")) - alert_pdas -= pda + if("reset_sql") + sql_filter_dept = "" + return TRUE - // Sort the forms. - if(href_list["sort"]) - var/sortdep = sanitizeSQL(href_list["sort"]) - SQLquery = "SELECT id, name, department FROM ss13_forms WHERE department LIKE '%[sortdep]%' ORDER BY id" + if("print_form") + var/printid = text2num(params["id"]) + if(!printid) + return TRUE + if(!SSdbcore.Connect()) + to_chat(usr, SPAN_WARNING("Connection to the database lost. Aborting.")) + return TRUE + var/datum/db_query/query = SSdbcore.NewQuery( + "SELECT id, name, data FROM ss13_forms WHERE id = :id", + list("id" = printid)) + if(!query.Execute()) + to_chat(usr, SPAN_WARNING("Connection to the database lost. Aborting.")) + qdel(query) + return TRUE + while(query.NextRow()) + var/form_id = query.item[1] + var/form_name = query.item[2] + var/form_data = html_encode(query.item[3]) + var/obj/item/paper/form_paper = new() + form_paper.color = "#fff9e8" + form_paper.set_content("NFC-[form_id] - [form_name]", form_data) + print(form_paper, user = usr) + paperstock-- + qdel(query) + return TRUE - if (href_list["resetSQL"]) - SQLquery = "SELECT id, name, department FROM ss13_forms ORDER BY id" + if("whatis") + var/whatisid = text2num(params["id"]) + if(!whatisid) + return TRUE + if(!SSdbcore.Connect()) + to_chat(usr, SPAN_WARNING("Connection to the database lost. Aborting.")) + return TRUE + var/datum/db_query/query = SSdbcore.NewQuery( + "SELECT id, name, department, info FROM ss13_forms WHERE id = :id", + list("id" = whatisid)) + if(!query.Execute()) + to_chat(usr, SPAN_WARNING("Connection to the database lost. Aborting.")) + qdel(query) + return TRUE + var/dat = "
Stellar Corporate Conglomerate Form
" + while(query.NextRow()) + dat += "SCCF-[query.item[1]]

" + dat += "[query.item[2]]
" + dat += "[query.item[3]] Department
" + dat += "[query.item[4]]" + dat += "
" + qdel(query) + usr << browse(HTML_SKELETON(dat), "window=Information;size=560x240") + return TRUE - // Print a form. - if(href_list["print"]) - var/printid = sanitizeSQL(href_list["print"]) - - if(!establish_db_connection(GLOB.dbcon)) - alert("Connection to the database lost. Aborting.") - if(!printid) - alert("Invalid query. Try again.") - var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, name, data FROM ss13_forms WHERE id=[printid]") - query.Execute() - - while(query.NextRow()) - var/id = query.item[1] - var/name = query.item[2] - var/data = query.item[3] - var/obj/item/paper/C = new() - C.color = "#fff9e8" - - //Let's start the BB >> HTML conversion! - - data = html_encode(data) - C.set_content("NFC-[id] - [name]", data) - print(C, user = usr) - - paperstock-- - - qdel(query) - - // Get extra information about the form. - if(href_list["whatis"]) - var/whatisid = sanitizeSQL(href_list["whatis"]) - - if(!establish_db_connection(GLOB.dbcon)) - alert("Connection to the database lost. Aborting.") - if(!whatisid) - alert("Invalid query. Try again.") - var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, name, department, info FROM ss13_forms WHERE id=:id",list("id"=whatisid)) - query.Execute() - var/dat = "
Stellar Corporate Conglomerate Form
" - while(query.NextRow()) - var/id = query.item[1] - var/name = query.item[2] - var/department = query.item[3] - var/info = query.item[4] - - dat += "SCCF-[id]

" - dat += "[name]
" - dat += "[department] Department
" - dat += "[info]" - dat += "
" - usr << browse(HTML_SKELETON(dat), "window=Information;size=560x240") - qdel(query) - - // Toggle the paper bin lid. - if(href_list["setLid"]) - lid = !lid - to_chat(usr, SPAN_NOTICE("You [lid ? "open" : "close"] the lid.")) - - updateUsrDialog() - return + if("toggle_lid") + lid = !lid + to_chat(usr, SPAN_NOTICE("You [lid ? "open" : "close"] the lid.")) + return TRUE //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messanging on that console (EXTREME priority...), but the code for that was removed. /obj/machinery/requests_console/attackby(obj/item/attacking_item, mob/user) - if (istype(attacking_item, /obj/item/card/id)) + if(istype(attacking_item, /obj/item/modular_computer)) + var/obj/item/modular_computer/pda = attacking_item + var/datum/weakref/ref = find_pda_ref(pda) + if(ref) + to_chat(user, SPAN_NOTICE("You unlink [alert_pdas[ref]] from \the [src]. It will no longer be notified of new requests.")) + alert_pdas -= ref + else + ref = WEAKREF(pda) + alert_pdas += ref + alert_pdas[ref] = pda.name + to_chat(user, SPAN_NOTICE("You link \the [pda] to \the [src]. It will now ping upon the arrival of a request to this machine.")) + return TRUE + if(istype(attacking_item, /obj/item/card/id)) if(!operable(MAINT)) return TRUE if(screen == RCS_MESSAUTH) - var/obj/item/card/id/T = attacking_item - msgVerified = "Verified by [T.registered_name], [T.assignment]" - updateUsrDialog() + var/obj/item/card/id/id_card = attacking_item + msgVerified = "[id_card.registered_name], [id_card.assignment]" + SStgui.update_uis(src) if(screen == RCS_ANNOUNCE) - var/obj/item/card/id/ID = attacking_item - if (ACCESS_RC_ANNOUNCE in ID.GetAccess()) + var/obj/item/card/id/auth_card = attacking_item + if(ACCESS_RC_ANNOUNCE in auth_card.GetAccess()) announceAuth = 1 - announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name + announcement.announcer = auth_card.assignment ? "[auth_card.assignment] [auth_card.registered_name]" : auth_card.registered_name else reset_message() to_chat(user, SPAN_WARNING("You are not authorized to send announcements.")) - updateUsrDialog() + SStgui.update_uis(src) return TRUE - else if (istype(attacking_item, /obj/item/stamp)) + else if(istype(attacking_item, /obj/item/stamp)) if(!operable(MAINT)) return if(screen == RCS_MESSAUTH) - var/obj/item/stamp/T = attacking_item - msgStamped = SPAN_NOTICE("Stamped with the [T.name]") - updateUsrDialog() + var/obj/item/stamp/used_stamp = attacking_item + msgStamped = used_stamp.name + SStgui.update_uis(src) return TRUE - else if (istype(attacking_item, /obj/item/paper_bundle)) - var/obj/item/paper_bundle/C = attacking_item + else if(istype(attacking_item, /obj/item/paper_bundle)) + var/obj/item/paper_bundle/paper_bundle = attacking_item if(lid) if(alert(user, "Do you want to restock \the [src] with \the [attacking_item]?", "Paper Restocking", "Yes", "No") == "No") to_chat(user, SPAN_NOTICE("You decide against restocking \the [src], noting that the lid is still open.")) return - paperstock += C.amount - user.drop_from_inventory(C,get_turf(src)) - qdel(C) + paperstock += paper_bundle.amount + user.drop_from_inventory(paper_bundle, get_turf(src)) + qdel(paper_bundle) audible_message("The Requests Console beeps, \"Paper added.\"") else if(screen == RCS_MAINMENU) //Faxing them papers fax_send(attacking_item, user) return TRUE - else if (istype(attacking_item, /obj/item/paper)) + else if(istype(attacking_item, /obj/item/paper)) if(lid) if(alert(user, "Do you want to restock \the [src] with \the [attacking_item]?", "Paper Restocking", "Yes", "No") == "No") to_chat(user, SPAN_NOTICE("You decide against restocking \the [src], noting that the lid is still open.")) return - var/obj/item/paper/C = attacking_item - user.drop_from_inventory(C,get_turf(src)) - qdel(C) + var/obj/item/paper/paper_item = attacking_item + user.drop_from_inventory(paper_item, get_turf(src)) + qdel(paper_item) paperstock++ audible_message("The Requests Console beeps, \"Paper added.\"") else if(screen == RCS_MAINMENU) //Faxing them papers @@ -511,7 +551,7 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) return TRUE return FALSE -/obj/machinery/requests_console/proc/fax_send(var/obj/item/O, var/mob/user) +/obj/machinery/requests_console/proc/fax_send(obj/item/fax_item, mob/user) var/sendto = tgui_input_list(user, "Select department.", "Send Fax", GLOB.allConsoles) if(!sendto) return @@ -521,34 +561,34 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) var/msg = "NOTICE: No server detected!" audible_message("The Requests Console beeps, [SPAN_WARNING(msg)]") return - for(var/cc in GLOB.allConsoles) - var/obj/machinery/requests_console/Console = cc - if(Console == sendto) + for(var/obj/machinery/requests_console/console in GLOB.allConsoles) + if(console == sendto) var/paperstock_usage = 1 - var/is_paper_bundle = istype(O, /obj/item/paper_bundle) + var/is_paper_bundle = istype(fax_item, /obj/item/paper_bundle) if(is_paper_bundle) - var/obj/item/paper_bundle/OPB = O - paperstock_usage = OPB.amount - if(Console.paperstock < paperstock_usage) + var/obj/item/paper_bundle/fax_bundle = fax_item + paperstock_usage = fax_bundle.amount + if(console.paperstock < paperstock_usage) audible_message("The Requests Console beeps, \"Error! Receiving console out of paper! Aborting!\"") return - playsound(Console.loc, 'sound/machines/twobeep.ogg', 40) - playsound(Console.loc, 'sound/items/polaroid1.ogg', 40) + playsound(console.loc, 'sound/machines/twobeep.ogg', 40) + playsound(console.loc, 'sound/items/polaroid1.ogg', 40) if(!is_paper_bundle) - var/obj/item/paper/P = copy(Console, O, FALSE, FALSE, 0, 15, user) - P.forceMove(Console.loc) + var/obj/item/paper/fax_copy = copy(console, fax_item, FALSE, FALSE, 0, 15, user) + fax_copy.forceMove(console.loc) else - var/obj/item/paper_bundle/PB = bundlecopy(Console, O, FALSE, 15, FALSE) - PB.forceMove(Console.loc) - Console.audible_message("The Requests Console beeps, \"Fax received.\"") - for(var/obj/item/modular_computer/pda in Console.alert_pdas) - var/message = "A fax has arrived!" - pda.get_notification(message, 1, "[Console.department] Requests Console") - Console.paperstock -= paperstock_usage + var/obj/item/paper_bundle/bundle_copy = bundlecopy(console, fax_item, FALSE, 15, FALSE) + bundle_copy.forceMove(console.loc) + console.audible_message("The Requests Console beeps, \"Fax received.\"") + for(var/datum/weakref/ref in console.alert_pdas) + var/obj/item/modular_computer/pda = ref.resolve() + if(pda) + pda.get_notification("A fax has arrived!", 1, "[console.department] Requests Console") + console.paperstock -= paperstock_usage audible_message("The Requests Console beeps, \"Fax sent.\"") return -/obj/machinery/requests_console/proc/reset_message(var/mainmenu = 0) +/obj/machinery/requests_console/proc/reset_message(mainmenu = FALSE) message = "" recipient = "" priority = 0 @@ -559,6 +599,12 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list()) if(mainmenu) screen = RCS_MAINMENU +/// Finds the weakref in `alert_pdas` that resolves to the given PDA. Returns null if the PDA is not linked. +/obj/machinery/requests_console/proc/find_pda_ref(obj/item/modular_computer/pda) + for(var/datum/weakref/ref in alert_pdas) + if(ref.resolve() == pda) + return ref + #undef PRESET_NORTH #undef PRESET_SOUTH #undef PRESET_WEST diff --git a/code/game/machinery/suit_cycler.dm b/code/game/machinery/suit_cycler.dm index 4c8863d20a3..3784cdb450b 100644 --- a/code/game/machinery/suit_cycler.dm +++ b/code/game/machinery/suit_cycler.dm @@ -21,7 +21,7 @@ user.drop_from_inventory(attacking_item, src);\ ##slot = attacking_item;\ update_icon();\ - updateUsrDialog();\ + SStgui.update_uis(src);\ return\ } @@ -189,7 +189,7 @@ occupant = M add_fingerprint(user) - updateUsrDialog() + SStgui.update_uis(src) update_icon() /obj/machinery/suit_cycler/attack_ai(mob/user) @@ -240,14 +240,14 @@ add_fingerprint(user) qdel(G) - updateUsrDialog() + SStgui.update_uis(src) update_icon() return else if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER) panel_open = !panel_open to_chat(user, SPAN_NOTICE("You [panel_open ? "open" : "close"] the maintenance panel.")) - updateUsrDialog() + SStgui.update_uis(src) update_icon() return @@ -289,7 +289,7 @@ emagged = TRUE safeties = FALSE req_access = list() - updateUsrDialog() + SStgui.update_uis(src) return 1 /obj/machinery/suit_cycler/attack_hand(mob/user) @@ -444,7 +444,7 @@ active = TRUE irradiating = 10 update_icon() - src.updateUsrDialog() + SStgui.update_uis(src) sleep(10) if(helmet) @@ -471,7 +471,7 @@ if(radiation_level > 1) mask.clean_blood() - src.updateUsrDialog() + SStgui.update_uis(src) return /obj/machinery/suit_cycler/process() @@ -511,7 +511,7 @@ playsound(loc, 'sound/machines/suitstorage_lockdoor.ogg', 50, FALSE) active = FALSE update_icon() - updateUsrDialog() + SStgui.update_uis(src) /obj/machinery/suit_cycler/proc/repair_suit() if(!suit || !suit.damage || !suit.can_breach) @@ -549,7 +549,7 @@ if(user) add_fingerprint(user) - updateUsrDialog() + SStgui.update_uis(src) update_icon() //There HAS to be a less bloated way to do this. TODO: some kind of table/icon name coding? ~Z diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index 3c630642b3f..6ff88830f55 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -111,39 +111,56 @@ if(!relay_information(signal, /obj/machinery/telecomms/hub)) relay_information(signal, /obj/machinery/telecomms/broadcaster) -/obj/machinery/telecomms/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) - rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) - var/authmsg = "[message]
" - if (id_auth) - authmsg += "[id_auth]
" - if (stamp) - authmsg += "[stamp]
" - for (var/obj/machinery/requests_console/Console in GLOB.allConsoles) - if (ckey(Console.department) == ckey(recipient)) - if(!Console.operable()) - Console.message_log += "Message lost due to console failure.
Please contact [station_name()] system adminsitrator or AI for technical assistance.
" - continue - if(Console.newmessagepriority < priority) - Console.newmessagepriority = priority - Console.icon_state = "req_comp[priority]" - switch(priority) - if(2) - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message("[icon2html(Console, viewers(get_turf(Console)))] *The Requests Console beeps: 'PRIORITY Alert in [sender]'",,5) - Console.message_log += "High Priority message from [sender]
[authmsg]" - for(var/obj/item/modular_computer/pda in Console.alert_pdas) - var/pda_message = "A high priority message has arrived!" - pda.get_notification(pda_message, 1, "[Console.department] Requests Console") - else - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message("[icon2html(Console, viewers(get_turf(Console)))] *The Requests Console beeps: 'Message from [sender]'",,4) - Console.message_log += "Message from [sender]
[authmsg]" - for(var/obj/item/modular_computer/pda in Console.alert_pdas) - var/pda_message = "A message has arrived!" - pda.get_notification(pda_message, 1, "[Console.department] Requests Console") - Console.set_light(2) +/// Display label for a message category code ("assist"/"supply"/"info"/"reply"). +/obj/machinery/telecomms/message_server/proc/rc_category_label(category) + switch(category) + if("assist") + return "Assistance Request" + if("supply") + return "Supply Request" + if("reply") + return "Reply" + else + return "Message" + +/obj/machinery/telecomms/message_server/proc/send_rc_message(recipient = "", sender = "", message = "", stamp = "", id_auth = "", priority = 1, category = "info") + rc_msgs += new/datum/data_rc_msg(recipient, sender, message, stamp, id_auth) + var/category_label = rc_category_label(category) + for(var/obj/machinery/requests_console/Console in GLOB.allConsoles) + if(ckey(Console.department) != ckey(recipient)) + continue + if(!Console.operable()) + Console.message_log += list(list( + "type" = "error", + "body" = "Message lost due to console failure. Please contact [station_name()] system administrator or AI for technical assistance." + )) + continue + if(Console.newmessagepriority < priority) + Console.newmessagepriority = priority + Console.update_icon() + var/list/entry = list( + "type" = "received", + "category" = category, + "priority" = priority >= 2 ? "high" : "normal", + "sender" = sender, + "body" = message, + "stamp" = stamp, + "id_auth" = id_auth + ) + Console.message_log += list(entry) + if(!Console.silent) + playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) + if(priority >= 2) + Console.audible_message("[icon2html(Console, viewers(get_turf(Console)))] *The Requests Console beeps: 'PRIORITY [category_label] from [sender]'",, 5) + else + Console.audible_message("[icon2html(Console, viewers(get_turf(Console)))] *The Requests Console beeps: '[category_label] from [sender]'",, 4) + var/notification_text = priority >= 2 ? "A high priority [lowertext(category_label)] has arrived!" : "A new [lowertext(category_label)] has arrived!" + for(var/datum/weakref/ref in Console.alert_pdas) + var/obj/item/modular_computer/pda = ref.resolve() + if(pda) + pda.get_notification(notification_text, 1, "[Console.department] Requests Console") + SStgui.update_uis(Console) + Console.set_light(2) /obj/machinery/telecomms/message_server/attack_hand(user as mob) diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 813cdc4ca7d..b0ebae0a223 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -108,7 +108,7 @@ update_icon() else to_chat(user, SPAN_WARNING("\The [src] is full.")) - updateUsrDialog() + SStgui.update_uis(src) return if(istype(attacking_item, /obj/item/tank/phoron)) if(tanks_phoron < max_tanks) @@ -120,7 +120,7 @@ update_icon() else to_chat(user, SPAN_WARNING("\The [src] is full.")) - updateUsrDialog() + SStgui.update_uis(src) return if(attacking_item.tool_behaviour == TOOL_WRENCH) if(anchored) diff --git a/code/modules/fabrication/fabricator_build.dm b/code/modules/fabrication/fabricator_build.dm index b36c7d9cb76..366a36798a5 100644 --- a/code/modules/fabrication/fabricator_build.dm +++ b/code/modules/fabrication/fabricator_build.dm @@ -46,7 +46,7 @@ start_building() else stop_building() - updateUsrDialog() + SStgui.update_uis(src) ///Tries to build the next item in the fabricator's queue /obj/machinery/fabricator/proc/try_queue_build(singleton/fabricator_recipe/recipe, multiplier) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 4a2899c8811..f0677ba1d4f 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -566,7 +566,7 @@ GLOBAL_LIST_EMPTY_TYPED(alloy_data, /datum/alloy) new /obj/item/ore/slag(output_turf) if(console) - console.updateUsrDialog() + SStgui.update_uis(console) /obj/machinery/mineral/processing_unit/RefreshParts() ..() diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 56b3bb71ba9..8b90a247f04 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -35,13 +35,13 @@ /obj/machinery/mineral/stacking_unit_console/proc/setup_machine(mob/user) if(!machine) - var/area/A = get_area(src) + var/area/machine_area = get_area(src) var/best_distance = INFINITY for(var/obj/machinery/mineral/stacking_machine/checked_machine in SSmachinery.machinery) if(id) if(checked_machine.id == id) machine = checked_machine - else if(!checked_machine.console && A == get_area(checked_machine) && get_dist_euclidian(checked_machine, src) < best_distance) + else if(!checked_machine.console && machine_area == get_area(checked_machine) && get_dist_euclidian(checked_machine, src) < best_distance) machine = checked_machine best_distance = get_dist_euclidian(checked_machine, src) if(machine) @@ -64,10 +64,17 @@ add_fingerprint(user) ui_interact(user) -/obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/ui_state/state = GLOB.default_state) +/obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user, datum/tgui/ui) if(!setup_machine(user)) return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "StackingMachine", "Stacking Machine") + ui.open() +/obj/machinery/mineral/stacking_unit_console/ui_data(mob/user) + if(!machine) + return list() var/list/data = list( "stack_amt" = machine.stack_amt, "contents" = list() @@ -75,41 +82,36 @@ for(var/stacktype in machine.stack_storage) if(machine.stack_storage[stacktype] > 0) data["contents"] += list(list( - "path" = stacktype, + "path" = "[stacktype]", "name" = machine.stack_paths[stacktype], "amount" = machine.stack_storage[stacktype] )) + return data - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "stacking_machine.tmpl", "Stacking Machine", 500, 400, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/mineral/stacking_unit_console/Topic(href, href_list) - if(..()) +/obj/machinery/mineral/stacking_unit_console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(!machine) return - if(href_list["change_stack"]) - var/choice = tgui_input_list(usr, "What would you like to set the stack amount to?", "Stacking", list(1,5,10,20,50)) - if(!choice) - return TRUE - machine.stack_amt = choice - return TRUE - - if(href_list["release_stack"]) - var/stacktype = text2path(href_list["release_stack"]) - if(!stacktype || !machine.stack_paths[stacktype]) - return - - if(machine.stack_storage[stacktype] > 0) - var/obj/item/stack/material/S = new stacktype(machine.output_turf) - S.amount = machine.stack_storage[stacktype] - machine.stack_storage[stacktype] = 0 + switch(action) + if("change_stack") + var/choice = tgui_input_list(usr, "What would you like to set the stack amount to?", "Stacking", list(1,5,10,20,50)) + if(!choice) + return TRUE + machine.stack_amt = choice return TRUE - add_fingerprint(usr) + if("release_stack") + var/stacktype = text2path(params["path"]) + if(!stacktype || !machine.stack_paths[stacktype]) + return + if(machine.stack_storage[stacktype] > 0) + var/obj/item/stack/material/new_stack = new stacktype(machine.output_turf) + new_stack.amount = machine.stack_storage[stacktype] + machine.stack_storage[stacktype] = 0 + return TRUE /**********************Mineral stacking unit**************************/ @@ -138,9 +140,9 @@ . = ..() for(var/stacktype in subtypesof(/obj/item/stack/material) - typesof(/obj/item/stack/material/cyborg)) - var/obj/item/stack/S = stacktype + var/obj/item/stack/stack_item = stacktype stack_storage[stacktype] = 0 - stack_paths[stacktype] = capitalize(initial(S.name)) + stack_paths[stacktype] = capitalize(initial(stack_item.name)) setup_io() @@ -167,15 +169,15 @@ return if(output_turf && input_turf) - for(var/obj/item/O in input_turf) - if(!O) + for(var/obj/item/item in input_turf) + if(!item) return - var/obj/item/stack/S = O - if(istype(S) && stack_storage[S.type] != null) - stack_storage[S.type] += S.amount - qdel(S) + var/obj/item/stack/stack_item = item + if(istype(stack_item) && stack_storage[stack_item.type] != null) + stack_storage[stack_item.type] += stack_item.amount + qdel(stack_item) else - O.forceMove(output_turf) + item.forceMove(output_turf) //Output amounts that are past stack_amt. for(var/sheet in stack_storage) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 378b068de23..6dcc5224e5b 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -179,7 +179,7 @@ GLOBAL_LIST_INIT(minevendor_list, list( user.put_in_hands(dispensed_equipment) return if(default_deconstruction_screwdriver(user, "mining-open", "mining", attacking_item)) - updateUsrDialog() + SStgui.update_uis(src) return if(default_deconstruction_crowbar(attacking_item)) return diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index 41a95e299f3..39d9117818d 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -1,5 +1,17 @@ // A vendor machine for modular computer portable devices - Laptops and Tablets +// Vendor machine state +#define LAPVEND_STATE_SELECT 0 // Select device type +#define LAPVEND_STATE_CONFIGURE 1 // Select hardware loadout +#define LAPVEND_STATE_PAYMENT 2 // Awaiting payment +#define LAPVEND_STATE_COMPLETE 3 // Thank-you screen + +// Device types +#define LAPVEND_DEVICE_NONE 0 +#define LAPVEND_DEVICE_LAPTOP 1 +#define LAPVEND_DEVICE_TABLET 2 +#define LAPVEND_DEVICE_PDA 3 + /obj/machinery/lapvend name = "computer vendor" desc = "A vending machine with microfabricator capable of dispensing various NT-branded computers." @@ -14,8 +26,8 @@ var/obj/item/modular_computer/handheld/pda/fabricated_pda // Utility vars - var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen - var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet + var/state = LAPVEND_STATE_SELECT + var/devtype = LAPVEND_DEVICE_NONE var/total_price = 0 // Price of currently vended device. // Device loadout @@ -30,8 +42,8 @@ // Removes all traces of old order and allows you to begin configuration from scratch. /obj/machinery/lapvend/proc/reset_order() - state = 0 - devtype = 0 + state = LAPVEND_STATE_SELECT + devtype = LAPVEND_DEVICE_NONE if(fabricated_laptop) qdel(fabricated_laptop) fabricated_laptop = null @@ -51,9 +63,9 @@ dev_aislot = 0 // Recalculates the price and optionally even fabricates the device. -/obj/machinery/lapvend/proc/fabricate_and_recalc_price(var/fabricate = 0) +/obj/machinery/lapvend/proc/fabricate_and_recalc_price(fabricate = FALSE) total_price = 0 - if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles + if(devtype == LAPVEND_DEVICE_LAPTOP) // Laptop, generally cheaper to make it accessible for most station roles if(fabricate) fabricated_laptop = new(src) total_price = 99 @@ -116,7 +128,7 @@ fabricated_laptop.ai_slot = new/obj/item/computer_hardware/ai_slot(fabricated_laptop) return total_price - else if(devtype == 2) // Tablet more expensive, not everyone could probably afford this. + else if(devtype == LAPVEND_DEVICE_TABLET) // Tablet more expensive, not everyone could probably afford this. if(fabricate) fabricated_tablet = new(src) total_price = 199 @@ -178,7 +190,7 @@ if(fabricate) fabricated_tablet.ai_slot = new/obj/item/computer_hardware/ai_slot(fabricated_tablet) return total_price - else if(devtype == 3) // PDA, same cost as tablet but smaller form factor + else if(devtype == LAPVEND_DEVICE_PDA) // PDA, same cost as tablet but smaller form factor if(fabricate) fabricated_pda = new(src) total_price = 199 @@ -243,96 +255,85 @@ return 0 +/obj/machinery/lapvend/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + switch(action) + if("pick_device") + if(src.state) // We've already picked a device type + return TRUE + devtype = text2num(params["devtype"]) + src.state = LAPVEND_STATE_CONFIGURE + fabricate_and_recalc_price(FALSE) + return TRUE + if("clean_order") + reset_order() + return TRUE -/obj/machinery/lapvend/Topic(href, href_list) - if(..()) - return 1 + if("confirm_order") + if(src.state != LAPVEND_STATE_CONFIGURE || !devtype) + return TRUE + src.state = LAPVEND_STATE_PAYMENT + fabricate_and_recalc_price(FALSE) + return TRUE - if(href_list["pick_device"]) - if(state) // We've already picked a device type - return 0 - devtype = text2num(href_list["pick_device"]) - state = 1 - fabricate_and_recalc_price(0) - return 1 - if(href_list["clean_order"]) - reset_order() - return 1 - if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode - return 0 - if(href_list["confirm_order"]) - state = 2 // Wait for ID swipe for payment processing - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_cpu"]) - dev_cpu = text2num(href_list["hw_cpu"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_battery"]) - dev_battery = text2num(href_list["hw_battery"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_disk"]) - dev_disk = text2num(href_list["hw_disk"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_netcard"]) - dev_netcard = text2num(href_list["hw_netcard"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_tesla"]) - dev_tesla = text2num(href_list["hw_tesla"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_nanoprint"]) - dev_nanoprint = text2num(href_list["hw_nanoprint"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_card"]) - dev_card = text2num(href_list["hw_card"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_aislot"]) - dev_aislot = text2num(href_list["hw_aislot"]) - fabricate_and_recalc_price(0) - return 1 - return 0 + if("set_hw") + if(src.state != LAPVEND_STATE_CONFIGURE || !devtype) + return TRUE + var/hw = params["hw"] + var/val = text2num(params["val"]) + switch(hw) + if("cpu") + dev_cpu = val + if("battery") + dev_battery = val + if("disk") + dev_disk = val + if("netcard") + dev_netcard = val + if("tesla") + dev_tesla = val + if("nanoprint") + dev_nanoprint = val + if("card") + dev_card = val + if("aislot") + dev_aislot = val + fabricate_and_recalc_price(FALSE) + return TRUE -/obj/machinery/lapvend/attack_hand(var/mob/user) +/obj/machinery/lapvend/attack_hand(mob/user) if(anchored) ui_interact(user) else to_chat(user, SPAN_NOTICE("\The [src] needs to be anchored to the floor to function!")) -/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/lapvend/ui_interact(mob/user, datum/tgui/ui) if(stat & (BROKEN | NOPOWER | MAINT)) - if(ui) - ui.close() - return 0 + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ComputerFabricator", "Personal Computer Vendor") + ui.open() - var/list/data[0] - data["state"] = state - if(state == 1) +/obj/machinery/lapvend/ui_data(mob/user) + var/list/data = list("state" = src.state) + if(src.state == LAPVEND_STATE_CONFIGURE) data["devtype"] = devtype + data["hw_cpu"] = dev_cpu data["hw_battery"] = dev_battery data["hw_disk"] = dev_disk data["hw_netcard"] = dev_netcard data["hw_tesla"] = dev_tesla data["hw_nanoprint"] = dev_nanoprint data["hw_card"] = dev_card - data["hw_cpu"] = dev_cpu data["hw_aislot"] = dev_aislot - if(state == 1 || state == 2) + if(src.state == LAPVEND_STATE_CONFIGURE || src.state == LAPVEND_STATE_PAYMENT) data["totalprice"] = total_price - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 400) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data /obj/machinery/lapvend/attackby(obj/item/attacking_item, mob/user) if(attacking_item.tool_behaviour == TOOL_WRENCH) @@ -349,10 +350,10 @@ to_chat(user, SPAN_NOTICE("You [anchored ? "un" : ""]secured \the [src]!")) anchored = !anchored return - else if(state == 2) // awaiting payment state - var/obj/item/card/id/I = attacking_item.GetID() - if(I) - if(process_payment(I, attacking_item)) + else if(state == LAPVEND_STATE_PAYMENT) // awaiting payment state + var/obj/item/card/id/id_card = attacking_item.GetID() + if(id_card) + if(process_payment(id_card, attacking_item)) create_device(user) return TRUE else if(istype(attacking_item, /obj/item/card/tech_support)) @@ -360,9 +361,9 @@ return TRUE return ..() -/obj/machinery/lapvend/proc/create_device(mob/user, var/message = "Enjoy your new product!") +/obj/machinery/lapvend/proc/create_device(mob/user, message = "Enjoy your new product!") fabricate_and_recalc_price(TRUE) - if((devtype == 1) && fabricated_laptop) + if((devtype == LAPVEND_DEVICE_LAPTOP) && fabricated_laptop) fabricated_laptop.forceMove(src.loc) if(fabricated_laptop.battery_module) fabricated_laptop.battery_module.charge_to_full() @@ -372,14 +373,14 @@ if(Adjacent(user)) user.put_in_hands(fabricated_laptop) fabricated_laptop = null - else if((devtype == 2) && fabricated_tablet) + else if((devtype == LAPVEND_DEVICE_TABLET) && fabricated_tablet) fabricated_tablet.forceMove(src.loc) if(fabricated_tablet.battery_module) fabricated_tablet.battery_module.charge_to_full() if(Adjacent(user)) user.put_in_hands(fabricated_tablet) fabricated_tablet = null - else if ((devtype == 3) && fabricated_pda) + else if((devtype == LAPVEND_DEVICE_PDA) && fabricated_pda) fabricated_pda.forceMove(src.loc) if(fabricated_pda.battery_module) fabricated_pda.battery_module.charge_to_full() @@ -388,61 +389,70 @@ fabricated_pda = null ping(message) intent_message(MACHINE_SOUND) - state = 3 + state = LAPVEND_STATE_COMPLETE -// Simplified payment processing, returns 1 on success. -/obj/machinery/lapvend/proc/process_payment(var/obj/item/card/id/I, var/obj/item/ID_container) - var/obj/item/spacecash/S = null - if (istype(ID_container, /obj/item/spacecash)) - S = ID_container - if(I==ID_container || ID_container == null) - visible_message("\The [usr] swipes \the [I] through \the [src].") +// Simplified payment processing, returns TRUE on success. +/obj/machinery/lapvend/proc/process_payment(obj/item/card/id/id_card, obj/item/id_container) + var/obj/item/spacecash/cash = null + if(istype(id_container, /obj/item/spacecash)) + cash = id_container + if(id_card == id_container || !id_container) + visible_message("\The [usr] swipes \the [id_card] through \the [src].") else - visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + visible_message("\The [usr] swipes \the [id_container] through \the [src].") playsound(src.loc, 'sound/machines/id_swipe.ogg', 50, 1) - if(I) + if(id_card) //Allow BSTs to take stuff from vendors, for debugging and adminbus purposes - if (istype(I, /obj/item/card/id/bst)) - return 1 - var/datum/money_account/customer_account = SSeconomy.get_account(I.associated_account_number) - if (!customer_account || customer_account.suspended) + if(istype(id_card, /obj/item/card/id/bst)) + return TRUE + var/datum/money_account/customer_account = SSeconomy.get_account(id_card.associated_account_number) + if(!customer_account || customer_account.suspended) ping("Connection error. Unable to connect to account.") - return 0 + return FALSE if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - customer_account = SSeconomy.attempt_account_access(I.associated_account_number, attempt_pin, 2) + customer_account = SSeconomy.attempt_account_access(id_card.associated_account_number, attempt_pin, 2) if(!customer_account) ping("Unable to access account: incorrect credentials.") - return 0 + return FALSE if(total_price > customer_account.money) ping("Insufficient funds in account.") - return 0 + return FALSE else customer_account.money -= total_price - var/datum/transaction/T = new() - T.target_name = "Computer Manufacturer (via [src.name])" - T.purpose = "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"]." - T.amount = total_price - T.source_terminal = src.name - T.date = worlddate2text() - T.time = worldtime2text() - SSeconomy.add_transaction_log(customer_account,T) - return 1 - else if(S) - if(total_price > S.worth) + var/datum/transaction/transaction = new() + transaction.target_name = "Computer Manufacturer (via [src.name])" + transaction.purpose = "Purchase of [(devtype == LAPVEND_DEVICE_LAPTOP) ? "laptop computer" : "tablet microcomputer"]." + transaction.amount = total_price + transaction.source_terminal = src.name + transaction.date = worlddate2text() + transaction.time = worldtime2text() + SSeconomy.add_transaction_log(customer_account, transaction) + return TRUE + else if(cash) + if(total_price > cash.worth) ping("Insufficient funds!") - return 0 + return FALSE else - S.worth -= total_price - if(S.worth <= 0) - qdel(S) + cash.worth -= total_price + if(cash.worth <= 0) + qdel(cash) else - S.update_icon() - return 1 + cash.update_icon() + return TRUE else // just incase ping("You cannot pay with this!") - return 0 + return FALSE + +#undef LAPVEND_STATE_SELECT +#undef LAPVEND_STATE_CONFIGURE +#undef LAPVEND_STATE_PAYMENT +#undef LAPVEND_STATE_COMPLETE +#undef LAPVEND_DEVICE_NONE +#undef LAPVEND_DEVICE_LAPTOP +#undef LAPVEND_DEVICE_TABLET +#undef LAPVEND_DEVICE_PDA diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm index 5bf6720203e..4f6295720dc 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -281,13 +281,13 @@ params["turn"] = pick("45", "-45") else connected.relaymove(usr, connected.dir, accellimit) - addtimer(CALLBACK(src, PROC_REF(updateUsrDialog)), connected.burn_delay + 1) // remove when turning into vueui + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), connected.burn_delay + 1) if (action == "turn") var/ndir = text2num(params["turn"]) if(connected.can_turn()) connected.turn_ship(ndir) - addtimer(CALLBACK(src, PROC_REF(updateUsrDialog)), min(connected.vessel_mass / 10, 1) SECONDS + 1) + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), min(connected.vessel_mass / 10, 1) SECONDS + 1) if (action == "combat_turn") var/ndir = text2num(params["combat_turn"]) @@ -299,7 +299,7 @@ if (action == "brake") connected.decelerate() - addtimer(CALLBACK(src, PROC_REF(updateUsrDialog)), connected.burn_delay + 1) + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), connected.burn_delay + 1) if (action == "apilot") autopilot = !autopilot @@ -309,7 +309,10 @@ return TRUE add_fingerprint(usr) - updateUsrDialog() + SStgui.update_uis(src) + +/obj/machinery/computer/ship/helm/proc/refresh_ui() + SStgui.update_uis(src) /obj/machinery/computer/ship/navigation name = "navigation console" diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 7a9fcd6b0f3..ca5614cbaf2 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -77,7 +77,7 @@ src.beaker = attacking_item user.drop_from_inventory(attacking_item, src) to_chat(user, "You add the beaker to the machine!") - src.updateUsrDialog() + SStgui.update_uis(src) icon_state = "mixer1" CHEMMASTER_BOTTLE_SOUND @@ -91,7 +91,7 @@ src.loaded_pill_bottle = attacking_item user.drop_from_inventory(attacking_item, src) to_chat(user, "You add the pill bottle into the dispenser slot!") - src.updateUsrDialog() + SStgui.update_uis(src) else if(attacking_item.tool_behaviour == TOOL_WRENCH) anchored = !anchored to_chat(user, "You [anchored ? "attach" : "detach"] the [src] [anchored ? "to" : "from"] the ground") @@ -381,7 +381,7 @@ src.beaker = attacking_item user.drop_from_inventory(attacking_item,src) update_icon() - src.updateUsrDialog() + SStgui.update_uis(src) return 0 if(holdingitems && holdingitems.len >= limit) @@ -412,7 +412,7 @@ else to_chat(user, "You fill \the [src] from \the [attacking_item].") - src.updateUsrDialog() + SStgui.update_uis(src) return 0 if(!sheet_reagents[attacking_item.type] && (!attacking_item.reagents || !attacking_item.reagents.total_volume)) @@ -422,7 +422,7 @@ user.remove_from_mob(attacking_item) attacking_item.forceMove(src) holdingitems += attacking_item - src.updateUsrDialog() + SStgui.update_uis(src) return 0 /obj/machinery/reagentgrinder/attack_ai(mob/user as mob) @@ -491,7 +491,7 @@ eject() if ("detach") detach(usr) - src.updateUsrDialog() + SStgui.update_uis(src) return 1 /obj/machinery/reagentgrinder/proc/detach(var/mob/user) @@ -576,7 +576,7 @@ /obj/machinery/reagentgrinder/proc/grind_reset() inuse = FALSE - updateUsrDialog() + SStgui.update_uis(src) /obj/machinery/reagentgrinder/mouse_drop_receive(atom/dropped, mob/user, params) diff --git a/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm index 90014083a14..9659872fc07 100644 --- a/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm +++ b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm @@ -1,9 +1,18 @@ +// Continuous-stress radiation loop tuning. See `process()` block for the full rationale. +#define RC_RAD_MAX 50 // hard cap on emission power +#define RC_RAD_RAMP_NORMAL 1 // severity added per second while shield is down (no spike) +#define RC_RAD_RAMP_SPIKE 10 // severity added per second while shield is down during a radspike +#define RC_RAD_DECAY_RATE 25 // severity removed per second while shield is up (or after spike ends) +#define RC_SPIKE_INTERVAL_LO 15 // seconds between spike events (lower bound) +#define RC_SPIKE_INTERVAL_HI 25 // seconds between spike events (upper bound) +#define RC_SPIKE_DURATION_LO 5 // seconds a single spike lasts (lower bound) +#define RC_SPIKE_DURATION_HI 7 // seconds a single spike lasts (upper bound) /obj/machinery/radiocarbon_spectrometer name = "radiocarbon spectrometer" desc = "A specialised, complex scanner for gleaning information on all manner of small things." - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE atom_flags = ATOM_FLAG_OPEN_CONTAINER icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "spectrometer" @@ -12,7 +21,7 @@ active_power_usage = 300 //var/obj/item/reagent_containers/glass/coolant_container - var/scanning = 0 + var/scanning = FALSE var/report_num = 0 // var/obj/item/scanned_item @@ -40,9 +49,21 @@ var/tleft_retarget_optimal_wavelength = 0 var/maser_efficiency = 0 // - var/radiation = 0 //0-100 mSv - var/t_left_radspike = 0 - var/rad_shield = 0 + /// Current emission power being pushed into SSradiation. Climbs while the shield is down, + /// decays while it is up. Capped to RC_RAD_MAX so even RAD_SHIELDED suits keep meaningful + /// mitigation at adjacency (over the cap, the armor_pen formula in /mob/living/rad_act + /// starts overwhelming the suit's RAD value). + var/radiation_emission = 0 + /// TRUE while a radspike event is currently active. Increases the shield-down ramp rate + /// from RC_RAD_RAMP_NORMAL to RC_RAD_RAMP_SPIKE. + var/spike_active = FALSE + /// Seconds remaining in the current spike event. Used and decremented only while + /// `spike_active` is TRUE. + var/spike_remaining = 0 + /// Seconds until the next spike event begins. Counted down only while `spike_active` + /// is FALSE; reaches 0 → flips spike_active TRUE. + var/t_to_next_spike = 0 + var/rad_shield = FALSE /obj/machinery/radiocarbon_spectrometer/Initialize() . = ..() @@ -60,7 +81,7 @@ coolant_reagents_purity[/singleton/reagent/coolant] = 1 coolant_reagents_purity[/singleton/reagent/adminordrazine] = 2 -/obj/machinery/radiocarbon_spectrometer/attack_hand(var/mob/user as mob) +/obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user) ui_interact(user) /obj/machinery/radiocarbon_spectrometer/attackby(obj/item/attacking_item, mob/user) @@ -70,24 +91,24 @@ if(istype(attacking_item, /obj/item/stack/nanopaste)) var/choice = alert("What do you want to do with the nanopaste?","Radiometric Scanner","Scan nanopaste","Fix seal integrity") if(choice == "Fix seal integrity") - var/obj/item/stack/nanopaste/N = attacking_item - var/amount_used = min(N.get_amount(), 10 - scanner_seal_integrity / 10) - N.use(amount_used) + var/obj/item/stack/nanopaste/nanopaste_stack = attacking_item + var/amount_used = min(nanopaste_stack.get_amount(), 10 - scanner_seal_integrity / 10) + nanopaste_stack.use(amount_used) scanner_seal_integrity = round(scanner_seal_integrity + amount_used * 10) return if(istype(attacking_item, /obj/item/reagent_containers/glass)) var/choice = alert("What do you want to do with the container?","Radiometric Scanner","Add coolant","Empty coolant","Scan container") if(choice == "Add coolant") - var/obj/item/reagent_containers/glass/G = attacking_item - var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, G.reagents.total_volume) - G.reagents.trans_to(src, amount_transferred) + var/obj/item/reagent_containers/glass/glass_container = attacking_item + var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, glass_container.reagents.total_volume) + glass_container.reagents.trans_to(src, amount_transferred) to_chat(user, "You empty [amount_transferred]u of coolant into [src].") update_coolant() return else if(choice == "Empty coolant") - var/obj/item/reagent_containers/glass/G = attacking_item - var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, src.reagents.total_volume) - src.reagents.trans_to(G, amount_transferred) + var/obj/item/reagent_containers/glass/glass_container = attacking_item + var/amount_transferred = min(glass_container.reagents.maximum_volume - glass_container.reagents.total_volume, src.reagents.total_volume) + src.reagents.trans_to(glass_container, amount_transferred) to_chat(user, "You remove [amount_transferred]u of coolant from [src].") update_coolant() return @@ -102,65 +123,51 @@ var/total_purity = 0 fresh_coolant = 0 coolant_purity = 0 - var/num_reagent_types = 0 - for (var/_current_reagent in reagents.reagent_volumes) - var/singleton/reagent/current_reagent = GET_SINGLETON(_current_reagent) - if (!current_reagent) + for(var/reagent_type in reagents.reagent_volumes) + var/singleton/reagent/reagent = GET_SINGLETON(reagent_type) + if(!reagent) continue - var/cur_purity = coolant_reagents_purity[_current_reagent] - if(!cur_purity) - cur_purity = 0.1 - else if(cur_purity > 1) - cur_purity = 1 - total_purity += cur_purity * REAGENT_VOLUME(reagents, _current_reagent) - fresh_coolant += REAGENT_VOLUME(reagents, _current_reagent) - num_reagent_types += 1 + var/purity_value = coolant_reagents_purity[reagent_type] + if(!purity_value) + purity_value = 0.1 + else if(purity_value > 1) + purity_value = 1 + total_purity += purity_value * REAGENT_VOLUME(reagents, reagent_type) + fresh_coolant += REAGENT_VOLUME(reagents, reagent_type) if(total_purity && fresh_coolant) coolant_purity = total_purity / fresh_coolant -/obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - +/obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, datum/tgui/ui) if(user.stat) return - - // this is the data which will be sent to the ui - var/data[0] - data["scanned_item"] = (scanned_item ? scanned_item.name : "") - data["scanned_item_desc"] = (scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "") - data["last_scan_data"] = last_scan_data - // - data["scan_progress"] = round(scanner_progress) - data["scanning"] = scanning - // - data["scanner_seal_integrity"] = round(scanner_seal_integrity) - data["scanner_rpm"] = round(scanner_rpm) - data["scanner_temperature"] = round(scanner_temperature) - // - data["coolant_usage_rate"] = "[coolant_usage_rate]" - data["unused_coolant_abs"] = round(fresh_coolant) - data["unused_coolant_per"] = round(fresh_coolant / reagents.maximum_volume * 100) - data["coolant_purity"] = "[coolant_purity * 100]" - // - data["optimal_wavelength"] = round(optimal_wavelength) - data["maser_wavelength"] = round(maser_wavelength) - data["maser_efficiency"] = round(maser_efficiency * 100) - // - data["radiation"] = round(radiation) - data["t_left_radspike"] = round(t_left_radspike) - data["rad_shield_on"] = rad_shield - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "geoscanner.tmpl", "High Res Radiocarbon Spectrometer", 900, 825) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GeoScanner", "High Res Radiocarbon Spectrometer") ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) + +/obj/machinery/radiocarbon_spectrometer/ui_data(mob/user) + return list( + "scanned_item" = scanned_item ? scanned_item.name : "", + "scanned_item_desc" = scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "", + "last_scan_data" = last_scan_data, + "scan_progress" = round(scanner_progress), + "scanning" = scanning, + "scanner_seal_integrity" = round(scanner_seal_integrity), + "scanner_rpm" = round(scanner_rpm), + "scanner_temperature" = round(scanner_temperature), + "coolant_usage_rate" = coolant_usage_rate, + "unused_coolant_abs" = round(fresh_coolant), + "unused_coolant_per" = round(fresh_coolant / reagents.maximum_volume * 100), + "coolant_purity" = round(coolant_purity * 100), + "optimal_wavelength" = round(optimal_wavelength), + "maser_wavelength" = round(maser_wavelength), + "maser_efficiency" = round(maser_efficiency * 100), + "radiation_emission" = round(radiation_emission), + "radiation_max" = RC_RAD_MAX, + "spike_active" = spike_active, + "t_to_next_spike" = round(t_to_next_spike), + "rad_shield_on" = rad_shield + ) /obj/machinery/radiocarbon_spectrometer/process() if(scanning) @@ -187,21 +194,32 @@ //each unit of coolant scanner_temperature += scanner_rpm * deltaT * 0.05 - //radiation - t_left_radspike -= deltaT - if(t_left_radspike > 0) - //ordinary radiation - radiation = rand() * 15 + // Continuous-stress radiation loop with random spike events. Emission climbs while the + // shield is down and decays when it is raised. RC_RAD_RAMP_NORMAL is the baseline rate; + // during a spike the rate switches to RC_RAD_RAMP_SPIKE, fast enough that letting it run + // 5+ seconds will saturate to the cap. Spike events fire on a randomised interval so the + // player has to actually watch the meter / geiger rather than memorise a tempo. The cap + // (RC_RAD_MAX) is set just below where /mob/living/rad_act's armor_pen formula starts + // overwhelming RAD_SHIELDED suits, so the anomaly suit (RAD=100) still grants meaningful + // mitigation at adjacency even at the worst case. + if(spike_active) + spike_remaining -= deltaT + if(spike_remaining <= 0) + spike_active = FALSE + t_to_next_spike = rand(RC_SPIKE_INTERVAL_LO, RC_SPIKE_INTERVAL_HI) else - //radspike - if(t_left_radspike > -5) - radiation = rand() * 15 + 85 - if(!rad_shield) - //irradiate nearby mobs - for(var/mob/living/M in view(7,src)) - M.apply_damage(radiation / 25, DAMAGE_RADIATION, damage_flags = DAMAGE_FLAG_DISPERSED) - else - t_left_radspike = pick(10,15,25) + t_to_next_spike -= deltaT + if(t_to_next_spike <= 0) + spike_active = TRUE + spike_remaining = rand(RC_SPIKE_DURATION_LO, RC_SPIKE_DURATION_HI) + + if(rad_shield) + radiation_emission = max(radiation_emission - RC_RAD_DECAY_RATE * deltaT, 0) + else + var/ramp = spike_active ? RC_RAD_RAMP_SPIKE : RC_RAD_RAMP_NORMAL + radiation_emission = min(radiation_emission + ramp * deltaT, RC_RAD_MAX) + if(radiation_emission > 0) + SSradiation.radiate(src, radiation_emission) //use some coolant to cool down if(coolant_usage_rate > 0) @@ -248,15 +266,17 @@ last_process_worldtime = world.time /obj/machinery/radiocarbon_spectrometer/proc/stop_scanning() - scanning = 0 + scanning = FALSE scanner_rpm_dir = 1 scanner_rpm = 0 optimal_wavelength = 0 maser_efficiency = 0 maser_wavelength = 0 coolant_usage_rate = 0 - radiation = 0 - t_left_radspike = 0 + radiation_emission = 0 + spike_active = FALSE + spike_remaining = 0 + t_to_next_spike = 0 if(used_coolant) src.reagents.remove_any(used_coolant) used_coolant = 0 @@ -266,99 +286,111 @@ if(scanned_item) //create report - var/obj/item/paper/P = new(src) - P.name = "[src] report #[++report_num]: [scanned_item.name]" - P.stamped = list(/obj/item/stamp) - P.overlays = list("paper_stamped") + var/obj/item/paper/report_paper = new(src) + report_paper.name = "[src] report #[++report_num]: [scanned_item.name]" + report_paper.stamped = list(/obj/item/stamp) + report_paper.overlays = list("paper_stamped") //work out data var/data = " - Mundane object: [scanned_item.desc ? scanned_item.desc : "No information on record."]
" - var/datum/geosample/G + var/datum/geosample/geosample_data switch(scanned_item.type) if(/obj/item/ore) - var/obj/item/ore/O = scanned_item - if(O.geologic_data) - G = O.geologic_data + var/obj/item/ore/ore_item = scanned_item + if(ore_item.geologic_data) + geosample_data = ore_item.geologic_data if(/obj/item/rocksliver) - var/obj/item/rocksliver/O = scanned_item - if(O.geologic_data) - G = O.geologic_data + var/obj/item/rocksliver/rock_sliver = scanned_item + if(rock_sliver.geologic_data) + geosample_data = rock_sliver.geologic_data if(/obj/item/archaeological_find) data = " - Mundane object (archaic xenos origins)
" - var/obj/item/archaeological_find/A = scanned_item - if(A.talking_atom) + var/obj/item/archaeological_find/arch_find = scanned_item + if(arch_find.talking_atom) data = " - Exhibits properties consistent with sonic reproduction and audio capture technologies.
" - var/anom_found = 0 - if(G) - data = " - Spectometric analysis on mineral sample has determined type [GLOB.finds_as_strings[GLOB.responsive_carriers.Find(G.source_mineral)]]
" - if(G.age_billion > 0) - data += " - Radiometric dating shows age of [G.age_billion].[G.age_million] billion years
" - else if(G.age_million > 0) - data += " - Radiometric dating shows age of [G.age_million].[G.age_thousand] million years
" + var/anom_found = FALSE + if(geosample_data) + data = " - Spectometric analysis on mineral sample has determined type [GLOB.finds_as_strings[GLOB.responsive_carriers.Find(geosample_data.source_mineral)]]
" + if(geosample_data.age_billion > 0) + data += " - Radiometric dating shows age of [geosample_data.age_billion].[geosample_data.age_million] billion years
" + else if(geosample_data.age_million > 0) + data += " - Radiometric dating shows age of [geosample_data.age_million].[geosample_data.age_thousand] million years
" else - data += " - Radiometric dating shows age of [G.age_thousand * 1000 + G.age] years
" + data += " - Radiometric dating shows age of [geosample_data.age_thousand * 1000 + geosample_data.age] years
" data += " - Chromatographic analysis shows the following materials present:
" - for(var/carrier in G.find_presence) - if(G.find_presence[carrier]) + for(var/carrier in geosample_data.find_presence) + if(geosample_data.find_presence[carrier]) var/index = GLOB.responsive_carriers.Find(carrier) if(index > 0 && index <= GLOB.finds_as_strings.len) - data += " > [100 * G.find_presence[carrier]]% [GLOB.finds_as_strings[index]]
" + data += " > [100 * geosample_data.find_presence[carrier]]% [GLOB.finds_as_strings[index]]
" - if(G.artifact_id && G.artifact_distance >= 0) - anom_found = 1 - data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [G.artifact_id]
" - data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [G.artifact_distance]m
" + if(geosample_data.artifact_id && geosample_data.artifact_distance >= 0) + anom_found = TRUE + data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [geosample_data.artifact_id]
" + data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [geosample_data.artifact_distance]m
" if(!anom_found) data += " - No anomalous data
" - P.info = "[src] analysis report #[report_num]
" - P.info += "Scanned item: [scanned_item.name]

" + data - last_scan_data = P.info - P.forceMove(src.loc) + report_paper.info = "[src] analysis report #[report_num]
" + report_paper.info += "Scanned item: [scanned_item.name]

" + data + last_scan_data = report_paper.info + report_paper.forceMove(src.loc) scanned_item.forceMove(src.loc) scanned_item = null -/obj/machinery/radiocarbon_spectrometer/Topic(href, href_list) +/obj/machinery/radiocarbon_spectrometer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return if(stat & (NOPOWER|BROKEN)) - return 0 // don't update UIs attached to this object + return - if(href_list["scanItem"]) - if(scanning) - stop_scanning() - else - if(scanned_item) - if(scanner_seal_integrity > 0) - scanner_progress = 0 - scanning = 1 - t_left_radspike = pick(5,10,15) - to_chat(usr, SPAN_NOTICE("Scan initiated.")) - else - to_chat(usr, SPAN_WARNING("Could not initiate scan, seal requires replacing.")) + switch(action) + if("scan_item") + if(scanning) + stop_scanning() else - to_chat(usr, SPAN_WARNING("Insert an item to scan.")) + if(scanned_item) + if(scanner_seal_integrity > 0) + scanner_progress = 0 + scanning = TRUE + t_to_next_spike = rand(RC_SPIKE_INTERVAL_LO, RC_SPIKE_INTERVAL_HI) + to_chat(usr, SPAN_NOTICE("Scan initiated.")) + else + to_chat(usr, SPAN_WARNING("Could not initiate scan, seal requires replacing.")) + else + to_chat(usr, SPAN_WARNING("Insert an item to scan.")) + return TRUE - if(href_list["maserWavelength"]) - maser_wavelength = max(min(maser_wavelength + 1000 * text2num(href_list["maserWavelength"]), 10000), 1) + if("maser_wavelength") + maser_wavelength = clamp(maser_wavelength + 1000 * text2num(params["delta"]), 1, 10000) + return TRUE - if(href_list["coolantRate"]) - coolant_usage_rate = max(min(coolant_usage_rate + text2num(href_list["coolantRate"]), 10000), 0) + if("coolant_rate") + coolant_usage_rate = clamp(coolant_usage_rate + text2num(params["delta"]), 0, 10000) + return TRUE - if(href_list["toggle_rad_shield"]) - if(rad_shield) - rad_shield = 0 - else - rad_shield = 1 + if("toggle_rad_shield") + rad_shield = !rad_shield + return TRUE - if(href_list["ejectItem"]) - if(scanned_item) - scanned_item.forceMove(src.loc) - scanned_item = null + if("eject_item") + if(scanned_item) + scanned_item.forceMove(src.loc) + scanned_item = null + return TRUE - add_fingerprint(usr) - return 1 // update UIs attached to this object +#undef RC_RAD_MAX +#undef RC_RAD_RAMP_NORMAL +#undef RC_RAD_RAMP_SPIKE +#undef RC_RAD_DECAY_RATE +#undef RC_SPIKE_INTERVAL_LO +#undef RC_SPIKE_INTERVAL_HI +#undef RC_SPIKE_DURATION_LO +#undef RC_SPIKE_DURATION_HI diff --git a/code/modules/synthesized_instruments/echo_editor.dm b/code/modules/synthesized_instruments/echo_editor.dm index 9bd4f3e335e..4727f033798 100644 --- a/code/modules/synthesized_instruments/echo_editor.dm +++ b/code/modules/synthesized_instruments/echo_editor.dm @@ -1,63 +1,62 @@ -/datum/nano_module/echo_editor +/datum/instrument_ui/echo_editor name = "Echo Editor" - available_to_ai = 0 var/datum/sound_player/player - var/atom/source -/datum/nano_module/echo_editor/New(datum/sound_player/player) +/datum/instrument_ui/echo_editor/New(datum/sound_player/player) + ..() src.host = player.actual_instrument src.player = player -/datum/nano_module/echo_editor/ui_interact(mob/user, ui_key = "echo_editor", datum/nanoui/ui = null, force_open = 0) - var/list/list/data = list() - data["echo_params"] = list() - for (var/i=1 to 18) - var/list/echo_data = list() - echo_data["index"] = i - echo_data["name"] = GLOB.musical_config.echo_param_names[i] - echo_data["value"] = src.player.echo[i] - echo_data["real"] = GLOB.musical_config.echo_params_bounds[i][3] - data["echo_params"] += list(echo_data) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new (user, src, ui_key, "echo_editor.tmpl", "Echo Editor", 300, 600) - ui.set_initial_data(data) +/datum/instrument_ui/echo_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EchoEditor", "Echo Editor") ui.open() +/datum/instrument_ui/echo_editor/ui_data(mob/user) + var/list/echo_params = list() + for(var/i = 1 to 18) + echo_params += list(list( + "index" = i, + "name" = GLOB.musical_config.echo_param_names[i], + "value" = src.player.echo[i], + "real" = GLOB.musical_config.echo_params_bounds[i][3] + )) + return list("echo_params" = echo_params) -/datum/nano_module/echo_editor/Topic(href, href_list) - if (..()) - return 1 +/datum/instrument_ui/echo_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return - var/target = href_list["target"] - var/index = text2num(href_list["index"]) - if (href_list["index"] && !(index in 1 to 18)) - to_chat(usr, "Wrong index was provided: [index]") - return 0 + var/index = text2num(params["index"]) + if(!isnum(index) || !(index in 1 to 18)) + return - var/name = GLOB.musical_config.echo_param_names[index] - var/desc = GLOB.musical_config.echo_param_desc[index] - var/default = GLOB.musical_config.echo_default[index] + var/param_name = GLOB.musical_config.echo_param_names[index] + var/param_desc = GLOB.musical_config.echo_param_desc[index] + var/param_default = GLOB.musical_config.echo_default[index] var/list/bounds = GLOB.musical_config.echo_params_bounds[index] var/bound_min = bounds[1] var/bound_max = bounds[2] var/reals_allowed = bounds[3] - switch (target) - if ("set") - var/new_value = min(max(input(usr, "[name]: [bound_min] - [bound_max]") as num, bound_min), bound_max) - if (!isnum(new_value)) - return + switch(action) + if("set") + var/new_value = tgui_input_number(usr, "[param_name]: [bound_min] - [bound_max]", "Echo Parameter", src.player.echo[index], bound_max, bound_min) + if(isnull(new_value)) + return TRUE new_value = reals_allowed ? new_value : round(new_value) - src.player.echo[index] = new_value - if ("reset") - src.player.echo[index] = default - if ("reset_all") + src.player.echo[index] = clamp(new_value, bound_min, bound_max) + return TRUE + if("reset") + src.player.echo[index] = param_default + return TRUE + if("reset_all") src.player.echo = GLOB.musical_config.echo_default.Copy() - if ("desc") - to_chat(usr, "[name]: from [bound_min] to [bound_max] (default: [default])
[desc]") - - return 1 + return TRUE + if("desc") + to_chat(usr, "[param_name]: from [bound_min] to [bound_max] (default: [param_default])
[param_desc]") + return TRUE diff --git a/code/modules/synthesized_instruments/env_editor.dm b/code/modules/synthesized_instruments/env_editor.dm index bc1f42a03ba..733e21191fb 100644 --- a/code/modules/synthesized_instruments/env_editor.dm +++ b/code/modules/synthesized_instruments/env_editor.dm @@ -1,65 +1,72 @@ -/datum/nano_module/env_editor +/datum/instrument_ui/env_editor name = "Environment Editor" - available_to_ai = 0 var/datum/sound_player/player -/datum/nano_module/env_editor/New(datum/sound_player/player) +/datum/instrument_ui/env_editor/New(datum/sound_player/player) + ..() src.host = player.actual_instrument src.player = player -/datum/nano_module/env_editor/ui_interact(mob/user, ui_key = "env_editor", datum/nanoui/ui = null, force_open = 0) - var/list/list/data = list() - data["env_params"] = list() - for (var/i=1 to 23) - var/list/env_data = list() - env_data["index"] = i - env_data["name"] = GLOB.musical_config.env_param_names[i] - env_data["value"] = src.player.env[i] - env_data["real"] = GLOB.musical_config.env_params_bounds[i][3] - data["env_params"] += list(env_data) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new (user, src, ui_key, "env_editor.tmpl", "Environment Editor", 300, 800) - ui.set_initial_data(data) +/datum/instrument_ui/env_editor/ui_interact(mob/user, datum/tgui/ui) + if(!GLOB.musical_config.env_settings_available) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EnvEditor", "Environment Editor") ui.open() +/datum/instrument_ui/env_editor/ui_data(mob/user) + var/list/env_params = list() + for(var/i = 1 to 23) + var/list/bounds = GLOB.musical_config.env_params_bounds[i] + env_params += list(list( + "index" = i, + "name" = GLOB.musical_config.env_param_names[i], + "value" = src.player.env[i], + "min" = bounds[1], + "max" = bounds[2], + "real" = bounds[3], + "default" = GLOB.musical_config.env_default[i] + )) + return list("env_params" = env_params) -/datum/nano_module/env_editor/Topic(href, href_list) - if (!GLOB.musical_config.env_settings_available) - return 0 +/datum/instrument_ui/env_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return - if (..()) - return 1 + if(!GLOB.musical_config.env_settings_available) + return - var/target = href_list["target"] - var/index = text2num(href_list["index"]) - if (href_list["index"] && !(index in 1 to 23)) - to_chat(usr, "Wrong index was provided: [index]") - return 0 + if(action == "reset_all") + src.player.env = GLOB.musical_config.env_default.Copy() + return TRUE - var/name = GLOB.musical_config.env_param_names[index] - var/desc = GLOB.musical_config.env_param_desc[index] - var/default = GLOB.musical_config.env_default[index] + var/index = text2num(params["index"]) + if(!isnum(index) || !(index in 1 to 23)) + return + + var/param_name = GLOB.musical_config.env_param_names[index] + var/param_desc = GLOB.musical_config.env_param_desc[index] + var/param_default = GLOB.musical_config.env_default[index] var/list/bounds = GLOB.musical_config.env_params_bounds[index] var/bound_min = bounds[1] var/bound_max = bounds[2] var/reals_allowed = bounds[3] - switch (target) - if ("set") - var/new_value = min(max(input(usr, "[name]: [bound_min] - [bound_max]") as num, bound_min), bound_max) - if (!isnum(new_value)) - return + switch(action) + if("set") + var/new_value = tgui_input_number(usr, "[param_name]: [bound_min] - [bound_max]", "Environment Parameter", src.player.env[index], bound_max, bound_min) + if(isnull(new_value)) + return TRUE new_value = reals_allowed ? new_value : round(new_value) - src.player.env[index] = new_value - if ("reset") - src.player.env[index] = default - if ("reset_all") - src.player.env = GLOB.musical_config.env_default.Copy() - if ("desc") - to_chat(usr, "[name]: from [bound_min] to [bound_max] (default: [default])
[desc]") - - return 1 + src.player.env[index] = clamp(new_value, bound_min, bound_max) + return TRUE + if("reset") + src.player.env[index] = param_default + return TRUE + if("desc") + to_chat(usr, "[param_name]: from [bound_min] to [bound_max] (default: [param_default])
[param_desc]") + return TRUE diff --git a/code/modules/synthesized_instruments/globals.dm b/code/modules/synthesized_instruments/globals.dm index cfcf37bef6f..8b2e31d181f 100644 --- a/code/modules/synthesized_instruments/globals.dm +++ b/code/modules/synthesized_instruments/globals.dm @@ -21,6 +21,9 @@ GLOBAL_DATUM_INIT(musical_config, /datum/musical_config, new()) var/usage_info_event_resolution = 8 var/env_settings_available = 1 + /// When FALSE, the Custom virtual environment cannot be selected from the synthesizer UI. + /// The env editor still exists but stays unreachable through the normal flow. + var/can_use_custom = FALSE var/list/env_default = list(7.5, 1.0, -1000, -100, 0, 1.49, 0.83, 1.0, -2602, 0.0007, 200, 0.011, 0.25, 0.0, 0.25, 0.0, -5.0, 5000, 250.0, 0.0, 100, 100, 63) var/list/list/env_params_bounds = list( diff --git a/code/modules/synthesized_instruments/instrument_ui.dm b/code/modules/synthesized_instruments/instrument_ui.dm new file mode 100644 index 00000000000..2ffcff86a11 --- /dev/null +++ b/code/modules/synthesized_instruments/instrument_ui.dm @@ -0,0 +1,15 @@ +// Base datum for synthesized instrument UI modules (song editor, echo editor, usage info). +// Provides the minimum interface expected by SStgui: a host reference and ui_host(). +/datum/instrument_ui + /// Display name shown in the TGUI window title bar. + var/name + /// The instrument atom that owns this UI datum; used to resolve the UI host for SStgui. + var/atom/host + +/// Delegates host resolution to the referenced instrument atom, enabling SStgui to find the UI host. +/datum/instrument_ui/ui_host(mob/user) + return host?.ui_host() + +/datum/instrument_ui/Destroy() + host = null + return ..() diff --git a/code/modules/synthesized_instruments/real_instruments.dm b/code/modules/synthesized_instruments/real_instruments.dm index 76e7b833854..17e6d2bf208 100644 --- a/code/modules/synthesized_instruments/real_instruments.dm +++ b/code/modules/synthesized_instruments/real_instruments.dm @@ -3,13 +3,13 @@ /datum/real_instrument var/datum/instrument/instruments var/datum/sound_player/player - var/datum/nano_module/song_editor/song_editor - var/datum/nano_module/usage_info/usage_info + var/datum/instrument_ui/song_editor/song_editor + var/datum/instrument_ui/usage_info/usage_info var/maximum_lines var/maximum_line_length var/obj/owner - var/datum/nano_module/env_editor/env_editor - var/datum/nano_module/echo_editor/echo_editor + var/datum/instrument_ui/env_editor/env_editor + var/datum/instrument_ui/echo_editor/echo_editor /datum/real_instrument/New(obj/who, datum/sound_player/how, datum/instrument/what) player = how @@ -18,142 +18,9 @@ maximum_line_length = GLOB.musical_config.max_line_length instruments = what //This can be a list, or it can also not be one -/datum/real_instrument/proc/Topic_call(href, href_list, user) - var/target = href_list["target"] - var/value = text2num(href_list["value"]) - if (href_list["value"] && !isnum(value)) - to_chat(user, "Non-numeric value was given") - return 0 - - - switch (target) - if ("tempo") src.player.song.tempo = src.player.song.sanitize_tempo(src.player.song.tempo + value*world.tick_lag) - if ("play") - src.player.song.playing = value - if (src.player.song.playing) - GLOB.instrument_synchronizer.raise_event(player.actual_instrument) - src.player.song.play_song(user) - if ("wait") - if(value) - src.player.wait = WEAKREF(user) - else - src.player.wait = null - if ("newsong") - src.player.song.lines.Cut() - src.player.song.tempo = src.player.song.sanitize_tempo(5) // default 120 BPM - if ("import") - var/t = "" - do - t = html_encode(input(user, "Please paste the entire song, formatted:", "[owner.name]", t) as message) - if(!CanInteractWith(user, owner, GLOB.physical_state)) - return - - if(length(t) >= 2*src.maximum_lines*src.maximum_line_length) - var/cont = input(user, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") - if(!CanInteractWith(user, owner, GLOB.physical_state)) - return - if(cont == "no") - break - while(length(t) > 2*src.maximum_lines*src.maximum_line_length) - if (length(t)) - src.player.song.lines = splittext(t, "\n") - if(copytext(src.player.song.lines[1],1,6) == "BPM: ") - if(text2num(copytext(src.player.song.lines[1],6)) != 0) - src.player.song.tempo = src.player.song.sanitize_tempo(600 / text2num(copytext(src.player.song.lines[1],6))) - src.player.song.lines.Cut(1,2) - else - src.player.song.tempo = src.player.song.sanitize_tempo(5) - else - src.player.song.tempo = src.player.song.sanitize_tempo(5) // default 120 BPM - if(src.player.song.lines.len > maximum_lines) - to_chat(user,"Too many lines!") - src.player.song.lines.Cut(maximum_lines+1) - var/linenum = 1 - for(var/l in src.player.song.lines) - if(length(l) > maximum_line_length) - to_chat(user, "Line [linenum] too long!") - src.player.song.lines.Remove(l) - else - linenum++ - if ("show_song_editor") - if (!src.song_editor) - src.song_editor = new (host = src.owner, song = src.player.song) - src.song_editor.ui_interact(user) - - if ("show_usage") - if (!src.usage_info) - src.usage_info = new (owner, src.player) - src.usage_info.ui_interact(user) - if ("volume") - src.player.volume = min(max(min(player.volume+text2num(value), 100), 0), player.max_volume) - if ("transposition") - src.player.song.transposition = max(min(player.song.transposition+value, GLOB.musical_config.highest_transposition), GLOB.musical_config.lowest_transposition) - if ("min_octave") - src.player.song.octave_range_min = max(min(player.song.octave_range_min+value, GLOB.musical_config.highest_octave), GLOB.musical_config.lowest_octave) - src.player.song.octave_range_max = max(player.song.octave_range_max, player.song.octave_range_min) - if ("max_octave") - src.player.song.octave_range_max = max(min(player.song.octave_range_max+value, GLOB.musical_config.highest_octave), GLOB.musical_config.lowest_octave) - src.player.song.octave_range_min = min(player.song.octave_range_max, player.song.octave_range_min) - if ("sustain_timer") - src.player.song.sustain_timer = max(min(player.song.sustain_timer+value, GLOB.musical_config.longest_sustain_timer), 1) - if ("soft_coeff") - var/new_coeff = input(user, "from [GLOB.musical_config.gentlest_drop] to [GLOB.musical_config.steepest_drop]") as num - if(!CanInteractWith(user, owner, GLOB.physical_state)) - return - new_coeff = round(min(max(new_coeff, GLOB.musical_config.gentlest_drop), GLOB.musical_config.steepest_drop), 0.001) - src.player.song.soft_coeff = new_coeff - if ("instrument") - if (!islist(instruments)) - return - var/list/as_list = instruments - var/list/categories = list() - for (var/key in as_list) - var/datum/instrument/instrument = as_list[key] - categories |= instrument.category - - var/category = input(user, "Choose a category") as null|anything in categories - if(!CanInteractWith(user, owner, GLOB.physical_state)) - return - var/list/instruments_available = list() - for (var/key in as_list) - var/datum/instrument/instrument = as_list[key] - if (instrument.category == category) - instruments_available += key - - var/new_instrument = input(user, "Choose an instrument") as null|anything in instruments_available - if(!CanInteractWith(user, owner, GLOB.physical_state)) - return - if (new_instrument) - src.player.song.instrument_data = as_list[new_instrument] - if ("autorepeat") src.player.song.autorepeat = value - if ("decay") src.player.song.linear_decay = value - if ("echo") src.player.apply_echo = value - if ("show_env_editor") - if (GLOB.musical_config.env_settings_available) - if (!src.env_editor) - src.env_editor = new (src.player) - src.env_editor.ui_interact(user) - else - to_chat(user, "Virtual environment is disabled") - - if ("show_echo_editor") - if (!src.echo_editor) - src.echo_editor = new (src.player) - src.echo_editor.ui_interact(user) - - if ("select_env") - if (value in -1 to 26) - src.player.virtual_environment_selected = round(value) - else - return 0 - - return 1 - - - -/datum/real_instrument/proc/ui_call(mob/user, ui_key, datum/nanoui/ui = null, force_open = 0) - var/list/data - data = list( +/// Builds the data list consumed by the Synthesizer TGUI. Called from each instrument atom's ui_data(). +/datum/real_instrument/proc/build_ui_data() + return list( "playback" = list( "playing" = src.player.song.playing, "autorepeat" = src.player.song.autorepeat, @@ -162,7 +29,7 @@ "basic_options" = list( "cur_instrument" = src.player.song.instrument_data.name, "volume" = src.player.volume, - "BPM" = round(600 / src.player.song.tempo), + "bpm" = round(600 / src.player.song.tempo), "transposition" = src.player.song.transposition, "octave_range" = list( "min" = src.player.song.octave_range_min, @@ -172,7 +39,8 @@ "advanced_options" = list( "all_environments" = GLOB.musical_config.all_environments, "selected_environment" = GLOB.musical_config.id_to_environment(src.player.virtual_environment_selected), - "apply_echo" = src.player.apply_echo + "apply_echo" = src.player.apply_echo, + "can_use_custom" = GLOB.musical_config.can_use_custom ), "sustain" = list( "linear_decay_active" = src.player.song.linear_decay, @@ -188,22 +56,156 @@ "channels" = src.player.song.available_channels, "events" = src.player.event_manager.events.len, "max_channels" = GLOB.musical_config.channels_per_instrument, - "max_events" = GLOB.musical_config.max_events, + "max_events" = GLOB.musical_config.max_events ) ) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new (user, src.owner, ui_key, "synthesizer.tmpl", owner.name, 600, 800) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - - +/// Dispatches a TGUI action sent from the Synthesizer interface. Returns TRUE if handled. +/datum/real_instrument/proc/handle_ui_act(action, list/params, mob/user) + var/value = text2num(params["value"]) + switch(action) + if("tempo") + src.player.song.tempo = src.player.song.sanitize_tempo(src.player.song.tempo + value * world.tick_lag) + return TRUE + if("play") + src.player.song.playing = value + if(src.player.song.playing) + GLOB.instrument_synchronizer.raise_event(player.actual_instrument) + src.player.song.play_song(user) + return TRUE + if("wait") + if(value) + src.player.wait = WEAKREF(user) + else + src.player.wait = null + return TRUE + if("newsong") + src.player.song.lines.Cut() + src.player.song.tempo = src.player.song.sanitize_tempo(5) // default 120 BPM + return TRUE + if("import") + var/t = "" + do + t = html_encode(input(user, "Please paste the entire song, formatted:", "[owner.name]", t) as message) + if(!CanInteractWith(user, owner, GLOB.physical_state)) + return TRUE + if(length(t) >= 2 * src.maximum_lines * src.maximum_line_length) + var/cont = input(user, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(!CanInteractWith(user, owner, GLOB.physical_state)) + return TRUE + if(cont == "no") + break + while(length(t) > 2 * src.maximum_lines * src.maximum_line_length) + if(length(t)) + src.player.song.lines = splittext(t, "\n") + if(copytext(src.player.song.lines[1], 1, 6) == "BPM: ") + if(text2num(copytext(src.player.song.lines[1], 6)) != 0) + src.player.song.tempo = src.player.song.sanitize_tempo(600 / text2num(copytext(src.player.song.lines[1], 6))) + src.player.song.lines.Cut(1, 2) + else + src.player.song.tempo = src.player.song.sanitize_tempo(5) + else + src.player.song.tempo = src.player.song.sanitize_tempo(5) // default 120 BPM + if(src.player.song.lines.len > maximum_lines) + to_chat(user, "Too many lines!") + src.player.song.lines.Cut(maximum_lines + 1) + var/linenum = 1 + for(var/l in src.player.song.lines) + if(length(l) > maximum_line_length) + to_chat(user, "Line [linenum] too long!") + src.player.song.lines.Remove(l) + else + linenum++ + return TRUE + if("show_song_editor") + if(!src.song_editor) + src.song_editor = new(host = src.owner, song = src.player.song) + src.song_editor.ui_interact(user) + return TRUE + if("show_usage") + if(!src.usage_info) + src.usage_info = new(owner, src.player) + src.usage_info.ui_interact(user) + return TRUE + if("volume") + src.player.volume = min(max(min(player.volume + value, 100), 0), player.max_volume) + return TRUE + if("transposition") + src.player.song.transposition = max(min(player.song.transposition + value, GLOB.musical_config.highest_transposition), GLOB.musical_config.lowest_transposition) + return TRUE + if("min_octave") + src.player.song.octave_range_min = max(min(player.song.octave_range_min + value, GLOB.musical_config.highest_octave), GLOB.musical_config.lowest_octave) + src.player.song.octave_range_max = max(player.song.octave_range_max, player.song.octave_range_min) + return TRUE + if("max_octave") + src.player.song.octave_range_max = max(min(player.song.octave_range_max + value, GLOB.musical_config.highest_octave), GLOB.musical_config.lowest_octave) + src.player.song.octave_range_min = min(player.song.octave_range_max, player.song.octave_range_min) + return TRUE + if("sustain_timer") + src.player.song.sustain_timer = max(min(player.song.sustain_timer + value, GLOB.musical_config.longest_sustain_timer), 1) + return TRUE + if("soft_coeff") + var/new_coeff = input(user, "from [GLOB.musical_config.gentlest_drop] to [GLOB.musical_config.steepest_drop]") as num + if(!CanInteractWith(user, owner, GLOB.physical_state)) + return TRUE + new_coeff = round(min(max(new_coeff, GLOB.musical_config.gentlest_drop), GLOB.musical_config.steepest_drop), 0.001) + src.player.song.soft_coeff = new_coeff + return TRUE + if("instrument") + if(!islist(instruments)) + return TRUE + var/list/as_list = instruments + var/list/categories = list() + for(var/key in as_list) + var/datum/instrument/instrument = as_list[key] + categories |= instrument.category + var/category = input(user, "Choose a category") as null|anything in categories + if(!CanInteractWith(user, owner, GLOB.physical_state)) + return TRUE + var/list/instruments_available = list() + for(var/key in as_list) + var/datum/instrument/instrument = as_list[key] + if(instrument.category == category) + instruments_available += key + var/new_instrument = input(user, "Choose an instrument") as null|anything in instruments_available + if(!CanInteractWith(user, owner, GLOB.physical_state)) + return TRUE + if(new_instrument) + src.player.song.instrument_data = as_list[new_instrument] + return TRUE + if("autorepeat") + src.player.song.autorepeat = value + return TRUE + if("decay") + src.player.song.linear_decay = value + return TRUE + if("echo") + src.player.apply_echo = value + return TRUE + if("show_env_editor") + if(GLOB.musical_config.env_settings_available) + if(!src.env_editor) + src.env_editor = new(src.player) + src.env_editor.ui_interact(user) + else + to_chat(user, "Virtual environment is disabled") + return TRUE + if("show_echo_editor") + if(!src.echo_editor) + src.echo_editor = new(src.player) + src.echo_editor.ui_interact(user) + return TRUE + if("select_env") + if(value in -1 to 26) + var/new_id = round(value) + if(GLOB.musical_config.is_custom_env(new_id) && !GLOB.musical_config.can_use_custom) + return TRUE + src.player.virtual_environment_selected = new_id + return TRUE + return FALSE /datum/real_instrument/Destroy() - if (islist(instruments)) + if(islist(instruments)) var/list/aslist = instruments QDEL_LIST_ASSOC_VAL(aslist) else @@ -226,9 +228,9 @@ /obj/structure/synthesized_instrument/Initialize() . = ..() - for (var/type in typesof(path)) + for(var/type in typesof(path)) var/datum/instrument/new_instrument = new type - if (!new_instrument.id) continue + if(!new_instrument.id) continue new_instrument.create_full_sample_deviation_map() src.instruments[new_instrument.name] = new_instrument src.real_instrument = new /datum/real_instrument(src, new sound_player(src, instruments[pick(instruments)]), instruments) @@ -241,26 +243,28 @@ /obj/structure/synthesized_instrument/attack_hand(mob/user) src.interact(user) - /obj/structure/synthesized_instrument/interact(mob/user) // CONDITIONS ..(user) that shit in subclasses src.ui_interact(user) +/obj/structure/synthesized_instrument/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Synthesizer", name) + ui.open() -/obj/structure/synthesized_instrument/ui_interact(mob/user, ui_key = "instrument", datum/nanoui/ui = null, force_open = 0) - real_instrument.ui_call(user,ui_key,ui,force_open) +/obj/structure/synthesized_instrument/ui_data(mob/user) + return real_instrument.build_ui_data() +/obj/structure/synthesized_instrument/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + return real_instrument.handle_ui_act(action, params, usr) /obj/structure/synthesized_instrument/proc/shouldStopPlaying(mob/user) return 0 -/obj/structure/synthesized_instrument/Topic(href, href_list) - if (..()) - return 1 - - return real_instrument.Topic_call(href, href_list, usr) - - //////////////////////// //DEVICE VERSION //////////////////////// @@ -275,41 +279,46 @@ /obj/item/synthesized_instrument/Initialize() . = ..() - for (var/type in typesof(path)) + for(var/type in typesof(path)) var/datum/instrument/new_instrument = new type - if (!new_instrument.id) continue + if(!new_instrument.id) continue new_instrument.create_full_sample_deviation_map() src.instruments[new_instrument.name] = new_instrument src.real_instrument = new /datum/real_instrument(src, new sound_player(src, instruments[pick(instruments)]), instruments) /obj/item/synthesized_instrument/Destroy() QDEL_NULL(src.real_instrument) - if (islist(instruments)) + if(islist(instruments)) var/list/as_list = instruments - for (var/key in as_list) + for(var/key in as_list) qdel(as_list[key]) instruments = null . = ..() - -/obj/item/synthesized_instrument/attack_self(mob/user as mob) +/obj/item/synthesized_instrument/attack_self(mob/user) src.interact(user) - /obj/item/synthesized_instrument/interact(mob/user) // CONDITIONS ..(user) that shit in subclasses src.ui_interact(user) +/obj/item/synthesized_instrument/ui_interact(mob/user, datum/tgui/ui) + if(!real_instrument) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Synthesizer", name) + ui.open() -/obj/item/synthesized_instrument/ui_interact(mob/user, ui_key = "instrument", datum/nanoui/ui = null, force_open = 0) - if (real_instrument) - real_instrument.ui_call(user,ui_key,ui,force_open) +/obj/item/synthesized_instrument/ui_data(mob/user) + return real_instrument.build_ui_data() +/obj/item/synthesized_instrument/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(!real_instrument) + return + return real_instrument.handle_ui_act(action, params, usr) /obj/item/synthesized_instrument/proc/shouldStopPlaying(mob/user) return !(src && in_range(src, user)) - -/obj/item/synthesized_instrument/Topic(href, href_list) - if (..()) - return 1 - - return real_instrument.Topic_call(href, href_list, usr) diff --git a/code/modules/synthesized_instruments/song.dm b/code/modules/synthesized_instruments/song.dm index 14ec0c6ddcb..1242de628b6 100644 --- a/code/modules/synthesized_instruments/song.dm +++ b/code/modules/synthesized_instruments/song.dm @@ -132,7 +132,7 @@ var/obj/structure/synthesized_instrument/S = src.player.actual_instrument var/datum/real_instrument/R = S.real_instrument if (R.song_editor) - SSnanoui.update_uis(R.song_editor) + SStgui.update_uis(R.song_editor) for (var/notes in splittext(lowertext(line), ",")) var/list/components = splittext(notes, "/") var/duration = sanitize_tempo(src.tempo) diff --git a/code/modules/synthesized_instruments/song_editor.dm b/code/modules/synthesized_instruments/song_editor.dm index ee2f4ddc4b4..732039dbeee 100644 --- a/code/modules/synthesized_instruments/song_editor.dm +++ b/code/modules/synthesized_instruments/song_editor.dm @@ -1,109 +1,107 @@ -/datum/nano_module/song_editor +/datum/instrument_ui/song_editor name = "Song Editor" - available_to_ai = 0 var/datum/synthesized_song/song var/show_help = 0 var/page = 1 -/datum/nano_module/song_editor/New(host, topic_manager, datum/synthesized_song/song) +/datum/instrument_ui/song_editor/New(host, datum/synthesized_song/song) ..() src.host = host src.song = song -/datum/nano_module/song_editor/Destroy() +/datum/instrument_ui/song_editor/Destroy() song = null return ..() -/datum/nano_module/song_editor/proc/pages() +/datum/instrument_ui/song_editor/proc/pages() return Ceil(src.song.lines.len / GLOB.musical_config.song_editor_lines_per_page) -/datum/nano_module/song_editor/proc/current_page() +/datum/instrument_ui/song_editor/proc/current_page() return src.song.current_line > 0 ? Ceil(src.song.current_line / GLOB.musical_config.song_editor_lines_per_page) : min(src.page, pages()) -/datum/nano_module/song_editor/proc/page_bounds(page_num) +/datum/instrument_ui/song_editor/proc/page_bounds(page_num) return list( max(min(1 + GLOB.musical_config.song_editor_lines_per_page * (page_num-1), src.song.lines.len), 1), min(GLOB.musical_config.song_editor_lines_per_page * page_num, src.song.lines.len)) -/datum/nano_module/song_editor/ui_interact(mob/user, ui_key = "song_editor", datum/nanoui/ui = null, force_open = 0) - var/list/data = list() - - var/current_page = src.current_page() - var/list/line_bounds = src.page_bounds(src.current_page()) - - data["lines"] = src.song.lines.Copy(line_bounds[1], line_bounds[2]+1) - data["active_line"] = src.song.current_line - data["max_lines"] = GLOB.musical_config.max_lines - data["max_line_length"] = GLOB.musical_config.max_line_length - data["tick_lag"] = world.tick_lag - data["show_help"] = src.show_help - data["page_num"] = current_page - data["page_offset"] = GLOB.musical_config.song_editor_lines_per_page * (current_page-1) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new (user, src, ui_key, "song_editor.tmpl", "Song Editor", 550, 600) - ui.set_initial_data(data) +/datum/instrument_ui/song_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SongEditor", "Song Editor") ui.open() +/datum/instrument_ui/song_editor/ui_data(mob/user) + var/current_page = src.current_page() + var/list/line_bounds = src.page_bounds(current_page) + return list( + "lines" = src.song.lines.Copy(line_bounds[1], line_bounds[2]+1), + "active_line" = src.song.current_line, + "max_lines" = GLOB.musical_config.max_lines, + "max_line_length"= GLOB.musical_config.max_line_length, + "tick_lag" = world.tick_lag, + "show_help" = src.show_help, + "page_num" = current_page, + "page_offset" = GLOB.musical_config.song_editor_lines_per_page * (current_page-1), + "total_pages" = src.pages() + ) -/datum/nano_module/song_editor/Topic(href, href_list) - if (..()) - return 1 +/datum/instrument_ui/song_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return - var/target = href_list["target"] - var/value = text2num(href_list["value"]) - if (href_list["value"] && !isnum(value)) - to_chat(usr, "Non-numeric value was supplied") - return 0 - - switch (target) + switch(action) if("newline") - var/newline = html_encode(input(usr, "Enter your line: ") as text|null) + var/newline = tgui_input_text(usr, "Enter your line:", "New Line") if(!newline) - return - if(src.song.lines.len > GLOB.musical_config.max_lines) - return + return TRUE + if(src.song.lines.len >= GLOB.musical_config.max_lines) + return TRUE if(length(newline) > GLOB.musical_config.max_line_length) newline = copytext(newline, 1, GLOB.musical_config.max_line_length) src.song.lines.Add(newline) + return TRUE if("deleteline") // This could kill the server if the synthesizer was playing, props to BeTePb // Impossible to do now. Dumbing down this section. - var/num = round(value) - if(num > src.song.lines.len || num < 1) - return + var/num = round(text2num(params["value"])) + if(num < 1 || num > src.song.lines.len) + return TRUE src.song.lines.Cut(num, num+1) + return TRUE if("modifyline") - var/num = round(value) - if(num > src.song.lines.len || num < 1) - return - var/content = html_encode(input(usr, "Enter your line: ", "Edit line", src.song.lines[num]) as text|null) - if(num > src.song.lines.len || num < 1) - return - if(!content) - return + var/num = round(text2num(params["value"])) + if(num < 1 || num > src.song.lines.len) + return TRUE + var/content = tgui_input_text(usr, "Enter your line:", "Edit Line", src.song.lines[num]) + if(!content || num < 1 || num > src.song.lines.len) + return TRUE if(length(content) > GLOB.musical_config.max_line_length) content = copytext(content, 1, GLOB.musical_config.max_line_length) src.song.lines[num] = content + return TRUE - if ("help") - src.show_help = value + if("help") + src.show_help = text2num(params["value"]) ? 1 : 0 + return TRUE - if ("next_page") + if("next_page") src.page = max(min(src.page + 1, src.pages()), 1) + return TRUE - if ("prev_page") + if("prev_page") src.page = max(min(src.page - 1, src.pages()), 1) + return TRUE - if ("last_page") + if("last_page") src.page = src.pages() - if ("first_page") - src.page = 1 + return TRUE - return 1 + if("first_page") + src.page = 1 + return TRUE diff --git a/code/modules/synthesized_instruments/usage_info.dm b/code/modules/synthesized_instruments/usage_info.dm index 308bad2ba0b..b2dbee6ef98 100644 --- a/code/modules/synthesized_instruments/usage_info.dm +++ b/code/modules/synthesized_instruments/usage_info.dm @@ -1,29 +1,28 @@ -/datum/nano_module/usage_info +/datum/instrument_ui/usage_info name = "Usage Info" - available_to_ai = 0 var/datum/sound_player/player -/datum/nano_module/usage_info/New(atom/source, datum/sound_player/player) +/datum/instrument_ui/usage_info/New(atom/source, datum/sound_player/player) + ..() src.host = source src.player = player //This will let you easily monitor when you're going overboard with tempo and sound duration, generally if the bars fill up it is BAD -/datum/nano_module/usage_info/ui_interact(mob/user, ui_key = "usage_info", datum/nanoui/ui = null, force_open = 0) - var/static/list/data = list() - data.Cut() - data["channels_left"] = GLOB.sound_channels.available_channels.stack.len - data["events_active"] = src.player.event_manager.events.len - data["max_channels"] = GLOB.sound_channels.channel_ceiling - data["max_events"] = GLOB.musical_config.max_events - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new (user, src, ui_key, "song_usage_info.tmpl", "Usage info", 500, 150) - ui.set_initial_data(data) +/datum/instrument_ui/usage_info/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SongUsageInfo", "Usage Info") ui.open() - ui.set_auto_update(1) + +/datum/instrument_ui/usage_info/ui_data(mob/user) + return list( + "channels_left" = GLOB.sound_channels.available_channels.stack.len, + "events_active" = src.player.event_manager.events.len, + "max_channels" = GLOB.sound_channels.channel_ceiling, + "max_events" = GLOB.musical_config.max_events + ) -/datum/nano_module/usage_info/Destroy() +/datum/instrument_ui/usage_info/Destroy() player = null - ..() + return ..() diff --git a/html/changelogs/arrow768-tgui-batch-2.yml b/html/changelogs/arrow768-tgui-batch-2.yml new file mode 100644 index 00000000000..48f74db4553 --- /dev/null +++ b/html/changelogs/arrow768-tgui-batch-2.yml @@ -0,0 +1,72 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: arrow768 + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Updates the Computer Fabricator to TGUI" + - refactor: "Updates the Instruments to TGUI" + - refactor: "Updates the Geo Scanner to TGUI" + - refactor: "Updates the Stacking Machine to TGUI" + - refactor: "Updates the Robotics Console to TGUI" + - refactor: "Updates the Requests Console to TGUI" + - refactor: "Updates the Synthesizer/Instrument main panel to TGUI" + - refactor: "Updates the Nuclear Bomb control panel to TGUI" + - rscadd: "Requests Console messages are now typed (Assistance Request, Supply Request, Information, Reply); the type is shown in the recipient's log, audible alert, and PDA notification." + - rscadd: "Reconstructs the Synthesizer's Virtual Environment Editor (was broken since added — its NanoUI template never existed). The Custom environment preset that depends on it is gated behind a new musical_config.can_use_custom flag (default off, matching prior behavior)." + - bugfix: "Fixes the Autolathe UI re-opening after every print job." + - bugfix: "Fixes the Song Editor's currently-playing line indicator not advancing during playback." + - bugfix: "Fixes a class of bugs where TGUI windows for the Air Alarm, Portable Turret, Mining Vendor, Mineral Processor, Tank Dispenser, Helm Console, all Chemistry machines, and Suit Cycler would force themselves back open after every action, even when the player had closed them." + - bugfix: "Fixes the spatial sound system silently dropping every sound that used the 'None' (-1) environment id or a 23-element custom environment list. The validator was rejecting both as invalid, so synthesizer notes played with Custom or None mode produced no audible output at all." + - admin: "Adds two requests consoles and two fax machines to the bridge on the runtime test map." diff --git a/maps/runtime/runtime.dmm b/maps/runtime/runtime.dmm index 0ab80b12fc2..9836e4a1788 100644 --- a/maps/runtime/runtime.dmm +++ b/maps/runtime/runtime.dmm @@ -1760,16 +1760,15 @@ /turf/simulated/floor/tiled/dark/full, /area/supply/dock) "hc" = ( -/obj/effect/floor_decal/corner/dark_blue/full, -/obj/effect/floor_decal/corner/dark_blue{ - dir = 4 - }, /obj/structure/cable/yellow{ icon_state = "4-8" }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, +/obj/effect/floor_decal/corner/dark_blue{ + dir = 6 + }, /turf/simulated/floor/tiled/dark, /area/bridge) "hd" = ( @@ -2115,6 +2114,13 @@ }, /turf/simulated/wall/r_wall, /area/bridge) +"iL" = ( +/obj/effect/floor_decal/corner/dark_blue/full, +/obj/effect/floor_decal/corner/dark_blue{ + dir = 4 + }, +/turf/simulated/floor/tiled/dark, +/area/bridge) "iM" = ( /obj/machinery/atmospherics/pipe/simple/heat_exchanging{ dir = 6 @@ -2628,19 +2634,14 @@ /turf/simulated/floor/plating, /area/construction/storage) "lO" = ( -/obj/machinery/button/remote/blast_door{ - desc = "A remote control-switch for the AI core maintenance door."; - id = "runtime_blastdoors"; - name = "Runtime Safety Blast Doors"; - pixel_x = 5; - pixel_y = 28; - dir = 1 - }, /obj/machinery/atmospherics/pipe/simple/visible/yellow{ dir = 10 }, /turf/simulated/floor/carpet/rubber, /area/bridge) +"lQ" = ( +/turf/simulated/wall/r_wall, +/area/template_noop) "lR" = ( /obj/machinery/hologram/holopad/long_range, /obj/effect/overmap/visitable/ship/runtime, @@ -3071,6 +3072,10 @@ }, /turf/simulated/floor/tiled/gridded, /area/hallway/primary/central_two) +"ow" = ( +/obj/machinery/power/apc/north, +/turf/simulated/floor/carpet/rubber, +/area/bridge) "ox" = ( /obj/effect/map_effect/window_spawner/full/reinforced/firedoor, /turf/simulated/floor/tiled/full, @@ -3090,6 +3095,15 @@ /obj/machinery/alarm/south, /turf/simulated/floor/tiled/dark/full, /area/hallway/primary/central_one) +"oO" = ( +/obj/effect/floor_decal/corner/dark_blue{ + dir = 5 + }, +/obj/structure/bed/stool/chair/office/bridge{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark, +/area/bridge) "oR" = ( /obj/structure/lattice/catwalk/indoor, /obj/structure/railing/mapped{ @@ -3477,12 +3491,6 @@ /turf/simulated/floor/tiled/gridded, /area/shuttle/runtime) "qU" = ( -/obj/effect/floor_decal/corner/dark_blue{ - dir = 5 - }, -/obj/structure/bed/stool/chair/office/bridge{ - dir = 1 - }, /obj/structure/cable/yellow{ icon_state = "2-4" }, @@ -3873,6 +3881,18 @@ }, /turf/simulated/floor/airless, /area/construction/storage) +"ts" = ( +/obj/structure/table/glass, +/obj/machinery/requests_console/east{ + announcementConsole = 1; + department = "Bridge"; + departmentType = 7 + }, +/obj/machinery/photocopier/faxmachine{ + department = "Bridge" + }, +/turf/simulated/floor/carpet/rubber, +/area/bridge) "tt" = ( /obj/structure/lattice/catwalk, /obj/machinery/door/blast/regular{ @@ -4006,11 +4026,13 @@ /turf/simulated/floor/plating, /area/construction/storage) "um" = ( -/obj/effect/floor_decal/industrial/warning/full, -/obj/machinery/iff_beacon/name_change{ - pixel_y = 5 +/obj/effect/floor_decal/corner/dark_blue/full{ + dir = 4 }, -/turf/simulated/floor/airless, +/obj/effect/floor_decal/corner/dark_blue{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark, /area/bridge) "un" = ( /obj/effect/floor_decal/industrial/warning{ @@ -4600,6 +4622,17 @@ }, /turf/simulated/floor/reinforced/phoron, /area/construction/storage) +"xJ" = ( +/obj/structure/table/glass, +/obj/machinery/requests_console/west{ + department = "Security"; + departmentType = 7 + }, +/obj/machinery/photocopier/faxmachine{ + department = "Security" + }, +/turf/simulated/floor/carpet/rubber, +/area/bridge) "xL" = ( /obj/effect/floor_decal/corner/grey{ dir = 1 @@ -5205,6 +5238,14 @@ }, /turf/simulated/floor/tiled/full, /area/hallway/primary/central_one) +"By" = ( +/obj/effect/map_effect/window_spawner/full/reinforced/firedoor, +/obj/machinery/door/blast/regular/open{ + id = "runtime_blastdoors"; + dir = 4 + }, +/turf/simulated/floor/tiled/full, +/area/template_noop) "BF" = ( /obj/effect/floor_decal/industrial/warning/full, /obj/machinery/shipsensors/strong{ @@ -5510,7 +5551,6 @@ /turf/simulated/floor/tiled/full, /area/hallway/primary/central_two) "Dl" = ( -/obj/machinery/power/apc/north, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -6720,11 +6760,15 @@ /turf/simulated/floor/tiled/gridded, /area/turret_protected/ai) "Kj" = ( -/obj/effect/floor_decal/industrial/warning/full, -/obj/machinery/shipsensors/strong{ - pixel_y = 4 +/obj/machinery/button/remote/blast_door{ + desc = "A remote control-switch for the AI core maintenance door."; + id = "runtime_blastdoors"; + name = "Runtime Safety Blast Doors"; + pixel_x = 5; + pixel_y = 28; + dir = 1 }, -/turf/simulated/floor/airless, +/turf/simulated/floor/carpet/rubber, /area/bridge) "Kn" = ( /obj/machinery/atmospherics/pipe/simple/visible/cyan{ @@ -7688,6 +7732,13 @@ }, /turf/simulated/floor/tiled, /area/hallway/primary/central_one) +"QL" = ( +/obj/effect/floor_decal/industrial/warning/full, +/obj/machinery/shipsensors/strong{ + pixel_y = 4 + }, +/turf/simulated/floor/airless, +/area/template_noop) "QM" = ( /obj/machinery/door/blast/regular/open{ id = "runtime_mixchamber_int"; @@ -7879,14 +7930,11 @@ /turf/simulated/floor/reinforced/airless, /area/construction/storage) "RH" = ( -/obj/effect/floor_decal/corner/dark_blue/full{ +/obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, /obj/effect/floor_decal/corner/dark_blue{ - dir = 1 - }, -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 4 + dir = 9 }, /turf/simulated/floor/tiled/dark, /area/bridge) @@ -8236,6 +8284,13 @@ }, /turf/simulated/floor/tiled/white, /area/hallway/primary/central_one) +"TF" = ( +/obj/effect/floor_decal/industrial/warning/full, +/obj/machinery/iff_beacon/name_change{ + pixel_y = 5 + }, +/turf/simulated/floor/airless, +/area/template_noop) "TG" = ( /obj/effect/floor_decal/industrial/warning{ dir = 1 @@ -77739,8 +77794,8 @@ uh uh uh uh -uh mA +lQ ig ig ig @@ -77996,10 +78051,10 @@ uh uh uh uh -uh mA -Kj +QL JX +xJ up MI up @@ -78253,10 +78308,10 @@ uh uh uh uh -uh mA +lQ ig -ig +Kj lO UW kP @@ -78511,9 +78566,9 @@ uh uh uh uh -uh -JX +By QU +um RH nN iT @@ -78768,9 +78823,9 @@ uh uh uh uh -uh -JX +By yW +oO qU lR ck @@ -79025,9 +79080,9 @@ uh uh uh uh -uh -JX +By tf +iL hc pz Ji @@ -79281,10 +79336,10 @@ uh uh uh uh -uh mA +lQ ig -ig +ow Dl OF EH @@ -79538,10 +79593,10 @@ uh uh uh uh -uh mA -um +TF JX +ts IJ IJ IJ @@ -79795,8 +79850,8 @@ uh uh uh uh -uh mA +lQ ig ig ig diff --git a/nano/templates/computer_fabricator.tmpl b/nano/templates/computer_fabricator.tmpl deleted file mode 100644 index eee7e747229..00000000000 --- a/nano/templates/computer_fabricator.tmpl +++ /dev/null @@ -1,69 +0,0 @@ -{{:helper.link('Clear Order', 'circle', { "clean_order" : 1 })}}

-Your new computer device you always dreamed of is just four steps away...
-{{if data.state == 0}} -
-

Step 1: Select your device type

- {{:helper.link('Laptop', 'calc', { "pick_device" : 1 })}} - {{:helper.link('Tablet', 'calc', { "pick_device" : 2 })}} - {{:helper.link('PDA', 'calc', { "pick_device" : 3 })}} -
-{{else data.state == 1}} -
-

Step 2: Personalise your device

- - - - - - - - - - -
Current Price: - {{:data.totalprice}} -
Battery: - {{:helper.link('Small', null, { "hw_battery" : 1 }, data.hw_battery == 1 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_battery" : 2 }, data.hw_battery == 2 ? 'selected' : null)}} - {{:helper.link('Advanced', null, { "hw_battery" : 3 }, data.hw_battery == 3 ? 'selected' : null)}} -
Hard Drive: - {{:helper.link('Small', null, { "hw_disk" : 1 }, data.hw_disk == 1 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_disk" : 2 }, data.hw_disk == 2 ? 'selected' : null)}} - {{:helper.link('Advanced', null, { "hw_disk" : 3 }, data.hw_disk == 3 ? 'selected' : null)}} -
Network Card: - {{:helper.link('None', null, { "hw_netcard" : 0 }, data.hw_netcard == 0 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_netcard" : 1 }, data.hw_netcard == 1 ? 'selected' : null)}} - {{:helper.link('Advanced', null, { "hw_netcard" : 2 }, data.hw_netcard == 2 ? 'selected' : null)}} -
Processor Unit: - {{:helper.link('Standard', null, { "hw_cpu" : 1 }, data.hw_cpu == 1 ? 'selected' : null)}} - {{:helper.link('Advanced', null, { "hw_cpu" : 2 }, data.hw_cpu == 2 ? 'selected' : null)}} -
Tesla Relay: - {{:helper.link('None', null, { "hw_tesla" : 0 }, data.hw_tesla == 0 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_tesla" : 1 }, data.hw_tesla == 1 ? 'selected' : null)}} -
Nano Printer: - {{:helper.link('None', null, { "hw_nanoprint" : 0 }, data.hw_nanoprint == 0 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_nanoprint" : 1 }, data.hw_nanoprint == 1 ? 'selected' : null)}} -
Card Reader: - {{:helper.link('None', null, { "hw_card" : 0 }, data.hw_card == 0 ? 'selected' : null)}} - {{:helper.link('Standard', null, { "hw_card" : 1 }, data.hw_card == 1 ? 'selected' : null)}} -
Confirm Order: - {{:helper.link('CONFIRM', null, { "confirm_order" : 1 })}} -
-
- Battery allows your device to operate without external utility power source. Advanced batteries increase battery life.
- Hard Drive stores file on your device. Advanced drives can store more files, but use more power, shortening battery life.
- Network Card allows your device to wirelessly connect to stationwide NTNet network. Basic cards are limited to on-station use, while advanced cards can operate anywhere near the station, which includes the asteroid outposts.
- Processor Unit is critical for your device's functionality. It allows you to run programs from your hard drive. Advanced CPUs use more power, but allow you to run more programs on background at once.
- Tesla Relay is an advanced wireless power relay that allows your device to connect to nearby area power controller to provide alternative power source.
- Nano Printer is device that allows for various paperwork manipulations, such as, scanning of documents or printing new ones. This device was certified EcoFriendlyPlus and is capable of recycling existing paper for printing purposes.
- Card Reader adds a slot that allows you to manipulate RFID cards. Please note that this is not necessary to allow the device to read your identification, it is just necessary to manipulate other cards. -
-{{else data.state == 2}} -

Step 3: Payment

- Your device is now ready for fabrication..
- Please swipe your identification card to finish purchase.
- Total price: {{:data.totalprice}} -{{else data.state == 3}} -

Step 4: Thank you for your purchase

- Should you experience any issues with your new device, contact technical support at support@computerservice.nt -{{/if}} diff --git a/nano/templates/echo_editor.tmpl b/nano/templates/echo_editor.tmpl deleted file mode 100644 index 8e2e791b634..00000000000 --- a/nano/templates/echo_editor.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -

Echo editor:

-{{for data.echo_params}} -
-
- {{:value.name}} - {{if value.real}} -   (real value) - {{else}} -   (integer value) - {{/if}} -
-
-
- {{:value.value}} -
- {{:helper.link('?', null, {'target': 'desc', 'index': value.index})}} - {{:helper.link('R', null, {'target': 'reset', 'index': value.index})}} - {{:helper.link('E', null, {'target': 'set', 'index': value.index})}} -
-
-{{/for}} \ No newline at end of file diff --git a/nano/templates/geoscanner.tmpl b/nano/templates/geoscanner.tmpl deleted file mode 100644 index 90d86b03aba..00000000000 --- a/nano/templates/geoscanner.tmpl +++ /dev/null @@ -1,185 +0,0 @@ - - -

Machine Status

-
-
- {{:helper.link(data.scanning ? 'Halt Scan' : 'Begin Scan', 'signal-diag', {'scanItem' : 1}, null)}} -
-
- {{:helper.link('Eject item', 'eject', {'ejectItem' : 1}, (data.scanned_item && !data.scanning) ? null : 'disabled')}} -
-
- -
-
-
Item:
-
- {{if data.scanned_item}} - {{:data.scanned_item}} - {{else}} - No item inserted - {{/if}} -
-
-
-
Heuristic analysis:
-
- {{if data.scanned_item_desc}} - {{:data.scanned_item_desc}} - {{/if}} -
-
-
- -

Scanner

-
-
Scan progress:
-
- {{:helper.displayBar(data.scan_progress, 0, 100, 'good')}} - {{:data.scan_progress}} % -
-
- {{if data.scan_progress >= 100}} - Scan completed successfully. - {{/if}} -
-
-
-
Vacuum seal integrity:
-
- {{:helper.displayBar(data.scanner_seal_integrity, 0, 100, ((data.scanner_seal_integrity < 66) ? ((data.scanner_seal_integrity < 33) ? 'bad' : 'average') : 'good'))}} - {{:data.scanner_seal_integrity}} % -
-
- {{if data.scanner_seal_integrity < 25}} - Warning! Vacuum seal breach will result in scan failure! - {{/if}} -
-
- -

MASER

-
-
MASER Efficiency:
-
- {{:helper.displayBar(data.maser_efficiency, 1, 100, ((data.maser_efficiency < 66) ? ((data.maser_efficiency) < 33 ? 'bad' : 'average') : 'good'))}} - {{:data.maser_efficiency}} % -
-
- {{if data.maser_efficiency < 50}} - Match wavelengths to progress the scan. - {{/if}} -
-
-
-
Optimal Wavelength:
-
- {{:helper.displayBar(data.optimal_wavelength, 1, 10000, 'good')}} - {{:data.optimal_wavelength}} MHz -
-
-
-
Current Wavelength:
-
- {{:helper.displayBar(data.maser_wavelength, 1, 10000, 'good')}} - {{:data.maser_wavelength}} MHz -
-
- {{:helper.link('-2 GHz', null, {'maserWavelength' : -2}, null)}} - {{:helper.link('-1 GHz', null, {'maserWavelength' : -1}, null)}} - {{:helper.link('-0.5 GHz', null, {'maserWavelength' : -0.5}, null)}} -
-
- {{:helper.link('+0.5 GHz', null, {'maserWavelength' : 0.5}, null)}} - {{:helper.link('+1 GHz', null, {'maserWavelength' : 1}, null)}} - {{:helper.link('+2 GHz', null, {'maserWavelength' : 2}, null)}} -
-
- -

Environment / Internal

-
-
Centrifuge speed:
-
- {{:helper.displayBar(data.scanner_rpm, 0, 1000, 'good')}} - {{:data.scanner_rpm}} RPM -
-
-
-
Internal temperature:
-
- {{:helper.displayBar(data.scanner_temperature, 0, 1273, (data.scanner_temperature > 250 ? (data.scanner_temperature > 1000 ? 'bad' : 'average') : 'good'))}} - {{:data.scanner_temperature}} K -
-
- {{if data.scanner_temperature > 1000}} - Warning! Exceeding 1200K will result in scan failure! - {{/if}} -
-
- -

Radiation

-
-
Ambient radiation:
-
- {{:helper.displayBar(data.radiation, 0, 100, ((data.radiation > 15) ? ((data.radiation > 65) ? 'bad' : 'average') : 'good'))}} - {{:data.radiation}} mSv -
-
- {{:helper.link(data.rad_shield_on ? 'Disable Radiation Shielding' : 'Enable Radiation Shielding', 'radiation', {'toggle_rad_shield' : 1}, null)}} - {{if data.rad_shield_on}} - Shield blocking scanner. - {{/if}} -
-
- -

Cooling

-
-
Coolant remaining:
-
- {{:helper.displayBar(data.unused_coolant_per, 0, 100, ((data.unused_coolant_per < 66) ? ((data.unused_coolant_per < 33) ? 'bad' : 'average') : 'good'))}} - {{:data.unused_coolant_abs}} u -
-
- {{if data.unused_coolant_per < 20}} - Warning! Coolant stocks low! - {{/if}} -
-
-
-
Coolant flow rate:
-
- {{:helper.displayBar(data.coolant_usage_rate, 0, 10, 'good')}} - {{:data.coolant_usage_rate}} u/s -
-
- {{:helper.link('Min u/s', null, {'coolantRate' : -10}, null)}} - {{:helper.link('-3 u/s', null, {'coolantRate' : -3}, null)}} - {{:helper.link('-1 u/s', null, {'coolantRate' : -1}, null)}} -
-
- {{:helper.link('+1 u/s', null, {'coolantRate' : 1}, null)}} - {{:helper.link('+3 u/s', null, {'coolantRate' : 3}, null)}} - {{:helper.link('Max u/s', null, {'coolantRate' : 10}, null)}} -
-
-
-
Coolant purity:
-
- {{:helper.displayBar(data.coolant_purity, 0, 100, ((data.coolant_purity < 66) ? ((data.coolant_purity < 33) ? 'bad' : 'average') : 'good'))}} - {{:data.coolant_purity}} % -
-
- {{if data.coolant_purity < 0.5}} - Warning! Check coolant for contaminants! - {{/if}} -
-
- -

Latest Results

-
-
- {{:data.last_scan_data}} -
-
diff --git a/nano/templates/nuclear_bomb.tmpl b/nano/templates/nuclear_bomb.tmpl deleted file mode 100644 index 53a0466cf82..00000000000 --- a/nano/templates/nuclear_bomb.tmpl +++ /dev/null @@ -1,68 +0,0 @@ - -
- Authorization Disk: {{if data.auth}}{{:helper.link('++++++++++', 'eject', {'auth' : 1})}} {{else}} {{:helper.link('----------', 'disk', {'auth' : 1})}}{{/if}} -
-
-
-
Status: {{:data.authstatus}} - {{:data.safe}}
-
Timer: {{:data.time}}
-
-
-
- {{if data.auth && data.yescode}} -
- Timer: {{:helper.link('On', 'play', {'timer' : 1}, data.timer ? 'redButton' : '')}}{{:helper.link('Off', 'stop', {'timer' : 0}, !data.timer ? 'selected' : '')}} -
-
- Time: {{:helper.link('--', '', {'time' : -10}, data.time <= 120 ? 'disabled' : '')}}{{:helper.link('-', '', {'time' : -1}, data.time <= 120 ? 'disabled' : '')}} {{:data.time}} {{:helper.link('+', '', {'time' : 1})}}{{:helper.link('++', '', {'time' : 10})}} -
- {{else}} -
- Timer: {{:helper.link('On', 'play', null, 'disabled')}}{{:helper.link('Off', 'pause', null, 'disabled')}} -
-
- Time: {{:helper.link('-', '', null, 'disabled')}}{{:helper.link('-', '', null, 'disabled')}} {{:data.time}} {{:helper.link('+', '', null, 'disabled')}}{{:helper.link('++', '', null, 'disabled')}} -
- {{/if}} -
-
- {{if data.auth && data.yescode}} -
- Safety: {{:helper.link('Engaged', 'info', {'safety' : 1}, data.safety ? 'selected' : '')}}{{:helper.link('Disengaged', 'alert', {'safety' : 0}, data.safety ? '' : 'redButton')}} -
-
- Anchor: {{:helper.link('Engaged', 'locked', {'anchor' : 1}, data.anchored ? 'selected' : '')}}{{:helper.link('Disengaged', 'unlocked', {'anchor' : 0}, data.anchored ? '' : 'selected')}} -
- {{else}} -
- Safety: {{:helper.link('Engaged', 'info', null, 'disabled')}}{{:helper.link('Disengaged', 'alert', null, 'disabled')}} -
-
- Anchor: {{:helper.link('Engaged', 'locked', null, 'disabled')}}{{:helper.link('Disengaged', 'unlocked', null, 'disabled')}} -
- {{/if}} -
-
-
-
-
- >{{if data.message}} {{:data.message}}{{/if}} -
-
-
- {{:helper.link('1', '', {'type' : 1})}}{{:helper.link('2', '', {'type' : 2})}}{{:helper.link('3', '', {'type' : 3})}} -
-
- {{:helper.link('4', '', {'type' : 4})}}{{:helper.link('5', '', {'type' : 5})}}{{:helper.link('6', '', {'type' : 6})}} -
-
- {{:helper.link('7', '', {'type' : 7})}}{{:helper.link('8', '', {'type' : 8})}}{{:helper.link('9', '', {'type' : 9})}} -
-
- {{:helper.link('R', '', {'type' : 'R'})}}{{:helper.link('0', '', {'type' : 0})}}{{:helper.link('E', '', {'type' : 'E'})}} -
-
-
diff --git a/nano/templates/requests_console.tmpl b/nano/templates/requests_console.tmpl deleted file mode 100644 index fdd96ecf599..00000000000 --- a/nano/templates/requests_console.tmpl +++ /dev/null @@ -1,169 +0,0 @@ - - -{{if data.screen == 1}} -

Request assistance from another department.

- - {{for data.assist_dept}} - {{if value != data.department}} - - - - - - {{/if}} - {{empty}} - - {{/for}} -
{{:value}} -
{{:helper.link('Message', null, { 'write' : value , 'priority' : 1 })}}
{{:helper.link('High Priority', null, { 'write' : value , 'priority' : 2 })}}
There are no available departments to request assistance from.

-
{{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}}
-{{else data.screen == 2}} -

Request supplies from another department.

- - {{for data.supply_dept}} - {{if value != data.department}} - - - - - - {{/if}} - {{empty}} - - {{/for}} -
{{:value}} -
{{:helper.link('Message', null, { 'write' : value , 'priority' : 1 })}}
{{:helper.link('High Priority', null, { 'write' : value , 'priority' : 2 })}}
There are no available departments to request supplies from.

-
{{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}}
-{{else data.screen == 3}} -

Relay info to another department.

- - {{for data.info_dept}} - {{if value != data.department}} - - - - - - {{/if}} - {{empty}} - - {{/for}} -
{{:value}} -
{{:helper.link('Message', null, { 'write' : value , 'priority' : 1 })}}
{{:helper.link('High Priority', null, { 'write' : value , 'priority' : 2 })}}
There are no available departments to relay information to.

-
{{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}}
-{{else data.screen == 4}} -
Message sent successfully.
-
{{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
-{{else data.screen == 5}} -
An Error occured. Message not sent.
-
{{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
-{{else data.screen == 6}} -
- {{for data.message_log}} -
{{:value}}
- {{empty}} -
No messages have been received.
- {{/for}} -
-
{{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}}
-{{else data.screen == 7}} -

Message Authentication


-
-
Message for {{:data.recipient}}: {{:data.message}}
-
Validated by: {{:data.msgVerified}}
-
Stamped by: {{:data.msgStamped}}
-
-
- {{:helper.link('Send Message', 'arrowthick-1-e', { 'department' : data.recipient })}} - {{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}} -
-{{else data.screen == 8}} -

Station wide announcement

-
Message: {{:data.message}} {{:helper.link('Write Message', 'pencil', { 'writeAnnouncement' : 1 })}}
-
- {{if data.announceAuth}} -
ID verified. Authentication accepted.
- {{else}} -
Swipe your ID card to authenticate yourself.
- {{/if}} -
-
- {{:helper.link('Announce', 'signal-diag', { 'sendAnnouncement' : 1 }, (data.announceAuth && data.message) ? null : 'disabled' )}} - {{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}} -
-{{else data.screen == 9}} -

Forms Database:


- {{if data.sql_error}} -
ERROR: Unable to contact external database. Please contact your system administrator for assistance.
- {{else}} - - - - - - - - - {{for data.forms}} - - - - - - - - {{/for}} -
NCF IDForm NameDept.Print
{{:value.id}}
{{:value.name}}
{{:helper.link(value.department, null, { 'sort' : value.department })}}
{{:helper.link('Print', 'gear', { 'print' : value.id })}}
{{:helper.link('?', null, { 'whatis' : value.id })}}
- {{/if}} -
-
- {{:helper.link('Reset Search', 'gear', { 'resetSQL' : 1 })}} - {{:helper.link('Back', 'arrowreturnthick-1-w', { 'setScreen' : 0 })}} -
-{{else}} - {{if data.newmessagepriority == 1}} -
There are new messages
- {{else data.newmessagepriority == 2}} -
NEW PRIORITY MESSAGES
- {{/if}} -
{{:helper.link('View Messages', data.newmessagepriority ? 'mail-closed' : 'mail-open', { 'setScreen' : 6 })}}
-
-
{{:helper.link('Request Assistance', 'gear', { 'setScreen' : 1 })}}
-
{{:helper.link('Request Supplies', 'gear', { 'setScreen' : 2 })}}
-
{{:helper.link('Relay Anonymous Information', 'gear', { 'setScreen' : 3})}}
-
{{:helper.link('Forms Database', 'gear', { 'setScreen' : 9 })}}
-
- {{if data.announcementConsole}} -
{{:helper.link('Send Station-wide Announcement', 'signal-diag', { 'setScreen' : 8})}}
-
- {{/if}} -
{{:helper.link(data.silent ? 'Speaker OFF' : 'Speaker ON', data.silent ? 'volume-off' : 'volume-on', { 'toggleSilent' : 1})}}
-
-
{{:helper.link(data.lid ? 'Close lid' : 'Open lid', 'gear', { 'setLid' : 1 })}}
-
{{:data.paper}} paper in stock
-
{{:helper.link('Link PDA', 'volume-on', { 'linkpda' : 1 })}}
-
-
PDAs to alert:
- {{if data.pda_list}} - - {{for data.pda_list}} - - - - - {{/for}} -
{{:value.name}} -
{{:helper.link('Unlink', 'volume-off', { 'unlink' : value.pda })}}
- {{/if}} -{{/if}} diff --git a/nano/templates/robot_control.tmpl b/nano/templates/robot_control.tmpl deleted file mode 100644 index 1fd7a05e37b..00000000000 --- a/nano/templates/robot_control.tmpl +++ /dev/null @@ -1,108 +0,0 @@ -{{if !data.is_ai}} -
-
- Emergency Self-Destruct: -
-
- {{if data.safety}} - {{:helper.link('ARM', 'unlocked', {'arm' : 1})}} - {{:helper.link('DETONATE', 'radiation', {'nuke' : 1}, 'disabled')}} - {{else}} - {{:helper.link('DISARM', 'locked', {'arm' : 1})}} - {{:helper.link('DETONATE', 'radiation', {'nuke' : 1}, null, 'redButton')}} - {{/if}} -
-
-
-{{/if}} -{{for data.robots}} -
-
-

{{:value.name}}

-

Information

- - Status: - - - {{:value.status}} - - - Master AI: - - - {{:value.master_ai}} - - - Module: - - - {{:value.module}} - - - {{if value.hackable}} - - Safeties: - - - ENABLED - - {{else value.hacked}} - - Safeties: - - - DISABLED - - {{/if}} -

Power Cell

- {{if value.cell}} - - Rating : - - - {{:value.cell_capacity}} - - {{:helper.displayBar(value.cell_percentage, 0, 100, (value.cell_percentage >= 50) ? 'good' : (value.cell_percentage >= 25) ? 'average' : 'bad')}} - {{:value.cell_percentage}} % - {{else}} - Not Installed - {{:helper.displayBar(100, 0, 100, 'bad')}} - N/A % - {{/if}} -

Actions

- - Access: - - - {{if value.access == 0}} - {{:helper.link('Role Specific Access', 'locked', {'access' : value.name})}} - {{else}} - {{:helper.link('All Access', 'unlocked', {'access' : value.name})}} - {{/if}} - - - Lockdown Status: - - - {{if value.status == "Operational"}} - {{:helper.link('Lockdown', 'locked', {'lockdown' : value.name})}} - {{else}} - {{:helper.link('Unlock', 'unlocked', {'lockdown' : value.name})}} - {{/if}} - - - Self Destruct: - - - {{:helper.link('Self-Destruct', 'radiation', {'detonate' : value.name}, null, 'redButton')}} - - {{if value.hackable}} - - Hacking Action: - - - {{:helper.link('Hack', 'calculator', {'hack' : value.name}, null, 'redButton')}} - - {{/if}} -
-{{/for}} \ No newline at end of file diff --git a/nano/templates/song_editor.tmpl b/nano/templates/song_editor.tmpl deleted file mode 100644 index 56618b947e8..00000000000 --- a/nano/templates/song_editor.tmpl +++ /dev/null @@ -1,43 +0,0 @@ -

Song editor (current tick-lag {{:data.tick_lag}})

-{{if data.show_help}} - Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-).
- Every note in a chord will play together, with chord timed by the tempo.
-
- Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
- By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
- Example: C,D,E,F,G,A,B will play a C major scale.
- After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
- Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
- A pause may be denoted by an empty chord: C,E,,C,G
- To make a chord be a different time, end it with /x, where the chord length will be length
- defined by tempo / x: C,G/2,E/4
- Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 -
- Lines may be up to 50 characters.
- A song may only contain up to 200 lines.
-{{/if}} -
- {{:helper.link(data.show_help ? 'Hide help' : 'Show help', null, {'target': 'help', 'value': data.show_help ? 0 : 1})}} - {{:helper.link('New line', null, {'target': 'newline'})}} -
-
- Current page: {{:data.page_num}} - {{:helper.link('<', null, {'target': 'prev_page'})}} - {{:helper.link('>', null, {'target': 'next_page'})}} -
-
-{{for data.lines}} - {{if data.active_line == data.page_offset+index+1}} -
- {{/if}} -
- {{:helper.link(data.page_offset+index+1, null, {'target': 'deleteline', 'value':data.page_offset+index+1})}} -
-
- {{:helper.link(value, null, {'target': 'modifyline', 'value':data.page_offset+index+1})}} -
- {{if data.active_line == data.page_offset+index+1}} -
- {{/if}} -{{/for}} -
\ No newline at end of file diff --git a/nano/templates/song_usage_info.tmpl b/nano/templates/song_usage_info.tmpl deleted file mode 100644 index a50fa71c9ae..00000000000 --- a/nano/templates/song_usage_info.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -
-
- Free channels: -
-
- {{:helper.displayBar(data.channels_left, 0, data.max_channels)}} -
-
-
-
- Events: -
-
- {{:helper.displayBar(data.events_active, 0, data.max_events)}} -
-
\ No newline at end of file diff --git a/nano/templates/stacking_machine.tmpl b/nano/templates/stacking_machine.tmpl deleted file mode 100644 index db9bb4ee285..00000000000 --- a/nano/templates/stacking_machine.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -
- {{for data.contents}} -
-
{{:value.name}}
-
{{:value.amount}}
-
{{:helper.link('Eject', 'eject', {'release_stack' : value.path})}}
-
- {{empty}} - No materials loaded. - {{/for}} -
-
-
-
Stacking:
-
{{:data.stack_amt}}
-
{{:helper.link('Change', 'wrench', {'change_stack' : 1})}}
-
-
diff --git a/nano/templates/synthesizer.tmpl b/nano/templates/synthesizer.tmpl deleted file mode 100644 index 08c1f210884..00000000000 --- a/nano/templates/synthesizer.tmpl +++ /dev/null @@ -1,168 +0,0 @@ -
- {{:helper.link('Start new song', null, {'target': 'newsong'})}} - {{:helper.link('Import song', null, {'target': 'import'})}} -
- -{{if data.show.playback}} -

Player

-
-
- Playback: -
-
- {{:helper.link('Play', null, {'target': 'play', 'value': 1}, data.playback.playing ? 'selected' : null)}} - {{:helper.link('Stop', null, {'target': 'play', 'value': 0}, !data.playback.playing ? 'selected' : null)}} -
-
- -
-
- Autorepeat: -
-
- {{:helper.link('On', null, {'target': 'autorepeat', 'value': 1}, data.playback.autorepeat ? 'selected' : null)}} - {{:helper.link('Off', null, {'target': 'autorepeat', 'value': 0}, !data.playback.autorepeat ? 'selected' : null)}} -
-
- -
-
- Wait: -
-
- {{:helper.link('On', null, {'target': 'wait', 'value': 1}, data.playback.wait ? 'selected' : null)}} - {{:helper.link('Off', null, {'target': 'wait', 'value': 0}, !data.playback.wait ? 'selected' : null)}} -
-
- -{{/if}} - -

Basic options

-
-
- Volume: -
-
- {{:helper.link('--', null, {'target': 'volume', 'value': -10})}} - {{:helper.link('-', null, {'target': 'volume', 'value': -1})}} - {{:helper.displayBar(data.basic_options.volume, 0, 100)}} - {{:helper.link('+', null, {'target': 'volume', 'value': 1})}} - {{:helper.link('++', null, {'target': 'volume', 'value': 10})}} -
-
- -
-
- Instrument: -
-
- {{:helper.link(data.basic_options.cur_instrument, null, {'target' : 'instrument'})}} -
-
- -
-
- BPM: -
-
- {{:helper.link('-', null, {'target' : 'tempo', 'value': 1})}} -
{{:data.basic_options.BPM}}
- {{:helper.link('+', null, {'target' : 'tempo', 'value': -1})}} -
-
- -
-
- Transposition: -
-
- {{:helper.link('-', null, {'target' : 'transposition', 'value': -1})}} -
{{:data.basic_options.transposition}}
- {{:helper.link('+', null, {'target' : 'transposition', 'value': 1})}} -
-
- -
-
- Octave range: -
-
-
- MIN: {{:data.basic_options.octave_range.min}} - {{:helper.link('-', null, {'target' : 'min_octave', 'value': -1})}} - {{:helper.link('+', null, {'target' : 'min_octave', 'value': 1})}} -
-
- MAX: {{:data.basic_options.octave_range.max}} - {{:helper.link('-', null, {'target' : 'max_octave', 'value': -1})}} - {{:helper.link('+', null, {'target' : 'max_octave', 'value': 1})}} -
-
-
- -

Sustain

-
-
- Exponential decay: -
-
- {{:helper.link('On', null, {'target': 'decay', 'value': 0}, data.sustain.linear_decay_active ? null : 'selected')}} - {{:helper.link('Off', null, {'target': 'decay', 'value': 1}, !data.sustain.linear_decay_active ? null : 'selected')}} -
-
-{{if data.sustain.linear_decay_active}} -
-
- Sustain timer: -
-
- {{:helper.link('--', null, {'target': 'sustain_timer', 'value': -10})}} - {{:helper.link('-', null, {'target': 'sustain_timer', 'value': -1})}} -
{{:data.sustain.sustain_timer}}
- {{:helper.link('+', null, {'target': 'sustain_timer', 'value': 1})}} - {{:helper.link('++', null, {'target': 'sustain_timer', 'value': 10})}} -
-
-{{else}} -
-
- Exponential value: -
-
- {{:data.sustain.soft_coeff}} - {{:helper.link('Change', null, {'target': 'soft_coeff'})}} -
-
-{{/if}} - -

Advanced options

-
- {{if data.show.debug_button}} - {{:helper.link('Debug panel', null, {'target': 'debug'})}} - {{/if}} - {{if data.show.custom_env_options && data.show.env_settings}} - {{:helper.link('Open virtual environment editor', null, {'target': 'show_env_editor'})}} - {{/if}} - {{:helper.link('Open echo editor', null, {'target': 'show_echo_editor'})}} - {{:helper.link('Open song editor', null, {'target': 'show_song_editor'})}} - {{:helper.link(data.advanced_options.apply_echo ? 'Do not apply echo' : 'Apply echo', null, {'target': 'echo', 'value': data.advanced_options.apply_echo ? 0 : 1})}} -
-{{if data.show.env_settings}} -
-
- Virtual environment: -
-
- {{for data.advanced_options.all_environments}} - {{if value == "Custom"}} - {{:helper.link(value, null, {'target': 'select_env', 'value': index-1}, data.advanced_options.selected_environment == value ? 'selected': 'disabled')}} - {{else}} - {{:helper.link(value, null, {'target': 'select_env', 'value': index-1}, data.advanced_options.selected_environment == value ? 'selected': null)}} - {{/if}} - {{/for}} -
-
-{{/if}} - -

Status

-{{:helper.link('Open usage info', null, {'target': 'show_usage'})}} \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator.tsx b/tgui/packages/tgui/interfaces/ComputerFabricator.tsx new file mode 100644 index 00000000000..744b05502cd --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator.tsx @@ -0,0 +1,297 @@ +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, Section } from '../components'; +import { Window } from '../layouts'; + +type ComputerFabricatorData = { + state: number; + devtype?: number; + hw_cpu?: number; + hw_battery?: number; + hw_disk?: number; + hw_netcard?: number; + hw_tesla?: number; + hw_nanoprint?: number; + hw_card?: number; + hw_aislot?: number; + totalprice?: number; +}; + +const LAPTOP_CPU_OPTIONS = [ + { val: 1, label: 'Default (Atom, +0 cr)' }, + { val: 2, label: 'Upgraded (+199 cr)' }, +]; + +const TABLET_CPU_OPTIONS = [ + { val: 1, label: 'Default (Atom Nano, +0 cr)' }, + { val: 2, label: 'Advanced (+299 cr)' }, +]; + +const LAPTOP_BATTERY_OPTIONS = [ + { val: 1, label: 'Micro 500C (+0 cr)' }, + { val: 2, label: 'Basic 750C (+99 cr)' }, + { val: 3, label: 'Advanced 1100C (+499 cr)' }, +]; + +const TABLET_BATTERY_OPTIONS = [ + { val: 1, label: 'Basic 300C (+0 cr)' }, + { val: 2, label: 'Upgraded 500C (+99 cr)' }, + { val: 3, label: 'Advanced 750C (+149 cr)' }, +]; + +const LAPTOP_DISK_OPTIONS = [ + { val: 1, label: '32GQ (+0 cr)' }, + { val: 2, label: '128GQ (+99 cr)' }, + { val: 3, label: '256GQ (+299 cr)' }, +]; + +const TABLET_DISK_OPTIONS = [ + { val: 1, label: '32GQ (+0 cr)' }, + { val: 2, label: '64GQ (+49 cr)' }, + { val: 3, label: '128GQ (+129 cr)' }, +]; + +const LAPTOP_NETCARD_OPTIONS = [ + { val: 0, label: 'None (+0 cr)' }, + { val: 1, label: 'Short-Range (+99 cr)' }, + { val: 2, label: 'Long-Range (+299 cr)' }, +]; + +const TABLET_NETCARD_OPTIONS = [ + { val: 0, label: 'None (+0 cr)' }, + { val: 1, label: 'Short-Range (+49 cr)' }, + { val: 2, label: 'Long-Range (+129 cr)' }, +]; + +const HwRow = ({ + label, + hwKey, + current, + options, + act, +}: { + label: string; + hwKey: string; + current: number; + options: { val: number; label: string }[]; + act: any; +}) => ( + + {options.map((opt) => ( +