From c72eadb03676dbec39fd36d869742b704d90fe12 Mon Sep 17 00:00:00 2001 From: Filatelele Date: Wed, 8 May 2024 12:56:27 +0300 Subject: [PATCH] tweak(UI): adds alota TGUI PR #12410 --- code/_helpers/global_lists.dm | 3 + code/game/machinery/computer/guestpass.dm | 261 +++++++------ code/game/machinery/computer/prisoner.dm | 159 ++++---- code/game/machinery/spaceheater.dm | 107 +++--- code/game/mecha/mecha.dm | 14 +- code/game/mecha/mecha_control_console.dm | 197 +++++----- code/game/objects/items/implants/implant.dm | 8 + .../game/objects/structures/under_wardrobe.dm | 130 ++++--- code/modules/hydroponics/seed_storage.dm | 262 ++++++------- .../silicon/robot/drone/drone_console.dm | 212 +++++------ .../packages/tgui/interfaces/DroneConsole.tsx | 232 ++++++++++++ tgui/packages/tgui/interfaces/Guestpass.tsx | 343 ++++++++++++++++++ .../tgui/interfaces/MechaControlConsole.tsx | 196 ++++++++++ .../interfaces/PrisonerImplantManager.tsx | 157 ++++++++ tgui/packages/tgui/interfaces/SeedStorage.tsx | 161 ++++++++ tgui/packages/tgui/interfaces/SpaceHeater.tsx | 76 ++++ .../tgui/interfaces/UnderWardrobe.tsx | 150 ++++++++ 17 files changed, 2042 insertions(+), 626 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/DroneConsole.tsx create mode 100644 tgui/packages/tgui/interfaces/Guestpass.tsx create mode 100644 tgui/packages/tgui/interfaces/MechaControlConsole.tsx create mode 100644 tgui/packages/tgui/interfaces/PrisonerImplantManager.tsx create mode 100644 tgui/packages/tgui/interfaces/SeedStorage.tsx create mode 100644 tgui/packages/tgui/interfaces/SpaceHeater.tsx create mode 100644 tgui/packages/tgui/interfaces/UnderWardrobe.tsx diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 003098511b6..7fc15ca8f27 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -72,6 +72,9 @@ var/global/list/rune_list = new() var/global/list/syndicate_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) +/// Implants +GLOBAL_LIST_EMPTY(implants_list) + /// Associative list of string -> string, where key is armor class and value is an attack type it protects against. GLOBAL_LIST_INIT(descriptive_attack_types, list( "melee" = "blunt force", diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index b58c54d2ba5..22fb613edd9 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -70,7 +70,6 @@ if(istype(O, /obj/item/card/id)) if(!giver && user.drop(O, src)) giver = O - updateUsrDialog() else if(giver) to_chat(user, "There is already ID card inside.") return @@ -79,114 +78,162 @@ /obj/machinery/computer/guestpass/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/guestpass/attack_hand(mob/user as mob) - if(..()) - return +/obj/machinery/computer/guestpass/attack_hand(mob/user) + . = ..() - user.set_machine(src) - var/dat = "" + tgui_interact(user) - if (mode == 1) //Logs - dat += "

Activity log


" - for (var/entry in internal_log) - dat += "[entry]

" - dat += "Print
" - dat += "Back
" - else - dat += "

Guest pass terminal #[uid]


" - dat += "View activity log

" - dat += "Issuing ID: [giver]
" - dat += "Issued to: [giv_name]
" - dat += "Reason: [reason]
" - dat += "Duration (minutes): [duration] m
" - dat += "Access to areas:
" - if (giver && giver.access) - for (var/A in giver.access) - var/area = get_access_desc(A) - if (A in accesses) - area = "[area]" - dat += "[area]
" - dat += "
Issue pass
" - - show_browser(user, dat, "window=guestpass;size=400x520") - onclose(user, "guestpass") - - -/obj/machinery/computer/guestpass/OnTopic(mob/user, href_list, state) - if (href_list["mode"]) - mode = text2num(href_list["mode"]) - . = TOPIC_REFRESH - - else if (href_list["choice"]) - switch(href_list["choice"]) - if ("giv_name") - var/nam = sanitize(input(user, "Person pass is issued to", "Name", giv_name) as text|null) - if (nam && CanUseTopic(user, state)) - giv_name = nam - if ("reason") - var/reas = sanitize(input(user, "Reason why pass is issued", "Reason", reason) as text|null) - if(reas && CanUseTopic(user, state)) - reason = reas - if ("duration") - var/dur = input(user, "Duration (in minutes) during which pass is valid (up to 30 minutes).", "Duration") as num|null - if (dur && CanUseTopic(user, state)) - if (dur > 0 && dur <= 30) - duration = dur - else - to_chat(user, "Invalid duration.") - if ("access") - var/A = text2num(href_list["access"]) - if (A in accesses) - accesses.Remove(A) - else if(giver && (A in giver.access)) - accesses.Add(A) - . = TOPIC_REFRESH - else if (href_list["action"]) - switch(href_list["action"]) - if ("id") - if (giver) - giver.dropInto(user.loc) - if(ishuman(user)) - user.pick_or_drop(giver) +/obj/machinery/computer/guestpass/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + + if (!ui) + ui = new(user, src, "Guestpass", src) + ui.open() + +/obj/machinery/computer/guestpass/tgui_data(mob/user) + var/list/data = list( + "scanName" = giver?.registered_name, + "areas", + "issueLog" = list(internal_log), + ) + + var/list/regions = list() + for(var/i in ACCESS_REGION_SECURITY to ACCESS_REGION_SUPPLY) // code/game/jobs/_access_defs.dm + var/list/region = list( + "name" = get_region_accesses_name(i), + "id" = i, + ) + var/list/accessess_data = list() + for(var/j in get_region_accesses(i)) + var/list/access = list() + access["name"] = get_access_desc(j) + access["id"] = j + access["req"] = (j in accesses) ? TRUE : FALSE + accessess_data[++accessess_data.len] = access + + region["accesses"] = accessess_data + regions[++regions.len] = region + + data["regions"] = regions + + return data + +/obj/machinery/computer/guestpass/tgui_act(action, params) + . = ..() + if(.) + return + + switch(action) + if("select_access") + if(isnull(params["area"])) + return + + if(LAZYISIN(accesses, text2num(params["area"]))) + accesses -= text2num(params["area"]) + else + accesses |= text2num(params["area"]) + return TRUE + + if("scan") + if(giver) + if(ishuman(usr)) + usr.pick_or_drop(giver, get_turf(usr)) giver = null - accesses.Cut() else - var/obj/item/I = user.get_active_hand() - if(istype(I, /obj/item/card/id) && user.drop(I, src)) + giver.forceMove(get_turf(src)) + giver = null + accesses.Cut() + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + if(usr.drop(I, src)) giver = I - . = TOPIC_REFRESH - if ("print") - var/dat = "

Activity log of guest pass terminal #[uid]


" - for (var/entry in internal_log) - dat += "[entry]

