mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
[MIRROR] moves robot sleepers to tgui (#11849)
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
999a0816fa
commit
a5adea312e
@@ -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.
|
||||
|
||||
|
||||
@@ -238,7 +238,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = "<TITLE>[name] Console</TITLE><BR>"
|
||||
/obj/item/dogborg/sleeper/tgui_state(mob/user)
|
||||
return GLOB.tgui_conscious_state
|
||||
|
||||
if(islist(injection_chems)) //Only display this if we're a drug-dispensing doggo.
|
||||
dat += "<h3>Injector</h3>"
|
||||
if(patient)// && patient.health > min_health) //Not necessary, leave the buttons on, but the feedback during injection will give more information.
|
||||
for(var/re in injection_chems)
|
||||
var/datum/reagent/C = SSchemistry.chemical_reagents[re]
|
||||
if(C)
|
||||
dat += "<A href='byond://?src=\ref[src];inject=[C.id]'>Inject [C.name]</A><BR>"
|
||||
else
|
||||
for(var/re in injection_chems)
|
||||
var/datum/reagent/C = SSchemistry.chemical_reagents[re]
|
||||
if(C)
|
||||
dat += span_linkOff("Inject [C.name]") + "<BR>"
|
||||
/obj/item/dogborg/sleeper/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "RobotSleeper", "[name] Console")
|
||||
ui.open()
|
||||
|
||||
dat += "<h3>[name] Status</h3>"
|
||||
dat += "<div style='display: flex; flex-wrap: wrap; flex-direction: row;'>"
|
||||
dat += "<A id='refbutton' href='byond://?src=\ref[src];refresh=1'>Refresh</A>"
|
||||
dat += "<A href='byond://?src=\ref[src];eject=1'>Eject All</A>"
|
||||
dat += "<A href='byond://?src=\ref[src];port=1'>Eject port: [eject_port]</A>"
|
||||
dat += "<A href='byond://?src=\ref[src];ingest=1'>Vore All</A>" //might as well make it obvious
|
||||
if(!cleaning)
|
||||
dat += "<A href='byond://?src=\ref[src];clean=1'>Self-Clean</A>"
|
||||
else
|
||||
dat += span_linkOff("Self-Clean")
|
||||
if(medsensor)
|
||||
dat += "<A href='byond://?src=\ref[src];analyze=1'>Analyze Patient</A>"
|
||||
if(delivery)
|
||||
dat += "<BR><h3>Cargo Compartment</h3><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];deliveryslot=1'>Active Slot: [delivery_tag]</A>"
|
||||
if(islist(deliverylists[delivery_tag]))
|
||||
dat += "<A href='byond://?src=\ref[src];slot_eject=1'>Eject Slot</A>"
|
||||
dat += "</div>"
|
||||
dat += "<div class='statusDisplay'>"
|
||||
/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.") + "<BR>"
|
||||
dat += span_gray("([contents.Join(", ")])") + "<BR><BR>"
|
||||
if(!isrobot(user))
|
||||
return data
|
||||
|
||||
if(ore_storage)
|
||||
dat += "<font color='red'><B>Current ore capacity:</B> [current_capacity] / [max_ore_storage].</font><BR>"
|
||||
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.") + "<BR>"
|
||||
dat += span_gray("Cargo compartment slot: Cargo 1.") + "<BR>"
|
||||
if(length(deliveryslot_1))
|
||||
dat += span_gray("([deliveryslot_1.Join(", ")])") + "<BR>"
|
||||
dat += span_gray("Cargo compartment slot: Cargo 2.") + "<BR>"
|
||||
if(length(deliveryslot_2))
|
||||
dat += span_gray("([deliveryslot_2.Join(", ")])") + "<BR>"
|
||||
dat += span_gray("Cargo compartment slot: Cargo 3.") + "<BR>"
|
||||
if(length(deliveryslot_3))
|
||||
dat += span_gray("([deliveryslot_3.Join(", ")])") + "<BR>"
|
||||
dat += span_red("Cargo compartment slot: Fuel.") + "<BR>"
|
||||
dat += span_red("([jointext(contents - (deliveryslot_1 + deliveryslot_2 + deliveryslot_3),", ")])") + "<BR><BR>"
|
||||
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.") + "<BR>"
|
||||
/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.")) + "<BR>"
|
||||
|
||||
//Preserved items count when the list is populated
|
||||
if(length(items_preserved))
|
||||
dat += span_red("[length(items_preserved)] uncleanable object(s).") + "<BR>"
|
||||
|
||||
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 += "<BR>"
|
||||
var/health = "\t-Overall Health %: [round(100 * (patient.health / patient.getMaxHealth()))]"
|
||||
dat += (patient.health > 0 ? span_white(health) : span_red(health))
|
||||
dat += "<BR>"
|
||||
var/brute = "\t-Brute Damage %: [patient.getBruteLoss()]"
|
||||
dat += (patient.getBruteLoss() < 60 ? span_gray(brute) : span_red(brute))
|
||||
dat += "<BR>"
|
||||
var/oxygen = "\t-Respiratory Damage %: [patient.getOxyLoss()]"
|
||||
dat += (patient.getOxyLoss() < 60 ? span_gray(oxygen) : span_red(oxygen))
|
||||
dat += "<BR>"
|
||||
var/toxic = "\t-Toxin Content %: [patient.getToxLoss()]"
|
||||
dat += (patient.getToxLoss() < 60 ? span_gray(toxic) : span_red(toxic))
|
||||
dat += "<BR>"
|
||||
var/burn = "\t-Burn Severity %: [patient.getFireLoss()]"
|
||||
dat += (patient.getFireLoss() < 60 ? span_gray(burn) : span_red(burn))
|
||||
dat += "<BR>"
|
||||
|
||||
if(round(patient.paralysis / 4) >= 1)
|
||||
dat += text("<HR>Patient paralyzed for: []<BR>", round(patient.paralysis / 4) >= 1 ? "[round(patient.paralysis / 4)] seconds" : "None")
|
||||
if(patient.getBrainLoss())
|
||||
dat += "<div class='line'>" + span_orange("Significant brain damage detected.") + "</div><br>"
|
||||
if(patient.getCloneLoss())
|
||||
dat += "<div class='line'>" + span_orange("Patient may be improperly cloned.") + "</div><br>"
|
||||
if(patient)
|
||||
var/list/ingested_reagents = list()
|
||||
if(patient.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in patient.reagents.reagent_list)
|
||||
dat += "<div class='line'><div style='width: 170px;' class='statusLabel'>[R.name]:</div><div class='statusValue'>[round(R.volume, 0.1)] units</div></div><br>"
|
||||
dat += "</div>"
|
||||
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)))
|
||||
|
||||
@@ -1681,3 +1681,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
|
||||
|
||||
@@ -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<number>(0);
|
||||
@@ -146,7 +147,11 @@ export const ModifyRobot = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Window width={target?.module ? 900 : 400} height={700}>
|
||||
<Window
|
||||
width={target?.module ? 900 : 400}
|
||||
height={700}
|
||||
theme={theme || 'ntos'}
|
||||
>
|
||||
<Window.Content>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item>
|
||||
|
||||
@@ -38,6 +38,7 @@ export type Data = {
|
||||
law_sets: law_pack[];
|
||||
active_ais: DropdownEntry[];
|
||||
selected_ai: string | null;
|
||||
theme?: string;
|
||||
};
|
||||
|
||||
export type DropdownEntry = {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Button, Dropdown, Section, Stack } from 'tgui-core/components';
|
||||
import type { Data } from '../types';
|
||||
|
||||
export const CargoCompartment = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const { delivery_lists, delivery_tag } = data;
|
||||
|
||||
return (
|
||||
<Section fill title="Cargo Compartment">
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<Dropdown
|
||||
onSelected={(value: string) =>
|
||||
act('deliveryslot', { value: value })
|
||||
}
|
||||
options={Object.keys(delivery_lists)}
|
||||
selected={delivery_tag}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
disabled={!delivery_lists[delivery_tag]}
|
||||
onClick={() => act('slot_eject')}
|
||||
>
|
||||
Eject Slot
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Button, Section, Stack } from 'tgui-core/components';
|
||||
import type { Data, RobotChem } from '../types';
|
||||
|
||||
export const InjectorPanel = (props: { robotChems: RobotChem[] }) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const { our_patient } = data;
|
||||
const { robotChems } = props;
|
||||
|
||||
return (
|
||||
<Section fill title="Injector">
|
||||
{robotChems.length ? (
|
||||
<Stack wrap="wrap">
|
||||
{robotChems.map((chem) => (
|
||||
<Stack.Item basis="49%" key={chem.id}>
|
||||
<Button
|
||||
fluid
|
||||
disabled={!our_patient || our_patient.stat === 2}
|
||||
onClick={() => act('inject', { value: chem.id })}
|
||||
>
|
||||
Inject {chem.name}
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
) : (
|
||||
<Box color="red">No chems found.</Box>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Section, Stack } from 'tgui-core/components';
|
||||
import { SleeperButtons } from './SleeperStatusElements/SleeperButtons';
|
||||
import { SleeperStatusPanel } from './SleeperStatusElements/SleeperStatusPanel';
|
||||
|
||||
export const SleeperStatus = (props: { name: string }) => {
|
||||
const { name } = props;
|
||||
|
||||
return (
|
||||
<Section fill title={`${name} Status`}>
|
||||
<Stack fill vertical>
|
||||
<SleeperButtons />
|
||||
<Stack.Divider />
|
||||
<Stack.Item grow>
|
||||
<SleeperStatusPanel name={name} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Button, Dropdown, Stack } from 'tgui-core/components';
|
||||
import { EJECTION_OPTIONS } from '../../constants';
|
||||
import type { Data } from '../../types';
|
||||
|
||||
export const SleeperButtons = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const { eject_port, cleaning, medsensor, name } = data;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Item>
|
||||
<Stack align="center">
|
||||
<Stack.Item>
|
||||
<Box color="label">Eject Port:</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Dropdown
|
||||
onSelected={(value: string) => act('port', { value: value })}
|
||||
options={EJECTION_OPTIONS}
|
||||
selected={eject_port}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<Button.Confirm
|
||||
onClick={() => act('eject')}
|
||||
tooltip="Eject all your sleeper contents."
|
||||
>
|
||||
Eject All
|
||||
</Button.Confirm>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button.Confirm
|
||||
onClick={() => act('ingest')}
|
||||
tooltip="Moves all your sleepr contents into your currently selected vorebelly."
|
||||
>
|
||||
Vore All
|
||||
</Button.Confirm>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button.Confirm
|
||||
onClick={() => act('clean')}
|
||||
disabled={cleaning}
|
||||
color={cleaning ? 'red' : undefined}
|
||||
tooltip={`Self-Cleaning mode will fill your ${name} with causic enzymes to remove any objects or biomatter, and convert them into energy.`}
|
||||
>
|
||||
Self-Clean
|
||||
</Button.Confirm>
|
||||
</Stack.Item>
|
||||
{!!medsensor && (
|
||||
<Stack.Item>
|
||||
<Button
|
||||
onClick={() => act('analyze')}
|
||||
tooltip="Scan patient for detailed information."
|
||||
>
|
||||
Analyze Patient
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Fragment } from 'react';
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Stack } from 'tgui-core/components';
|
||||
import { filterFuel, summarizeItems } from '../../functions';
|
||||
import type { Data } from '../../types';
|
||||
|
||||
export const SleeperCargoStatus = (props) => {
|
||||
const { data } = useBackend<Data>();
|
||||
const { deliveryslot_1, deliveryslot_2, deliveryslot_3, contents } = data;
|
||||
|
||||
const cargoSlots = [deliveryslot_1, deliveryslot_2, deliveryslot_3];
|
||||
|
||||
return (
|
||||
<>
|
||||
{cargoSlots.map((slot, index) => (
|
||||
<Fragment key={index}>
|
||||
<Stack.Item>
|
||||
<Box color="label">Cargo compartment slot: Cargo {index}.</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color="label">{summarizeItems(slot)}</Box>
|
||||
</Stack.Item>
|
||||
</Fragment>
|
||||
))}
|
||||
<Stack.Item>
|
||||
<Box color="red">Cargo compartment slot: Fuel.</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color="red">
|
||||
{summarizeItems(filterFuel(contents, cargoSlots.flat()))}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Divider />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Stack } from 'tgui-core/components';
|
||||
import { STAT_TO_COLOR } from '../../constants';
|
||||
import type { Data } from '../../types';
|
||||
|
||||
export const SleeperPatient = (props: { name: string }) => {
|
||||
const { data } = useBackend<Data>();
|
||||
const { our_patient } = data;
|
||||
const { name } = props;
|
||||
|
||||
if (!our_patient) {
|
||||
return `${name} Unoccupied.`;
|
||||
}
|
||||
|
||||
const isParalysed = Math.round(our_patient.paralysis / 4) >= 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Item grow>
|
||||
<Stack>
|
||||
<Stack.Item>{our_patient.name}</Stack.Item>
|
||||
<Stack.Item>{`=>`}</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={Object.values(STAT_TO_COLOR)[our_patient.stat]}>
|
||||
{Object.keys(STAT_TO_COLOR)[our_patient.stat]}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.crit_pulse ? 'red' : undefined}>
|
||||
- Pulse, bpm: {our_patient.pulse}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.health > 0 ? undefined : 'red'}>
|
||||
{`- Overall Health: ${((100 * our_patient.health) / our_patient.max_health).toFixed(0)}`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.brute < 60 ? 'label' : 'red'}>
|
||||
{`- Brute Damage: ${our_patient.brute.toFixed(0)}`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.oxy < 60 ? 'label' : 'red'}>
|
||||
{`- Respiratory Damage: ${our_patient.oxy.toFixed(0)}`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.tox < 60 ? 'label' : 'red'}>
|
||||
{`- Toxin Content: ${our_patient.tox.toFixed(0)}`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Box color={our_patient.burn < 60 ? 'label' : 'red'}>
|
||||
{`- Burn Severity: ${our_patient.burn.toFixed(0)}`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
{(isParalysed ||
|
||||
!!our_patient.braindamage ||
|
||||
!!our_patient.clonedamage ||
|
||||
!!our_patient.ingested_reagents) && <Stack.Divider />}
|
||||
{isParalysed && (
|
||||
<Stack.Item>
|
||||
<Box bold>
|
||||
{`Patient paralyzed for: ${Math.round(our_patient.paralysis / 4)} seonds`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!our_patient.braindamage && (
|
||||
<Stack.Item>
|
||||
<Box color="orange">Significant brain damage detected.</Box>
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!our_patient.clonedamage && (
|
||||
<Stack.Item>
|
||||
<Box color="orange">Patient may be improperly cloned.</Box>
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!our_patient.ingested_reagents &&
|
||||
our_patient.ingested_reagents.map((reagent) => (
|
||||
<Stack.Item key={reagent.name}>
|
||||
<Box inline preserveWhitespace color="label">
|
||||
{`${reagent.name}: `}
|
||||
</Box>
|
||||
<Box inline>{Math.round(reagent.volume * 10) / 10} units</Box>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Stack } from 'tgui-core/components';
|
||||
import { filterFuel } from '../../functions';
|
||||
import type { Data } from '../../types';
|
||||
|
||||
export const SleeperSelfClean = (props) => {
|
||||
const { data } = useBackend<Data>();
|
||||
const { cleaning, items_preserved, contents } = data;
|
||||
|
||||
const perservedCount = items_preserved.length;
|
||||
const cleanableCount = filterFuel(contents, items_preserved).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!!cleaning && (
|
||||
<Stack.Item>
|
||||
{cleanableCount ? (
|
||||
<>
|
||||
<Box color="red" inline>
|
||||
Self-cleaning mode.
|
||||
</Box>
|
||||
<Box inline preserveWhitespace>
|
||||
{`${cleanableCount} object${cleanableCount > 1 ? 's' : ''} remaining.`}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
!!items_preserved && (
|
||||
<Box color="red">
|
||||
Self-cleaning done. Eject remaining objects now.
|
||||
</Box>
|
||||
)
|
||||
)}
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!perservedCount && (
|
||||
<Stack.Item>
|
||||
<Box color="red">
|
||||
{`${perservedCount} uncleanable object${perservedCount > 1 ? 's' : ''}.`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!cleaning || (!!perservedCount && <Stack.Divider />)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Box, Section, Stack } from 'tgui-core/components';
|
||||
import { currentLoadToColor, summarizeItems } from '../../functions';
|
||||
import type { Data } from '../../types';
|
||||
import { SleeperCargoStatus } from './SleeperCargoStatus';
|
||||
import { SleeperPatient } from './SleeperPatient';
|
||||
import { SleeperSelfClean } from './SleeperSelfClean';
|
||||
|
||||
export const SleeperStatusPanel = (props: { name: string }) => {
|
||||
const { data } = useBackend<Data>();
|
||||
const {
|
||||
delivery,
|
||||
compactor,
|
||||
contents,
|
||||
max_item_count,
|
||||
ore_storage,
|
||||
current_capacity,
|
||||
max_ore_storage,
|
||||
} = data;
|
||||
const { name } = props;
|
||||
|
||||
return (
|
||||
<Section fill scrollable>
|
||||
<Stack fill vertical>
|
||||
{(!!delivery || !!compactor) && !!contents.length && (
|
||||
<>
|
||||
<Stack.Item>
|
||||
<Box bold inline>
|
||||
Current load:
|
||||
</Box>
|
||||
<Box
|
||||
inline
|
||||
preserveWhitespace
|
||||
color={currentLoadToColor(contents.length, max_item_count)}
|
||||
>
|
||||
{` ${contents.length} / ${max_item_count} objects.`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
{delivery ? (
|
||||
<SleeperCargoStatus />
|
||||
) : (
|
||||
<Stack.Item>
|
||||
<Box color="label">{summarizeItems(contents)}</Box>
|
||||
</Stack.Item>
|
||||
)}
|
||||
<Stack.Divider />
|
||||
</>
|
||||
)}
|
||||
{!!ore_storage && (
|
||||
<>
|
||||
<Stack.Item>
|
||||
<Box bold inline>
|
||||
Current ore capacity:
|
||||
</Box>
|
||||
<Box
|
||||
inline
|
||||
preserveWhitespace
|
||||
color={currentLoadToColor(current_capacity, max_ore_storage)}
|
||||
>
|
||||
{` ${current_capacity} / ${max_ore_storage} ores.`}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Divider />
|
||||
</>
|
||||
)}
|
||||
<SleeperSelfClean />
|
||||
<SleeperPatient name={name} />
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
6
tgui/packages/tgui/interfaces/RobotSleeper/constants.ts
Normal file
6
tgui/packages/tgui/interfaces/RobotSleeper/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const EJECTION_OPTIONS = ['disposal', 'ingestion'];
|
||||
export const STAT_TO_COLOR = {
|
||||
Conscious: 'green',
|
||||
Unconscious: 'orange',
|
||||
DEAD: 'red',
|
||||
};
|
||||
38
tgui/packages/tgui/interfaces/RobotSleeper/functions.ts
Normal file
38
tgui/packages/tgui/interfaces/RobotSleeper/functions.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export function currentLoadToColor(
|
||||
current: number,
|
||||
maximum: number,
|
||||
): string | undefined {
|
||||
const fillState = current / maximum;
|
||||
if (fillState < 0.75) {
|
||||
return undefined;
|
||||
}
|
||||
if (fillState < 1) {
|
||||
return 'yellow';
|
||||
}
|
||||
return 'red';
|
||||
}
|
||||
|
||||
export function summarizeItems(items: string[]): string {
|
||||
const countMap = items.reduce<Record<string, number>>((acc, item) => {
|
||||
acc[item] = (acc[item] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return [...new Set(items)]
|
||||
.map((item) => (countMap[item] > 1 ? `${item} x${countMap[item]}` : item))
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
export function filterFuel(contents: string[], cargo: string[]): string[] {
|
||||
const cargoCopy = [...cargo];
|
||||
|
||||
return contents.reduce<string[]>((acc, item) => {
|
||||
const index = cargoCopy.indexOf(item);
|
||||
if (index !== -1) {
|
||||
cargoCopy.splice(index, 1);
|
||||
} else {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
34
tgui/packages/tgui/interfaces/RobotSleeper/index.tsx
Normal file
34
tgui/packages/tgui/interfaces/RobotSleeper/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Window } from 'tgui/layouts';
|
||||
import { Stack } from 'tgui-core/components';
|
||||
import { CargoCompartment } from './SubElements/CargoCompartment';
|
||||
import { InjectorPanel } from './SubElements/InjectorPanel';
|
||||
import { SleeperStatus } from './SubElements/SleeperStatus';
|
||||
import type { Data } from './types';
|
||||
|
||||
export const RobotSleeper = (props) => {
|
||||
const { data } = useBackend<Data>();
|
||||
|
||||
const { theme, chems, delivery, name } = data;
|
||||
return (
|
||||
<Window width={450} height={500} theme={theme || 'ntos'}>
|
||||
<Window.Content>
|
||||
<Stack vertical fill>
|
||||
{!!chems && (
|
||||
<Stack.Item>
|
||||
<InjectorPanel robotChems={chems} />
|
||||
</Stack.Item>
|
||||
)}
|
||||
{!!delivery && (
|
||||
<Stack.Item>
|
||||
<CargoCompartment />
|
||||
</Stack.Item>
|
||||
)}
|
||||
<Stack.Item grow>
|
||||
<SleeperStatus name={name ?? 'Unknown'} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
44
tgui/packages/tgui/interfaces/RobotSleeper/types.ts
Normal file
44
tgui/packages/tgui/interfaces/RobotSleeper/types.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { BooleanLike } from 'tgui-core/react';
|
||||
|
||||
export type Data = {
|
||||
name?: string;
|
||||
theme?: string;
|
||||
chems?: RobotChem[];
|
||||
|
||||
our_patient: Patient | null;
|
||||
eject_port: string;
|
||||
cleaning: BooleanLike;
|
||||
medsensor: BooleanLike;
|
||||
delivery: BooleanLike;
|
||||
delivery_tag: string;
|
||||
delivery_lists: Record<string, string[]>;
|
||||
compactor: BooleanLike;
|
||||
max_item_count: number;
|
||||
ore_storage: BooleanLike;
|
||||
current_capacity: number;
|
||||
max_ore_storage: number;
|
||||
contents: string[];
|
||||
deliveryslot_1: string[];
|
||||
deliveryslot_2: string[];
|
||||
deliveryslot_3: string[];
|
||||
items_preserved: string[];
|
||||
};
|
||||
|
||||
export type RobotChem = { id: string; name: string };
|
||||
|
||||
export type Patient = {
|
||||
name: string;
|
||||
stat: number;
|
||||
pulse: number;
|
||||
crit_pulse: BooleanLike;
|
||||
health: number;
|
||||
max_health: number;
|
||||
brute: number;
|
||||
oxy: number;
|
||||
tox: number;
|
||||
burn: number;
|
||||
paralysis: number;
|
||||
braindamage: BooleanLike;
|
||||
clonedamage: BooleanLike;
|
||||
ingested_reagents: { name: string; volume: number }[];
|
||||
};
|
||||
Reference in New Issue
Block a user