This commit is contained in:
SandPoot
2022-02-04 13:06:20 -03:00
46 changed files with 1445 additions and 682 deletions
+202
View File
@@ -0,0 +1,202 @@
import { useBackend, useLocalState } from '../backend';
import { Button, LabeledList, Section, ProgressBar, Flex, Box, Table, Collapsible, Input, Dimmer, Icon } from '../components';
import { Window } from '../layouts';
import { capitalize } from "common/string";
export const Autolathe = (props, context) => {
const { act, data } = useBackend(context);
// Extract `health` and `color` variables from the `data` object.
const {
materialtotal,
materialsmax,
materials = [],
categories = [],
designs = [],
active,
} = data;
const [
current_category,
setCategory,
] = useLocalState(context, 'current_category', "None");
const filteredmaterials = materials.filter(material =>
material.mineral_amount > 0);
return (
<Window
width={600}
height={600}>
<Window.Content scrollable>
<Section title="Total Materials">
<LabeledList>
<LabeledList.Item
label="Total Materials">
<ProgressBar
value={materialtotal}
minValue={0}
maxValue={materialsmax}
ranges={{
"good": [materialsmax * 0.85, materialsmax],
"average": [materialsmax * 0.25, materialsmax * 0.85],
"bad": [0, materialsmax * 0.25],
}}>
{materialtotal + '/' + materialsmax + ' cm³'}
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item>
{filteredmaterials.length > 0 && (
<Collapsible title="Materials">
<LabeledList>
{filteredmaterials.map(filteredmaterial => (
<LabeledList.Item
key={filteredmaterial.id}
label={capitalize(filteredmaterial.name)}>
<ProgressBar
style={{
transform: 'scaleX(-1) scaleY(1)',
}}
value={materialsmax - filteredmaterial.mineral_amount}
maxValue={materialsmax}
color="black"
backgroundColor={filteredmaterial.matcolour}>
<div style={{ transform: 'scaleX(-1)' }}>{filteredmaterial.mineral_amount + ' cm³'}</div>
</ProgressBar>
</LabeledList.Item>
))}
</LabeledList>
</Collapsible>)}
</LabeledList.Item>
</LabeledList>
</Section>
<Section
title="Search">
<Input fluid
placeholder="Search Recipes..."
selfClear
onChange={(e, value) => {
if (value.length) {
act('search', {
to_search: value,
});
setCategory('results for "' + value + '"');
}
}} />
</Section>
<Section title="Categories">
<Box>
{categories.map(category => (
// eslint-disable-next-line react/jsx-key
<Button
selected={current_category === category}
content={category}
onClick={() => {
act('category', {
selectedCategory: category,
});
setCategory(category);
}} />
))}
</Box>
</Section>
{current_category.toString() !== "None" && (
<Section
title={'Displaying ' + current_category.toString()}
buttons={(
<Button
icon="times"
content="Close Category"
onClick={() => {
act('menu');
setCategory("None");
}} />
)}>
{active === 1 && (
<Dimmer fontSize="32px">
<Icon name="cog" spin />
{'Building items...'}
</Dimmer>
)}
<Flex direction="row" wrap="nowrap">
<Table>
{designs.length
&& (designs.map(design => (
<Table.Row
key={design.id}>
<Flex.Item>
<Button
content={design.name}
disabled={design.buildable}
onClick={() => act('make', {
id: design.id,
multiplier: '1',
})} />
</Flex.Item>
{design.sheet ? (
<Table.Cell>
<Flex.Item grow={1}>
<Button
icon="hammer"
content="10"
disabled={!design.mult10}
onClick={() => act('make', {
id: design.id,
multiplier: '10',
})} />
<Button
icon="hammer"
content="25"
disabled={!design.mult25}
onClick={() => act('make', {
id: design.id,
multiplier: '25',
})} />
</Flex.Item>
</Table.Cell>
) : (
<Table.Cell>
<Flex.Item grow={3}>
<Button
icon="hammer"
content="5"
disabled={!design.mult5}
onClick={() => act('make', {
id: design.id,
multiplier: '5',
})} />
<Button
icon="hammer"
content="10"
disabled={!design.mult10}
onClick={() => act('make', {
id: design.id,
multiplier: '10',
})} />
</Flex.Item>
</Table.Cell>
)}
<Table.Cell>
<Button.Input
content={"[Max:" + design.maxmult + ']'}
maxValue={design.maxmult}
disabled={design.buildable}
backgroundColor={design.buildable ? '#999999' : 'default'}
onCommit={(e, value) => act('make', {
id: design.id,
multiplier: value,
})} />
</Table.Cell>
{design.cost}
</Table.Row>
))) || (
<Table.Row>
<Table.Cell>
{"No designs found."}
</Table.Cell>
</Table.Row>
)}
</Table>
</Flex>
</Section>
)}
</Window.Content>
</Window>
);
};
@@ -0,0 +1,136 @@
/* eslint-disable react/jsx-closing-tag-location */
import { useBackend } from '../backend';
import { Box, Button, Flex, Icon, LabeledList, Modal, Section, Table, Tooltip } from '../components';
import { Window } from '../layouts';
export const CustomShuttleConsole = (props, context) => {
const { act, data } = useBackend(context);
const {
docked_location,
ship_name = "ERROR",
shuttle_mass = 0,
engine_force = 0,
engines = 0,
calculated_speed = 0,
calculated_non_operational_thrusters = 0,
calculated_consumption = 0,
calculated_cooldown = 0,
locations = [],
destination = null,
} = data;
return (
<Window
width={385}
height={475}>
<Window.Content overflow="auto">
<Section fluid fill grow={1}>
{!docked_location ? (
<Modal
ml={1}
mt={1}
width={26}
height={12}
fontSize="28px"
fontFamily="monospace"
textAlign="center">
<Flex>
<Flex.Item mt={8} ml={3}>
<Icon
name="minus-circle" />
</Flex.Item>
<Flex.Item
mt={5}
ml={2}
color="yellow">
{"Shuttle Link Required."}
</Flex.Item>
</Flex>
</Modal>
) : (
<>
<font size="+3"><center>{ship_name}</center></font>
<Section title="Info">
<LabeledList>
<LabeledList.Item label="Current Location">
{docked_location}
</LabeledList.Item>
<LabeledList.Item label="Shuttle Mass">
{shuttle_mass / 10}ton{shuttle_mass !== 1 ? "s" : null}
</LabeledList.Item>
<LabeledList.Item label="Engine Force">
{engine_force}Kn ({engines} engine{engines !== 1 ? "s" : null})
</LabeledList.Item>
<LabeledList.Item label="Sublight speed">
{calculated_speed}ms{<sup>-1</sup>} {calculated_speed < 1 ? <Tooltip content="INSUFFICIENT ENGINE POWER"><Icon name="exclamation-triangle" color="yellow" /></Tooltip> : null}
</LabeledList.Item>
{calculated_non_operational_thrusters.len
? <LabeledList.Item label="Warning">
{calculated_non_operational_thrusters} thruster{calculated_non_operational_thrusters !== 1 ? "s are" : " is"} not operational.
</LabeledList.Item>
: null}
<LabeledList.Item label="Fuel Consumption">
{calculated_consumption} unit{calculated_consumption !== 1 ? "s" : null} per travel
</LabeledList.Item>
<LabeledList.Item label="Engine Cooldown">
{calculated_cooldown}s
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Destinations">
{locations.length===0 && (
<Box
mb={1.7}
color="bad">
No valid destinations
</Box>
) || (
<Table>
{locations.map(location => (
<tr class="Table__row candystripe" key={location.id}>
{location.name} <td>({location.dist}m)</td>
<td>
<Button
icon="crosshairs"
selected={location.id === destination}
onClick={() => act("setloc", {
setloc: location.id,
})} />
</td>
</tr>
))}
</Table>
)}
</Section>
<Section>
<Button
content="Initiate Flight"
icon="rocket"
disabled={calculated_speed < 1 || !destination}
onClick={() => act('fly')}
fluid />
</Section>
</>
)}
</Section>
</Window.Content>
</Window>
);
};
const getLocationNameById = (locations, id) => {
return locations?.find(location => location.id === id)?.name;
};
const getLocationIdByName = (locations, name) => {
return locations?.find(location => location.name === name)?.id;
};
const STATUS_COLOR_KEYS = {
"In Transit": "good",
"Idle": "average",
"Igniting": "average",
"Recharging": "average",
"Missing": "bad",
"Unauthorized Access": "bad",
"Locked": "bad",
};
+351
View File
@@ -0,0 +1,351 @@
import { multiline } from '../../common/string';
import { useBackend } from '../backend';
import { Button, Icon, LabeledControls, NoticeBox, Section, Slider, Stack, Tooltip } from '../components';
import { Window } from '../layouts';
type SimpleBotContext = {
can_hack: number;
locked: number;
emagged: number;
pai: Pai;
settings: Settings;
custom_controls: Controls;
};
type Pai = {
allow_pai: number;
card_inserted: number;
};
type Settings = {
power: number;
airplane_mode: number;
maintenance_lock: number;
patrol_station: number;
};
type Controls = {
[Control: string]: [Value: number];
};
export const SimpleBot = (_, context) => {
const { data } = useBackend<SimpleBotContext>(context);
const { can_hack, locked } = data;
const access = (!locked || can_hack);
return (
<Window width={450} height={300}>
<Window.Content>
<Stack fill vertical>
<Stack.Item>
<Section title="Settings" buttons={<TabDisplay />}>
{!access
? (<NoticeBox>Locked!</NoticeBox>)
: (<SettingsDisplay />)}
</Section>
</Stack.Item>
{access && (
<Stack.Item grow>
<Section fill scrollable title="Controls">
<ControlsDisplay />
</Section>
</Stack.Item>
)}
</Stack>
</Window.Content>
</Window>
);
};
/** Creates a lock button at the top of the controls */
const TabDisplay = (_, context) => {
const { act, data } = useBackend<SimpleBotContext>(context);
const { can_hack, locked, pai } = data;
const { allow_pai } = pai;
return (
<>
{!!can_hack && <HackButton />}
{!!allow_pai && <PaiButton />}
<Button
color="transparent"
icon={locked ? 'lock' : 'lock-open'}
onClick={() => act('lock')}
selected={locked}
tooltip={`${locked ? 'Unlock' : 'Lock'} the control panel.`}>
Controls Lock
</Button>
</>
);
};
/** If user is a bad silicon, they can press this button to hack the bot */
const HackButton = (_, context) => {
const { act, data } = useBackend<SimpleBotContext>(context);
const { can_hack, emagged } = data;
return (
<Button
color="danger"
disabled={!can_hack}
icon={emagged ? 'bug' : 'lock'}
onClick={() => act('hack')}
selected={!emagged}
tooltip={
!emagged
? 'Unlocks the safety protocols.'
: 'Resets the bot operating system.'
}>
{emagged ? 'Malfunctional' : 'Safety Lock'}
</Button>
);
};
/** Creates a button indicating PAI status and offers the eject action */
const PaiButton = (_, context) => {
const { act, data } = useBackend<SimpleBotContext>(context);
const { card_inserted } = data.pai;
if (!card_inserted) {
return (
<Button
color="transparent"
icon="robot"
tooltip={multiline`Insert an active PAI card to control this device.`}>
No PAI Inserted
</Button>
);
} else {
return (
<Button
disabled={!card_inserted}
icon="eject"
onClick={() => act('eject_pai')}
tooltip={multiline`Ejects the current PAI.`}>
Eject PAI
</Button>
);
}
};
/** Displays the bot's standard settings: Power, patrol, etc. */
const SettingsDisplay = (_, context) => {
const { act, data } = useBackend<SimpleBotContext>(context);
const { settings } = data;
const { airplane_mode, patrol_station, power, maintenance_lock } = settings;
return (
<LabeledControls>
<LabeledControls.Item label="Power">
<Tooltip content={`Powers ${power ? 'off' : 'on'} the bot.`}>
<Icon
size={2}
name="power-off"
color={power ? 'good' : 'gray'}
onClick={() => act('power')}
/>
</Tooltip>
</LabeledControls.Item>
<LabeledControls.Item label="Airplane Mode">
<Tooltip
content={`${
!airplane_mode ? 'Disables' : 'Enables'
} remote access via console.`}>
<Icon
size={2}
name="plane"
color={airplane_mode ? 'yellow' : 'gray'}
onClick={() => act('airplane')}
/>
</Tooltip>
</LabeledControls.Item>
<LabeledControls.Item label="Patrol Station">
<Tooltip
content={`${
patrol_station ? 'Disables' : 'Enables'
} automatic station patrol.`}>
<Icon
size={2}
name="map-signs"
color={patrol_station ? 'good' : 'gray'}
onClick={() => act('patrol')}
/>
</Tooltip>
</LabeledControls.Item>
<LabeledControls.Item label="Maintenance Lock">
<Tooltip
content={
maintenance_lock
? 'Opens the maintenance hatch for repairs.'
: 'Closes the maintenance hatch.'
}>
<Icon
size={2}
name="toolbox"
color={maintenance_lock ? 'yellow' : 'gray'}
onClick={() => act('maintenance')}
/>
</Tooltip>
</LabeledControls.Item>
</LabeledControls>
);
};
/** Iterates over custom controls.
* Calls the helper to identify which button to use.
*/
const ControlsDisplay = (_, context) => {
const { data } = useBackend<SimpleBotContext>(context);
const { custom_controls } = data;
return (
<LabeledControls wrap>
{Object.entries(custom_controls).map((control) => {
return (
<LabeledControls.Item
pb={2}
key={control[0]}
label={control[0]
.replace('_', ' ')
.replace(/(^\w{1})|(\s+\w{1})/g, (letter) =>
letter.toUpperCase())}>
<ControlHelper control={control} />
</LabeledControls.Item>
);
})}
</LabeledControls>
);
};
/** Helper function which identifies which button to create.
* Might need some fine tuning if you are using more advanced controls.
*/
const ControlHelper = (props, context) => {
const { act } = useBackend<SimpleBotContext>(context);
const { control } = props;
if (control[0] === 'sync_tech') {
/** Control is for sync - this is medbot specific */
return <MedbotSync />;
} else if (control[0] === 'heal_threshold') {
/** Control is a threshold - this is medbot specific */
return <MedbotThreshold control={control} />;
} else if (control[0] === 'injection_amount') {
/** Control is for injection - this is medbot specific */
return <InjectionThreshold control={control} />;
} else if (control[0] === 'tile_stack') {
return <FloorbotTiles control={control} />;
} else if (control[0] === 'line_mode') {
return <FloorbotLine control={control} />;
} else {
/** Control is a boolean of some type */
return (
<Icon
color={control[1] ? 'good' : 'gray'}
name={control[1] ? 'toggle-on' : 'toggle-off'}
size={2}
onClick={() => act(control[0])}
/>
);
}
};
/** Small button to sync medbots with research. */
const MedbotSync = (_, context) => {
const { act } = useBackend<SimpleBotContext>(context);
return (
<Tooltip
content={multiline`Synchronize surgical data with research network.
Improves Tending Efficiency.`}>
<Icon
color="purple"
name="cloud-download-alt"
size={2}
onClick={() => act('sync_tech')}
/>
</Tooltip>
);
};
/** Slider button for medbot injection thresholds */
const InjectionThreshold = (props, context) => {
const { act } = useBackend<SimpleBotContext>(context);
const { control } = props;
return (
<Tooltip content="Adjusts the amount for using when injecting.">
<Slider
minValue={1}
maxValue={15}
ranges={{
bad: [-Infinity, 5],
average: [6, 10],
good: [11, Infinity],
}}
step={2}
unit="u"
value={control[1]}
onChange={(_, value) => act(control[0], { amount: value })}
/>
</Tooltip>
);
};
/** Slider button for medbot healing thresholds */
const MedbotThreshold = (props, context) => {
const { act } = useBackend<SimpleBotContext>(context);
const { control } = props;
return (
<Tooltip content="Adjusts the sensitivity for damage treatment.">
<Slider
minValue={5}
maxValue={75}
ranges={{
good: [-Infinity, 15],
average: [15, 55],
bad: [55, Infinity],
}}
step={5}
unit="%"
value={control[1]}
onChange={(_, value) => act(control[0], { threshold: value })}
/>
</Tooltip>
);
};
/** Tile stacks for floorbots - shows number and eject button */
const FloorbotTiles = (props, context) => {
const { act } = useBackend<SimpleBotContext>(context);
const { control } = props;
return (
<Button
disabled={!control[1]}
icon={control[1] ? 'eject' : ''}
onClick={() => act('eject_tiles')}
tooltip="Number of floor tiles contained in the bot.">
{control[1] ? `${control[1]}` : 'Empty'}
</Button>
);
};
/** Direction indicator for floorbot when line mode is chosen. */
const FloorbotLine = (props, context) => {
const { act } = useBackend<SimpleBotContext>(context);
const { control } = props;
return (
<Tooltip content="Enables straight line tiling mode.">
<Icon
color={control[1] ? 'good' : 'gray'}
name={control[1] ? 'compass' : 'toggle-off'}
onClick={() => act('line_mode')}
size={!control[1] ? 2 : 1.5}>
{' '}
{control[1] ? control[1].toString().charAt(0).toUpperCase() : ''}
</Icon>
</Tooltip>
);
};