" - var/obj/item/paper/P = new /obj/item/paper(loc) - P.set_content(dat, "activity log", TRUE) - . = TOPIC_REFRESH - - if ("issue") - if (giver && accesses.len) - var/number = add_zero(random_id("guestpass_id_number",1000,9999), 4) - var/entry = "\[[stationtime2text()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Granted access to following areas: " - var/list/access_descriptors = list() - for (var/A in accesses) - if (A in giver.access) - access_descriptors += get_access_desc(A) - entry += english_list(access_descriptors, and_text = ", ") - entry += ". Expires at [worldtime2stationtime(world.time + duration MINUTES)]." - internal_log.Add(entry) - - var/obj/item/card/id/guest/pass = new(src.loc) - pass.temp_access = accesses.Copy() - pass.registered_name = giv_name - pass.expiration_time = world.time + duration MINUTES - pass.reason = reason - pass.SetName("guest pass #[number]") - pass.assignment = "Guest" - playsound(src.loc, 'sound/machines/ping.ogg', 25, 0) - . = TOPIC_REFRESH - else if(!giver) - to_chat(user, "Cannot issue pass without issuing ID.") - else if(!accesses.len) - to_chat(user, "Cannot issue pass without at least one granted access permission.") - if(.) - attack_hand(user) + return TRUE + + if("deselect_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + + accesses -= get_region_accesses(region) + return TRUE + + if("select_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + + var/list/new_accesses = get_region_accesses(region) + for(var/A in new_accesses) + if(A in giver.access) + accesses |= A + return TRUE + + if("print") + var/obj/item/paper/P = new /obj/item/paper(src) + var/dat = "

Activity log of guest pass terminal #[any2ref(src)]


" + for(var/entry in internal_log) + dat += "[entry]

" + P.name = "activity log" + P.info = dat + usr.pick_or_drop(P, get_turf(usr)) + return TRUE + + if("select_access") + var/access = text2num(params["access"]) + if(isnull(access)) + return + + accesses |= access + return TRUE + + if("select_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + + var/list/new_accesses = get_region_accesses(region) + for(var/A in new_accesses) + if(A in giver.access) + accesses.Add(A) + return TRUE + + if("deselect_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + accesses -= get_region_accesses(region) + return TRUE + + if("deselect_all") + accesses = list() + return TRUE + + if("select_all") + for(var/A in get_all_accesses()) + if(A in giver.access) + accesses += A + return TRUE + + if("issue") + if(!giver || !LAZYLEN(accesses)) + return + + var/number = add_zero(random_id("guestpass_id_number", 1000, 9999), 4) + var/entry = "\[[stationtime2text()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Granted access to following areas: " + var/list/access_descriptors = list() + for(var/A in accesses) + if(A in giver.access) + access_descriptors += get_access_desc(A) + entry += english_list(access_descriptors, and_text = ", ") + entry += ". Expires at [worldtime2stationtime(world.time + duration MINUTES)]." + internal_log.Add(entry) + + var/obj/item/card/id/guest/pass = new(loc) + pass.temp_access = accesses.Copy() + pass.registered_name = giv_name + pass.expiration_time = world.time + duration MINUTES + pass.reason = reason + pass.SetName("guest pass #[number]") + pass.assignment = "Guest" + playsound(get_turf(src), 'sound/machines/ping.ogg', 25, 0) diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 793dcf6ecbe..df39d375436 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -1,5 +1,3 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - /obj/machinery/computer/prisoner name = "prisoner management console" icon = 'icons/obj/computer.dmi' @@ -8,97 +6,86 @@ light_color = "#a91515" req_access = list(access_armory) circuit = /obj/item/circuitboard/prisoner - var/id = 0.0 + var/id = 0 var/temp = null var/status = 0 var/timeleft = 60 var/stop = 0.0 - var/screen = 0 // 0 - No Access Denied, 1 - Access allowed - - - attack_ai(mob/user as mob) - return src.attack_hand(user) - - attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "" - dat += "Prisoner Implant Manager System
" - if(screen == 0) - dat += "
Unlock Console" - else if(screen == 1) - dat += "
Chemical Implants
" - var/turf/Tr = null - for(var/obj/item/implant/chem/C in world) - Tr = get_turf(C) - if((Tr) && !AreConnectedZLevels(Tr.z, src.z)) continue // Out of range - if(!C.implanted) continue - dat += "[C.imp_in.name] | Remaining Units: [C.reagents.total_volume] | Inject: " - dat += "((1))" - dat += "((5))" - dat += "((10))
" - dat += "********************************
" - dat += "
Tracking Implants
" - for(var/obj/item/implant/tracking/T in world) - Tr = get_turf(T) - if((Tr) && !AreConnectedZLevels(Tr.z, src.z)) continue // Out of range - if(!T.implanted) continue - var/loc_display = "Space" - var/mob/living/carbon/M = T.imp_in - if(!istype(M.loc, /turf/space)) - var/turf/mob_loc = get_turf(M) - loc_display = mob_loc.loc - if(T.malfunction) - loc_display = pick(playerlocs) - dat += "ID: [T.id] | Location: [loc_display]
" - dat += "(Message Holder) |
" - dat += "********************************
" - dat += "
Lock Console" - - show_browser(user, dat, "window=computer;size=400x500") - onclose(user, "computer") - return +/obj/machinery/computer/prisoner/tgui_state(mob/user) + return GLOB.tgui_default_state - Process() - if(!..()) - src.updateDialog() +/obj/machinery/computer/prisoner/attack_hand(mob/user) + . = ..() + if(.) return + tgui_interact(user) + +/obj/machinery/computer/prisoner/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PrisonerImplantManager", name) + ui.open() + +/obj/machinery/computer/prisoner/tgui_data(mob/user) + var/list/data = list() + + for(var/obj/item/implant/I in GLOB.implants_list) + if(!istype(I, /obj/item/implant/tracking) && !istype(I, /obj/item/implant/chem)) + continue + + var/turf/T = get_turf(I) + if(!istype(T) || !AreConnectedZLevels(T?.z, z)) + continue + + if(!I.implanted) + continue + + var/list/implant_data = list( + "implantee" = I.imp_in?.name, + "ref" = any2ref(I), + ) - Topic(href, href_list) - if(..()) - return - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["inject1"]) - var/obj/item/implant/I = locate(href_list["inject1"]) - if(I) I.activate(1) - - else if(href_list["inject5"]) - var/obj/item/implant/I = locate(href_list["inject5"]) - if(I) I.activate(5) - - else if(href_list["inject10"]) - var/obj/item/implant/I = locate(href_list["inject10"]) - if(I) I.activate(10) - - else if(href_list["lock"]) - if(src.allowed(usr)) - screen = !screen - else - to_chat(usr, "Unauthorized Access.") - playsound(src.loc, 'sound/signals/error29.ogg', 50, 0) - - else if(href_list["warn"]) - var/warning = sanitize(input(usr,"Message:","Enter your message here!","")) - if(!warning) return - var/obj/item/implant/I = locate(href_list["warn"]) - if((I)&&(I.imp_in)) - var/mob/living/carbon/R = I.imp_in - to_chat(R, "You hear a voice in your head saying: '[warning]'") - - src.updateUsrDialog() + if(istype(I, /obj/item/implant/tracking)) + var/obj/item/implant/tracking/tracking = I + var/loc_display = tracking.malfunction ? pick(playerlocs) : "Space" + var/coordinates = "error not found" + if(!tracking.malfunction && !isspaceturf(T)) + loc_display = get_area(T) + coordinates = "X: [T.x], Y: [T.y]" + implant_data["location"] = loc_display + implant_data["id"] = tracking.id + implant_data["coordinates"] = coordinates + data["trackingImplants"] += list(implant_data) + + if(istype(I, /obj/item/implant/chem)) + implant_data["remainingUnits"] = I.reagents?.total_volume + data["chemImplants"] += list(implant_data) + + return data + +/obj/machinery/computer/prisoner/tgui_act(action, params) + . = ..() + if(.) return + + switch(action) + if("inject") + var/obj/item/implant/I = locate(params["ref"]) in GLOB.implants_list + I?.activate(text2num(params["amt"])) + return TRUE + + if("warn") + var/warning = tgui_input_text(usr, "Message:", "Enter your message") + if(!warning) + return + + var/obj/item/implant/I = locate(params["ref"]) in GLOB.implants_list + var/mob/living/warned = I?.imp_in + if(!istype(warned)) + return + + to_chat(warned, SPAN_WARNING("You hear a voice in your head saying: '[warning]'")) + INVOKE_ASYNC(GLOBAL_PROC, /proc/tgui_alert, warned, warning, "You hear a voice in your head!") + return TRUE diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 0f5385ce49b..f866091ed22 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -14,7 +14,8 @@ atom_flags = ATOM_FLAG_CLIMBABLE clicksound = SFX_USE_LARGE_SWITCH turf_height_offset = 16 - + var/min_temperature = 0 CELSIUS + var/max_temperature = 90 CELSIUS /obj/machinery/space_heater/New() ..() @@ -85,75 +86,67 @@ shake_animation(stime = 4) return -/obj/machinery/space_heater/attack_hand(mob/user as mob) - ..() - interact(user) - -/obj/machinery/space_heater/interact(mob/user as mob) - +/obj/machinery/space_heater/attack_hand(mob/user) + . = ..() if(panel_open) - - var/list/dat = list() - dat += "Power cell: " - if(cell) - dat += "Installed
" - else - dat += "Removed
" - - dat += "Power Level: [cell ? round(CELL_PERCENT(cell),1) : 0]%

" - - dat += "Set Temperature: " - - dat += "-" - - dat += " [set_temperature]K ([CONV_KELVIN_CELSIUS(set_temperature)]°C)" - dat += "+
" - - var/datum/browser/popup = new(usr, "spaceheater", "Space Heater Control Panel") - popup.set_content(jointext(dat, null)) - popup.set_title_image(usr.browse_rsc_icon(src.icon, "sheater-standby")) - popup.open() + tgui_interact(user) else on = !on - user.visible_message("[user] switches [on ? "on" : "off"] the [src].","You switch [on ? "on" : "off"] the [src].") + user.visible_message(SPAN_NOTICE("[user] switches [on ? "on" : "off"] \the [src]."), \ + SPAN_NOTICE("You switch [on ? "on" : "off"] \the [src].")) update_icon() - return +/obj/machinery/space_heater/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) -/obj/machinery/space_heater/Topic(href, href_list, state = GLOB.physical_state) - if (..()) - close_browser(usr, "window=spaceheater") - usr.unset_machine() - return 1 + if(!ui) + ui = new(user, src, "SpaceHeater", src) + ui.open() - switch(href_list["op"]) +/obj/machinery/space_heater/tgui_data(mob/user) + var/list/data = list( + "cell" = istype(cell), + "charge" = istype(cell) ? round(cell.percent(), 1) : 0, + "temperature" = set_temperature, + "minTemperature" = min_temperature, + "maxTemperature" = max_temperature, + ) + return data - if("temp") - var/value = text2num(href_list["val"]) +/obj/machinery/space_heater/tgui_act(action, params) + . = ..() + if(.) + return - // limit to 0-90 degC - set_temperature = dd_range(0 CELSIUS, 90 CELSIUS, set_temperature + value) + switch(action) + if("changeTemperature") + var/new_temperature = params["useKelvin"] ? text2num(params["newTemp"]) : CONV_CELSIUS_KELVIN(text2num(params["newTemp"])) + set_temperature = dd_range(min_temperature, max_temperature, new_temperature) + return TRUE - if("cellremove") - if(panel_open && cell && !usr.get_active_hand()) - usr.visible_message("\The usr] removes \the [cell] from \the [src].", "You remove \the [cell] from \the [src].") - cell.update_icon() - usr.pick_or_drop(cell) - cell.add_fingerprint(usr) - cell = null - power_change() + if("cell") + if(!panel_open) + return + + switch(istype(cell)) + if(TRUE) + cell.update_icon() + usr.pick_or_drop(cell, usr.loc) + cell.add_fingerprint(usr) + usr.visible_message(SPAN_NOTICE("[usr] removes \the [cell] from \the [src]."), SPAN_NOTICE("You remove \the [cell] from \the [src].")) + cell = null + + if(FALSE) + var/obj/item/cell/C = usr.get_active_hand() + if(!istype(C)) + return - if("cellinstall") - if(panel_open && !cell) - var/obj/item/cell/C = usr.get_active_hand() - if(usr.drop(C, src)) - cell = C C.add_fingerprint(usr) - power_change() - usr.visible_message("[usr] inserts \the [C] into \the [src].", "You insert \the [C] into \the [src].") + usr.drop(C, src) + cell = C + usr.visible_message(SPAN_NOTICE("[usr] inserts \a [cell] into \the [src]."), SPAN_NOTICE("You insert \the [cell] into \the [src].")) - updateDialog() - return TOPIC_REFRESH + return TRUE /obj/machinery/space_heater/Process() if(on) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 89ae98554de..f790a1fa3ba 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1511,11 +1511,21 @@ output += "" return output +/obj/mecha/proc/get_mecha_log() + var/list/data = list() + + for(var/list/entry in log) + data.Add(list(list( + "time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"), + "message" = entry["message"], + ))) + + return data /obj/mecha/proc/get_log_html() - var/output = "[src.name] Log" + var/output = "[name] Log" for(var/list/entry in log) - output += {"
[time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
+ output += {"
[time2text(entry["time"], "DDD MMM DD hh:mm:ss")] [game_year]
[entry["message"]]
"} output += "" diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index ab15d6eab8f..caeb052106d 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,3 +1,5 @@ +GLOBAL_LIST_EMPTY(mecha_tracker_list) + /obj/machinery/computer/mecha name = "Exosuit Control" icon = 'icons/obj/computer.dmi' @@ -6,63 +8,63 @@ light_color = "#a97faa" req_access = list(access_robotics) circuit = /obj/item/circuitboard/mecha_control - var/list/located = list() + var/list/located var/screen = 0 - var/stored_data - - attack_ai(mob/user as mob) - return src.attack_hand(user) - - attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "[src.name]" - if(screen == 0) - dat += "

Tracking beacons data

" - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/answer = TR.get_mecha_info() - if(answer) - dat += {"
[answer]
- Send message
- Show exosuit log | (EMP pulse)
"} - - if(screen==1) - dat += "

Log contents

" - dat += "Return
" - dat += "[stored_data]" - - dat += "(Refresh)
" - dat += "" - - show_browser(user, dat, "window=computer;size=400x500") - onclose(user, "computer") + +/obj/machinery/computer/mecha/attack_hand(mob/user) + . = ..() + if(.) return - Topic(href, href_list) - if(..()) - return - var/datum/topic_input/F = new /datum/topic_input(href,href_list) - if(href_list["send_message"]) - var/obj/item/mecha_parts/mecha_tracking/MT = F.getObj("send_message") - var/message = sanitize(input(usr,"Input message","Transmit message") as text) - var/obj/mecha/M = MT.in_mecha() - if(message && M) - M.occupant_message(message) - return - if(href_list["shock"]) - var/obj/item/mecha_parts/mecha_tracking/MT = F.getObj("shock") - MT.shock() - if(href_list["get_log"]) - var/obj/item/mecha_parts/mecha_tracking/MT = F.getObj("get_log") - stored_data = MT.get_mecha_log() - screen = 1 - if(href_list["return"]) - screen = 0 - src.updateUsrDialog() + tgui_interact(user) + +/obj/machinery/computer/mecha/tgui_state(mob/user) + return GLOB.default_state + +/obj/machinery/computer/mecha/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MechaControlConsole", name) + ui.open() + +/obj/machinery/computer/mecha/tgui_data(mob/user) + var/list/data = list( + "beacons" = list() + ) + + for(var/thing in GLOB.mecha_tracker_list) + var/obj/item/mecha_parts/mecha_tracking/TR = thing + var/list/tr_data = TR.get_mecha_info() + if(tr_data) + data["beacons"] += list(tr_data) + + return data + +/obj/machinery/computer/mecha/tgui_act(action, params) + . = ..() + if(.) return + switch(action) + if("send_message") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt_ref"]) in GLOB.mecha_tracker_list + if(istype(MT)) + var/message = tgui_input_text(usr, "Input message", "Transmit message") + if(!message || !trim(message)) + return + + var/obj/mecha/M = MT.in_mecha() + if(M) + M.occupant_message(message) + return TRUE + if("shock") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt_ref"]) in GLOB.mecha_tracker_list + if(!istype(MT)) + return + + MT.shock() + return TRUE /obj/item/mecha_parts/mecha_tracking name = "Exosuit tracking beacon" @@ -71,49 +73,56 @@ icon_state = "motion2" origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) - proc/get_mecha_info() - if(!in_mecha()) - return 0 - var/obj/mecha/M = src.loc - var/cell_charge = M.get_charge() - var/answer = {"Name: [M.name]
- Integrity: [M.health/initial(M.health)*100]%
- Cell charge: [isnull(cell_charge)?"Not found":"[CELL_PERCENT(M.cell)]%"]
- Airtank: [M.return_pressure()]kPa
- Pilot: [M.occupant||"None"]
- Location: [get_area(M)||"Unknown"]
- Active equipment: [M.selected||"None"]"} - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer += "
Used cargo space: [length(RM.cargo)/RM.cargo_capacity*100]%
" - - return answer - - emp_act() - qdel(src) - return - - ex_act() - qdel(src) - return - - proc/in_mecha() - if(istype(src.loc, /obj/mecha)) - return src.loc - return 0 - - proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(2) - qdel(src) - - proc/get_mecha_log() - if(!src.in_mecha()) - return 0 - var/obj/mecha/M = src.loc - return M.get_log_html() - +/obj/item/mecha_parts/mecha_tracking/Initialize() + . = ..() + GLOB.mecha_tracker_list += src + +/obj/item/mecha_parts/mecha_tracking/Destroy() + GLOB.mecha_tracker_list -= src + return ..() + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() + var/obj/mecha/M = in_mecha() + if(!istype(M)) + return FALSE + + var/list/data = list( + "name" = M.name, + "cellCharge" = M.get_charge(), + "integrity" = M.health, + "integrityMax" = initial(M.health), + "airtank" = M.return_pressure(), + "pilot" = M.occupant, + "location" = get_area(M), + "equipment" = M.selected, + "ref" = any2ref(src), + "logs" = M.get_mecha_log(), + ) + + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + data["cargo"] = length(RM.cargo) + data["cargoCapacity"] = RM.cargo_capacity + + return data + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel_self() + +/obj/item/mecha_parts/mecha_tracking/ex_act() + qdel_self() + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(ismech(loc)) + return loc + + return FALSE + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(2) + qdel_self() /obj/structure/closet/crate/mechabeacons name = "exosuit tracking beacons crate" diff --git a/code/game/objects/items/implants/implant.dm b/code/game/objects/items/implants/implant.dm index 9456b62b69d..5b326b68ee3 100644 --- a/code/game/objects/items/implants/implant.dm +++ b/code/game/objects/items/implants/implant.dm @@ -14,6 +14,14 @@ var/malfunction = 0 var/known //if advanced scanners would name these in results +/obj/item/implant/Initialize() + . = ..() + GLOB.implants_list += src + +/obj/item/implant/Destroy() + GLOB.implants_list -= src + return ..() + /obj/item/implant/proc/trigger(emote, source) return diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index 6c68ea75122..0ddc4ac4333 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -39,77 +39,101 @@ LAZYREMOVE(amount_of_underwear_by_id_card, id_card) unregister_signal(id_card, SIGNAL_QDELETING) +/obj/structure/undies_wardrobe/proc/human_who_can_use_underwear(mob/living/carbon/human/H) + if(!istype(H) || !H.species || !(H.species.species_appearance_flags & HAS_UNDERWEAR)) + return FALSE + return TRUE + /obj/structure/undies_wardrobe/attack_hand(mob/user) if(!human_who_can_use_underwear(user)) - to_chat(user, "Sadly there's nothing in here for you to wear.") + to_chat(user, SPAN_WARNING("Sadly there's nothing in here for you to wear.")) return - interact(user) -/obj/structure/undies_wardrobe/interact(mob/living/carbon/human/H) - var/id = H.get_id_card() + tgui_interact(user) + +/obj/structure/undies_wardrobe/tgui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "UnderWardrobe", name) + ui.open() + +/obj/structure/undies_wardrobe/tgui_data(mob/user) + var/mob/living/carbon/human/H = user + var/obj/item/card/id/id = H?.get_id_card() + + var/list/data = list( + "user" = id? id.name : null, + "mayClaim" = id ? length(GLOB.underwear.categories) - LAZYACCESS(amount_of_underwear_by_id_card, id) : 0, + ) + + return data + +/obj/structure/undies_wardrobe/tgui_static_data(mob/user) + var/list/data = list( + "underwearCategories" = list() + ) - var/dat = list() - dat += "Underwear

" - dat += "You may claim [id ? length(GLOB.underwear.categories) - LAZYACCESS(amount_of_underwear_by_id_card, id) : 0] more article\s this shift.

" - dat += "Available Categories

" for(var/datum/category_group/underwear/UWC in GLOB.underwear.categories) - dat += "[UWC.name] (Select)
" - dat = jointext(dat,null) - show_browser(H, dat, "window=wardrobe;size=400x250") + var/list/cat_data = list("name" = UWC.name) -/obj/structure/undies_wardrobe/proc/human_who_can_use_underwear(mob/living/carbon/human/H) - if(!istype(H) || !H.species || !(H.species.species_appearance_flags & HAS_UNDERWEAR)) - return FALSE - return TRUE + for(var/datum/category_item/underwear/UWI in UWC.items) + if(!UWI.underwear_type) + continue -/obj/structure/undies_wardrobe/CanUseTopic(user) - if(!human_who_can_use_underwear(user)) - return STATUS_CLOSE + var/list/item_data = list( + "name" = UWI.name + ) + for(var/tweak in UWI.tweaks) + item_data["tweaks"] += list(tweak) - return ..() + cat_data["catItems"] += list(item_data) -/obj/structure/undies_wardrobe/Topic(href, href_list, state) - if(..()) - return TRUE + data["underwearCategories"] += list(cat_data) - var/mob/living/carbon/human/H = usr - if(href_list["select_underwear"]) - var/datum/category_group/underwear/UWC = GLOB.underwear.categories_by_name[href_list["select_underwear"]] - if(!UWC) - return - var/datum/category_item/underwear/UWI = input("Select your desired underwear:", "Choose underwear") as null|anything in exlude_none(UWC.items) - if(!UWI) - return + return data - var/list/metadata_list = list() - for(var/tweak in UWI.tweaks) - var/datum/gear_tweak/gt = tweak - var/metadata = gt.get_metadata(H, "Adjust underwear") - if(!metadata) - return - metadata_list["[gt]"] = metadata +/obj/structure/undies_wardrobe/tgui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return - if(!CanInteract(H, state)) - return + switch(action) + if("equip") + var/datum/category_group/underwear/UWC = GLOB.underwear.categories_by_name[params["underwearCat"]] + if(!istype(UWC)) + return - var/id = H.get_id_card() - if(!id) - audible_message("No ID card detected. Unable to acquire your underwear quota for this shift.", WARDROBE_BLIND_MESSAGE(H)) - return + var/datum/category_item/underwear/UWI = UWC.items_by_name[params["underwearItem"]] + if(!istype(UWI)) + return - var/current_quota = LAZYACCESS(amount_of_underwear_by_id_card, id) - if(current_quota >= length(GLOB.underwear.categories)) - audible_message("You have already used up your underwear quota for this shift. Please return previously acquired items to increase it.", WARDROBE_BLIND_MESSAGE(H)) - return - LAZYSET(amount_of_underwear_by_id_card, id, ++current_quota) + var/mob/living/carbon/human/H = usr + if(!istype(H)) + return - var/obj/UW = UWI.create_underwear(metadata_list) - H.pick_or_drop(UW, loc) + var/id = H.get_id_card() + if(!id) + audible_message("No ID card detected. Unable to acquire your underwear quota for this shift.", WARDROBE_BLIND_MESSAGE(H)) + return - . = TRUE + var/list/metadata_list = list() + for(var/tweak in UWI.tweaks) + var/datum/gear_tweak/gt = tweak + var/metadata = gt.get_metadata(H, "Adjust underwear") + if(!metadata) + return + metadata_list["[gt]"] = metadata + + var/current_quota = LAZYACCESS(amount_of_underwear_by_id_card, id) + if(current_quota >= length(GLOB.underwear.categories)) + audible_message("You have already used up your underwear quota for this shift. Please return previously acquired items to increase it.", WARDROBE_BLIND_MESSAGE(H)) + return - if(.) - interact(H) + LAZYSET(amount_of_underwear_by_id_card, id, ++current_quota) + var/obj/UW = UWI.create_underwear(metadata_list) + H.pick_or_drop(UW, loc) + return TRUE /obj/structure/undies_wardrobe/proc/exlude_none(list/L) . = L.Copy() diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 69bc042a046..88d2cd1fb15 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -206,148 +206,162 @@ ) /obj/machinery/seed_storage/attack_hand(mob/user as mob) - user.set_machine(src) - interact(user) + tgui_interact(user) -/obj/machinery/seed_storage/interact(mob/user as mob) - if (..()) - return +/obj/machinery/seed_storage/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SeedStorage") + ui.open() - var/dat = "

