TGUI Medical: Operating Computer

This commit is contained in:
mochi
2020-07-10 23:42:47 +02:00
parent 19bf49394e
commit 27d021e832
4 changed files with 292 additions and 254 deletions
+38 -36
View File
@@ -40,8 +40,7 @@
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/attack_hand(mob/user)
if(..(user))
@@ -52,16 +51,16 @@
add_fingerprint(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/operating/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455)
ui = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state)
ui.open()
ui.set_auto_update(1)
ui.set_autoupdate(TRUE)
/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/operating/tgui_data(mob/user)
var/data[0]
var/mob/living/carbon/human/occupant
if(table)
@@ -136,38 +135,40 @@
return data
/obj/machinery/computer/operating/Topic(href, href_list)
/obj/machinery/computer/operating/tgui_act(action, params)
if(..())
return 1
return
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
if(href_list["verboseOn"])
verbose=1
if(href_list["verboseOff"])
verbose=0
if(href_list["healthOn"])
healthAnnounce=1
if(href_list["healthOff"])
healthAnnounce=0
if(href_list["critOn"])
crit=1
if(href_list["critOff"])
crit=0
if(href_list["oxyOn"])
oxy=1
if(href_list["oxyOff"])
oxy=0
if(href_list["oxy_adj"]!=0)
oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"])
if(href_list["choiceOn"])
choice=1
if(href_list["choiceOff"])
choice=0
if(href_list["health_adj"]!=0)
healthAlarm=healthAlarm+text2num(href_list["health_adj"])
return
. = TRUE
switch(action)
if("verboseOn")
verbose = TRUE
if("verboseOff")
verbose = FALSE
if("healthOn")
healthAnnounce = TRUE
if("healthOff")
healthAnnounce = FALSE
if("critOn")
crit = TRUE
if("critOff")
crit = FALSE
if("oxyOn")
oxy = TRUE
if("oxyOff")
oxy = FALSE
if("oxy_adj")
oxyAlarm = clamp(text2num(params["new"]), -100, 100)
if("choiceOn")
choice = TRUE
if("choiceOff")
choice = FALSE
if("health_adj")
healthAlarm = clamp(text2num(params["new"]), -100, 100)
else
return FALSE
/obj/machinery/computer/operating/process()
@@ -178,6 +179,7 @@
atom_say("New patient detected, loading stats")
victim = table.victim
atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]")
SStgui.update_uis(src)
if(nextTick < world.time)
nextTick=world.time + OP_COMPUTER_COOLDOWN
if(crit && victim.health <= -50 )
-215
View File
@@ -1,215 +0,0 @@
<!--
Title: Operating Computer UI
Used In File(s): \code\game\machinery\computer\Operating.dm
-->
<h3>Patient Monitor</h3>
<div class="statusDisplay">
{{if data.choice==0}}
{{if !data.hasOccupant}}
<div class="line">No Patient Detected</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= data.occupant.maxHealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else data.occupant.health > 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}}
{{else data.occupant.health >= data.minhealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
</div>
{{if data.occupant.hasBlood}}
<br>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{if data.occupant.bloodPercent <= 60}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else data.occupant.bloodPercent <= 90}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{/if}}
<div class="line">
<div class="statusLabel">Blood Type:</div> <div class="statusValue">{{:data.occupant.bloodType}}</div>
</div>
</div>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.pulse}} bpm</div>
</div>
{{/if}}
{{if data.occupant.inSurgery}}
<div class="line">
<div class="statusLabel">Initiated Surgery Procedure:</div> <div class="statusValue">{{:data.occupant.surgeryName}}</div>
<br>
<div class="statusLabel">Next Step:</div> <div class="statusValue">{{:data.occupant.stepName}}</div>
</div>
{{/if}}
<br>
<br>
{{/if}}
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Options', 'gear', {'choiceOn' : 1})}}
</div>
</div>
{{else}}
<div class="item">
<div class="statusLabel">
Loudspeaker:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Health Announcer:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Health Announcer Threshold:</div>
{{:helper.link('-', null, {'health_adj' : -100}, (data.healthAlarm >= 0) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -10}, (data.healthAlarm >= -90) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -1}, (data.healthAlarm > -100) ? null : 'disabled')}}
{{if data.healthAlarm >= 100}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'good')}}
{{else data.healthAlarm > 0}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'average')}}
{{else data.healthAlarm >= -100}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'bad alignRight')}}
{{/if}}
{{:helper.link('+', null, {'health_adj' : 1}, (data.healthAlarm < 100) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 10}, (data.healthAlarm <= 90) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 100}, (data.healthAlarm <= 0) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.healthAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Oxygen Alarm:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Oxygen Alert Threshold:</div>
{{:helper.link('-', null, {'oxy_adj' : -100}, (data.oxyAlarm >= 100) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -10}, (data.oxyAlarm >= 10) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -1}, (data.oxyAlarm >= 0) ? null : 'disabled')}}
{{:helper.displayBar( data.oxyAlarm, 0, 200, 'good')}}
{{:helper.link('+', null, {'oxy_adj' : 1}, (data.oxyAlarm < 200) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 10}, (data.oxyAlarm <= 190) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 100}, (data.oxyAlarm <= 100) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.oxyAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Critical Alert:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}}
</div>
</div>
{{/if}}
</div>
@@ -0,0 +1,251 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Window } from "../layouts";
import { Box, Button, Flex, Icon, Knob, LabeledList, Section, Tabs, ProgressBar } from "../components";
const stats = [
['good', 'Conscious'],
['average', 'Unconscious'],
['bad', 'DEAD'],
];
const damages = [
['Resp.', 'oxyLoss'],
['Toxin', 'toxLoss'],
['Brute', 'bruteLoss'],
['Burn', 'fireLoss'],
];
const damageRange = {
average: [0.25, 0.5],
bad: [0.5, Infinity],
};
const tempColors = [
'bad',
'average',
'average',
'good',
'average',
'average',
'bad',
];
export const OperatingComputer = (props, context) => {
const { act, data } = useBackend(context);
const {
hasOccupant,
choice,
} = data;
let body;
if (!choice) {
body = hasOccupant
? <OperatingComputerPatient />
: <OperatingComputerUnoccupied />;
} else {
body = <OperatingComputerOptions />;
}
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
<Tabs>
<Tabs.Tab
selected={!choice}
icon="user"
onClick={() => act('choiceOff')}>
Patient
</Tabs.Tab>
<Tabs.Tab
selected={!!choice}
icon="cog"
onClick={() => act('choiceOn')}>
Options
</Tabs.Tab>
</Tabs>
<Section flexGrow="1">
{body}
</Section>
</Window.Content>
</Window>
);
};
const OperatingComputerPatient = (props, context) => {
const { data } = useBackend(context);
const {
occupant,
} = data;
return (
<Fragment>
<Section title="Patient" level="2">
<LabeledList>
<LabeledList.Item label="Name">
{occupant.name}
</LabeledList.Item>
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
{stats[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min="0"
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
ranges={{
good: [0.5, Infinity],
average: [0, 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
{damages.map((d, i) => (
<LabeledList.Item key={i} label={d[0] + " Damage"}>
<ProgressBar
key={i}
min="0"
max="100"
value={occupant[d[1]] / 100}
ranges={damageRange}>
{round(occupant[d[1]])}
</ProgressBar>
</LabeledList.Item>
))}
<LabeledList.Item label="Temperature">
<ProgressBar
min="0"
max={occupant.maxTemp}
value={occupant.bodyTemperature / occupant.maxTemp}
color={tempColors[occupant.temperatureSuitability + 3]}>
{round(occupant.btCelsius)}&deg;C, {round(occupant.btFaren)}&deg;F
</ProgressBar>
</LabeledList.Item>
{!!occupant.hasBlood && (
<Fragment>
<LabeledList.Item label="Blood Level">
<ProgressBar
min="0"
max={occupant.bloodMax}
value={occupant.bloodLevel / occupant.bloodMax}
ranges={{
bad: [-Infinity, 0.6],
average: [0.6, 0.9],
good: [0.6, Infinity],
}}>
{occupant.bloodPercent}%, {occupant.bloodLevel}cl
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Pulse">
{occupant.pulse} BPM
</LabeledList.Item>
</Fragment>
)}
</LabeledList>
</Section>
<Section title="Current Procedure" level="2">
{occupant.inSurgery ? (
<LabeledList>
<LabeledList.Item label="Procedure">
{occupant.surgeryName}
</LabeledList.Item>
<LabeledList.Item label="Next Step">
{occupant.stepName}
</LabeledList.Item>
</LabeledList>
) : (
<Box color="label">
No procedure ongoing.
</Box>
)}
</Section>
</Fragment>
);
};
const OperatingComputerUnoccupied = () => {
return (
<Flex textAlign="center" height="100%">
<Flex.Item grow="1" align="center" color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No patient detected.
</Flex.Item>
</Flex>
);
};
const OperatingComputerOptions = (props, context) => {
const { act, data } = useBackend(context);
const {
verbose,
health,
healthAlarm,
oxy,
oxyAlarm,
crit,
} = data;
return (
<LabeledList>
<LabeledList.Item label="Loudspeaker">
<Button
selected={verbose}
icon={verbose ? "toggle-on" : "toggle-off"}
content={verbose ? "On" : "Off"}
onClick={() => act(verbose ? 'verboseOff' : 'verboseOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Health Announcer">
<Button
selected={health}
icon={health ? "toggle-on" : "toggle-off"}
content={health ? "On" : "Off"}
onClick={() => act(health ? 'healthOff' : 'healthOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Health Announcer Threshold">
<Knob
bipolar
minValue="-100"
maxValue="100"
value={healthAlarm}
stepPixelSize="5"
ml="0"
onChange={(e, val) => act('health_adj', {
new: val,
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Oxygen Alarm">
<Button
selected={oxy}
icon={oxy ? "toggle-on" : "toggle-off"}
content={oxy ? "On" : "Off"}
onClick={() => act(oxy ? 'oxyOff' : 'oxyOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Oxygen Alarm Threshold">
<Knob
bipolar
minValue="-100"
maxValue="100"
value={oxyAlarm}
stepPixelSize="5"
ml="0"
onChange={(e, val) => act('oxy_adj', {
new: val,
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Critical Alert">
<Button
selected={crit}
icon={crit ? "toggle-on" : "toggle-off"}
content={crit ? "On" : "Off"}
onClick={() => act(crit ? 'critOff' : 'critOn')}
/>
</LabeledList.Item>
</LabeledList>
);
};
File diff suppressed because one or more lines are too long