mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 06:00:16 +01:00
[MIRROR] New MOD Suit UI [MDB IGNORE] (#22718)
* New MOD Suit UI * Fix merge conflict+ bug that doesn't respect locked modsuits for non pais --------- Co-authored-by: Andrew <mt.forspam@gmail.com> Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com>
This commit is contained in:
+36
-21
@@ -6,24 +6,39 @@
|
||||
|
||||
/obj/item/mod/control/ui_data(mob/user)
|
||||
var/data = list()
|
||||
data["interface_break"] = interface_break
|
||||
data["malfunctioning"] = malfunctioning
|
||||
data["open"] = open
|
||||
data["active"] = active
|
||||
data["locked"] = locked
|
||||
data["complexity"] = complexity
|
||||
data["selected_module"] = selected_module?.name
|
||||
data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant"
|
||||
data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job"
|
||||
// SKYRAT EDIT START - pAIs in MODsuits
|
||||
data["pAI"] = mod_pai?.name
|
||||
data["ispAI"] = mod_pai ? mod_pai == user : FALSE
|
||||
// SKYRAT EDIT END
|
||||
data["core"] = core?.name
|
||||
data["charge"] = get_charge_percent()
|
||||
data["modules"] = list()
|
||||
// Suit information
|
||||
var/suit_status = list(
|
||||
"core_name" = core?.name,
|
||||
"cell_charge_current" = get_charge(),
|
||||
"cell_charge_max" = get_max_charge(),
|
||||
"active" = active,
|
||||
//"ai_name" = ai?.name, // SKYRAT EDIT REMOVAL - pAIs in MODsuits
|
||||
// Wires
|
||||
"open" = open,
|
||||
"seconds_electrified" = seconds_electrified,
|
||||
"malfunctioning" = malfunctioning,
|
||||
"locked" = locked,
|
||||
"interface_break" = interface_break,
|
||||
// Modules
|
||||
"complexity" = complexity,
|
||||
// SKYRAT EDIT START - pAIs in MODsuits
|
||||
"pAI" = mod_pai?.name,
|
||||
"ispAI" = mod_pai ? mod_pai == user : FALSE,
|
||||
// SKYRAT EDIT END
|
||||
)
|
||||
data["suit_status"] = suit_status
|
||||
// User information
|
||||
var/user_status = list(
|
||||
"user_name" = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "",
|
||||
"user_assignment" = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "",
|
||||
)
|
||||
data["user_status"] = user_status
|
||||
// Module information
|
||||
var/module_custom_status = list()
|
||||
var/module_info = list()
|
||||
for(var/obj/item/mod/module/module as anything in modules)
|
||||
var/list/module_data = list(
|
||||
module_custom_status += module.add_ui_data()
|
||||
module_info += list(list(
|
||||
"module_name" = module.name,
|
||||
"description" = module.desc,
|
||||
"module_type" = module.module_type,
|
||||
@@ -38,9 +53,9 @@
|
||||
"id" = module.tgui_id,
|
||||
"ref" = REF(module),
|
||||
"configuration_data" = module.get_configuration()
|
||||
)
|
||||
module_data += module.add_ui_data()
|
||||
data["modules"] += list(module_data)
|
||||
))
|
||||
data["module_custom_status"] = module_custom_status
|
||||
data["module_info"] = module_info
|
||||
return data
|
||||
|
||||
/obj/item/mod/control/ui_static_data(mob/user)
|
||||
@@ -59,7 +74,7 @@
|
||||
if(.)
|
||||
return
|
||||
// allowed() doesn't allow for pAIs
|
||||
if((!allowed(usr) || !ispAI(usr)) && locked) // SKYRAT EDIT - pAIs in MODsuits
|
||||
if(((locked && !ispAI(usr)) && !allowed(usr))) // SKYRAT EDIT CHANGE - ORIGINAL: if(locked && !allowed(usr))
|
||||
balloon_alert(usr, "insufficient access!")
|
||||
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||
return
|
||||
|
||||
@@ -165,10 +165,10 @@
|
||||
|
||||
/obj/item/mod/module/rad_protection/add_ui_data()
|
||||
. = ..()
|
||||
.["userradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : FALSE
|
||||
.["usertoxins"] = mod.wearer?.getToxLoss() || 0
|
||||
.["usermaxtoxins"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["threatlevel"] = perceived_threat_level
|
||||
.["is_user_irradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : FALSE
|
||||
.["background_radiation_level"] = perceived_threat_level
|
||||
.["health_max"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["loss_tox"] = mod.wearer?.getToxLoss() || 0
|
||||
|
||||
/obj/item/mod/module/rad_protection/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
overlay_state_active = "module_jetpack_on"
|
||||
/// Do we give the wearer a speed buff.
|
||||
var/full_speed = FALSE
|
||||
var/stabilize = FALSE
|
||||
var/stabilize = TRUE
|
||||
var/thrust_callback
|
||||
|
||||
/obj/item/mod/module/jetpack/Initialize(mapload)
|
||||
@@ -182,8 +182,12 @@
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
|
||||
incompatible_modules = list(/obj/item/mod/module/status_readout)
|
||||
tgui_id = "status_readout"
|
||||
/// Does this show damage types, body temp, satiety
|
||||
var/display_detailed_vitals = TRUE
|
||||
/// Does this show DNA data
|
||||
var/display_dna = FALSE
|
||||
/// Does this show the round ID and shift time?
|
||||
var/show_time = FALSE
|
||||
var/display_time = FALSE
|
||||
/// Death sound. May or may not be funny. Vareditable at your own risk.
|
||||
var/death_sound = 'sound/effects/flatline3.ogg'
|
||||
/// Death sound volume. Please be responsible with this.
|
||||
@@ -191,20 +195,22 @@
|
||||
|
||||
/obj/item/mod/module/status_readout/add_ui_data()
|
||||
. = ..()
|
||||
.["show_time"] = show_time
|
||||
.["statustime"] = station_time_timestamp()
|
||||
.["statusid"] = GLOB.round_id
|
||||
.["statushealth"] = mod.wearer?.health || 0
|
||||
.["statusmaxhealth"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["statusbrute"] = mod.wearer?.getBruteLoss() || 0
|
||||
.["statusburn"] = mod.wearer?.getFireLoss() || 0
|
||||
.["statustoxin"] = mod.wearer?.getToxLoss() || 0
|
||||
.["statusoxy"] = mod.wearer?.getOxyLoss() || 0
|
||||
.["statustemp"] = mod.wearer?.bodytemperature || 0
|
||||
.["statusnutrition"] = mod.wearer?.nutrition || 0
|
||||
.["statusfingerprints"] = mod.wearer ? md5(mod.wearer.dna.unique_identity) : null
|
||||
.["statusdna"] = mod.wearer?.dna.unique_enzymes
|
||||
.["statusviruses"] = null
|
||||
.["display_time"] = display_time
|
||||
.["shift_time"] = station_time_timestamp()
|
||||
.["shift_id"] = GLOB.round_id
|
||||
.["health"] = mod.wearer?.health || 0
|
||||
.["health_max"] = mod.wearer?.getMaxHealth() || 0
|
||||
if(display_detailed_vitals)
|
||||
.["loss_brute"] = mod.wearer?.getBruteLoss() || 0
|
||||
.["loss_fire"] = mod.wearer?.getFireLoss() || 0
|
||||
.["loss_tox"] = mod.wearer?.getToxLoss() || 0
|
||||
.["loss_oxy"] = mod.wearer?.getOxyLoss() || 0
|
||||
.["body_temperature"] = mod.wearer?.bodytemperature || 0
|
||||
.["nutrition"] = mod.wearer?.nutrition || 0
|
||||
if(display_dna)
|
||||
.["dna_unique_identity"] = mod.wearer ? md5(mod.wearer.dna.unique_identity) : null
|
||||
.["dna_unique_enzymes"] = mod.wearer?.dna.unique_enzymes
|
||||
.["viruses"] = null
|
||||
if(!length(mod.wearer?.diseases))
|
||||
return .
|
||||
var/list/viruses = list()
|
||||
@@ -216,10 +222,22 @@
|
||||
virus_data["maxstage"] = virus.max_stages
|
||||
virus_data["cure"] = virus.cure_text
|
||||
viruses += list(virus_data)
|
||||
.["statusviruses"] = viruses
|
||||
.["viruses"] = viruses
|
||||
|
||||
return .
|
||||
|
||||
/obj/item/mod/module/status_readout/get_configuration()
|
||||
. = ..()
|
||||
.["display_detailed_vitals"] = add_ui_configuration("Detailed Vitals", "bool", display_detailed_vitals)
|
||||
.["display_dna"] = add_ui_configuration("DNA Information", "bool", display_dna)
|
||||
|
||||
/obj/item/mod/module/status_readout/configure_edit(key, value)
|
||||
switch(key)
|
||||
if("display_detailed_vitals")
|
||||
display_detailed_vitals = text2num(value)
|
||||
if("display_dna")
|
||||
display_dna = text2num(value)
|
||||
|
||||
/obj/item/mod/module/status_readout/on_suit_activation()
|
||||
RegisterSignal(mod.wearer, COMSIG_LIVING_DEATH, PROC_REF(death_sound))
|
||||
|
||||
|
||||
@@ -20,22 +20,19 @@
|
||||
tgui_id = "health_analyzer"
|
||||
/// Scanning mode, changes how we scan something.
|
||||
var/mode = HEALTH_SCAN
|
||||
/// Do we relay the wearer's health data to the info tab? Mainly useful for turning off if you also have a status readout.
|
||||
var/show_vitals = TRUE
|
||||
|
||||
/// List of all scanning modes.
|
||||
var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN)
|
||||
|
||||
/obj/item/mod/module/health_analyzer/add_ui_data()
|
||||
. = ..()
|
||||
.["show_vitals"] = show_vitals
|
||||
if(show_vitals)
|
||||
.["userhealth"] = mod.wearer?.health || 0
|
||||
.["usermaxhealth"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["userbrute"] = mod.wearer?.getBruteLoss() || 0
|
||||
.["userburn"] = mod.wearer?.getFireLoss() || 0
|
||||
.["usertoxin"] = mod.wearer?.getToxLoss() || 0
|
||||
.["useroxy"] = mod.wearer?.getOxyLoss() || 0
|
||||
|
||||
.["health"] = mod.wearer?.health || 0
|
||||
.["health_max"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["loss_brute"] = mod.wearer?.getBruteLoss() || 0
|
||||
.["loss_fire"] = mod.wearer?.getFireLoss() || 0
|
||||
.["loss_tox"] = mod.wearer?.getToxLoss() || 0
|
||||
.["loss_oxy"] = mod.wearer?.getOxyLoss() || 0
|
||||
|
||||
return .
|
||||
|
||||
/obj/item/mod/module/health_analyzer/on_select_use(atom/target)
|
||||
@@ -56,16 +53,13 @@
|
||||
/obj/item/mod/module/health_analyzer/get_configuration()
|
||||
. = ..()
|
||||
.["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes)
|
||||
.["show_vitals"] = add_ui_configuration("Self Vitals Display", "bool", show_vitals)
|
||||
|
||||
|
||||
return .
|
||||
|
||||
/obj/item/mod/module/health_analyzer/configure_edit(key, value)
|
||||
switch(key)
|
||||
if("mode")
|
||||
mode = value
|
||||
if("show_vitals")
|
||||
show_vitals = value
|
||||
|
||||
#undef HEALTH_SCAN
|
||||
#undef WOUND_SCAN
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \
|
||||
and even useful information such as their overall health and wellness. This one comes with a clock that calibrates to the \
|
||||
local system time, and an operational ID number display. The vital monitor's speaker has been removed."
|
||||
show_time = TRUE
|
||||
display_time = TRUE
|
||||
death_sound = null
|
||||
death_sound_volume = null
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
capable of capturing and displaying all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \
|
||||
and even useful information such as their overall health and wellness. The vitals monitor also comes with a speaker, loud enough \
|
||||
to alert anyone nearby that someone has, in fact, died. This specific unit has a clock and operational ID readout."
|
||||
show_time = TRUE
|
||||
display_time = TRUE
|
||||
|
||||
///Blatant copy of the adrenaline boost module.
|
||||
/obj/item/mod/module/auto_doc
|
||||
|
||||
@@ -1,802 +0,0 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, ColorBox, LabeledList, ProgressBar, Section, Collapsible, Box, Icon, Stack, Table, Dimmer, NumberInput, Flex, AnimatedNumber, Dropdown } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const ConfigureNumberEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<NumberInput
|
||||
value={value}
|
||||
minValue={-50}
|
||||
maxValue={50}
|
||||
stepPixelSize={5}
|
||||
width="39px"
|
||||
onChange={(e, value) =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureBoolEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<Button.Checkbox
|
||||
checked={value}
|
||||
onClick={() =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': !value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureColorEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon="paint-brush"
|
||||
onClick={() =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ColorBox color={value} mr={0.5} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureListEntry = (props, context) => {
|
||||
const { name, value, values, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<Dropdown
|
||||
displayText={value}
|
||||
options={values}
|
||||
onSelected={(value) =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureDataEntry = (props, context) => {
|
||||
const { name, display_name, type, value, values, module_ref } = props;
|
||||
const configureEntryTypes = {
|
||||
number: <ConfigureNumberEntry {...props} />,
|
||||
bool: <ConfigureBoolEntry {...props} />,
|
||||
color: <ConfigureColorEntry {...props} />,
|
||||
list: <ConfigureListEntry {...props} />,
|
||||
};
|
||||
return (
|
||||
<Box>
|
||||
{display_name}: {configureEntryTypes[type]}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const RadCounter = (props, context) => {
|
||||
const { active, userradiated, usertoxins, usermaxtoxins, threatlevel } =
|
||||
props;
|
||||
return (
|
||||
<Stack fill textAlign="center">
|
||||
<Stack.Item grow>
|
||||
<Section
|
||||
title="Radiation Level"
|
||||
color={active && userradiated ? 'bad' : 'good'}>
|
||||
{active && userradiated ? 'IRRADIATED' : 'RADIATION-FREE'}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Toxins Level">
|
||||
<ProgressBar
|
||||
value={active ? usertoxins / usermaxtoxins : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={usertoxins} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section
|
||||
title="Hazard Level"
|
||||
color={active && threatlevel ? 'bad' : 'good'}
|
||||
bold>
|
||||
{active && threatlevel ? threatlevel : 0}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const HealthAnalyzer = (props, context) => {
|
||||
const {
|
||||
active,
|
||||
show_vitals,
|
||||
userhealth,
|
||||
usermaxhealth,
|
||||
userbrute,
|
||||
userburn,
|
||||
usertoxin,
|
||||
useroxy,
|
||||
} = props;
|
||||
return (
|
||||
<Section>
|
||||
{show_vitals ? (
|
||||
<>
|
||||
<Section title="Health">
|
||||
<ProgressBar
|
||||
value={active ? userhealth / usermaxhealth : 0}
|
||||
ranges={{
|
||||
good: [0.5, Infinity],
|
||||
average: [0.2, 0.5],
|
||||
bad: [-Infinity, 0.2],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? userhealth : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow>
|
||||
<Section title="Brute">
|
||||
<ProgressBar
|
||||
value={active ? userbrute / usermaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? userbrute : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Burn">
|
||||
<ProgressBar
|
||||
value={active ? userburn / usermaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? userburn : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Toxin">
|
||||
<ProgressBar
|
||||
value={active ? usertoxin / usermaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? usertoxin : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Suffocation">
|
||||
<ProgressBar
|
||||
value={active ? useroxy / usermaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? useroxy : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</>
|
||||
) : (
|
||||
<Section>
|
||||
{'Health Analyzer Vitals Readout Disabled In Settings'}
|
||||
</Section>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const StatusReadout = (props, context) => {
|
||||
const {
|
||||
active,
|
||||
show_time,
|
||||
statustime,
|
||||
statusid,
|
||||
statushealth,
|
||||
statusmaxhealth,
|
||||
statusbrute,
|
||||
statusburn,
|
||||
statustoxin,
|
||||
statusoxy,
|
||||
statustemp,
|
||||
statusnutrition,
|
||||
statusfingerprints,
|
||||
statusdna,
|
||||
statusviruses,
|
||||
} = props;
|
||||
return (
|
||||
<>
|
||||
{!!show_time && (
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow>
|
||||
<Section title="Operation Time">
|
||||
{active ? statustime : '00:00:00'}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Operation Number">
|
||||
{active ? statusid : '???'}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)}
|
||||
<Section title="Health">
|
||||
<ProgressBar
|
||||
value={active ? statushealth / statusmaxhealth : 0}
|
||||
ranges={{
|
||||
good: [0.5, Infinity],
|
||||
average: [0.2, 0.5],
|
||||
bad: [-Infinity, 0.2],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? statushealth : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow>
|
||||
<Section title="Brute">
|
||||
<ProgressBar
|
||||
value={active ? statusbrute / statusmaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? statusbrute : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Burn">
|
||||
<ProgressBar
|
||||
value={active ? statusburn / statusmaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? statusburn : 0} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Toxin">
|
||||
<ProgressBar
|
||||
value={active ? statustoxin / statusmaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={statustoxin} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Suffocation">
|
||||
<ProgressBar
|
||||
value={active ? statusoxy / statusmaxhealth : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={statusoxy} />
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow>
|
||||
<Section title="Body Temperature">{active ? statustemp : 0}</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section title="Nutrition Status">
|
||||
{active ? statusnutrition : 0}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<Section title="DNA">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Fingerprints">
|
||||
{active ? statusfingerprints : '???'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Unique Enzymes">
|
||||
{active ? statusdna : '???'}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
{!!active && !!statusviruses && (
|
||||
<Section title="Diseases">
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="signature"
|
||||
tooltip="Name"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="wind"
|
||||
tooltip="Type"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="bolt"
|
||||
tooltip="Stage"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="flask"
|
||||
tooltip="Cure"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{statusviruses.map((virus) => {
|
||||
return (
|
||||
<Table.Row key={virus.name}>
|
||||
<Table.Cell textAlign="center">{virus.name}</Table.Cell>
|
||||
<Table.Cell textAlign="center">{virus.type}</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{virus.stage}/{virus.maxstage}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">{virus.cure}</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ID2MODULE = {
|
||||
rad_counter: RadCounter,
|
||||
health_analyzer: HealthAnalyzer,
|
||||
status_readout: StatusReadout,
|
||||
};
|
||||
|
||||
const LockedInterface = () => (
|
||||
<Section align="center" fill>
|
||||
<Icon color="red" name="exclamation-triangle" size={15} />
|
||||
<Box fontSize="30px" color="red">
|
||||
ERROR: INTERFACE UNRESPONSIVE
|
||||
</Box>
|
||||
</Section>
|
||||
);
|
||||
|
||||
const LockedModule = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
return (
|
||||
<Dimmer>
|
||||
<Stack>
|
||||
<Stack.Item fontSize="16px" color="blue">
|
||||
SUIT UNPOWERED
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Dimmer>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureScreen = (props, context) => {
|
||||
const { configuration_data, module_ref } = props;
|
||||
const configuration_keys = Object.keys(configuration_data);
|
||||
return (
|
||||
<Dimmer backgroundColor="rgba(0, 0, 0, 0.8)">
|
||||
<Stack vertical>
|
||||
{configuration_keys.map((key) => {
|
||||
const data = configuration_data[key];
|
||||
return (
|
||||
<Stack.Item key={data.key}>
|
||||
<ConfigureDataEntry
|
||||
name={key}
|
||||
display_name={data.display_name}
|
||||
type={data.type}
|
||||
value={data.value}
|
||||
values={data.values}
|
||||
module_ref={module_ref}
|
||||
/>
|
||||
</Stack.Item>
|
||||
);
|
||||
})}
|
||||
<Stack.Item>
|
||||
<Box>
|
||||
<Button
|
||||
fluid
|
||||
onClick={props.onExit}
|
||||
icon="times"
|
||||
textAlign="center">
|
||||
Exit
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Dimmer>
|
||||
);
|
||||
};
|
||||
|
||||
const displayText = (param) => {
|
||||
switch (param) {
|
||||
case 1:
|
||||
return 'Use';
|
||||
case 2:
|
||||
return 'Toggle';
|
||||
case 3:
|
||||
return 'Select';
|
||||
}
|
||||
};
|
||||
|
||||
const ParametersSection = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
active,
|
||||
malfunctioning,
|
||||
locked,
|
||||
open,
|
||||
selected_module,
|
||||
complexity,
|
||||
complexity_max,
|
||||
wearer_name,
|
||||
wearer_job,
|
||||
pAI, // SKYRAT EDIT - pAIs in MODsuits
|
||||
ispAI, // SKYRAT EDIT - pAIs in MODsuits
|
||||
} = data;
|
||||
const status = malfunctioning
|
||||
? 'Malfunctioning'
|
||||
: active
|
||||
? 'Active'
|
||||
: 'Inactive';
|
||||
return (
|
||||
<Section title="Parameters">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Status"
|
||||
buttons={
|
||||
<Button
|
||||
icon="power-off"
|
||||
content={active ? 'Deactivate' : 'Activate'}
|
||||
onClick={() => act('activate')}
|
||||
/>
|
||||
}>
|
||||
{status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="ID Lock"
|
||||
buttons={
|
||||
<Button
|
||||
icon={locked ? 'lock-open' : 'lock'}
|
||||
content={locked ? 'Unlock' : 'Lock'}
|
||||
onClick={() => act('lock')}
|
||||
/>
|
||||
}>
|
||||
{locked ? 'Locked' : 'Unlocked'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Cover">
|
||||
{open ? 'Open' : 'Closed'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Selected Module">
|
||||
{selected_module || 'None'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Complexity">
|
||||
{complexity} ({complexity_max})
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Occupant">
|
||||
{wearer_name}, {wearer_job}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Onboard pAI"
|
||||
buttons={
|
||||
// SKYRAT EDIT START - pAIs in MODsuits
|
||||
pAI && !ispAI ? (
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject pAI"
|
||||
onClick={() => act('remove_pai')}
|
||||
/>
|
||||
) : (
|
||||
<> </>
|
||||
)
|
||||
}>
|
||||
{pAI || 'None' /* SKYRAT EDIT END */}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const HardwareSection = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
active,
|
||||
control,
|
||||
helmet,
|
||||
chestplate,
|
||||
gauntlets,
|
||||
boots,
|
||||
core,
|
||||
charge,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Hardware">
|
||||
<Collapsible title="Parts">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Control Unit">{control}</LabeledList.Item>
|
||||
<LabeledList.Item label="Helmet">{helmet || 'None'}</LabeledList.Item>
|
||||
<LabeledList.Item label="Chestplate">
|
||||
{chestplate || 'None'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Gauntlets">
|
||||
{gauntlets || 'None'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Boots">{boots || 'None'}</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Collapsible>
|
||||
<Collapsible title="Core">
|
||||
{(core && (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Core Type">{core}</LabeledList.Item>
|
||||
<LabeledList.Item label="Core Charge">
|
||||
<ProgressBar
|
||||
value={charge / 100}
|
||||
content={charge + '%'}
|
||||
ranges={{
|
||||
good: [0.6, Infinity],
|
||||
average: [0.3, 0.6],
|
||||
bad: [-Infinity, 0.3],
|
||||
}}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
)) || (
|
||||
<Box color="bad" textAlign="center">
|
||||
No Core Detected
|
||||
</Box>
|
||||
)}
|
||||
</Collapsible>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const InfoSection = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { active, modules } = data;
|
||||
const info_modules = modules.filter((module) => !!module.id);
|
||||
|
||||
return (
|
||||
<Section title="Info">
|
||||
<Stack vertical>
|
||||
{(info_modules.length !== 0 &&
|
||||
info_modules.map((module) => {
|
||||
const Module = ID2MODULE[module.id];
|
||||
return (
|
||||
<Stack.Item key={module.ref}>
|
||||
{!active && <LockedModule />}
|
||||
<Module {...module} active={active} />
|
||||
</Stack.Item>
|
||||
);
|
||||
})) || <Box textAlign="center">No Info Modules Detected</Box>}
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ModuleSection = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { complexity_max, modules } = data;
|
||||
const [configureState, setConfigureState] = useLocalState(
|
||||
context,
|
||||
'module_configuration',
|
||||
null
|
||||
);
|
||||
return (
|
||||
<Section title="Modules" fill>
|
||||
<Flex direction="column">
|
||||
{(modules.length !== 0 &&
|
||||
modules.map((module) => {
|
||||
return (
|
||||
<Flex.Item key={module.ref}>
|
||||
<Collapsible title={module.module_name}>
|
||||
<Section>
|
||||
{configureState === module.ref && (
|
||||
<ConfigureScreen
|
||||
configuration_data={module.configuration_data}
|
||||
module_ref={module.ref}
|
||||
onExit={() => setConfigureState(null)}
|
||||
/>
|
||||
)}
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="save"
|
||||
tooltip="Complexity"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="plug"
|
||||
tooltip="Idle Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="lightbulb"
|
||||
tooltip="Active Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="bolt"
|
||||
tooltip="Use Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="hourglass-half"
|
||||
tooltip="Cooldown"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="tasks"
|
||||
tooltip="Actions"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.module_complexity}/{complexity_max}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.idle_power}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.active_power}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.use_power}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{(module.cooldown > 0 && module.cooldown / 10) || '0'}
|
||||
/{module.cooldown_time / 10}s
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
<Button
|
||||
onClick={() => act('select', { 'ref': module.ref })}
|
||||
icon="bullseye"
|
||||
selected={module.module_active}
|
||||
tooltip={displayText(module.module_type)}
|
||||
tooltipPosition="left"
|
||||
disabled={!module.module_type}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => setConfigureState(module.ref)}
|
||||
icon="cog"
|
||||
selected={configureState === module.ref}
|
||||
tooltip="Configure"
|
||||
tooltipPosition="left"
|
||||
disabled={module.configuration_data.length === 0}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => act('pin', { 'ref': module.ref })}
|
||||
icon="thumbtack"
|
||||
selected={module.pinned}
|
||||
tooltip="Pin"
|
||||
tooltipPosition="left"
|
||||
disabled={!module.module_type}
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
<Box>{module.description}</Box>
|
||||
</Section>
|
||||
</Collapsible>
|
||||
</Flex.Item>
|
||||
);
|
||||
})) || (
|
||||
<Flex.Item>
|
||||
<Box textAlign="center">No Modules Detected</Box>
|
||||
</Flex.Item>
|
||||
)}
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const MODsuitContent = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { ui_theme, interface_break } = data;
|
||||
return (
|
||||
<Section>
|
||||
{(!!interface_break && <LockedInterface />) || (
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<ParametersSection />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<HardwareSection />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<InfoSection />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<ModuleSection />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const MODsuit = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { ui_theme, interface_break } = data;
|
||||
return (
|
||||
<Window
|
||||
width={400}
|
||||
height={525}
|
||||
theme={ui_theme}
|
||||
title="MOD Interface Panel"
|
||||
resizable>
|
||||
<Window.Content scrollable={!interface_break}>
|
||||
<MODsuitContent />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,736 @@
|
||||
import { BooleanLike } from 'common/react';
|
||||
import { formatSiUnit } from '../format';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, ColorBox, LabeledList, ProgressBar, Section, Collapsible, Box, Icon, Stack, Table, Dimmer, NumberInput, AnimatedNumber, Dropdown, NoticeBox } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type MODsuitData = {
|
||||
// Static
|
||||
ui_theme: string;
|
||||
control: string;
|
||||
complexity_max: number;
|
||||
helmet: string;
|
||||
chestplate: string;
|
||||
gauntlets: string;
|
||||
boots: string;
|
||||
// Dynamic
|
||||
suit_status: SuitStatus;
|
||||
user_status: UserStatus;
|
||||
module_custom_status: ModuleCustomStatus;
|
||||
module_info: Module[];
|
||||
};
|
||||
|
||||
type SuitStatus = {
|
||||
core_name: string;
|
||||
cell_charge_current: number;
|
||||
cell_charge_max: number;
|
||||
active: BooleanLike;
|
||||
open: BooleanLike;
|
||||
seconds_electrified: number;
|
||||
malfunctioning: BooleanLike;
|
||||
locked: BooleanLike;
|
||||
interface_break: BooleanLike;
|
||||
complexity: number;
|
||||
selected_module: string;
|
||||
ai_name: string;
|
||||
pAI: string; // SKYRAT EDIT ADDITION - pAIs in MODsuits
|
||||
ispAI: BooleanLike; // SKYRAT EDIT ADDITION - pAIs in MODsuits
|
||||
};
|
||||
|
||||
type UserStatus = {
|
||||
user_name: string;
|
||||
user_assignment: string;
|
||||
};
|
||||
|
||||
type ModuleCustomStatus = {
|
||||
health: number;
|
||||
health_max: number;
|
||||
loss_brute: number;
|
||||
loss_fire: number;
|
||||
loss_tox: number;
|
||||
loss_oxy: number;
|
||||
is_user_irradiated: BooleanLike;
|
||||
background_radiation_level: number;
|
||||
display_time: BooleanLike;
|
||||
shift_time: string;
|
||||
shift_id: string;
|
||||
body_temperature: number;
|
||||
nutrition: number;
|
||||
dna_unique_identity: string;
|
||||
dna_unique_enzymes: string;
|
||||
viruses: VirusData[];
|
||||
};
|
||||
|
||||
type VirusData = {
|
||||
name: string;
|
||||
type: string;
|
||||
stage: number;
|
||||
maxstage: number;
|
||||
cure: string;
|
||||
};
|
||||
|
||||
type Module = {
|
||||
module_name: string;
|
||||
description: string;
|
||||
module_type: number;
|
||||
module_active: BooleanLike;
|
||||
pinned: BooleanLike;
|
||||
idle_power: number;
|
||||
active_power: number;
|
||||
use_power: number;
|
||||
module_complexity: number;
|
||||
cooldown_time: number;
|
||||
cooldown: number;
|
||||
id: string;
|
||||
ref: string;
|
||||
configuration_data: ModuleConfig[];
|
||||
};
|
||||
|
||||
type ModuleConfig = {
|
||||
display_name: string;
|
||||
type: string;
|
||||
value: number;
|
||||
values: [];
|
||||
};
|
||||
|
||||
export const MODsuit = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const { ui_theme } = data;
|
||||
const { interface_break } = data.suit_status;
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={600}
|
||||
theme={ui_theme}
|
||||
title="MOD Interface Panel"
|
||||
resizable>
|
||||
<Window.Content scrollable={!interface_break}>
|
||||
<MODsuitContent />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const MODsuitContent = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const { interface_break } = data.suit_status;
|
||||
return (
|
||||
<Box>
|
||||
{interface_break ? (
|
||||
<LockedInterface />
|
||||
) : (
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<Stack>
|
||||
<Stack.Item grow>
|
||||
<SuitStatusSection />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<UserStatusSection />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<ModuleSection />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<HardwareSection />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureNumberEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<NumberInput
|
||||
value={value}
|
||||
minValue={-50}
|
||||
maxValue={50}
|
||||
stepPixelSize={5}
|
||||
width="39px"
|
||||
onChange={(e, value) =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureBoolEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<Button.Checkbox
|
||||
checked={value}
|
||||
onClick={() =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': !value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureColorEntry = (props, context) => {
|
||||
const { name, value, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon="paint-brush"
|
||||
onClick={() =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ColorBox color={value} mr={0.5} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureListEntry = (props, context) => {
|
||||
const { name, value, values, module_ref } = props;
|
||||
const { act } = useBackend(context);
|
||||
return (
|
||||
<Dropdown
|
||||
displayText={value}
|
||||
options={values}
|
||||
onSelected={(value) =>
|
||||
act('configure', {
|
||||
'key': name,
|
||||
'value': value,
|
||||
'ref': module_ref,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureDataEntry = (props, context) => {
|
||||
const { name, display_name, type, value, values, module_ref } = props;
|
||||
const configureEntryTypes = {
|
||||
number: <ConfigureNumberEntry {...props} />,
|
||||
bool: <ConfigureBoolEntry {...props} />,
|
||||
color: <ConfigureColorEntry {...props} />,
|
||||
list: <ConfigureListEntry {...props} />,
|
||||
};
|
||||
return (
|
||||
<LabeledList.Item label={display_name}>
|
||||
{configureEntryTypes[type]}
|
||||
</LabeledList.Item>
|
||||
);
|
||||
};
|
||||
|
||||
const LockedInterface = () => (
|
||||
<Section align="center" fill>
|
||||
<Icon color="red" name="exclamation-triangle" size={15} />
|
||||
<Box fontSize="30px" color="red">
|
||||
ERROR: INTERFACE UNRESPONSIVE
|
||||
</Box>
|
||||
</Section>
|
||||
);
|
||||
|
||||
const LockedModule = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
return (
|
||||
<Dimmer>
|
||||
<Stack>
|
||||
<Stack.Item fontSize="16px" color="blue">
|
||||
SUIT UNPOWERED
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Dimmer>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfigureScreen = (props, context) => {
|
||||
const { configuration_data, module_ref, module_name } = props;
|
||||
const configuration_keys = Object.keys(configuration_data);
|
||||
return (
|
||||
<Box pb={1}>
|
||||
<LabeledList>
|
||||
{configuration_keys.map((key) => {
|
||||
const data = configuration_data[key];
|
||||
return (
|
||||
<ConfigureDataEntry
|
||||
key={data.key}
|
||||
name={key}
|
||||
display_name={data.display_name}
|
||||
type={data.type}
|
||||
value={data.value}
|
||||
values={data.values}
|
||||
module_ref={module_ref}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const moduleTypeAction = (param) => {
|
||||
switch (param) {
|
||||
case 1:
|
||||
return 'Use';
|
||||
case 2:
|
||||
return 'Toggle';
|
||||
case 3:
|
||||
return 'Select';
|
||||
}
|
||||
};
|
||||
|
||||
const radiationLevels = (param) => {
|
||||
switch (param) {
|
||||
case 1:
|
||||
return 'Low';
|
||||
case 2:
|
||||
return 'Medium';
|
||||
case 3:
|
||||
return 'High';
|
||||
case 4:
|
||||
return 'Extreme';
|
||||
}
|
||||
};
|
||||
|
||||
const SuitStatusSection = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const {
|
||||
core_name,
|
||||
cell_charge_current,
|
||||
cell_charge_max,
|
||||
active,
|
||||
open,
|
||||
seconds_electrified,
|
||||
malfunctioning,
|
||||
locked,
|
||||
ai_name,
|
||||
pAI,
|
||||
ispAI,
|
||||
} = data.suit_status;
|
||||
const { display_time, shift_time, shift_id } = data.module_custom_status;
|
||||
const status = malfunctioning
|
||||
? 'Malfunctioning'
|
||||
: active
|
||||
? 'Active'
|
||||
: 'Inactive';
|
||||
const charge_percent = Math.round(
|
||||
(100 * cell_charge_current) / cell_charge_max
|
||||
);
|
||||
|
||||
return (
|
||||
<Section
|
||||
title="Suit Status"
|
||||
fill
|
||||
buttons={
|
||||
<Button
|
||||
icon="power-off"
|
||||
color={active ? 'good' : 'default'}
|
||||
content={status}
|
||||
onClick={() => act('activate')}
|
||||
/>
|
||||
}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Charge">
|
||||
<ProgressBar
|
||||
value={cell_charge_current / cell_charge_max}
|
||||
ranges={{
|
||||
good: [0.6, Infinity],
|
||||
average: [0.3, 0.6],
|
||||
bad: [-Infinity, 0.3],
|
||||
}}
|
||||
style={{
|
||||
'text-shadow': '1px 1px 0 black',
|
||||
}}>
|
||||
{!core_name
|
||||
? 'No Core Detected'
|
||||
: cell_charge_max === 1
|
||||
? 'Power Cell Missing'
|
||||
: cell_charge_current === 1e31
|
||||
? 'Infinite'
|
||||
: `${formatSiUnit(
|
||||
cell_charge_current * 1000,
|
||||
0,
|
||||
'J'
|
||||
)} of ${formatSiUnit(
|
||||
cell_charge_max * 1000,
|
||||
0,
|
||||
'J'
|
||||
)} (${charge_percent}%)`}
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="ID Lock">
|
||||
<Button
|
||||
icon={locked ? 'lock' : 'lock-open'}
|
||||
color={locked ? 'good' : 'default'}
|
||||
content={locked ? 'Locked' : 'Unlocked'}
|
||||
onClick={() => act('lock')}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
{!!open && (
|
||||
<LabeledList.Item label="Cover">
|
||||
<Box color="red">Open</Box>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!seconds_electrified && (
|
||||
<LabeledList.Item label="Circuits">
|
||||
<Box color="red">Shorted</Box>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!ai_name && (
|
||||
<LabeledList.Item label="AI Core">{ai_name}</LabeledList.Item>
|
||||
)}
|
||||
{/* SKYRAT EDIT START - pAIs in MODsuits*/}
|
||||
<LabeledList.Item label="Onboard pAI">
|
||||
{pAI || 'None'}
|
||||
{!!pAI && !ispAI && (
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject pAI"
|
||||
onClick={() => act('remove_pai')}
|
||||
/>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
{/* SKYRAT EDIT END */}
|
||||
</LabeledList>
|
||||
{!!display_time && (
|
||||
<Section title="Operation" mt={2}>
|
||||
<LabeledList.Item label="Time">
|
||||
{active ? shift_time : '00:00:00'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Number">
|
||||
{active && shift_id ? shift_id : '???'}
|
||||
</LabeledList.Item>
|
||||
</Section>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const HardwareSection = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const { control, helmet, chestplate, gauntlets, boots } = data;
|
||||
const { pAI, core_name } = data.suit_status; /* SKYRAT EDIT CHANGE - pAI */
|
||||
return (
|
||||
<Section title="Hardware" style={{ 'text-transform': 'capitalize' }}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="pAI Card" /* SKYRAT EDIT CHANGE - pAI */>
|
||||
{pAI || 'No pAI Card Detected' /* SKYRAT EDIT CHANGE - pAI */}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Core">
|
||||
{core_name || 'No Core Detected'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Control Unit">{control}</LabeledList.Item>
|
||||
<LabeledList.Item label="Helmet">{helmet || 'None'}</LabeledList.Item>
|
||||
<LabeledList.Item label="Chestplate">
|
||||
{chestplate || 'None'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Gauntlets">
|
||||
{gauntlets || 'None'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Boots">{boots || 'None'}</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const UserStatusSection = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const { active } = data.suit_status;
|
||||
const { user_name, user_assignment } = data.user_status;
|
||||
const {
|
||||
health,
|
||||
health_max,
|
||||
loss_brute,
|
||||
loss_fire,
|
||||
loss_tox,
|
||||
loss_oxy,
|
||||
is_user_irradiated,
|
||||
background_radiation_level,
|
||||
body_temperature,
|
||||
nutrition,
|
||||
dna_unique_identity,
|
||||
dna_unique_enzymes,
|
||||
viruses,
|
||||
} = data.module_custom_status;
|
||||
return (
|
||||
<Section title="User Status" fill>
|
||||
{!active && <LockedModule />}
|
||||
<LabeledList>
|
||||
{health !== undefined && (
|
||||
<LabeledList.Item label="Health">
|
||||
<ProgressBar
|
||||
value={active ? health / health_max : 0}
|
||||
ranges={{
|
||||
good: [0.5, Infinity],
|
||||
average: [0.2, 0.5],
|
||||
bad: [-Infinity, 0.2],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? health : 0} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{loss_brute !== undefined && (
|
||||
<LabeledList.Item label="Brute Damage">
|
||||
<ProgressBar
|
||||
value={active ? loss_brute / health_max : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? loss_brute : 0} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{loss_fire !== undefined && (
|
||||
<LabeledList.Item label="Burn Damage">
|
||||
<ProgressBar
|
||||
value={active ? loss_fire / health_max : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? loss_fire : 0} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{loss_oxy !== undefined && (
|
||||
<LabeledList.Item label="Oxy Damage">
|
||||
<ProgressBar
|
||||
value={active ? loss_oxy / health_max : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? loss_oxy : 0} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{loss_tox !== undefined && (
|
||||
<LabeledList.Item label="Tox Damage">
|
||||
<ProgressBar
|
||||
value={active ? loss_tox / health_max : 0}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.2],
|
||||
average: [0.2, 0.5],
|
||||
bad: [0.5, Infinity],
|
||||
}}>
|
||||
<AnimatedNumber value={active ? loss_tox : 0} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{background_radiation_level !== undefined && (
|
||||
<LabeledList.Item label="Radiation">
|
||||
{!active ? (
|
||||
'Unknown'
|
||||
) : is_user_irradiated ? (
|
||||
<NoticeBox danger>User Irradiated</NoticeBox>
|
||||
) : background_radiation_level ? (
|
||||
<NoticeBox>
|
||||
{`Background: ${radiationLevels(background_radiation_level)}`}
|
||||
</NoticeBox>
|
||||
) : (
|
||||
<NoticeBox info>Not Detected</NoticeBox>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{body_temperature !== undefined && (
|
||||
<LabeledList.Item label="Body Temp">
|
||||
{`${active ? Math.round(body_temperature) : 0} K`}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{nutrition !== undefined && (
|
||||
<LabeledList.Item label="Satiety Level">
|
||||
{`${active ? Math.round(nutrition) : 0}`}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
<LabeledList.Item label="Name">{user_name}</LabeledList.Item>
|
||||
<LabeledList.Item label="Assignment">
|
||||
{user_assignment}
|
||||
</LabeledList.Item>
|
||||
{dna_unique_identity !== undefined && (
|
||||
<LabeledList.Item label="Fingerprints">
|
||||
<Box
|
||||
style={{
|
||||
'word-break': 'break-all',
|
||||
'word-wrap': 'break-word',
|
||||
}}>
|
||||
{active ? dna_unique_identity : '???'}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{dna_unique_enzymes !== undefined && (
|
||||
<LabeledList.Item label="Enzymes">
|
||||
<Box
|
||||
style={{
|
||||
'word-break': 'break-all',
|
||||
'word-wrap': 'break-word',
|
||||
}}>
|
||||
{active ? dna_unique_enzymes : '???'}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
{!!viruses && (
|
||||
<Section title="Diseases">
|
||||
{viruses.map((virus) => {
|
||||
return (
|
||||
<Collapsible title={virus.name} key={virus.name}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Spread">
|
||||
{virus.type}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Stage">
|
||||
{virus.stage}/{virus.maxstage}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Cure">{virus.cure}</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Collapsible>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ModuleSection = (props, context) => {
|
||||
const { act, data } = useBackend<MODsuitData>(context);
|
||||
const { complexity_max, module_info } = data;
|
||||
const { complexity } = data.suit_status;
|
||||
const [configureState, setConfigureState] = useLocalState(
|
||||
context,
|
||||
'module_configuration',
|
||||
''
|
||||
);
|
||||
return (
|
||||
<Section
|
||||
title="Modules"
|
||||
fill
|
||||
buttons={`${complexity} of ${complexity_max} complexity used`}>
|
||||
{!module_info.length ? (
|
||||
<NoticeBox>No Modules Detected</NoticeBox>
|
||||
) : (
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell colspan={3}>Actions</Table.Cell>
|
||||
<Table.Cell>Name</Table.Cell>
|
||||
<Table.Cell width={1} textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="plug"
|
||||
tooltip="Idle Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width={1} textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="lightbulb"
|
||||
tooltip="Active Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width={1} textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="bolt"
|
||||
tooltip="Use Power Cost"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width={1} textAlign="center">
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="save"
|
||||
tooltip="Complexity"
|
||||
tooltipPosition="top"
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{module_info.map((module) => {
|
||||
return (
|
||||
<Table.Row key={module.ref}>
|
||||
<Table.Cell width={1}>
|
||||
<Button
|
||||
onClick={() => act('select', { 'ref': module.ref })}
|
||||
icon={
|
||||
module.module_type === 3
|
||||
? module.module_active
|
||||
? 'check-square-o'
|
||||
: 'square-o'
|
||||
: 'power-off'
|
||||
}
|
||||
selected={module.module_active}
|
||||
tooltip={moduleTypeAction(module.module_type)}
|
||||
tooltipPosition="left"
|
||||
disabled={!module.module_type || module.cooldown > 0}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width={1}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setConfigureState(
|
||||
configureState === module.ref ? '' : module.ref
|
||||
)
|
||||
}
|
||||
icon="cog"
|
||||
selected={configureState === module.ref}
|
||||
tooltip="Configure"
|
||||
tooltipPosition="left"
|
||||
disabled={module.configuration_data.length === 0}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width={1}>
|
||||
<Button
|
||||
onClick={() => act('pin', { 'ref': module.ref })}
|
||||
icon="thumbtack"
|
||||
selected={module.pinned}
|
||||
tooltip="Pin"
|
||||
tooltipPosition="left"
|
||||
disabled={!module.module_type}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Collapsible
|
||||
title={module.module_name}
|
||||
color={module.module_active ? 'green' : 'default'}>
|
||||
<Section mr={-19}>{module.description}</Section>
|
||||
</Collapsible>
|
||||
{configureState === module.ref && (
|
||||
<ConfigureScreen
|
||||
configuration_data={module.configuration_data}
|
||||
module_ref={module.ref}
|
||||
module_name={module.module_name}
|
||||
/>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">{module.idle_power}</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.active_power}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">{module.use_power}</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{module.module_complexity}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BooleanLike } from 'common/react';
|
||||
import { formatPower } from '../format';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, LabeledList, NumberInput, ProgressBar, Chart, Section, Stack, Icon } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
@@ -80,13 +81,10 @@ export const SolarControl = (props, context) => {
|
||||
bad: [-Infinity, 0.33],
|
||||
}}>
|
||||
{capacity > 0
|
||||
? Math.round(supply).toLocaleString() +
|
||||
' of ' +
|
||||
Math.round(capacity).toLocaleString() +
|
||||
' W (' +
|
||||
Math.round((100 * supply) / capacity) +
|
||||
'%)'
|
||||
: 0 + ' W'}
|
||||
? `${formatPower(supply)} of ${formatPower(
|
||||
capacity
|
||||
)} (${Math.round((100 * supply) / capacity)}%)`
|
||||
: formatPower(0)}
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
|
||||
Reference in New Issue
Block a user