Seed storage contents

" - if (piles.len == 0) - dat += "No seeds" - else - dat += "" - dat += "" - if ("stats" in scanner) - dat += "" - if ("temperature" in scanner) - dat += "" - if ("light" in scanner) - dat += "" - if ("soil" in scanner) - dat += "" - dat += "" - for (var/datum/seed_pile/S in piles) - var/datum/seed/seed = S.seed_type - if(!seed) - continue - dat += "" - dat += "" - dat += "" - if ("stats" in scanner) - dat += "" - if(seed.get_trait(TRAIT_HARVEST_REPEAT)) - dat += "" - else - dat += "" - if ("temperature" in scanner) - dat += "" - if ("light" in scanner) - dat += "" - if ("soil" in scanner) - if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS)) - if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) < 0.05) - dat += "" - else if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0.2) - dat += "" - else - dat += "" +/obj/machinery/seed_storage/tgui_data(mob/user) + var/list/data = list( + "seeds" = list() + ) + for(var/datum/seed_pile/S in piles) + var/datum/seed/seed = S.seed_type + if(!seed) + continue + var/list/seed_type = list("name" = seed.seed_name, "uid" = seed.uid, "pile_id" = S.ID) + var/list/traits = list() + + if("stats" in scanner) + data["scan_stats"] = TRUE + seed_type["endurance"] = seed.get_trait(TRAIT_ENDURANCE) + seed_type["yield"] = seed.get_trait(TRAIT_YIELD) + seed_type["maturation"] = seed.get_trait(TRAIT_MATURATION) + seed_type["production"] = seed.get_trait(TRAIT_PRODUCTION) + seed_type["potency"] = seed.get_trait(TRAIT_POTENCY) + if(seed.get_trait(TRAIT_HARVEST_REPEAT)) + seed_type["harvest"] = "multiple" + else + seed_type["harvest"] = "single" + + if("temperature" in scanner) + data["scan_temperature"] = TRUE + seed_type["ideal_heat"] = "[seed.get_trait(TRAIT_IDEAL_HEAT)] K" + + if("light" in scanner) + data["scan_light"] = TRUE + seed_type["ideal_light"] = "[seed.get_trait(TRAIT_IDEAL_LIGHT)] L" + + if("soil" in scanner) + data["scan_soil"] = TRUE + if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS)) + if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) < 0.05) + seed_type["nutrient_consumption"] = "Low" + else if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0.2) + seed_type["nutrient_consumption"] = "High" else - dat += "" - if(seed.get_trait(TRAIT_REQUIRES_WATER)) - if(seed.get_trait(TRAIT_WATER_CONSUMPTION) < 1) - dat += "" - else if(seed.get_trait(TRAIT_WATER_CONSUMPTION) > 5) - dat += "" - else - dat += "" + seed_type["nutrient_consumption"] = "Average" + else + seed_type["nutrient_consumption"] = "No" + + if(seed.get_trait(TRAIT_REQUIRES_WATER)) + if(seed.get_trait(TRAIT_WATER_CONSUMPTION) < 1) + seed_type["water_consumption"] = "Low" + else if(seed.get_trait(TRAIT_WATER_CONSUMPTION) > 5) + seed_type["water_consumption"] = "High" else - dat += "" - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
NameVarietyEYMPrPtHarvestTempLightNutriWaterNotesAmount
[seed.seed_name]#[seed.uid][seed.get_trait(TRAIT_ENDURANCE)][seed.get_trait(TRAIT_YIELD)][seed.get_trait(TRAIT_MATURATION)][seed.get_trait(TRAIT_PRODUCTION)][seed.get_trait(TRAIT_POTENCY)]MultipleSingle[seed.get_trait(TRAIT_IDEAL_HEAT)] K[seed.get_trait(TRAIT_IDEAL_LIGHT)] LLowHighNormNoLowHighNormNo" - switch(seed.get_trait(TRAIT_CARNIVOROUS)) - if(1) - dat += "CARN " - if(2) - dat += "CARN " - switch(seed.get_trait(TRAIT_SPREAD)) - if(1) - dat += "VINE " - if(2) - dat += "VINE " - if ("pressure" in scanner) - if(seed.get_trait(TRAIT_LOWKPA_TOLERANCE) < 20) - dat += "LP " - if(seed.get_trait(TRAIT_HIGHKPA_TOLERANCE) > 220) - dat += "HP " - if ("temperature" in scanner) - if(seed.get_trait(TRAIT_HEAT_TOLERANCE) > 30) - dat += "TEMRES " - else if(seed.get_trait(TRAIT_HEAT_TOLERANCE) < 10) - dat += "TEMSEN " - if ("light" in scanner) - if(seed.get_trait(TRAIT_LIGHT_TOLERANCE) > 10) - dat += "LIGRES " - else if(seed.get_trait(TRAIT_LIGHT_TOLERANCE) < 3) - dat += "LIGSEN " - if(seed.get_trait(TRAIT_TOXINS_TOLERANCE) < 3) - dat += "TOXSEN " - else if(seed.get_trait(TRAIT_TOXINS_TOLERANCE) > 6) - dat += "TOXRES " - if(seed.get_trait(TRAIT_PEST_TOLERANCE) < 3) - dat += "PESTSEN " - else if(seed.get_trait(TRAIT_PEST_TOLERANCE) > 6) - dat += "PESTRES " - if(seed.get_trait(TRAIT_WEED_TOLERANCE) < 3) - dat += "WEEDSEN " - else if(seed.get_trait(TRAIT_WEED_TOLERANCE) > 6) - dat += "WEEDRES " - if(seed.get_trait(TRAIT_PARASITE)) - dat += "PAR " - if ("temperature" in scanner) - if(seed.get_trait(TRAIT_ALTER_TEMP) > 0) - dat += "TEMP+ " - if(seed.get_trait(TRAIT_ALTER_TEMP) < 0) - dat += "TEMP- " - if(seed.get_trait(TRAIT_BIOLUM)) - dat += "LUM " - dat += "[S.amount]Vend Purge
" - - show_browser(user, dat, "window=seedstorage") - onclose(user, "seedstorage") - -/obj/machinery/seed_storage/Topic(href, list/href_list) - if (..()) + seed_type["water_consumption"] = "Average" + else + seed_type["water_consumption"] = "No" + + switch(seed.get_trait(TRAIT_CARNIVOROUS)) + if(1) + traits += "CARN" + if(2) + traits += "CARN (!)" + + switch(seed.get_trait(TRAIT_SPREAD)) + if(1) + traits += "VINE" + if(2) + traits += "VINE (!)" + + if ("pressure" in scanner) + if(seed.get_trait(TRAIT_LOWKPA_TOLERANCE) < 20) + traits += "LP" + if(seed.get_trait(TRAIT_HIGHKPA_TOLERANCE) > 220) + traits += "HP" + + if ("temperature" in scanner) + if(seed.get_trait(TRAIT_HEAT_TOLERANCE) > 30) + traits += "TEMRES" + else if(seed.get_trait(TRAIT_HEAT_TOLERANCE) < 10) + traits += "TEMSEN" + + if ("light" in scanner) + if(seed.get_trait(TRAIT_LIGHT_TOLERANCE) > 10) + traits += "LIGRES" + else if(seed.get_trait(TRAIT_LIGHT_TOLERANCE) < 3) + traits += "LIGSEN" + + if(seed.get_trait(TRAIT_TOXINS_TOLERANCE) < 3) + traits += "TOXSEN" + else if(seed.get_trait(TRAIT_TOXINS_TOLERANCE) > 6) + traits += "TOXRES" + + if(seed.get_trait(TRAIT_PEST_TOLERANCE) < 3) + traits += "PESTSEN" + else if(seed.get_trait(TRAIT_PEST_TOLERANCE) > 6) + traits += "PESTRES" + + if(seed.get_trait(TRAIT_WEED_TOLERANCE) < 3) + traits += "WEEDSEN" + else if(seed.get_trait(TRAIT_WEED_TOLERANCE) > 6) + traits += "WEEDRES" + + if(seed.get_trait(TRAIT_PARASITE)) + traits += "PAR" + + if("temperature" in scanner) + if(seed.get_trait(TRAIT_ALTER_TEMP) > 0) + traits += "TEMP+" + if(seed.get_trait(TRAIT_ALTER_TEMP) < 0) + traits += "TEMP-" + + if(seed.get_trait(TRAIT_BIOLUM)) + traits += "LUM" + + seed_type["amount"] = S.amount + seed_type["traits"] = english_list(traits) + data["seeds"] += list(seed_type) + + return data + +/obj/machinery/seed_storage/tgui_act(action, params) + . = ..() + if(.) return - var/task = href_list["task"] - var/ID = text2num(href_list["id"]) - for (var/datum/seed_pile/N in piles) - if (N.ID == ID) - if (task == "vend") + switch(action) + if("vend") + for(var/datum/seed_pile/N in piles) + if(N.ID != text2num(params["ID"])) + continue + var/obj/O = pick(N.seeds) - if (O) + if(istype(O)) --N.amount N.seeds -= O - if (N.amount <= 0 || N.seeds.len <= 0) + if(N.amount <=0 || N.seeds.len <= 0) piles -= N qdel(N) O.dropInto(loc) else piles -= N qdel(N) - else if (task == "purge") - for (var/obj/O in N.seeds) + + return TRUE + + if("purge") + for(var/datum/seed_pile/N in piles) + for(var/obj/O in N.seeds) qdel(O) piles -= N qdel(N) - break - updateUsrDialog() + return TRUE /obj/machinery/seed_storage/attackby(obj/item/O as obj, mob/user as mob) if (istype(O, /obj/item/seeds)) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 419ce04de8d..4848a05748d 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -8,116 +8,122 @@ req_access = list(access_engine_equip) circuit = /obj/item/circuitboard/drone_control - //Used when pinging drones. + /// Used when pinging drones. var/drone_call_area = "Engineering" - //Used to enable or disable drone fabrication. + /// Used to enable or disable drone fabrication. var/obj/machinery/drone_fabricator/dronefab + /// Cooldown for area pings + var/ping_cooldown = 0 -/obj/machinery/computer/drone_control/attack_ai(mob/user as mob) - return src.attack_hand(user) -/obj/machinery/computer/drone_control/attack_hand(mob/user as mob) - if(..()) +/obj/machinery/computer/drone_control/attack_hand(mob/user) + . = ..() + if(.) return - if(!allowed(user)) - to_chat(user, "Access denied.") + tgui_interact(user) + +/obj/machinery/computer/drone_control/tgui_state(mob/user) + return GLOB.default_state + +/obj/machinery/computer/drone_control/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DroneConsole", src) + ui.open() + +/obj/machinery/computer/drone_control/tgui_data(mob/user) + var/list/data = list( + "drone_fab" = istype(dronefab), + "fab_power" = (dronefab?.stat & NOPOWER) ? FALSE : TRUE, + "drone_prod" = dronefab?.produce_drones, + "drone_progress" = clamp(dronefab?.drone_progress, 0, 100), + ) + data["selected_area"] = drone_call_area + data["ping_cd"] = ping_cooldown > world.time ? TRUE : FALSE + + data["drones"] = list() + for(var/mob/living/silicon/robot/drone/D in GLOB.silicon_mob_list) + var/area/A = get_area(D) + var/turf/T = get_turf(D) + var/list/drone_data = list( + name = D.real_name, + ref = any2ref(D), + stat = D.stat, + client = D.client ? TRUE : FALSE, + health = round(D.health / D.maxHealth, 0.1), + charge = round(D.cell.charge / D.cell.maxcharge, 0.1), + location = "[A] ([T.x], [T.y])", + ) + data["drones"] += list(drone_data) + return data + +/obj/machinery/computer/drone_control/tgui_static_data(mob/user) + var/list/data = list() + data["area_list"] = GLOB.tagger_locations + return data + +/obj/machinery/computer/drone_control/tgui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) return - - user.set_machine(src) - var/dat = "" - dat += "Maintenance Units
" - - for(var/mob/living/silicon/robot/drone/D in world) - if(D.z != src.z) - continue - dat += "
[D.real_name] ([D.stat == 2 ? "INACTIVE" : "ACTIVE"])" - dat += "
Cell charge: [D.cell.charge]/[D.cell.maxcharge]." - dat += "
Currently located in: [get_area(D)]." - dat += "
Resync | Shutdown
" - - dat += "

