mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-16 21:23:20 +00:00
265 lines
8.2 KiB
JavaScript
265 lines
8.2 KiB
JavaScript
import { round } from 'common/math';
|
|
import { Fragment } from 'inferno';
|
|
import { useBackend } from '../backend';
|
|
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from '../components';
|
|
import { Window } from '../layouts';
|
|
|
|
const stats = [
|
|
['good', 'Alive'],
|
|
['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 Sleeper = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { hasOccupant } = data;
|
|
const body = hasOccupant ? <SleeperMain /> : <SleeperEmpty />;
|
|
return (
|
|
<Window width={550} height={760} resizable>
|
|
<Window.Content className="Layout__content--flexColumn">{body}</Window.Content>
|
|
</Window>
|
|
);
|
|
};
|
|
|
|
const SleeperMain = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { occupant, dialysis, stomachpumping } = data;
|
|
return (
|
|
<Fragment>
|
|
<SleeperOccupant />
|
|
<SleeperDamage />
|
|
<SleeperDialysisPump title="Dialysis" active={dialysis} actToDo="togglefilter" />
|
|
<SleeperDialysisPump title="Stomach Pump" active={stomachpumping} actToDo="togglepump" />
|
|
<SleeperChemicals />
|
|
</Fragment>
|
|
);
|
|
};
|
|
|
|
const SleeperOccupant = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { occupant, auto_eject_dead, stasis } = data;
|
|
return (
|
|
<Section
|
|
title="Occupant"
|
|
buttons={
|
|
<Fragment>
|
|
<Box color="label" inline>
|
|
Auto-eject if dead:
|
|
</Box>
|
|
<Button
|
|
icon={auto_eject_dead ? 'toggle-on' : 'toggle-off'}
|
|
selected={auto_eject_dead}
|
|
content={auto_eject_dead ? 'On' : 'Off'}
|
|
onClick={() => act('auto_eject_dead_' + (auto_eject_dead ? 'off' : 'on'))}
|
|
/>
|
|
<Button icon="user-slash" content="Eject" onClick={() => act('ejectify')} />
|
|
<Button content={stasis} onClick={() => act('changestasis')} />
|
|
</Fragment>
|
|
}>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Name">{occupant.name}</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],
|
|
}}>
|
|
{round(occupant.health, 0)}
|
|
</ProgressBar>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
|
|
{stats[occupant.stat][1]}
|
|
</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, 0)}°C,
|
|
{round(occupant.btFaren, 0)}°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" verticalAlign="middle">
|
|
{occupant.pulse} BPM
|
|
</LabeledList.Item>
|
|
</Fragment>
|
|
)}
|
|
</LabeledList>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const SleeperDamage = (props, context) => {
|
|
const { data } = useBackend(context);
|
|
const { occupant } = data;
|
|
return (
|
|
<Section title="Damage">
|
|
<LabeledList>
|
|
{damages.map((d, i) => (
|
|
<LabeledList.Item key={i} label={d[0]}>
|
|
<ProgressBar key={i} min="0" max="100" value={occupant[d[1]] / 100} ranges={damageRange}>
|
|
{round(occupant[d[1]], 0)}
|
|
</ProgressBar>
|
|
</LabeledList.Item>
|
|
))}
|
|
</LabeledList>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const SleeperDialysisPump = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { isBeakerLoaded, beakerMaxSpace, beakerFreeSpace } = data;
|
|
const { active, actToDo, title } = props;
|
|
const canDialysis = active && beakerFreeSpace > 0;
|
|
return (
|
|
<Section
|
|
title={title}
|
|
buttons={
|
|
<Fragment>
|
|
<Button
|
|
disabled={!isBeakerLoaded || beakerFreeSpace <= 0}
|
|
selected={canDialysis}
|
|
icon={canDialysis ? 'toggle-on' : 'toggle-off'}
|
|
content={canDialysis ? 'Active' : 'Inactive'}
|
|
onClick={() => act(actToDo)}
|
|
/>
|
|
<Button disabled={!isBeakerLoaded} icon="eject" content="Eject" onClick={() => act('removebeaker')} />
|
|
</Fragment>
|
|
}>
|
|
{isBeakerLoaded ? (
|
|
<LabeledList>
|
|
<LabeledList.Item label="Remaining Space">
|
|
<ProgressBar
|
|
min="0"
|
|
max={beakerMaxSpace}
|
|
value={beakerFreeSpace / beakerMaxSpace}
|
|
ranges={{
|
|
good: [0.5, Infinity],
|
|
average: [0.25, 0.5],
|
|
bad: [-Infinity, 0.25],
|
|
}}>
|
|
{beakerFreeSpace}u
|
|
</ProgressBar>
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
) : (
|
|
<Box color="label">No beaker loaded.</Box>
|
|
)}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const SleeperChemicals = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { occupant, chemicals, maxchem, amounts } = data;
|
|
return (
|
|
<Section title="Chemicals" flexGrow="1">
|
|
{chemicals.map((chem, i) => {
|
|
let barColor = '';
|
|
let odWarning;
|
|
if (chem.overdosing) {
|
|
barColor = 'bad';
|
|
odWarning = (
|
|
<Box color="bad">
|
|
<Icon name="exclamation-circle" />
|
|
Overdosing!
|
|
</Box>
|
|
);
|
|
} else if (chem.od_warning) {
|
|
barColor = 'average';
|
|
odWarning = (
|
|
<Box color="average">
|
|
<Icon name="exclamation-triangle" />
|
|
Close to overdosing
|
|
</Box>
|
|
);
|
|
}
|
|
return (
|
|
<Box key={i} backgroundColor="rgba(0, 0, 0, 0.33)" mb="0.5rem">
|
|
<Section title={chem.title} level="3" mx="0" lineHeight="18px" buttons={odWarning}>
|
|
<Flex align="flex-start">
|
|
<ProgressBar min="0" max={maxchem} value={chem.occ_amount / maxchem} color={barColor} mr="0.5rem">
|
|
{chem.pretty_amount}/{maxchem}u
|
|
</ProgressBar>
|
|
{amounts.map((a, i) => (
|
|
<Button
|
|
key={i}
|
|
disabled={!chem.injectable || chem.occ_amount + a > maxchem || occupant.stat === 2}
|
|
icon="syringe"
|
|
content={a}
|
|
mb="0"
|
|
height="19px"
|
|
onClick={() =>
|
|
act('chemical', {
|
|
chemid: chem.id,
|
|
amount: a,
|
|
})
|
|
}
|
|
/>
|
|
))}
|
|
</Flex>
|
|
</Section>
|
|
</Box>
|
|
);
|
|
})}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const SleeperEmpty = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
const { isBeakerLoaded } = data;
|
|
return (
|
|
<Section textAlign="center" flexGrow="1">
|
|
<Flex height="100%">
|
|
<Flex.Item grow="1" align="center" color="label">
|
|
<Icon name="user-slash" mb="0.5rem" size="5" />
|
|
<br />
|
|
No occupant detected.
|
|
{(isBeakerLoaded && (
|
|
<Box>
|
|
<Button icon="eject" content="Remove Beaker" onClick={() => act('removebeaker')} />
|
|
</Box>
|
|
)) ||
|
|
null}
|
|
</Flex.Item>
|
|
</Flex>
|
|
</Section>
|
|
);
|
|
};
|