mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
Ready for testmerge
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
/*
|
||||
Asset cache quick users guide:
|
||||
|
||||
Make a datum at the bottom of this file with your assets for your thing.
|
||||
The simple subsystem will most like be of use for most cases.
|
||||
Then call get_asset_datum() with the type of the datum you created and store the return
|
||||
Then call .send(client) on that stored return value.
|
||||
|
||||
You can set verify to TRUE if you want send() to sleep until the client has the assets.
|
||||
*/
|
||||
|
||||
@@ -26,7 +28,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
|
||||
//This proc sends the asset to the client, but only if it needs it.
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
|
||||
/proc/send_asset(client/client, asset_name, verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
@@ -70,7 +72,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
return 1
|
||||
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
|
||||
/proc/send_asset_list(client/client, list/asset_list, verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
@@ -120,7 +122,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
/proc/getFilesSlow(client/client, list/files, register_asset = TRUE)
|
||||
var/concurrent_tracker = 1
|
||||
for(var/file in files)
|
||||
if (!client)
|
||||
@@ -138,13 +140,13 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//if it's an icon or something be careful, you'll have to copy it before further use.
|
||||
/proc/register_asset(var/asset_name, var/asset)
|
||||
/proc/register_asset(asset_name, asset)
|
||||
SSassets.cache[asset_name] = asset
|
||||
|
||||
//Generated names do not include file extention.
|
||||
//Used mainly for code that deals with assets in a generic way
|
||||
//The same asset will always lead to the same asset name
|
||||
/proc/generate_asset_name(var/file)
|
||||
/proc/generate_asset_name(file)
|
||||
return "asset.[md5(fcopy_rsc(file))]"
|
||||
|
||||
|
||||
@@ -154,7 +156,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
GLOBAL_LIST_EMPTY(asset_datums)
|
||||
|
||||
//get an assetdatum or make a new one
|
||||
/proc/get_asset_datum(var/type)
|
||||
/proc/get_asset_datum(type)
|
||||
return GLOB.asset_datums[type] || new type()
|
||||
|
||||
/datum/asset
|
||||
@@ -321,6 +323,13 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
var/size_id = sprite[SPR_SIZE]
|
||||
return {"<span class="[name][size_id] [sprite_name]"></span>"}
|
||||
|
||||
/datum/asset/spritesheet/proc/icon_class_name(sprite_name)
|
||||
var/sprite = sprites[sprite_name]
|
||||
if (!sprite)
|
||||
return null
|
||||
var/size_id = sprite[SPR_SIZE]
|
||||
return {"[name][size_id] [sprite_name]"}
|
||||
|
||||
#undef SPR_SIZE
|
||||
#undef SPR_IDX
|
||||
#undef SPRSZ_COUNT
|
||||
@@ -388,7 +397,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
"shim-html5shiv.js" = 'tgui-next/packages/tgui/public/shim-html5shiv.js',
|
||||
"shim-ie8.js" = 'tgui-next/packages/tgui/public/shim-ie8.js',
|
||||
"shim-dom4.js" = 'tgui-next/packages/tgui/public/shim-dom4.js',
|
||||
"shim-css-om.js" = 'tgui-next/packages/tgui/public/shim-css-om.js'
|
||||
"shim-css-om.js" = 'tgui-next/packages/tgui/public/shim-css-om.js',
|
||||
)
|
||||
|
||||
/datum/asset/group/tgui
|
||||
@@ -519,7 +528,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
|
||||
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
|
||||
)
|
||||
|
||||
|
||||
/datum/asset/simple/IRV
|
||||
assets = list(
|
||||
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="browserOutput.css" />
|
||||
<link rel="stylesheet" type="text/css" href="spritesheet_chat.css" />
|
||||
<link rel="stylesheet" type="text/css" id="colorPresetLink"/>
|
||||
<script type="text/javascript" src="errorHandler.js"></script>
|
||||
<script type="text/javascript" src="jquery.min.js"></script>
|
||||
<script type="text/javascript" src="json2.min.js"></script>
|
||||
</head>
|
||||
@@ -56,4 +55,4 @@
|
||||
<audio class="hidden" id="adminMusic" autoplay></audio>
|
||||
<script type="text/javascript" src="browserOutput.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
******************************************/
|
||||
|
||||
//DEBUG STUFF
|
||||
var triggerError = attachErrorHandler('chatDebug', true);
|
||||
var escaper = encodeURIComponent || escape;
|
||||
var decoder = decodeURIComponent || unescape;
|
||||
window.onerror = function(msg, url, line, col, error) {
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
(function(window, navigator) {
|
||||
|
||||
var escaper = encodeURIComponent || escape;
|
||||
|
||||
var triggerError = function(msg, url, line, col, error) {
|
||||
window.onerror(msg, url, line, col, error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Directs JS errors to a byond proc for logging
|
||||
*
|
||||
* @param string file Name of the logfile to dump errors in, do not prepend with data/
|
||||
* @param boolean overrideDefault True to prevent default JS errors (an big honking error prompt thing)
|
||||
* @return boolean
|
||||
*/
|
||||
var attach = function(file, overrideDefault) {
|
||||
overrideDefault = typeof overrideDefault === 'undefined' ? false : overrideDefault;
|
||||
file = escaper(file);
|
||||
|
||||
window.onerror = function(msg, url, line, col, error) {
|
||||
var extra = !col ? '' : ' | column: ' + col;
|
||||
extra += !error ? '' : ' | error: ' + error;
|
||||
extra += !navigator.userAgent ? '' : ' | user agent: ' + navigator.userAgent;
|
||||
var debugLine = 'Error: ' + msg + ' | url: ' + url + ' | line: ' + line + extra;
|
||||
window.location = '?action=debugFileOutput&file=' + file + '&message=' + escaper(debugLine);
|
||||
return overrideDefault;
|
||||
};
|
||||
|
||||
return triggerError;
|
||||
};
|
||||
|
||||
window.attachErrorHandler = attach;
|
||||
|
||||
}(window, window.navigator));
|
||||
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* This is a generic handler for logging your dumb JS errors generated by html popups
|
||||
*
|
||||
* 1. Add your logfile to the validFiles list
|
||||
* 2. Include the "browserassets/js/errorHandler.js" file in your html file (if using chui, skip this step) (look at browserOutput.html for an example)
|
||||
* 3. Call attachErrorHandler('yourLogFile'); at the top of your JS (again see browserOutput.js for an example)
|
||||
*/
|
||||
|
||||
/datum/debugFileOutput
|
||||
var/directory = "data/popupErrors" //where to shove all the logfiles
|
||||
var/ext = "log" //file extension
|
||||
var/logFileLimit = 52428800 //50mb, so yeah pretty permissive
|
||||
|
||||
//Add your dumb file here. This is so some schmuck can't just shit out a bunch of spam logfiles and use all the diskspace. Relative to src.directory
|
||||
var/list/validFiles = list(
|
||||
"chatDebug",
|
||||
"tooltipDebug",
|
||||
"chemDispenser",
|
||||
"banPanel",
|
||||
"stationNamer"
|
||||
)
|
||||
|
||||
/datum/debugFileOutput/proc/error(fileName, message, client/C)
|
||||
if (!fileName || !message)
|
||||
return 0
|
||||
|
||||
if (!(fileName in src.validFiles))
|
||||
throw EXCEPTION("Debug log file '[fileName].[src.ext]' is not a valid path.")
|
||||
|
||||
var/logFile = file("[src.directory]/[fileName].[src.ext]")
|
||||
var/fileSize = length(logFile)
|
||||
if (fileSize >= src.logFileLimit)
|
||||
CRASH("Debug Error Handling encountered an error! This is highly ironic! File: '[fileName]' has exceeded the filesize limit of: [src.logFileLimit] bytes")
|
||||
|
||||
message = "\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: \[[C && C.key ? C.key : "Unknown Client"]\] triggered: [message]"
|
||||
WRITE_FILE(logFile, message)
|
||||
return 1
|
||||
|
||||
/datum/debugFileOutput/proc/clear(fileName)
|
||||
if (!fileName)
|
||||
return 0
|
||||
|
||||
if (!fexists("[src.directory]/[fileName].[src.ext]"))
|
||||
throw EXCEPTION("Debug log file '[fileName].[src.ext]' does not exist.")
|
||||
|
||||
if (!(fileName in src.validFiles))
|
||||
throw EXCEPTION("Debug log file '[fileName].[src.ext]' is not a valid path.")
|
||||
|
||||
fdel("[src.directory]/[fileName].[src.ext]")
|
||||
return 1
|
||||
|
||||
/datum/debugFileOutput/proc/clearAll()
|
||||
var/list/deleted = new()
|
||||
for (var/fileName in src.validFiles)
|
||||
if (fexists("[src.directory]/[fileName].[src.ext]"))
|
||||
fdel("[src.directory]/[fileName].[src.ext]")
|
||||
deleted += fileName
|
||||
|
||||
return deleted
|
||||
|
||||
|
||||
GLOBAL_DATUM_INIT(debugFileOutput, /datum/debugFileOutput, new)
|
||||
|
||||
/client/Topic(href, href_list)
|
||||
..()
|
||||
|
||||
if (href_list["action"] && href_list["action"] == "debugFileOutput" && href_list["file"] && href_list["message"])
|
||||
var/file = href_list["file"]
|
||||
var/message = href_list["message"]
|
||||
GLOB.debugFileOutput.error(file, message, src)
|
||||
|
||||
/client/proc/deleteJsLogFile(fileName as text)
|
||||
set category = "Debug"
|
||||
set name = "Delete JS Logfile"
|
||||
set desc = "Delete a logfile for JS error reporting. Be sure you want to do this!"
|
||||
set popup_menu = 0
|
||||
if(!holder)
|
||||
return
|
||||
if (!fileName)
|
||||
return
|
||||
|
||||
GLOB.debugFileOutput.clear(fileName)
|
||||
|
||||
log_admin("[key_name(usr)] deleted the '[fileName]' JS logfile")
|
||||
message_admins("[key_name_admin(usr)] deleted the '[fileName]' JS logfile")
|
||||
|
||||
/client/proc/deleteAllJsLogFiles()
|
||||
set category = null
|
||||
set name = "Delete All JS Logfiles"
|
||||
set desc = "Delete all logfiles for JS error reporting. Be extra sure you want to do this!"
|
||||
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
if (alert("Are you really sure you want to delete every single JS logfile?", "No", "Yes") == "No")
|
||||
return
|
||||
|
||||
var/list/summary = GLOB.debugFileOutput.clearAll()
|
||||
var/friendlySummary = summary.Join(", ")
|
||||
|
||||
log_admin("[key_name(usr)] deleted every JS logfile! ([friendlySummary])")
|
||||
message_admins("[key_name_admin(usr)] deleted every JS logfile! ([friendlySummary])")
|
||||
6393
tgstation.dme
6393
tgstation.dme
File diff suppressed because it is too large
Load Diff
@@ -1,180 +0,0 @@
|
||||
import { toFixed } from 'common/math';
|
||||
import { toTitleCase } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, Icon, LabeledList, ProgressBar, Section } from '../components';
|
||||
|
||||
export const ChemDispenser = props => {
|
||||
const { act, data } = useBackend(props);
|
||||
const recording = !!data.recordingRecipe;
|
||||
// TODO: Change how this piece of shit is built on server side
|
||||
// It has to be a list, not a fucking OBJECT!
|
||||
const recipes = Object.keys(data.recipes)
|
||||
.map(name => ({
|
||||
name,
|
||||
contents: data.recipes[name],
|
||||
}));
|
||||
const beakerTransferAmounts = data.beakerTransferAmounts || [];
|
||||
const beakerContents = recording
|
||||
&& Object.keys(data.recordingRecipe)
|
||||
.map(id => ({
|
||||
id,
|
||||
name: toTitleCase(id.replace(/_/, ' ')),
|
||||
volume: data.recordingRecipe[id],
|
||||
}))
|
||||
|| data.beakerContents
|
||||
|| [];
|
||||
return (
|
||||
<Fragment>
|
||||
<Section
|
||||
title="Status"
|
||||
buttons={recording && (
|
||||
<Box inline mx={1} color="red">
|
||||
<Icon name="circle" mr={1} />
|
||||
Recording
|
||||
</Box>
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Energy">
|
||||
<ProgressBar
|
||||
value={data.energy / data.maxEnergy}
|
||||
content={toFixed(data.energy) + ' units'} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="Recipes"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
{!recording && (
|
||||
<Box inline mx={1}>
|
||||
<Button
|
||||
color="transparent"
|
||||
content="Clear recipes"
|
||||
onClick={() => act('clear_recipes')} />
|
||||
</Box>
|
||||
)}
|
||||
{!recording && (
|
||||
<Button
|
||||
icon="circle"
|
||||
disabled={!data.isBeakerLoaded}
|
||||
content="Record"
|
||||
onClick={() => act('record_recipe')} />
|
||||
)}
|
||||
{recording && (
|
||||
<Button
|
||||
icon="ban"
|
||||
color="transparent"
|
||||
content="Discard"
|
||||
onClick={() => act('cancel_recording')} />
|
||||
)}
|
||||
{recording && (
|
||||
<Button
|
||||
icon="save"
|
||||
color="green"
|
||||
content="Save"
|
||||
onClick={() => act('save_recording')} />
|
||||
)}
|
||||
</Fragment>
|
||||
)}>
|
||||
<Box mr={-1}>
|
||||
{recipes.map(recipe => (
|
||||
<Button key={recipe.name}
|
||||
icon="tint"
|
||||
width="129.5px"
|
||||
lineHeight="21px"
|
||||
content={recipe.name}
|
||||
onClick={() => act('dispense_recipe', {
|
||||
recipe: recipe.name,
|
||||
})} />
|
||||
))}
|
||||
{recipes.length === 0 && (
|
||||
<Box color="light-gray">
|
||||
No recipes.
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Section>
|
||||
<Section
|
||||
title="Dispense"
|
||||
buttons={(
|
||||
beakerTransferAmounts.map(amount => (
|
||||
<Button key={amount}
|
||||
icon="plus"
|
||||
selected={amount === data.amount}
|
||||
content={amount}
|
||||
onClick={() => act('amount', {
|
||||
target: amount,
|
||||
})} />
|
||||
))
|
||||
)}>
|
||||
<Box mr={-1}>
|
||||
{data.chemicals.map(chemical => (
|
||||
<Button key={chemical.id}
|
||||
icon="tint"
|
||||
width="129.5px"
|
||||
lineHeight="21px"
|
||||
content={chemical.title}
|
||||
onClick={() => act('dispense', {
|
||||
reagent: chemical.id,
|
||||
})} />
|
||||
))}
|
||||
</Box>
|
||||
</Section>
|
||||
<Section
|
||||
title="Beaker"
|
||||
buttons={(
|
||||
beakerTransferAmounts.map(amount => (
|
||||
<Button key={amount}
|
||||
icon="minus"
|
||||
disabled={recording}
|
||||
content={amount}
|
||||
onClick={() => act('remove', { amount })} />
|
||||
))
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Beaker"
|
||||
buttons={!!data.isBeakerLoaded && (
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
disabled={!data.isBeakerLoaded}
|
||||
onClick={() => act('eject')} />
|
||||
)}>
|
||||
{recording
|
||||
&& 'Virtual beaker'
|
||||
|| data.isBeakerLoaded
|
||||
&& (
|
||||
<Fragment>
|
||||
<AnimatedNumber
|
||||
initial={0}
|
||||
value={data.beakerCurrentVolume} />
|
||||
/{data.beakerMaxVolume} units
|
||||
</Fragment>
|
||||
)
|
||||
|| 'No beaker'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Contents">
|
||||
<Box color="label">
|
||||
{(!data.isBeakerLoaded && !recording) && 'N/A'
|
||||
|| beakerContents.length === 0 && 'Nothing'}
|
||||
</Box>
|
||||
{beakerContents.map(chemical => (
|
||||
<Box
|
||||
key={chemical.name}
|
||||
color="label">
|
||||
<AnimatedNumber
|
||||
initial={0}
|
||||
value={chemical.volume} />
|
||||
{' '}
|
||||
units of {chemical.name}
|
||||
</Box>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
import { round, toFixed } from 'common/math';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, LabeledList, NumberInput, Section } from '../components';
|
||||
import { BeakerContents } from './common/BeakerContents';
|
||||
|
||||
export const ChemHeater = props => {
|
||||
const { act, data } = useBackend(props);
|
||||
const {
|
||||
targetTemp,
|
||||
isActive,
|
||||
isBeakerLoaded,
|
||||
currentTemp,
|
||||
beakerCurrentVolume,
|
||||
beakerMaxVolume,
|
||||
beakerContents = [],
|
||||
} = data;
|
||||
return (
|
||||
<Fragment>
|
||||
<Section
|
||||
title="Thermostat"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={isActive ? 'power-off' : 'times'}
|
||||
selected={isActive}
|
||||
content={isActive ? 'On' : 'Off'}
|
||||
onClick={() => act('power')} />
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Target">
|
||||
<NumberInput
|
||||
width="65px"
|
||||
unit="K"
|
||||
step={2}
|
||||
stepPixelSize={1}
|
||||
value={round(targetTemp)}
|
||||
minValue={0}
|
||||
maxValue={1000}
|
||||
onDrag={(e, value) => act('temperature', {
|
||||
target: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Reading">
|
||||
<Box
|
||||
width="60px"
|
||||
textAlign="right">
|
||||
{isBeakerLoaded && (
|
||||
<AnimatedNumber
|
||||
value={currentTemp}
|
||||
format={value => toFixed(value) + ' K'} />
|
||||
) || '—'}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="Beaker"
|
||||
buttons={!!isBeakerLoaded && (
|
||||
<Fragment>
|
||||
<Box inline color="label" mr={2}>
|
||||
{beakerCurrentVolume} / {beakerMaxVolume} units
|
||||
</Box>
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
onClick={() => act('eject')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<BeakerContents
|
||||
beakerLoaded={isBeakerLoaded}
|
||||
beakerContents={beakerContents} />
|
||||
</Section>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
@@ -1,367 +0,0 @@
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { act } from '../byond';
|
||||
import { AnimatedNumber, Box, Button, ColorBox, LabeledList, NumberInput, Section, Table } from '../components';
|
||||
|
||||
export const ChemMaster = props => {
|
||||
const { state } = props;
|
||||
const { config, data } = state;
|
||||
const { ref } = config;
|
||||
const {
|
||||
screen,
|
||||
beakerContents = [],
|
||||
bufferContents = [],
|
||||
beakerCurrentVolume,
|
||||
beakerMaxVolume,
|
||||
isBeakerLoaded,
|
||||
isPillBottleLoaded,
|
||||
pillBottleCurrentAmount,
|
||||
pillBottleMaxAmount,
|
||||
} = data;
|
||||
if (screen === 'analyze') {
|
||||
return <AnalysisResults state={state} />;
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<Section
|
||||
title="Beaker"
|
||||
buttons={!!data.isBeakerLoaded && (
|
||||
<Fragment>
|
||||
<Box inline color="label" mr={2}>
|
||||
<AnimatedNumber
|
||||
value={beakerCurrentVolume}
|
||||
initial={0} />
|
||||
{` / ${beakerMaxVolume} units`}
|
||||
</Box>
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
onClick={() => act(ref, 'eject')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
{!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}
|
||||
state={state}
|
||||
chemical={chemical}
|
||||
transferTo="buffer" />
|
||||
))}
|
||||
</ChemicalBuffer>
|
||||
</Section>
|
||||
<Section
|
||||
title="Buffer"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<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(ref, 'toggleMode')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
{bufferContents.length === 0 && (
|
||||
<Box color="label" mt="3px" mb="5px">
|
||||
Buffer is empty.
|
||||
</Box>
|
||||
)}
|
||||
<ChemicalBuffer>
|
||||
{bufferContents.map(chemical => (
|
||||
<ChemicalBufferEntry
|
||||
key={chemical.id}
|
||||
state={state}
|
||||
chemical={chemical}
|
||||
transferTo="beaker" />
|
||||
))}
|
||||
</ChemicalBuffer>
|
||||
</Section>
|
||||
<Section
|
||||
title="Packaging">
|
||||
<PackagingControls state={state} />
|
||||
</Section>
|
||||
{!!isPillBottleLoaded && (
|
||||
<Section
|
||||
title="Pill Bottle"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Box inline color="label" mr={2}>
|
||||
{pillBottleCurrentAmount} / {pillBottleMaxAmount} pills
|
||||
</Box>
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
onClick={() => act(ref, 'ejectPillBottle')} />
|
||||
</Fragment>
|
||||
)} />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ChemicalBuffer = Table;
|
||||
|
||||
const ChemicalBufferEntry = props => {
|
||||
const { state, chemical, transferTo } = props;
|
||||
const { ref } = state.config;
|
||||
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(ref, 'transfer', {
|
||||
id: chemical.id,
|
||||
amount: 1,
|
||||
to: transferTo,
|
||||
})} />
|
||||
<Button
|
||||
content="5"
|
||||
onClick={() => act(ref, 'transfer', {
|
||||
id: chemical.id,
|
||||
amount: 5,
|
||||
to: transferTo,
|
||||
})} />
|
||||
<Button
|
||||
content="10"
|
||||
onClick={() => act(ref, 'transfer', {
|
||||
id: chemical.id,
|
||||
amount: 10,
|
||||
to: transferTo,
|
||||
})} />
|
||||
<Button
|
||||
content="All"
|
||||
onClick={() => act(ref, 'transfer', {
|
||||
id: chemical.id,
|
||||
amount: 1000,
|
||||
to: transferTo,
|
||||
})} />
|
||||
<Button
|
||||
icon="ellipsis-h"
|
||||
title="Custom amount"
|
||||
onClick={() => act(ref, 'transfer', {
|
||||
id: chemical.id,
|
||||
amount: -1,
|
||||
to: transferTo,
|
||||
})} />
|
||||
<Button
|
||||
icon="question"
|
||||
title="Analyze"
|
||||
onClick={() => act(ref, '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={14}
|
||||
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"
|
||||
content={sideNote} />
|
||||
</LabeledList.Item>
|
||||
);
|
||||
};
|
||||
|
||||
class PackagingControls extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
pillAmount: 1,
|
||||
patchAmount: 1,
|
||||
bottleAmount: 1,
|
||||
packAmount: 1,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { state, props } = this;
|
||||
const { ref } = props.state.config;
|
||||
const {
|
||||
pillAmount,
|
||||
patchAmount,
|
||||
bottleAmount,
|
||||
packAmount,
|
||||
} = this.state;
|
||||
const {
|
||||
condi,
|
||||
chosenPillStyle,
|
||||
pillStyles = [],
|
||||
} = props.state.data;
|
||||
return (
|
||||
<LabeledList>
|
||||
{!condi && (
|
||||
<LabeledList.Item label="Pill type">
|
||||
{pillStyles.map(pill => (
|
||||
<Button
|
||||
key={pill.id}
|
||||
width={5}
|
||||
selected={pill.id === chosenPillStyle}
|
||||
textAlign="center"
|
||||
color="transparent"
|
||||
onClick={() => act(ref, '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) => this.setState({
|
||||
pillAmount: value,
|
||||
})}
|
||||
onCreate={() => act(ref, 'create', {
|
||||
type: 'pill',
|
||||
amount: pillAmount,
|
||||
volume: 'auto',
|
||||
})} />
|
||||
)}
|
||||
{!condi && (
|
||||
<PackagingControlsItem
|
||||
label="Patches"
|
||||
amount={patchAmount}
|
||||
amountUnit="patches"
|
||||
sideNote="max 40u"
|
||||
onChangeAmount={(e, value) => this.setState({
|
||||
patchAmount: value,
|
||||
})}
|
||||
onCreate={() => act(ref, 'create', {
|
||||
type: 'patch',
|
||||
amount: patchAmount,
|
||||
volume: 'auto',
|
||||
})} />
|
||||
)}
|
||||
{!condi && (
|
||||
<PackagingControlsItem
|
||||
label="Bottles"
|
||||
amount={bottleAmount}
|
||||
amountUnit="bottles"
|
||||
sideNote="max 30u"
|
||||
onChangeAmount={(e, value) => this.setState({
|
||||
bottleAmount: value,
|
||||
})}
|
||||
onCreate={() => act(ref, 'create', {
|
||||
type: 'bottle',
|
||||
amount: bottleAmount,
|
||||
volume: 'auto',
|
||||
})} />
|
||||
)}
|
||||
{!!condi && (
|
||||
<PackagingControlsItem
|
||||
label="Packs"
|
||||
amount={packAmount}
|
||||
amountUnit="packs"
|
||||
sideNote="max 10u"
|
||||
onChangeAmount={(e, value) => this.setState({
|
||||
packAmount: value,
|
||||
})}
|
||||
onCreate={() => act(ref, 'create', {
|
||||
type: 'condimentPack',
|
||||
amount: packAmount,
|
||||
volume: 'auto',
|
||||
})} />
|
||||
)}
|
||||
{!!condi && (
|
||||
<PackagingControlsItem
|
||||
label="Bottles"
|
||||
amount={bottleAmount}
|
||||
amountUnit="bottles"
|
||||
sideNote="max 50u"
|
||||
onChangeAmount={(e, value) => this.setState({
|
||||
bottleAmount: value,
|
||||
})}
|
||||
onCreate={() => act(ref, 'create', {
|
||||
type: 'condimentBottle',
|
||||
amount: bottleAmount,
|
||||
volume: 'auto',
|
||||
})} />
|
||||
)}
|
||||
</LabeledList>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AnalysisResults = props => {
|
||||
const { state } = props;
|
||||
const { ref } = state.config;
|
||||
const { analyzeVars } = state.data;
|
||||
return (
|
||||
<Section
|
||||
title="Analysis Results"
|
||||
buttons={(
|
||||
<Button
|
||||
icon="arrow-left"
|
||||
content="Back"
|
||||
onClick={() => act(ref, 'goScreen', {
|
||||
screen: 'home',
|
||||
})} />
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{analyzeVars.name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="State">
|
||||
{analyzeVars.state}
|
||||
</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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user