Request drone presence in area: [drone_call_area] (Send ping)" - - dat += "

Drone fabricator: " - dat += "[dronefab ? "[(dronefab.produce_drones && !(dronefab.stat & NOPOWER)) ? "ACTIVE" : "INACTIVE"]" : "FABRICATOR NOT DETECTED. (search)"]" - show_browser(user, dat, "window=computer;size=400x500") - onclose(user, "computer") - return - - -/obj/machinery/computer/drone_control/Topic(href, href_list) - if((. = ..())) + . = TRUE + + switch(action) + if("find_fab") + find_fab(usr) + return TRUE + + if("toggle_fab") + if(QDELETED(dronefab)) + dronefab = null + return + + dronefab.produce_drones = !dronefab.produce_drones + var/toggle = dronefab.produce_drones ? "enable" : "disable" + show_splash_text(usr, "Drone production toggled", SPAN_NOTICE("You [toggle] drone production in the nearby fabricator.")) + message_admins("[key_name_admin(usr)] [toggle]d maintenance drone production from the control console.") + log_game("[key_name(usr)] [toggle]d maintenance drone production from the control console.") + return TRUE + + if("set_area") + drone_call_area = params["area"] + return TRUE + + if("ping") + ping_cooldown = world.time + 15 SECONDS + show_splash_text(usr, "Maintenance request issued!", SPAN_NOTICE("You issue a maintenance request for all active drones, highlighting [drone_call_area].")) + for(var/mob/living/silicon/robot/drone/D in GLOB.silicon_mob_list) + if(D.client && D.stat == CONSCIOUS) + to_chat(usr, SPAN_NOTICE("Maintenance drone presence requested in: [drone_call_area].")) + return TRUE + + if("resync") + var/mob/living/silicon/robot/drone/D = locate(params["ref"]) in GLOB.silicon_mob_list + if(D) + show_splash_text(usr, "Laws synced", SPAN_NOTICE("You issue a law synchronization directive for the drone.")) + D.law_resync() + return TRUE + + if("shutdown") + var/mob/living/silicon/robot/drone/D = locate(params["ref"]) in GLOB.silicon_mob_list + if(D) + show_splash_text(usr, "Drone shut down", SPAN_WARNING("You issue a recall command for the unfortunate drone.")) + if(D != usr) // Don't need to bug admins about a suicide + message_admins("[key_name_admin(usr)] issued recall order for drone [key_name_admin(D)] from control console.") + log_game("[key_name(usr)] issued recall order for [key_name(D)] from control console.") + D.shut_down() + return TRUE + +/obj/machinery/computer/drone_control/proc/find_fab(mob/user) + if(dronefab) return - if(!allowed(usr)) - to_chat(usr, "Access denied.") + for(var/obj/machinery/drone_fabricator/fab in get_area(src)) + if(fab.stat & NOPOWER) + continue + dronefab = fab + if(user) + show_splash_text(user, "Dronefab located!", SPAN_NOTICE("Drone fabricator located.")) return - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if (href_list["setarea"]) - - //Probably should consider using another list, but this one will do. - var/t_area = input("Select the area to ping.", "Set Target Area", null) as null|anything in GLOB.tagger_locations - - if(!t_area) - return - - drone_call_area = t_area - to_chat(usr, "You set the area selector to [drone_call_area].") - - else if (href_list["ping"]) - - to_chat(usr, "You issue a maintenance request for all active drones, highlighting [drone_call_area].") - for(var/mob/living/silicon/robot/drone/D in world) - if(D.client && D.stat == 0) - to_chat(D, "-- Maintenance drone presence requested in: [drone_call_area].") - - else if (href_list["resync"]) - - var/mob/living/silicon/robot/drone/D = locate(href_list["resync"]) - - if(D.stat != 2) - to_chat(usr, "You issue a law synchronization directive for the drone.") - D.law_resync() - - else if (href_list["shutdown"]) - - var/mob/living/silicon/robot/drone/D = locate(href_list["shutdown"]) - - if(D.stat != 2) - to_chat(usr, "You issue a kill command for the unfortunate drone.") - message_admins("[key_name_admin(usr)] issued kill order for drone [key_name_admin(D)] from control console.") - log_game("[key_name(usr)] issued kill order for [key_name(src)] from control console.") - D.shut_down() - - else if (href_list["search_fab"]) - if(dronefab) - return - - for(var/obj/machinery/drone_fabricator/fab in oview(3,src)) - - if(fab.stat & NOPOWER) - continue - - dronefab = fab - to_chat(usr, "Drone fabricator located.") - return - - to_chat(usr, "Unable to locate drone fabricator.") - - else if (href_list["toggle_fab"]) - - if(!dronefab) - return - - if(get_dist(src,dronefab) > 3) - dronefab = null - to_chat(usr, "Unable to locate drone fabricator.") - return - - dronefab.produce_drones = !dronefab.produce_drones - to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") - - src.updateUsrDialog() + if(user) + show_splash_text(user, "Unable to locate!", SPAN_WARNING("Unable to locate drone fabricator.")) diff --git a/tgui/packages/tgui/interfaces/DroneConsole.tsx b/tgui/packages/tgui/interfaces/DroneConsole.tsx new file mode 100644 index 00000000000..d59072678a6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DroneConsole.tsx @@ -0,0 +1,232 @@ +import { toTitleCase } from "common/string"; +import { useBackend } from "../backend"; +import { + Box, + Button, + Divider, + Dropdown, + Stack, + LabeledList, + NoticeBox, + ProgressBar, + Section, +} from "../components"; +import { Window } from "../layouts"; + +export const DroneConsole = (props, context) => { + return ( + + + + + + + + + ); +}; + +interface Drone { + name: string; + ref: string; + stat: number; + client: boolean; + health: number; + charge: number; + location: string; +} + +interface DroneConsoleData { + drone_fab: boolean; + fab_power: boolean; + drone_prod: boolean; + drone_progress: number; + drones: Drone[]; + area_list: String[]; + selected_area: string; + ping_cd: number; +} + +const Fabricator = (props, context) => { + const { act, data } = useBackend(context); + const { drone_fab, fab_power, drone_prod, drone_progress } = data; + + let FabDetected = () => { + if (drone_fab) { + return ( + + + + [ {fab_power ? "Online" : "Offline"} ] + + + + + + + ); + } else { + return ( + + + FABRICATOR NOT DETECTED. + + + } + > + + {currentBeaconLog.logs.map((message: Message) => ( + + {message.time}{" "} + {decodeHtmlEntities(message.message)} + + ))} + + + ) : ( + <> + {beacons.map((beacon) => ( +
+ + + act("shock", { mt_ref: beacon.ref })} + /> + + } + > + + + + + + {(beacon?.cellCharge && ( + + )) || No Cell Installed} + + + {beacon.airtank}kPa + + + {beacon.pilot || "Unoccupied"} + + + {toTitleCase(beacon.location) || "Unknown"} + + + {beacon.equipment || "None"} + + {(beacon.cargoCapacity && ( + + + + )) || + null} + +
+ ))} + + )} + + ) : ( + <> + + + No beacons detected! + + + + )} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PrisonerImplantManager.tsx b/tgui/packages/tgui/interfaces/PrisonerImplantManager.tsx new file mode 100644 index 00000000000..9e89aadf0fd --- /dev/null +++ b/tgui/packages/tgui/interfaces/PrisonerImplantManager.tsx @@ -0,0 +1,157 @@ +import { useBackend } from "../backend"; +import { Window } from "../layouts"; +import { + Box, + Section, + Stack, + LabeledList, + Button, + Divider, + LabeledControls, +} from "../components"; + +interface Implant { + implantee: string; + ref: string; + location: string; + id: number; + remainingUnits: number; + coordinates: string; +} + +interface InputData { + chemImplants: Implant[]; + trackingImplants: Implant[]; +} + +export const PrisonerImplantManager = (props: any, context: any) => { + const { act, data } = useBackend(context); + const { trackingImplants, chemImplants } = data; + + return ( + + + + +
+ {chemImplants?.length ? ( + + {chemImplants?.map((implant) => ( + + + Subject: {implant.implantee} + +
+ + + {implant.remainingUnits} u. + + + + + + + + +
+
+
+ ))} +
+ ) : ( + <> + + + No implants. + + + + )} +
+
+ +
+ {trackingImplants?.length ? ( + + {trackingImplants?.map((implant) => ( + + + Subject: {implant.implantee} + +
+ + + {implant.id} + + + {implant.location} + + + {implant.coordinates} + + +
+ +
+
+
+ ))} +
+ ) : ( + <> + + + No implants. + + + + )} +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/SeedStorage.tsx b/tgui/packages/tgui/interfaces/SeedStorage.tsx new file mode 100644 index 00000000000..58223cc8586 --- /dev/null +++ b/tgui/packages/tgui/interfaces/SeedStorage.tsx @@ -0,0 +1,161 @@ +import { BooleanLike } from "../../common/react"; +import { useBackend } from "../backend"; +import { + Button, + Dimmer, + Divider, + LabeledList, + Section, + Stack, + Table, +} from "../components"; +import { Window } from "../layouts"; +import { useLocalState } from "../backend"; +import { capitalize } from "../../common/string"; + +export type SeedData = { + scan_stats: BooleanLike; + scan_temperature: BooleanLike; + scan_light: BooleanLike; + scan_soil: BooleanLike; + seeds: Seed[]; +}; + +type Seed = { + name: string; + uid: number; + pile_id: number; + endurance: any; + yield: any; + maturation: any; + production: any; + potency: any; + harvest: string; + ideal_heat: string; + ideal_light: string; + nutrient_consumption: string; + water_consumption: string; + traits: string; + amount: number; +}; + +export const SeedStorage = (props, context) => { + const { act, data } = useBackend(context); + + const [selectedSeed, setSelectedSeed] = useLocalState( + context, + "spellsNameFilter", + null + ); + + return ( + + +
act("purge")} + /> + } + > + {data.seeds.map((seed: Seed) => ( + + ))} +
+ {selectedSeed && ( + +
+ + + {selectedSeed.endurance} + + + {selectedSeed.yield} + + + {selectedSeed.maturation} + + + {selectedSeed.production} + + + {selectedSeed.potency} + + + {selectedSeed.harvest} + + {data.scan_temperature && ( + + {selectedSeed.ideal_heat} + + )} + {data.scan_light && ( + + {selectedSeed.ideal_light} + + )} + {data.scan_soil && ( + <> + + {selectedSeed.nutrient_consumption} + + + {selectedSeed.water_consumption} + + + )} + + {selectedSeed.traits} + + + {selectedSeed.amount} + + + + + + + + + +
+
+ )} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/SpaceHeater.tsx b/tgui/packages/tgui/interfaces/SpaceHeater.tsx new file mode 100644 index 00000000000..2d5a8e8b968 --- /dev/null +++ b/tgui/packages/tgui/interfaces/SpaceHeater.tsx @@ -0,0 +1,76 @@ +import { useBackend } from "../backend"; +import { Button, LabeledList, NumberInput, Section } from "../components"; +import { Window } from "../layouts"; +import { useLocalState } from "../backend"; + +function kelvinToCelsius(kelvin: number) { + return kelvin - 273.15; +} + +type InputData = { + cell: boolean; + charge: number; + temperature: number; + minTemperature: number; + maxTemperature: number; +}; + +export const SpaceHeater = (props: any, context: any) => { + const { act, data } = useBackend(context); + const [useKelvin, setUseKelvin] = useLocalState( + context, + "useKelvin", + true + ); + + return ( + + +
setUseKelvin(!useKelvin)}> + Switch Units + + } + > + + + { + act("changeTemperature", { + newTemp: value, + useKelvin: useKelvin, + }); + }} + > + + + + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/UnderWardrobe.tsx b/tgui/packages/tgui/interfaces/UnderWardrobe.tsx new file mode 100644 index 00000000000..57736177e60 --- /dev/null +++ b/tgui/packages/tgui/interfaces/UnderWardrobe.tsx @@ -0,0 +1,150 @@ +import { BooleanLike } from "common/react"; +import { useBackend, useLocalState } from "../backend"; +import { + Button, + Section, + NoticeBox, + Stack, + Divider, + Table, + Tabs, +} from "../components"; +import { Window } from "../layouts"; + +interface UnderwearItem { + name: string; +} + +interface UnderwearCategory { + name: string; + catItems: UnderwearItem[]; +} + +interface InputData { + mayClaim: number; + underwearCategories: UnderwearCategory[]; +} + +const MAX_PER_PAGE = 18; + +const numberWithinRange = (min: number, n: number, max: number) => + Math.min(Math.max(n, min), max); + +export const UnderWardrobe = (props: any, context: any) => { + const { data, act } = useBackend(context); + + const [selectedUndieCategory, setSelectedUndieCategory] = useLocalState( + context, + "itemCategory", + data.underwearCategories[0]?.name + ); + + const [currentPage, setCurrentPage] = useLocalState( + context, + "currentPage", + 1 + ); + + const underwear = + data.underwearCategories.find( + (category) => category.name === selectedUndieCategory + )?.catItems || []; + + const totalPages = Math.ceil(underwear?.length / MAX_PER_PAGE); + + return ( + + +
+ + + + {data.underwearCategories?.map( + (category: UnderwearCategory) => ( + { + setSelectedUndieCategory(category.name), + setCurrentPage(1); + }} + > + {category.name} ({category.catItems?.length || 0}) + + ) + )} + + + + + + +
+ + {underwear + .slice( + (currentPage - 1) * MAX_PER_PAGE, + currentPage * MAX_PER_PAGE + ) + .map((item: UnderwearItem) => ( + <> + + {item.name} + + + + + + ))} +
+ + {underwear?.length >= MAX_PER_PAGE ? ( + <> + + + + + + + + + + + ) : ( + <> + )} +
+
+
+
+
+
+ ); +};