From b67798eb2cf2aee5910bde2df0a2616185487a84 Mon Sep 17 00:00:00 2001
From: Kashargul <144968721+Kashargul@users.noreply.github.com>
Date: Tue, 21 Oct 2025 19:28:23 +0200
Subject: [PATCH] moves robot sleepers to tgui (#18646)
* moves robot sleepers to tgui
* .
* .
* urg
* .
* urg
* .
* .
* there too
* .
* in case of emagging
* .
---
code/datums/datum.dm | 2 +-
.../gamemodes/events/holidays/Holidays.dm | 4 +
code/modules/admin/modify_robot.dm | 3 +
.../silicon/robot/dogborg/dog_sleeper.dm | 319 +++++++-----------
.../modules/mob/living/silicon/robot/robot.dm | 5 +
.../tgui/interfaces/ModifyRobot/index.tsx | 9 +-
.../tgui/interfaces/ModifyRobot/types.ts | 1 +
.../SubElements/CargoCompartment.tsx | 32 ++
.../SubElements/InjectorPanel.tsx | 31 ++
.../SubElements/SleeperStatus.tsx | 19 ++
.../SleeperStatusElements/SleeperButtons.tsx | 68 ++++
.../SleeperCargoStatus.tsx | 36 ++
.../SleeperStatusElements/SleeperPatient.tsx | 92 +++++
.../SleeperSelfClean.tsx | 45 +++
.../SleeperStatusPanel.tsx | 71 ++++
.../tgui/interfaces/RobotSleeper/constants.ts | 6 +
.../tgui/interfaces/RobotSleeper/functions.ts | 38 +++
.../tgui/interfaces/RobotSleeper/index.tsx | 34 ++
.../tgui/interfaces/RobotSleeper/types.ts | 44 +++
19 files changed, 661 insertions(+), 198 deletions(-)
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/CargoCompartment.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/InjectorPanel.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatus.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatusElements/SleeperButtons.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatusElements/SleeperCargoStatus.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatusElements/SleeperPatient.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatusElements/SleeperSelfClean.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/SubElements/SleeperStatusElements/SleeperStatusPanel.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/constants.ts
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/functions.ts
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/index.tsx
create mode 100644 tgui/packages/tgui/interfaces/RobotSleeper/types.ts
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index b7362deaa9..762bfa722c 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -102,7 +102,7 @@
*/
/datum/proc/Destroy(force=FALSE)
SHOULD_CALL_PARENT(TRUE)
- //SHOULD_NOT_SLEEP(TRUE)
+ SHOULD_NOT_SLEEP(TRUE)
tag = null
weak_reference = null //ensure prompt GCing of weakref.
diff --git a/code/game/gamemodes/events/holidays/Holidays.dm b/code/game/gamemodes/events/holidays/Holidays.dm
index 2af9e9d3b8..16a873073e 100644
--- a/code/game/gamemodes/events/holidays/Holidays.dm
+++ b/code/game/gamemodes/events/holidays/Holidays.dm
@@ -244,7 +244,11 @@ GLOBAL_LIST_EMPTY(Holiday) //Holidays are lists now, so we can have more than on
GLOB.Holiday = list()
var/H = tgui_input_text(src,"What holiday is it today?","Set Holiday")
+ if(!H)
+ return
var/B = tgui_input_text(src,"Now explain what the holiday is about","Set Holiday", multiline = TRUE, prevent_enter = TRUE)
+ if(!B)
+ return
GLOB.Holiday[H] = B
diff --git a/code/modules/admin/modify_robot.dm b/code/modules/admin/modify_robot.dm
index 25cab53eb9..29b2c562d7 100644
--- a/code/modules/admin/modify_robot.dm
+++ b/code/modules/admin/modify_robot.dm
@@ -57,6 +57,9 @@
var/datum/asset/spritesheet_batched/robot_icons/spritesheet = GLOB.robot_sprite_sheets[target.modtype]
if(target)
+ var/ui_theme = target.get_ui_theme()
+ if(ui_theme)
+ .["theme"] = ui_theme
.["target"] = list()
.["target"]["name"] = target.name
.["target"]["ckey"] = target.ckey
diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm
index b294bfd082..649ea0c683 100644
--- a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm
+++ b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm
@@ -16,7 +16,6 @@
var/list/injection_chems = list(REAGENT_ID_INAPROVALINE, REAGENT_ID_BICARIDINE, REAGENT_ID_KELOTANE, REAGENT_ID_ANTITOXIN, REAGENT_ID_DEXALIN, REAGENT_ID_TRICORDRAZINE, REAGENT_ID_SPACEACILLIN, REAGENT_ID_TRAMADOL) //The borg is able to heal every damage type. As a nerf, they use 750 charge per injection.
var/eject_port = "ingestion"
var/list/items_preserved = list()
- var/UI_open = FALSE
var/stabilizer = TRUE
var/compactor = FALSE
var/analyzer = FALSE
@@ -199,13 +198,8 @@
dlist.Cut()
if(length(contents) > 0)
hound.visible_message(span_warning("[hound.name] empties out their contents via their [eject_port] port."), span_notice("You empty your contents via your [eject_port] port."))
- for(var/C in contents)
- if(ishuman(C))
- var/mob/living/carbon/human/person = C
- person.forceMove(get_turf(src))
- else
- var/obj/T = C
- T.loc = hound.loc
+ for(var/atom/movable/content in contents)
+ content.forceMove(get_turf(src))
playsound(src, 'sound/effects/splat.ogg', 50, 1)
update_patient()
@@ -231,206 +225,143 @@
/obj/item/dogborg/sleeper/attack_self(mob/user)
if(..())
return
- sleeperUI(user)
+ tgui_interact(user)
-/obj/item/dogborg/sleeper/proc/sleeperUI(mob/user)
- var/dat = "
"
+/obj/item/dogborg/sleeper/tgui_static_data(mob/user)
+ var/list/data = ..()
- if(!delivery && compactor && length(contents))//garbage counter for trashpup
- dat += span_red(span_bold("Current load:") + " [length(contents)] / [max_item_count] objects.") + "
"
- dat += span_gray("([contents.Join(", ")])") + "
"
+ if(!isrobot(user))
+ return data
- if(ore_storage)
- dat += "
Current ore capacity: [current_capacity] / [max_ore_storage]."
+ var/mob/living/silicon/robot/robot_user = user
+ var/list/robot_chems = list()
+ for(var/re in injection_chems)
+ var/datum/reagent/possible_reagent = SSchemistry.chemical_reagents[re]
+ UNTYPED_LIST_ADD(robot_chems, list("id" = possible_reagent.id, "name" = possible_reagent.name))
- if(delivery && length(contents))
- dat += span_red(span_bold("Current load:") + " [length(contents)] / [max_item_count] objects.") + "
"
- dat += span_gray("Cargo compartment slot: Cargo 1.") + "
"
- if(length(deliveryslot_1))
- dat += span_gray("([deliveryslot_1.Join(", ")])") + "
"
- dat += span_gray("Cargo compartment slot: Cargo 2.") + "
"
- if(length(deliveryslot_2))
- dat += span_gray("([deliveryslot_2.Join(", ")])") + "
"
- dat += span_gray("Cargo compartment slot: Cargo 3.") + "
"
- if(length(deliveryslot_3))
- dat += span_gray("([deliveryslot_3.Join(", ")])") + "
"
- dat += span_red("Cargo compartment slot: Fuel.") + "
"
- dat += span_red("([jointext(contents - (deliveryslot_1 + deliveryslot_2 + deliveryslot_3),", ")])") + "
"
+ data["name"] = name
+ var/robot_theme = robot_user.get_ui_theme()
+ if(robot_theme)
+ data["theme"] = robot_theme
+ data["chems"] = robot_chems
+ return data
- //Cleaning and there are still un-preserved items
- if(cleaning && length(contents - items_preserved))
- dat += span_red(span_bold("Self-cleaning mode.") + " [length(contents - items_preserved)] object(s) remaining.") + "
"
+/obj/item/dogborg/sleeper/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/patient_data
- //There are no items to be processed other than un-preserved items
- else if(cleaning && length(items_preserved))
- dat += span_red(span_bold("Self-cleaning done. Eject remaining objects now.")) + "
"
-
- //Preserved items count when the list is populated
- if(length(items_preserved))
- dat += span_red("[length(items_preserved)] uncleanable object(s).") + "
"
-
- if(!patient)
- dat += "[src.name] Unoccupied"
- else
- dat += "[patient.name] => "
-
- switch(patient.stat)
- if(0)
- dat += span_green("Conscious")
- if(1)
- dat += span_orange("Unconscious")
- else
- dat += span_red("DEAD")
-
- var/pulse = "\t-Pulse, bpm: [patient.get_pulse(GETPULSE_TOOL)]"
- dat += (patient.pulse == PULSE_NONE || patient.pulse == PULSE_THREADY ? span_red(pulse) : span_white(pulse))
- dat += "
"
- var/health = "\t-Overall Health %: [round(100 * (patient.health / patient.getMaxHealth()))]"
- dat += (patient.health > 0 ? span_white(health) : span_red(health))
- dat += "
"
- var/brute = "\t-Brute Damage %: [patient.getBruteLoss()]"
- dat += (patient.getBruteLoss() < 60 ? span_gray(brute) : span_red(brute))
- dat += "
"
- var/oxygen = "\t-Respiratory Damage %: [patient.getOxyLoss()]"
- dat += (patient.getOxyLoss() < 60 ? span_gray(oxygen) : span_red(oxygen))
- dat += "
"
- var/toxic = "\t-Toxin Content %: [patient.getToxLoss()]"
- dat += (patient.getToxLoss() < 60 ? span_gray(toxic) : span_red(toxic))
- dat += "
"
- var/burn = "\t-Burn Severity %: [patient.getFireLoss()]"
- dat += (patient.getFireLoss() < 60 ? span_gray(burn) : span_red(burn))
- dat += "
"
-
- if(round(patient.paralysis / 4) >= 1)
- dat += text("
Patient paralyzed for: []
", round(patient.paralysis / 4) >= 1 ? "[round(patient.paralysis / 4)] seconds" : "None")
- if(patient.getBrainLoss())
- dat += "
" + span_orange("Significant brain damage detected.") + "
"
- if(patient.getCloneLoss())
- dat += "
" + span_orange("Patient may be improperly cloned.") + "
"
+ if(patient)
+ var/list/ingested_reagents = list()
if(patient.reagents.reagent_list.len)
- for(var/datum/reagent/R in patient.reagents.reagent_list)
- dat += "
[R.name]:
[round(R.volume, 0.1)] units
"
- dat += "
"
+ for(var/datum/reagent/ingested in patient.reagents.reagent_list)
+ UNTYPED_LIST_ADD(ingested_reagents, list("name" = ingested.name, "volume" = ingested.volume))
- var/datum/browser/popup = new(user, "sleeper_b", "[name] Console", 450, 500, src)
- popup.set_content(dat)
- popup.open()
- UI_open = TRUE
- return
+ patient_data = list(
+ "name" = patient.name,
+ "stat" = patient.stat,
+ "pulse" = patient.get_pulse(GETPULSE_TOOL),
+ "crit_pulse" = (patient.pulse == PULSE_NONE || patient.pulse == PULSE_THREADY),
+ "health" = patient.health,
+ "max_health" = patient.getMaxHealth(),
+ "brute" = patient.getBruteLoss(),
+ "oxy" = patient.getOxyLoss(),
+ "tox" = patient.getToxLoss(),
+ "burn" = patient.getFireLoss(),
+ "paralysis" = patient.paralysis,
+ "braindamage" = !!patient.getBrainLoss(),
+ "clonedamage" = !!patient.getCloneLoss(),
+ "ingested_reagents" = ingested_reagents
+ )
-/obj/item/dogborg/sleeper/Topic(href, href_list)
- if(..() || usr == patient)
- return
- usr.set_machine(src)
- if(href_list["refresh"])
- update_patient()
- src.updateUsrDialog(usr)
- sleeperUI(usr)
- return
- if(href_list["eject"])
- go_out()
- sleeperUI(usr)
- return
- if(href_list["close"])
- UI_open = FALSE
- return
- if(href_list["clean"])
- if(!cleaning)
- var/confirm = tgui_alert(usr, "You are about to engage self-cleaning mode. This will fill your [src] with caustic enzymes to remove any objects or biomatter, and convert them into energy. Are you sure?", "Confirmation", list("Self-Clean", "Cancel"))
- if(confirm == "Self-Clean")
- if(cleaning)
- return
- else
- cleaning = 1
- drain(startdrain)
- START_PROCESSING(SSobj, src)
- update_patient()
- if(patient)
- to_chat(patient, span_danger("[hound.name]'s [src.name] fills with caustic enzymes around you!"))
- return
- if(cleaning)
- sleeperUI(usr)
- return
- if(href_list["analyze"]) //DO HEALTH ANALYZER STUFF HERE.
- med_analyzer.scan_mob(patient,hound)
- if(href_list["port"])
- switch(eject_port)
- if("ingestion")
- eject_port = "disposal"
- if("disposal")
- eject_port = "ingestion"
- sleeperUI(usr)
- return
- if (href_list["ingest"])
- vore_ingest_all()
- sleeperUI(usr)
- return
- if(href_list["deliveryslot"])
- var/tag = tgui_input_list(usr, "Select active delivery slot:", "Slot Choice", deliverylists)
- if(!tag)
- return 0
- delivery_tag = tag
- sleeperUI(usr)
- return
- if(href_list["slot_eject"])
- if(length(deliverylists[delivery_tag]) > 0)
+ var/list/data = list(
+ "our_patient" = patient_data,
+ "eject_port" = eject_port,
+ "cleaning" = cleaning,
+ "medsensor" = medsensor,
+ "delivery" = delivery,
+ "delivery_tag" = delivery_tag,
+ "delivery_lists" = deliverylists,
+ "compactor" = compactor,
+ "max_item_count" = max_item_count,
+ "ore_storage" = ore_storage,
+ "current_capacity" = current_capacity,
+ "max_ore_storage" = max_ore_storage,
+ "contents" = contents,
+ "deliveryslot_1" = deliveryslot_1,
+ "deliveryslot_2" = deliveryslot_2,
+ "deliveryslot_3" = deliveryslot_3,
+ "items_preserved" = items_preserved,
+ )
+ return data
+/obj/item/dogborg/sleeper/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ if(ui.user == patient)
+ return FALSE
+
+ switch(action)
+ if("eject")
+ go_out()
+ return TRUE
+ if("clean")
+ if(cleaning)
+ return FALSE
+ cleaning = TRUE
+ drain(startdrain)
+ START_PROCESSING(SSobj, src)
+ update_patient()
+ if(patient)
+ to_chat(patient, span_danger("[hound.name]'s [src.name] fills with caustic enzymes around you!"))
+ return TRUE
+ if("analyze")
+ med_analyzer.scan_mob(patient,hound)
+ return TRUE
+ if("port")
+ var/new_port = params["value"]
+ if(!(new_port in list("disposal", "ingestion")))
+ return FALSE
+ eject_port = new_port
+ return TRUE
+ if("ingest")
+ vore_ingest_all()
+ return TRUE
+ if("deliveryslot")
+ var/new_tag = params["value"]
+ if(!(new_tag in deliverylists))
+ return FALSE
+ delivery_tag = new_tag
+ return TRUE
+ if("slot_eject")
+ if(!length(deliverylists[delivery_tag]))
+ return FALSE
hound.visible_message(span_warning("[hound.name] empties out their cargo compartment via their [eject_port] port."), span_notice("You empty your cargo compartment via your [eject_port] port."))
- for(var/C in deliverylists[delivery_tag])
- if(ishuman(C))
- var/mob/living/carbon/human/person = C
- person.forceMove(get_turf(src))
- else
- var/obj/T = C
- T.loc = hound.loc
+ for(var/atom/movable/content in deliverylists[delivery_tag])
+ content.forceMove(get_turf(src))
playsound(src, 'sound/effects/splat.ogg', 50, 1)
update_patient()
deliverylists[delivery_tag].Cut()
- sleeperUI(usr)
- return
- if(patient && !(patient.stat & DEAD)) //What is bitwise NOT? ... Thought it was tilde.
- if(href_list["inject"] == REAGENT_ID_INAPROVALINE || patient.health > min_health)
- inject_chem(usr, href_list["inject"])
- else
- to_chat(usr, span_notice("ERROR: Subject is not in stable condition for injections."))
- else
- to_chat(usr, span_notice("ERROR: Subject cannot metabolise chemicals."))
-
- updateUsrDialog(usr)
- sleeperUI(usr) //Needs a callback to boop the page to refresh.
- return
+ return TRUE
+ if("inject")
+ if(!patient || (patient.stat & DEAD))
+ to_chat(ui.user, span_notice("ERROR: Subject cannot metabolise chemicals."))
+ return FALSE
+ var/selected_reagent = params["value"]
+ if(!(selected_reagent in injection_chems))
+ return FALSE
+ if(selected_reagent == REAGENT_ID_INAPROVALINE || patient.health > min_health)
+ inject_chem(ui.user, selected_reagent)
+ else
+ to_chat(ui.user, span_notice("ERROR: Subject is not in stable condition for injections."))
+ return TRUE
/obj/item/dogborg/sleeper/proc/inject_chem(mob/user, chem)
if(patient && patient.reagents)
@@ -451,8 +382,6 @@
hound = src.loc
if(!istype(hound,/mob/living/silicon/robot))
return
- if(UI_open == TRUE)
- sleeperUI(hound)
//Cleaning looks better with red on, even with nobody in it
if(cleaning || (length(contents) > 10) || (decompiler && (length(contents) > 5)) || (analyzer && (length(contents) > 1)))
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 2a3755d381..1c5a1c3e8a 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1674,3 +1674,8 @@
nutrition = 1000
to_chat(src, span_warning("You have purged most of the nutrition lingering in your systems."))
return TRUE
+
+/mob/living/silicon/robot/proc/get_ui_theme()
+ if(emagged)
+ return "syndicate"
+ return ui_theme
diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
index 93d8d013f3..8e3714e21f 100644
--- a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
+++ b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
@@ -12,7 +12,7 @@ import {
Tabs,
} from 'tgui-core/components';
-import { LawManagerLaws, LawManagerLawSets } from '../LawManager';
+import { LawManagerLawSets, LawManagerLaws } from '../LawManager';
import { ModifyRobotNoModule } from './ModifyRobotNoModule';
import { ModifyRobotAccess } from './ModifyRobotTabs/ModifyRobotAccess';
import { ModifyRobotComponent } from './ModifyRobotTabs/ModifyRobotComponent';
@@ -63,6 +63,7 @@ export const ModifyRobot = (props) => {
comms_options,
armour_options,
current_gear,
+ theme,
} = data;
const [tab, setTab] = useState