Medical TGUI: Chemical Dispenser

This commit is contained in:
mochi
2020-07-01 17:39:28 +02:00
parent 331f59899c
commit d3ed185f7a
10 changed files with 233 additions and 156 deletions
@@ -120,7 +120,6 @@
var/usedpower = cell.give(recharge_amount)
if(usedpower)
use_power(15 * recharge_amount)
SSnanoui.update_uis(src) // update all UIs attached to src
recharge_counter = 0
return
recharge_counter++
@@ -131,7 +130,6 @@
else
spawn(rand(0, 15))
stat |= NOPOWER
SSnanoui.update_uis(src) // update all UIs attached to src
/obj/machinery/chem_dispenser/ex_act(severity)
if(severity < 3)
@@ -145,19 +143,17 @@
beaker = null
overlays.Cut()
/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
/obj/machinery/chem_dispenser/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)
// 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)
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, "chem_dispenser.tmpl", ui_title, 390, 655)
// open the new ui window
ui = new(user, src, ui_key, "ChemDispenser", ui_title, 390, 655)
ui.open()
/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_dispenser/tgui_data(mob/user)
var/data[0]
data["hasPower"] = !(stat & NOPOWER)
data["amount"] = amount
data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI.
data["maxEnergy"] = cell.maxcharge * powerefficiency
@@ -187,63 +183,59 @@
return data
/obj/machinery/chem_dispenser/Topic(href, href_list)
/obj/machinery/chem_dispenser/tgui_act(actions, params)
if(..())
return TRUE
return
if(href_list["amount"])
amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
if(amount < 0) // Since the user can actually type the commands himself, some sanity checking
amount = 0
if(amount > 100)
amount = 100
if(href_list["dispense"])
if(!is_operational() || QDELETED(cell))
return
if(beaker && dispensable_reagents.Find(href_list["dispense"]))
var/datum/reagents/R = beaker.reagents
var/free = R.maximum_volume - R.total_volume
var/actual = min(amount, (cell.charge * powerefficiency) * 10, free)
if(!cell.use(actual / powerefficiency))
atom_say("Not enough energy to complete operation!")
switch(actions)
if("amount")
amount = clamp(round(text2num(params["amount"]), 1), 0, 100) // round to nearest 1 and clamp to 0 - 100
. = TRUE
if("dispense")
if(!is_operational() || QDELETED(cell))
return
if(beaker && dispensable_reagents.Find(params["reagent"]))
var/datum/reagents/R = beaker.reagents
var/free = R.maximum_volume - R.total_volume
var/actual = min(amount, (cell.charge * powerefficiency) * 10, free)
R.add_reagent(href_list["dispense"], actual)
overlays.Cut()
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
if(!cell.use(actual / powerefficiency))
atom_say("Not enough energy to complete operation!")
return
if(href_list["remove"])
if(beaker)
if(href_list["removeamount"])
var/amount = text2num(href_list["removeamount"])
R.add_reagent(params["reagent"], actual)
overlays.Cut()
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
. = TRUE
if("remove")
if(beaker && params["amount"])
var/amount = text2num(params["amount"])
if(isnum(amount) && (amount > 0))
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
var/id = params["reagent"]
R.remove_reagent(id, amount)
else if(isnum(amount) && (amount == -1)) //Isolate instead
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
var/id = params["reagent"]
R.isolate_reagent(id)
if(href_list["ejectBeaker"])
if(beaker)
beaker.forceMove(loc)
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
overlays.Cut()
add_fingerprint(usr)
return TRUE // update UIs attached to this object
. = TRUE
if("ejectBeaker")
if(beaker)
beaker.forceMove(loc)
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
overlays.Cut()
. = TRUE
if(.)
add_fingerprint(usr)
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(exchange_parts(user, I))
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
if(isrobot(user))
@@ -263,7 +255,7 @@
beaker = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You set [I] on the machine.</span>")
SSnanoui.update_uis(src) // update all UIs attached to src
SStgui.update_uis(src) // update all UIs attached to src
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
@@ -299,8 +291,7 @@
to_chat(user, unhack_message)
dispensable_reagents -= hacked_reagents
hackedcheck = FALSE
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I)
if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I))
@@ -328,7 +319,7 @@
if(stat & BROKEN)
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_dispenser/soda
icon_state = "soda_dispenser"
-93
View File
@@ -1,93 +0,0 @@
<!--
Title: Chem Dispenser 5000 UI
Used In File(s): \code\modules\reagents\Chemistry-Machinery.dm
-->
<div class="item">
<div class="itemLabel">
Energy:
</div>
<div class="itemContent">
{{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Dispense:
</div>
<div class="itemContent">
{{:helper.link('1', 'gear', {'amount' : 1}, (data.amount == 1) ? 'selected' : null)}}
{{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}}
{{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}}
{{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}}
{{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}}
{{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 100%;">
{{if data.glass}}
Drink Dispenser
{{else}}
Chemical Dispenser
{{/if}}
</div>
</div>
<div class="item">
<div class="itemContentWide" style="width: 100%;">
{{for data.chemicals}}
{{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}}
{{/for}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
{{if data.glass}}
Glass
{{else}}
Beaker
{{/if}} Contents
</div>
<div class="itemContent">
{{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
</div>
</div>
<div class="statusDisplay" style="min-height: 180px;">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{if data.isBeakerLoaded}}
{{if data.beakerCurrentVolume}}
<span class="floatRight" style="width: 115px;"><b>Dispose:</b></span>
{{/if}}
<b>Volume:&nbsp;{{:data.beakerCurrentVolume}}&nbsp;/&nbsp;{{:data.beakerMaxVolume}}</b><br>
{{for data.beakerContents}}
<div style="min-height: 22px;">
<div class="floatLeft">
<span class="highlight">
{{:value.volume}} units of {{:value.name}}
</span>
</div>
<div class="floatRight">
{{:helper.link('Isolate', null, {'remove' : value.id, 'removeamount' : -1})}}
{{:helper.link('1', null, {'remove' : value.id, 'removeamount' : 1})}}
{{:helper.link('5', null, {'remove' : value.id, 'removeamount' : 5})}}
{{:helper.link('10', null, {'remove' : value.id, 'removeamount' : 10})}}
{{:helper.link('ALL', null, {'remove' : value.id, 'removeamount' : value.volume})}}
</div>
</div>
<div style="height:2px"></div> <!-- This is necessary to prevent the link margins from hitting each other and causing weird things to happen. It's essentialy a smaller <br> tag. -->
{{empty}}
<span class="bad">{{if data.glass}}Glass{{else}}Beaker{{/if}} is empty
</span>
{{/for}}
{{else}}
<span class="average"><i>
No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded
</i></span>
{{/if}}
</div>
</div>
</div>
+2
View File
@@ -70,6 +70,8 @@ const styleMapperByPropName = {
// Direct mapping
display: mapRawPropTo('display'),
position: mapRawPropTo('position'),
float: mapRawPropTo('float'),
clear: mapRawPropTo('clear'),
overflow: mapRawPropTo('overflow'),
overflowX: mapRawPropTo('overflow-x'),
overflowY: mapRawPropTo('overflow-y'),
+4
View File
@@ -8,9 +8,11 @@ export const computeFlexProps = props => {
direction,
wrap,
align,
alignContent,
justify,
inline,
spacing = 0,
spacingPrecise = 0,
...rest
} = props;
return {
@@ -23,6 +25,7 @@ export const computeFlexProps = props => {
),
inline && 'Flex--inline',
spacing > 0 && 'Flex--spacing--' + spacing,
spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise,
className,
]),
style: {
@@ -30,6 +33,7 @@ export const computeFlexProps = props => {
'flex-direction': direction,
'flex-wrap': wrap,
'align-items': align,
'align-content': alignContent,
'justify-content': justify,
},
...rest,
@@ -20,6 +20,7 @@ export const LabeledListItem = props => {
labelColor = 'label',
color,
textAlign,
verticalAlign,
buttons,
content,
children,
@@ -33,6 +34,7 @@ export const LabeledListItem = props => {
<Box
as="td"
color={labelColor}
verticalAlign={verticalAlign}
className={classes([
'LabeledList__cell',
'LabeledList__label',
@@ -43,6 +45,7 @@ export const LabeledListItem = props => {
as="td"
color={color}
textAlign={textAlign}
verticalAlign={verticalAlign}
className={classes([
'LabeledList__cell',
'LabeledList__content',
@@ -0,0 +1,153 @@
import { round, toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Window } from "../layouts";
import { Box, Button, LabeledList, Section, ProgressBar, Flex } from "../components";
import { BeakerContents } from "../interfaces/common/BeakerContents";
const dispenseAmounts = [1, 5, 10, 20, 30, 50];
const removeAmounts = [1, 5, 10];
export const ChemDispenser = (props, context) => {
const { act, data } = useBackend(context);
const {
hasPower,
amount,
energy,
maxEnergy,
isBeakerLoaded,
beakerCurrentVolume,
beakerMaxVolume,
beakerContents = [],
chemicals = [],
} = data;
const flexFillers = [];
for (let i = 0; i < (chemicals.length + 1) % 3; i++) {
flexFillers.push(true);
}
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
<Section title="Settings" flex="content">
<LabeledList>
<LabeledList.Item label="Energy">
<ProgressBar
value={energy}
minValue={0}
maxValue={maxEnergy}
ranges={{
good: [maxEnergy * 0.5, Infinity],
average: [maxEnergy * 0.25, maxEnergy * 0.5],
bad: [-Infinity, maxEnergy * 0.25],
}}>
{energy} / {maxEnergy} Units
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Dispense" verticalAlign="middle">
<Flex direction="row" spacing="1">
{dispenseAmounts.map((a, i) => (
<Flex.Item key={i} grow="1">
<Button
icon="cog"
selected={amount === a}
content={a}
m="0"
width="100%"
onClick={() => act('amount', {
amount: a,
})}
/>
</Flex.Item>
))}
</Flex>
</LabeledList.Item>
</LabeledList>
</Section>
<Section
title={data.glass ? 'Drink Dispenser' : 'Chemical Dispenser'}
flexGrow="1">
<Flex
direction="row"
wrap="wrap"
height="100%"
spacingPrecise="2"
align="flex-start"
alignContent="flex-start">
{chemicals.map((c, i) => (
<Flex.Item key={i} grow="1" basis="25%" height="20px">
<Button
icon="arrow-circle-down"
width="100%"
height="100%"
align="flex-start"
content={c.title}
onClick={() => act('dispense', {
reagent: c.id,
})}
/>
</Flex.Item>
))}
{flexFillers.map((_, i) => (
<Flex.Item key={i} grow="1" basis="25%" height="20px" />
))}
</Flex>
</Section>
<Section
title="Beaker"
flex="content"
minHeight="25%"
buttons={(
<Box>
{!!isBeakerLoaded && (
<Box inline color="label" mr={2}>
{beakerCurrentVolume} / {beakerMaxVolume} units
</Box>
)}
<Button
icon="eject"
content="Eject"
disabled={!isBeakerLoaded}
onClick={() => act('ejectBeaker')}
/>
</Box>
)}>
<BeakerContents
beakerLoaded={isBeakerLoaded}
beakerContents={beakerContents}
buttons={chemical => (
<Fragment>
<Button
content="Isolate"
icon="compress-arrows-alt"
onClick={() => act('remove', {
reagent: chemical.id,
amount: -1,
})}
/>
{removeAmounts.map((a, i) => (
<Button
key={i}
content={a}
onClick={() => act('remove', {
reagent: chemical.id,
amount: a,
})}
/>
))}
<Button
content="ALL"
onClick={() => act('remove', {
reagent: chemical.id,
amount: chemical.volume,
})}
/>
</Fragment>
)}
/>
</Section>
</Window.Content>
</Window>
);
};
@@ -3,7 +3,7 @@ import { Box } from '../../components';
const formatUnits = a => a + ' unit' + (a === 1 ? '' : 's');
export const BeakerContents = props => {
const { beakerLoaded, beakerContents } = props;
const { beakerLoaded, beakerContents, buttons } = props;
return (
<Box>
{!beakerLoaded && (
@@ -16,8 +16,17 @@ export const BeakerContents = props => {
</Box>
)}
{beakerContents.map(chemical => (
<Box key={chemical.name} color="label">
{formatUnits(chemical.volume)} of {chemical.name}
<Box key={chemical.name} clear="both">
<Box color="label" float="left">
<Box verticalAlign="middle">
{formatUnits(chemical.volume)} of {chemical.name}
</Box>
</Box>
{!!buttons && (
<Box float="right">
{buttons(chemical)}
</Box>
)}
</Box>
))}
</Box>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -33,4 +33,12 @@
margin: 0 3px * $i;
}
}
.Flex--spacingPrecise--#{$i} {
margin: -1px * $i -1px * $i;
& > .Flex__item {
margin: 1px * $i 1px * $i;
}
}
}