TGUI Medical: Cryo Cell

This commit is contained in:
mochi
2020-07-03 11:50:45 +02:00
parent ec274da3ad
commit df72204803
4 changed files with 244 additions and 188 deletions
+46 -68
View File
@@ -103,7 +103,7 @@
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user)
if(O.loc == user) //no you can't pull things out of your ass
return
if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other
@@ -140,6 +140,7 @@
add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL)
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
@@ -177,7 +178,7 @@
return FALSE
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user)
if(user.stat)
return
go_out()
@@ -194,36 +195,18 @@
to_chat(usr, "<span class='boldnotice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/**
* The ui_interact proc is used to open and update Nano UIs
* If ui_interact is not used then the UI will not update correctly
* ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
*
* @return nothing
*/
/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/unary/cryo_cell/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)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480)
// open the new ui window
ui = new(user, src, ui_key, "Cryo", "Cryo Cell Control System", 520, 470)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user)
var/data[0]
data["isOperating"] = on
data["hasOccupant"] = occupant ? 1 : 0
data["hasOccupant"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -237,7 +220,7 @@
occupantData["toxLoss"] = occupant.getToxLoss()
occupantData["fireLoss"] = occupant.getFireLoss()
occupantData["bodyTemperature"] = occupant.bodytemperature
data["occupant"] = occupantData;
data["occupant"] = occupantData
data["cellTemperature"] = round(air_contents.temperature)
data["cellTemperatureStatus"] = "good"
@@ -246,7 +229,7 @@
else if(air_contents.temperature > TCRYO)
data["cellTemperatureStatus"] = "average"
data["isBeakerLoaded"] = beaker ? 1 : 0
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["beakerLabel"] = null
data["beakerVolume"] = 0
if(beaker)
@@ -259,48 +242,41 @@
data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE
return data
/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
if(usr == occupant)
return 0 // don't update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params)
if(..() || usr == occupant)
return
if(..())
return 0 // don't update UIs attached to this object
if(href_list["switchOn"])
on = TRUE
update_icon()
if(href_list["switchOff"])
on = FALSE
update_icon()
if(href_list["auto_eject_healthy_on"])
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if(href_list["auto_eject_healthy_off"])
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if(href_list["auto_eject_dead_on"])
auto_eject_prefs |= AUTO_EJECT_DEAD
if(href_list["auto_eject_dead_off"])
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if(href_list["ejectBeaker"])
if(beaker)
beaker.forceMove(get_step(loc, SOUTH))
beaker = null
if(href_list["ejectOccupant"])
if(!occupant || isslime(usr) || ispAI(usr))
return 0 // don't update UIs attached to this object
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
switch(action)
if("switchOn")
on = TRUE
update_icon()
if("switchOff")
on = FALSE
update_icon()
if("auto_eject_healthy_on")
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if("auto_eject_healthy_off")
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if("auto_eject_dead_on")
auto_eject_prefs |= AUTO_EJECT_DEAD
if("auto_eject_dead_off")
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if("ejectBeaker")
if(beaker)
beaker.forceMove(get_step(loc, SOUTH))
beaker = null
if("ejectOccupant")
if(!occupant || isslime(usr) || ispAI(usr))
return
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
else
return
add_fingerprint(usr)
return 1 // update UIs attached to this object
return TRUE // update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params)
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params)
if(istype(G, /obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/B = G
if(beaker)
@@ -313,6 +289,7 @@
beaker = B
add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]")
user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!")
SStgui.update_uis(src)
return
if(exchange_parts(user, G))
@@ -452,8 +429,9 @@
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
if(AUTO_EJECT_DEAD)
playsound(loc, 'sound/machines/buzz-sigh.ogg', 40)
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M)
if(!istype(M))
to_chat(usr, "<span class='danger'>The cryo cell cannot handle such a lifeform!</span>")
return
@@ -527,7 +505,7 @@
/datum/data/function/proc/reset()
return
/datum/data/function/proc/r_input(href, href_list, mob/user as mob)
/datum/data/function/proc/r_input(href, href_list, mob/user)
return
/datum/data/function/proc/display()
-117
View File
@@ -1,117 +0,0 @@
<!--
Title: Cryo Cell Status UI
Used In File(s): \code\game\machinery\cryo.dm
-->
<h3>Cryo Cell Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Cell Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;<i class="fa fa-arrow-right"></i>&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>
{{if data.occupant.stat < 2}}
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> 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"><i class="fa fa-arrow-right"></i> 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"><i class="fa fa-arrow-right"></i> 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"><i class="fa fa-arrow-right"></i> Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
{{/if}}
{{/if}}
<br>
<hr>
<div class="line">
<div class="statusLabel">Cell Temperature:</div><div class="statusValue">
<span class="{{:data.cellTemperatureStatus}}">{{:helper.smoothRound(data.cellTemperature)}} K</span>
</div>
</div>
</div>
<h3>Cryo Cell Operation</h3>
<div class="item">
<div class="itemLabel">
Cryo Cell Status:
</div>
<div class="itemContent" style="width: 40%;">
{{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Beaker:
</div>
<div class="itemContent" style="width: 40%;">
{{if data.isBeakerLoaded}}
{{:data.beakerLabel ? data.beakerLabel : '<span class="average">No label</span>'}}<br>
{{if data.beakerVolume}}
<span class="highlight">{{:helper.smoothRound(data.beakerVolume)}} units remaining</span><br>
{{else}}
<span class="bad">Beaker is empty</span>
{{/if}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject healthy patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_healthy_on' : 1}, data.auto_eject_healthy ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_healthy_off' : 0}, data.auto_eject_healthy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject dead patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}}
</div>
</div>
+195
View File
@@ -0,0 +1,195 @@
import { round, toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Window } from "../layouts";
import { AnimatedNumber, Box, Button, LabeledList, ProgressBar, Section } from "../components";
const damageTypes = [
{
label: "Respiratory",
type: "oxyLoss",
},
{
label: "Toxin",
type: "toxLoss",
},
{
label: "Brute",
type: "bruteLoss",
},
{
label: "Burn",
type: "fireLoss",
},
];
const statNames = [
["good", "Conscious"],
["average", "Unconscious"],
["bad", "DEAD"],
];
export const Cryo = (props, context) => {
return (
<Window>
<Window.Content className="Layout__content--flexColumn">
<CryoContent />
</Window.Content>
</Window>
);
};
const CryoContent = (props, context) => {
const { act, data } = useBackend(context);
const {
isOperating,
hasOccupant,
occupant = [],
cellTemperature,
cellTemperatureStatus,
isBeakerLoaded,
auto_eject_healthy,
auto_eject_dead,
} = data;
return (
<Fragment>
<Section
title="Occupant"
flexGrow="1"
buttons={(
<Button
icon="user-slash"
onClick={() => act('ejectOccupant')}
disabled={!hasOccupant}>
Eject
</Button>
)}>
<LabeledList>
<LabeledList.Item label="Occupant">
{occupant.name || 'No Occupant'}
</LabeledList.Item>
{!!hasOccupant && (
<Fragment>
<LabeledList.Item
label="State"
color={statNames[occupant.stat][0]}>
{statNames[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Temperature">
<AnimatedNumber
value={Math.round(occupant.bodyTemperature)} />
{' K'}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
value={occupant.health / occupant.maxHealth}
color={occupant.health > 0 ? 'good' : 'average'}>
<AnimatedNumber
value={Math.round(occupant.health)} />
</ProgressBar>
</LabeledList.Item>
{(damageTypes.map(damageType => (
<LabeledList.Item
key={damageType.id}
label={damageType.label}>
<ProgressBar
value={occupant[damageType.type]/100}
ranges={{ bad: [0.01, Infinity] }}>
<AnimatedNumber
value={Math.round(occupant[damageType.type])} />
</ProgressBar>
</LabeledList.Item>
)))}
</Fragment>
)}
</LabeledList>
</Section>
<Section
title="Cell"
buttons={(
<Button
icon="eject"
onClick={() => act('ejectBeaker')}
disabled={!isBeakerLoaded}>
Eject Beaker
</Button>
)}>
<LabeledList>
<LabeledList.Item label="Power">
<Button
icon="power-off"
onClick={() => act(isOperating ? 'switchOff' : 'switchOn')}
selected={isOperating}>
{isOperating ? "On" : "Off"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Temperature" color={cellTemperatureStatus}>
<AnimatedNumber value={cellTemperature} /> K
</LabeledList.Item>
<LabeledList.Item label="Beaker">
<CryoBeaker />
</LabeledList.Item>
<LabeledList.Item label="Auto-eject healthy occupants">
<Button
icon={auto_eject_healthy ? "toggle-on" : "toggle-off"}
selected={auto_eject_healthy}
mt="1rem"
onClick={() => act(auto_eject_healthy
? 'auto_eject_healthy_off'
: 'auto_eject_healthy_on'
)}>
{auto_eject_healthy ? "On" : "Off"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Auto-eject dead occupants">
<Button
icon={auto_eject_dead ? "toggle-on" : "toggle-off"}
selected={auto_eject_dead}
onClick={() => act(auto_eject_dead
? 'auto_eject_dead_off'
: 'auto_eject_dead_on'
)}>
{auto_eject_dead ? "On" : "Off"}
</Button>
</LabeledList.Item>
</LabeledList>
</Section>
</Fragment>
);
};
const CryoBeaker = (props, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerLabel,
beakerVolume,
} = data;
if (isBeakerLoaded) {
return (
<Fragment>
{beakerLabel
? beakerLabel
: (
<Box color="average">
No label
</Box>
)}
<Box color={!beakerVolume && "bad"}>
{beakerVolume ? (
<AnimatedNumber
value={beakerVolume}
format={v => Math.round(v) + " units remaining"}
/>
) : "Beaker is empty"}
</Box>
</Fragment>
);
} else {
return (
<Box color="average">
No beaker loaded
</Box>
);
}
};
File diff suppressed because one or more lines are too long