ChemMaster and CondiMaster refactored, prints items over time (#75849)

![ZceBfkJaFG](https://github.com/tgstation/tgstation/assets/3625094/aedf6178-dad7-40b2-a7ea-18da6f56e9e8)

## About The Pull Request

Chem master has been using some hard coded styles for containers, pills
and patches. Some people didn't bother to update styles, and they stayed
outdated.

Now it uses just a list of reagent containers instead, which makes it
easy to add new containers - just one line of code.

Also changed its sprite for better animations, emissive overlay, and to
display buffer reagents on the sprite itself.

Now it plays printing animation when you actually print containers and
servos have a purpose now - they increase the amount of containers
printed at once by up to 100% * part_tier. Pills and patches are always
printed at double amount.

And fixes #59734 

## Why It's Good For The Game

Makes chem master easier to manage in code and easier to use in-game.

Printing delay is added for consistency with other fabrication machines
(like lathes), to give a purpose for the servo motor used in
construction, for visuals (to show the printing animation only when
something is being printed, and not just when you move stuff in and out
of buffer as before) and to prevent people from spamming the world with
unneeded objects with 0 second delay.

## Changelog

🆑
qol: Chemmaster UI tweaked
fix: Fixed chem master showing wrong data during reagent analysis
fix: Fixed chem master not working with fermented drinks
image: Chemmaster resprited, now has an indicator for buffer reagents
refactor: Refactored chemmaster code, it now uses reagent containers
instead of styles
balance: Chemmaster now uses servos and has printing animation of 0.75
second duration. Outputs 1 container or up to 2 pills/patches per cycle
by default. Can be upgraded to output up to 4 and 8 with t4 servos.
/🆑
This commit is contained in:
Andrew
2023-06-11 13:52:39 -04:00
committed by GitHub
parent 3e51c8aeaa
commit d278980066
10 changed files with 957 additions and 962 deletions
@@ -0,0 +1,15 @@
///Icons for containers printed in ChemMaster
/datum/asset/spritesheet/chemmaster
name = "chemmaster"
/datum/asset/spritesheet/chemmaster/create_spritesheets()
var/list/ids = list()
for(var/category in GLOB.chem_master_containers)
for(var/obj/item/reagent_containers/container as anything in GLOB.chem_master_containers[category])
var/icon_file = initial(container.icon)
var/icon_state = initial(container.icon_state)
var/id = sanitize_css_class_name("[container]")
if(id in ids) // exclude duplicate containers
continue
ids += id
Insert(id, icon_file, icon_state)
File diff suppressed because it is too large Load Diff
@@ -340,6 +340,19 @@
icon_state = "condi_chocolate"
list_reagents = list(/datum/reagent/consumable/choccyshake = 10)
/obj/item/reagent_containers/condiment/hotsauce
name = "hotsauce bottle"
desc= "You can almost TASTE the stomach ulcers!"
icon_state = "hotsauce"
list_reagents = list(/datum/reagent/consumable/capsaicin = 50)
/obj/item/reagent_containers/condiment/coldsauce
name = "coldsauce bottle"
desc= "Leaves the tongue numb from its passage."
icon_state = "coldsauce"
list_reagents = list(/datum/reagent/consumable/frostoil = 50)
//Food packs. To easily apply deadly toxi... delicious sauces to your food!
/obj/item/reagent_containers/condiment/pack
@@ -44,3 +44,46 @@
desc = "Helps with brute and burn injuries. Slightly toxic."
list_reagents = list(/datum/reagent/medicine/c2/synthflesh = 20)
icon_state = "bandaid_both"
// Patch styles for chem master
/obj/item/reagent_containers/pill/patch/style
icon_state = "bandaid_blank"
/obj/item/reagent_containers/pill/patch/style/brute
icon_state = "bandaid_brute_2"
/obj/item/reagent_containers/pill/patch/style/burn
icon_state = "bandaid_burn_2"
/obj/item/reagent_containers/pill/patch/style/bruteburn
icon_state = "bandaid_both"
/obj/item/reagent_containers/pill/patch/style/toxin
icon_state = "bandaid_toxin_2"
/obj/item/reagent_containers/pill/patch/style/oxygen
icon_state = "bandaid_suffocation_2"
/obj/item/reagent_containers/pill/patch/style/omni
icon_state = "bandaid_mix"
/obj/item/reagent_containers/pill/patch/style/bruteplus
icon_state = "bandaid_brute"
/obj/item/reagent_containers/pill/patch/style/burnplus
icon_state = "bandaid_burn"
/obj/item/reagent_containers/pill/patch/style/toxinplus
icon_state = "bandaid_toxin"
/obj/item/reagent_containers/pill/patch/style/oxygenplus
icon_state = "bandaid_suffocation"
/obj/item/reagent_containers/pill/patch/style/monkey
icon_state = "bandaid_monke"
/obj/item/reagent_containers/pill/patch/style/clown
icon_state = "bandaid_clown"
/obj/item/reagent_containers/pill/patch/style/one
icon_state = "bandaid_1"
/obj/item/reagent_containers/pill/patch/style/two
icon_state = "bandaid_2"
/obj/item/reagent_containers/pill/patch/style/three
icon_state = "bandaid_3"
/obj/item/reagent_containers/pill/patch/style/four
icon_state = "bandaid_4"
/obj/item/reagent_containers/pill/patch/style/exclamation
icon_state = "bandaid_exclaimationpoint"
/obj/item/reagent_containers/pill/patch/style/question
icon_state = "bandaid_questionmark"
/obj/item/reagent_containers/pill/patch/style/colonthree
icon_state = "bandaid_colonthree"
@@ -302,3 +302,54 @@
icon_state = "pill8"
list_reagents = list(/datum/reagent/iron = 30)
rename_with_volume = TRUE
// Pill styles for chem master
/obj/item/reagent_containers/pill/style
icon_state = "pill0"
/obj/item/reagent_containers/pill/style/purplered
icon_state = "pill1"
/obj/item/reagent_containers/pill/style/greenwhite
icon_state = "pill2"
/obj/item/reagent_containers/pill/style/teal
icon_state = "pill3"
/obj/item/reagent_containers/pill/style/red
icon_state = "pill4"
/obj/item/reagent_containers/pill/style/redwhite
icon_state = "pill5"
/obj/item/reagent_containers/pill/style/tealbrown
icon_state = "pill6"
/obj/item/reagent_containers/pill/style/yellowflat
icon_state = "pill7"
/obj/item/reagent_containers/pill/style/tealflat
icon_state = "pill8"
/obj/item/reagent_containers/pill/style/whiteflat
icon_state = "pill9"
/obj/item/reagent_containers/pill/style/purpleflat
icon_state = "pill10"
/obj/item/reagent_containers/pill/style/limelat
icon_state = "pill11"
/obj/item/reagent_containers/pill/style/redflat
icon_state = "pill12"
/obj/item/reagent_containers/pill/style/greenpurpleflat
icon_state = "pill13"
/obj/item/reagent_containers/pill/style/yellowpurpleflat
icon_state = "pill14"
/obj/item/reagent_containers/pill/style/redyellowflat
icon_state = "pill15"
/obj/item/reagent_containers/pill/style/bluetealflat
icon_state = "pill16"
/obj/item/reagent_containers/pill/style/greenlimeflat
icon_state = "pill17"
/obj/item/reagent_containers/pill/style/white
icon_state = "pill18"
/obj/item/reagent_containers/pill/style/whitered
icon_state = "pill19"
/obj/item/reagent_containers/pill/style/purpleyellow
icon_state = "pill20"
/obj/item/reagent_containers/pill/style/blackwhite
icon_state = "pill21"
/obj/item/reagent_containers/pill/style/limewhite
icon_state = "pill22"
/obj/item/reagent_containers/pill/style/happy
icon_state = "pill_happy"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

+1
View File
@@ -2778,6 +2778,7 @@
#include "code\modules\asset_cache\assets\bibles.dm"
#include "code\modules\asset_cache\assets\body_zones.dm"
#include "code\modules\asset_cache\assets\chat.dm"
#include "code\modules\asset_cache\assets\chemmaster.dm"
#include "code\modules\asset_cache\assets\circuits.dm"
#include "code\modules\asset_cache\assets\common.dm"
#include "code\modules\asset_cache\assets\condiments.dm"
-438
View File
@@ -1,438 +0,0 @@
import { useBackend, useSharedState } from '../backend';
import { AnimatedNumber, Box, Button, ColorBox, LabeledList, NumberInput, Section, Table } from '../components';
import { Window } from '../layouts';
export const ChemMaster = (props, context) => {
const { data } = useBackend(context);
const { screen } = data;
return (
<Window width={465} height={550}>
<Window.Content scrollable>
{(screen === 'analyze' && <AnalysisResults />) || <ChemMasterContent />}
</Window.Content>
</Window>
);
};
const ChemMasterContent = (props, context) => {
const { act, data } = useBackend(context);
const {
screen,
beakerContents = [],
bufferContents = [],
beakerCurrentVolume,
beakerMaxVolume,
isBeakerLoaded,
isPillBottleLoaded,
pillBottleCurrentAmount,
pillBottleMaxAmount,
} = data;
if (screen === 'analyze') {
return <AnalysisResults />;
}
return (
<>
<Section
title="Beaker"
buttons={
!!data.isBeakerLoaded && (
<>
<Box inline color="label" mr={2}>
<AnimatedNumber value={beakerCurrentVolume} initial={0} />
{` / ${beakerMaxVolume} units`}
</Box>
<Button
icon="eject"
content="Eject"
onClick={() => act('eject')}
/>
</>
)
}>
{!isBeakerLoaded && (
<Box color="label" mt="3px" mb="5px">
No beaker loaded.
</Box>
)}
{!!isBeakerLoaded && beakerContents.length === 0 && (
<Box color="label" mt="3px" mb="5px">
Beaker is empty.
</Box>
)}
<ChemicalBuffer>
{beakerContents.map((chemical) => (
<ChemicalBufferEntry
key={chemical.id}
chemical={chemical}
transferTo="buffer"
/>
))}
</ChemicalBuffer>
</Section>
<Section
title="Buffer"
buttons={
<>
<Box inline color="label" mr={1}>
Mode:
</Box>
<Button
color={data.mode ? 'good' : 'bad'}
icon={data.mode ? 'exchange-alt' : 'times'}
content={data.mode ? 'Transfer' : 'Destroy'}
onClick={() => act('toggleMode')}
/>
</>
}>
{bufferContents.length === 0 && (
<Box color="label" mt="3px" mb="5px">
Buffer is empty.
</Box>
)}
<ChemicalBuffer>
{bufferContents.map((chemical) => (
<ChemicalBufferEntry
key={chemical.id}
chemical={chemical}
transferTo="beaker"
/>
))}
</ChemicalBuffer>
</Section>
<Section title="Packaging">
<PackagingControls />
</Section>
{!!isPillBottleLoaded && (
<Section
title="Pill Bottle"
buttons={
<>
<Box inline color="label" mr={2}>
{pillBottleCurrentAmount} / {pillBottleMaxAmount} pills
</Box>
<Button
icon="eject"
content="Eject"
onClick={() => act('ejectPillBottle')}
/>
</>
}
/>
)}
</>
);
};
const ChemicalBuffer = Table;
const ChemicalBufferEntry = (props, context) => {
const { act } = useBackend(context);
const { chemical, transferTo } = props;
return (
<Table.Row key={chemical.id}>
<Table.Cell color="label">
<AnimatedNumber value={chemical.volume} initial={0} />
{` units of ${chemical.name}`}
</Table.Cell>
<Table.Cell collapsing>
<Button
content="1"
onClick={() =>
act('transfer', {
id: chemical.id,
amount: 1,
to: transferTo,
})
}
/>
<Button
content="5"
onClick={() =>
act('transfer', {
id: chemical.id,
amount: 5,
to: transferTo,
})
}
/>
<Button
content="10"
onClick={() =>
act('transfer', {
id: chemical.id,
amount: 10,
to: transferTo,
})
}
/>
<Button
content="All"
onClick={() =>
act('transfer', {
id: chemical.id,
amount: 1000,
to: transferTo,
})
}
/>
<Button
icon="ellipsis-h"
title="Custom amount"
onClick={() =>
act('transfer', {
id: chemical.id,
amount: -1,
to: transferTo,
})
}
/>
<Button
icon="question"
title="Analyze"
onClick={() =>
act('analyze', {
id: chemical.id,
})
}
/>
</Table.Cell>
</Table.Row>
);
};
const PackagingControlsItem = (props) => {
const { label, amountUnit, amount, onChangeAmount, onCreate, sideNote } =
props;
return (
<LabeledList.Item label={label}>
<NumberInput
width="84px"
unit={amountUnit}
step={1}
stepPixelSize={15}
value={amount}
minValue={1}
maxValue={10}
onChange={onChangeAmount}
/>
<Button ml={1} content="Create" onClick={onCreate} />
<Box inline ml={1} color="label">
{sideNote}
</Box>
</LabeledList.Item>
);
};
const PackagingControls = (props, context) => {
const { act, data } = useBackend(context);
const [pillAmount, setPillAmount] = useSharedState(context, 'pillAmount', 1);
const [patchAmount, setPatchAmount] = useSharedState(
context,
'patchAmount',
1
);
const [bottleAmount, setBottleAmount] = useSharedState(
context,
'bottleAmount',
1
);
const [packAmount, setPackAmount] = useSharedState(context, 'packAmount', 1);
const {
condi,
chosenPillStyle,
chosenCondiStyle,
autoCondiStyle,
pillStyles = [],
condiStyles = [],
patch_style,
patch_styles = [],
} = data;
const autoCondiStyleChosen = autoCondiStyle === chosenCondiStyle;
return (
<LabeledList>
{!condi && (
<LabeledList.Item label="Pill type">
{pillStyles.map((pill) => (
<Button
key={pill.id}
width="30px"
selected={pill.id === chosenPillStyle}
textAlign="center"
color="transparent"
onClick={() => act('pillStyle', { id: pill.id })}>
<Box mx={-1} className={pill.className} />
</Button>
))}
</LabeledList.Item>
)}
{!condi && (
<PackagingControlsItem
label="Pills"
amount={pillAmount}
amountUnit="pills"
sideNote="max 50u"
onChangeAmount={(e, value) => setPillAmount(value)}
onCreate={() =>
act('create', {
type: 'pill',
amount: pillAmount,
volume: 'auto',
})
}
/>
)}
{!condi && (
<LabeledList.Item label="Patch type">
{patch_styles.map((patch) => (
<Button
key={patch.style}
selected={patch.style === patch_style}
textAlign="center"
color="transparent"
onClick={() =>
act('change_patch_style', { patch_style: patch.style })
}>
<Box mb={0} mt={1} className={patch.class_name} />
</Button>
))}
</LabeledList.Item>
)}
{!condi && (
<PackagingControlsItem
label="Patches"
amount={patchAmount}
amountUnit="patches"
sideNote="max 40u"
onChangeAmount={(e, value) => setPatchAmount(value)}
onCreate={() =>
act('create', {
type: 'patch',
amount: patchAmount,
volume: 'auto',
})
}
/>
)}
{!condi && (
<PackagingControlsItem
label="Tubes"
amount={bottleAmount}
amountUnit="tubes"
sideNote="max 30u"
onChangeAmount={(e, value) => setBottleAmount(value)}
onCreate={() =>
act('create', {
type: 'bottle',
amount: bottleAmount,
volume: 'auto',
})
}
/>
)}
{!!condi && (
<LabeledList.Item label="Bottle type">
<Button.Checkbox
onClick={() =>
act('condiStyle', {
id: autoCondiStyleChosen ? condiStyles[0].id : autoCondiStyle,
})
}
checked={autoCondiStyleChosen}
disabled={!condiStyles.length}>
Guess from contents
</Button.Checkbox>
</LabeledList.Item>
)}
{!!condi && !autoCondiStyleChosen && (
<LabeledList.Item label="">
{condiStyles.map((style) => (
<Button
key={style.id}
width="30px"
selected={style.id === chosenCondiStyle}
textAlign="center"
color="transparent"
title={style.title}
onClick={() => act('condiStyle', { id: style.id })}>
<Box mx={-1} className={style.className} />
</Button>
))}
</LabeledList.Item>
)}
{!!condi && (
<PackagingControlsItem
label="Bottles"
amount={bottleAmount}
amountUnit="bottles"
sideNote="max 50u"
onChangeAmount={(e, value) => setBottleAmount(value)}
onCreate={() =>
act('create', {
type: 'condimentBottle',
amount: bottleAmount,
volume: 'auto',
})
}
/>
)}
{!!condi && (
<PackagingControlsItem
label="Packs"
amount={packAmount}
amountUnit="packs"
sideNote="max 10u"
onChangeAmount={(e, value) => setPackAmount(value)}
onCreate={() =>
act('create', {
type: 'condimentPack',
amount: packAmount,
volume: 'auto',
})
}
/>
)}
</LabeledList>
);
};
const AnalysisResults = (props, context) => {
const { act, data } = useBackend(context);
const { analyzeVars } = data;
return (
<Section
title="Analysis Results"
buttons={
<Button
icon="arrow-left"
content="Back"
onClick={() =>
act('goScreen', {
screen: 'home',
})
}
/>
}>
<LabeledList>
<LabeledList.Item label="Name">{analyzeVars.name}</LabeledList.Item>
<LabeledList.Item label="State">{analyzeVars.state}</LabeledList.Item>
<LabeledList.Item label="pH">{analyzeVars.ph}</LabeledList.Item>
<LabeledList.Item label="Color">
<ColorBox color={analyzeVars.color} mr={1} />
{analyzeVars.color}
</LabeledList.Item>
<LabeledList.Item label="Description">
{analyzeVars.description}
</LabeledList.Item>
<LabeledList.Item label="Metabolization Rate">
{analyzeVars.metaRate} u/minute
</LabeledList.Item>
<LabeledList.Item label="Overdose Threshold">
{analyzeVars.overD}
</LabeledList.Item>
<LabeledList.Item label="Addiction Threshold">
{analyzeVars.addicD}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
@@ -0,0 +1,453 @@
import { BooleanLike, classes } from 'common/react';
import { capitalize } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { AnimatedNumber, Box, Button, Section, Table, NumberInput, Tooltip, LabeledList, ColorBox, ProgressBar, Stack, Divider } from '../components';
import { Window } from '../layouts';
type Data = {
reagentAnalysisMode: BooleanLike;
analysisData: Analysis;
isPrinting: BooleanLike;
printingProgress: number;
printingTotal: number;
transferMode: BooleanLike;
hasBeaker: BooleanLike;
beakerCurrentVolume: number;
beakerMaxVolume: number;
beakerContents: Reagent[];
bufferContents: Reagent[];
bufferCurrentVolume: number;
bufferMaxVolume: number;
categories: Category[];
selectedContainerRef: string;
selectedContainerVolume: number;
hasContainerSuggestion: BooleanLike;
doSuggestContainer: BooleanLike;
suggestedContainer: string;
};
type Analysis = {
name: string;
state: string;
pH: number;
color: string;
description: string;
purity: number;
metaRate: number;
overdose: number;
addictionTypes: string[];
};
type Category = {
name: string;
containers: Container[];
};
type Reagent = {
ref: string;
name: string;
volume: number;
};
type Container = {
icon: string;
ref: string;
name: string;
volume: number;
};
export const ChemMaster = (props, context) => {
const { data } = useBackend<Data>(context);
const { reagentAnalysisMode } = data;
return (
<Window width={400} height={620}>
<Window.Content scrollable>
{reagentAnalysisMode ? <AnalysisResults /> : <ChemMasterContent />}
</Window.Content>
</Window>
);
};
const ChemMasterContent = (props, context) => {
const { act, data } = useBackend<Data>(context);
const {
isPrinting,
printingProgress,
printingTotal,
transferMode,
hasBeaker,
beakerCurrentVolume,
beakerMaxVolume,
beakerContents,
bufferContents,
bufferCurrentVolume,
bufferMaxVolume,
categories,
selectedContainerVolume,
hasContainerSuggestion,
doSuggestContainer,
suggestedContainer,
} = data;
const [itemCount, setItemCount] = useLocalState(context, 'itemCount', 1);
return (
<Box>
<Section
title="Beaker"
buttons={
!!hasBeaker && (
<Box>
<Box inline color="label" mr={2}>
<AnimatedNumber value={beakerCurrentVolume} initial={0} />
{` / ${beakerMaxVolume} units`}
</Box>
<Button
icon="eject"
content="Eject"
onClick={() => act('eject')}
/>
</Box>
)
}>
{!hasBeaker && (
<Box color="label" my={'4px'}>
No beaker loaded.
</Box>
)}
{!!hasBeaker && beakerCurrentVolume === 0 && (
<Box color="label" my={'4px'}>
Beaker is empty.
</Box>
)}
<Table>
{beakerContents.map((chemical) => (
<ReagentEntry
key={chemical.ref}
chemical={chemical}
transferTo="buffer"
/>
))}
</Table>
</Section>
<Section
title="Buffer"
buttons={
<>
<Box inline color="label" mr={1}>
<AnimatedNumber value={bufferCurrentVolume} initial={0} />
{` / ${bufferMaxVolume} units`}
</Box>
<Button
color={transferMode ? 'good' : 'bad'}
icon={transferMode ? 'exchange-alt' : 'trash'}
content={transferMode ? 'Moving reagents' : 'Destroying reagents'}
onClick={() => act('toggleTransferMode')}
/>
</>
}>
{bufferContents.length === 0 && (
<Box color="label" my={'4px'}>
Buffer is empty.
</Box>
)}
<Table>
{bufferContents.map((chemical) => (
<ReagentEntry
key={chemical.ref}
chemical={chemical}
transferTo="beaker"
/>
))}
</Table>
</Section>
{!isPrinting && (
<Section
title="Packaging"
buttons={
bufferContents.length !== 0 &&
(!isPrinting ? (
<Box>
<NumberInput
unit={'items'}
step={1}
value={itemCount}
minValue={1}
maxValue={50}
onChange={(e, value) => {
setItemCount(value);
}}
/>
<Box inline mx={1}>
{`${
Math.round(
Math.min(
selectedContainerVolume,
bufferCurrentVolume / itemCount
) * 100
) / 100
} u. each`}
</Box>
<Button
content="Print"
icon="flask"
onClick={() =>
act('create', {
itemCount: itemCount,
})
}
/>
</Box>
) : (
<Button content="Printing..." icon="gear" iconSpin disabled />
))
}>
{!!hasContainerSuggestion && (
<Button.Checkbox
onClick={() => act('toggleContainerSuggestion')}
checked={doSuggestContainer}
mb={1}>
Guess container by main reagent in the buffer
</Button.Checkbox>
)}
{categories.map((category) => (
<Box key={category.name}>
<GroupTitle title={category.name} />
{category.containers.map(
(container) =>
(!hasContainerSuggestion || // Doesn't have suggestion
(!!hasContainerSuggestion && !doSuggestContainer) || // Has sugestion and it's disabled
(!!doSuggestContainer &&
container.ref === suggestedContainer)) && ( // Suggestion enabled and container matches
<ContainerButton
key={container.ref}
category={category}
container={container}
/>
)
)}
</Box>
))}
</Section>
)}
{!!isPrinting && (
<Section
title="Printing"
buttons={
<Button
color="bad"
icon="times"
content="Stop"
onClick={() => act('stopPrinting')}
/>
}>
<ProgressBar
value={printingProgress}
minValue={0}
maxValue={printingTotal}
color="good">
<Box
lineHeight={1.9}
style={{
'text-shadow': '1px 1px 0 black',
}}>
{`Printing ${printingProgress} out of ${printingTotal}`}
</Box>
</ProgressBar>
</Section>
)}
</Box>
);
};
const ReagentEntry = (props, context) => {
const { data, act } = useBackend<Data>(context);
const { chemical, transferTo } = props;
const { isPrinting } = data;
return (
<Table.Row key={chemical.ref}>
<Table.Cell color="label">
{`${chemical.name} `}
<AnimatedNumber value={chemical.volume} initial={0} />
{`u`}
</Table.Cell>
<Table.Cell collapsing>
<Button
content="1"
disabled={isPrinting}
onClick={() => {
act('transfer', {
reagentRef: chemical.ref,
amount: 1,
target: transferTo,
});
}}
/>
<Button
content="5"
disabled={isPrinting}
onClick={() =>
act('transfer', {
reagentRef: chemical.ref,
amount: 5,
target: transferTo,
})
}
/>
<Button
content="10"
disabled={isPrinting}
onClick={() =>
act('transfer', {
reagentRef: chemical.ref,
amount: 10,
target: transferTo,
})
}
/>
<Button
content="All"
disabled={isPrinting}
onClick={() =>
act('transfer', {
reagentRef: chemical.ref,
amount: 1000,
target: transferTo,
})
}
/>
<Button
icon="ellipsis-h"
title="Custom amount"
disabled={isPrinting}
onClick={() =>
act('transfer', {
reagentRef: chemical.ref,
amount: -1,
target: transferTo,
})
}
/>
<Button
icon="question"
title="Analyze"
onClick={() =>
act('analyze', {
reagentRef: chemical.ref,
})
}
/>
</Table.Cell>
</Table.Row>
);
};
const ContainerButton = ({ container, category }, context) => {
const { act, data } = useBackend<Data>(context);
const { isPrinting, selectedContainerRef } = data;
const isPillPatch = ['pills', 'patches'].includes(category.name);
return (
<Tooltip
key={container.ref}
content={`${capitalize(container.name)}\xa0(${container.volume}u)`}>
<Button
overflow="hidden"
color="transparent"
width={isPillPatch ? '32px' : '48px'}
height={isPillPatch ? '32px' : '48px'}
selected={container.ref === selectedContainerRef}
disabled={isPrinting}
p={0}
onClick={() => {
act('selectContainer', {
ref: container.ref,
});
}}>
<Box
m={isPillPatch ? '0' : '8px'}
style={{
'transform': 'scale(2)',
}}
className={classes(['chemmaster32x32', container.icon])}
/>
</Button>
</Tooltip>
) as any;
};
const AnalysisResults = (props, context) => {
const { act, data } = useBackend<Data>(context);
const {
name,
state,
pH,
color,
description,
purity,
metaRate,
overdose,
addictionTypes,
} = data.analysisData;
const purityLevel =
purity <= 0.5 ? 'bad' : purity <= 0.75 ? 'average' : 'good'; // Color names
return (
<Section
title="Analysis Results"
buttons={
<Button
icon="arrow-left"
content="Back"
onClick={() => act('stopAnalysis')}
/>
}>
<LabeledList>
<LabeledList.Item label="Name">{name}</LabeledList.Item>
<LabeledList.Item label="Purity">
<Box
style={{
'text-transform': 'capitalize',
}}
color={purityLevel}>
{purityLevel}
</Box>
</LabeledList.Item>
<LabeledList.Item label="pH">{pH}</LabeledList.Item>
<LabeledList.Item label="State">{state}</LabeledList.Item>
<LabeledList.Item label="Color">
<ColorBox color={color} mr={1} />
{color}
</LabeledList.Item>
<LabeledList.Item label="Description">{description}</LabeledList.Item>
<LabeledList.Item label="Metabolization Rate">
{metaRate} units/second
</LabeledList.Item>
<LabeledList.Item label="Overdose Threshold">
{overdose > 0 ? `${overdose} units` : 'N/A'}
</LabeledList.Item>
<LabeledList.Item label="Addiction Types">
{addictionTypes.length ? addictionTypes.toString() : 'N/A'}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const GroupTitle = ({ title }) => {
return (
<Stack my={1}>
<Stack.Item grow>
<Divider />
</Stack.Item>
<Stack.Item
style={{
'text-transform': 'capitalize',
}}
color={'gray'}>
{title}
</Stack.Item>
<Stack.Item grow>
<Divider />
</Stack.Item>
</Stack>
) as any;
};