mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-22 11:37:40 +01:00
Medical TGUI: Chemical Dispenser
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"> </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"> </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: {{:data.beakerCurrentVolume}} / {{: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>
|
||||
@@ -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'),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user