Merge pull request #3611 from nickofolas/improvement/work-in-progress-ui

UI: Refactor and redesign WorkInProgress interface
This commit is contained in:
hydroflame 2022-05-04 15:04:52 -04:00 committed by GitHub
commit bcbda22acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 636 additions and 598 deletions

@ -1,19 +1,8 @@
import { LinearProgress, Paper } from "@mui/material"; import { Paper } from "@mui/material";
import { Theme } from "@mui/material/styles";
import withStyles from "@mui/styles/withStyles";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { ProgressBar } from "../../ui/React/Progress";
const TimerProgress = withStyles((theme: Theme) => ({
root: {
backgroundColor: theme.palette.background.paper,
},
bar: {
transition: "none",
backgroundColor: theme.palette.primary.main,
},
}))(LinearProgress);
interface IProps { interface IProps {
millis: number; millis: number;
@ -43,10 +32,10 @@ export function GameTimer(props: IProps): React.ReactElement {
// TODO(hydroflame): there's like a bug where it triggers the end before the // TODO(hydroflame): there's like a bug where it triggers the end before the
// bar physically reaches the end // bar physically reaches the end
return props.noPaper ? ( return props.noPaper ? (
<TimerProgress variant="determinate" value={v} color="primary" /> <ProgressBar variant="determinate" value={v} color="primary" />
) : ( ) : (
<Paper sx={{ p: 1, mb: 1 }}> <Paper sx={{ p: 1, mb: 1 }}>
<TimerProgress variant="determinate" value={v} color="primary" /> <ProgressBar variant="determinate" value={v} color="primary" />
</Paper> </Paper>
); );
} }

@ -1,23 +1,19 @@
import { Box, Button, Paper, Tooltip, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { Box, Paper, Typography, Button, Tooltip } from "@mui/material";
import { CONSTANTS } from "../../../Constants"; import { CONSTANTS } from "../../../Constants";
import { Crimes } from "../../../Crime/Crimes"; import { Crimes } from "../../../Crime/Crimes";
import { numeralWrapper } from "../../../ui/numeralFormat";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { use } from "../../../ui/Context";
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum"; import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
import { use } from "../../../ui/Context";
import { numeralWrapper } from "../../../ui/numeralFormat";
import { ProgressBar } from "../../../ui/React/Progress";
import { Sleeve } from "../Sleeve"; import { Sleeve } from "../Sleeve";
import { SleeveTaskType } from "../SleeveTaskTypesEnum"; import { SleeveTaskType } from "../SleeveTaskTypesEnum";
import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { TravelModal } from "./TravelModal";
import { StatsElement, EarningsElement } from "./StatsElement";
import { MoreStatsModal } from "./MoreStatsModal";
import { MoreEarningsModal } from "./MoreEarningsModal"; import { MoreEarningsModal } from "./MoreEarningsModal";
import { MoreStatsModal } from "./MoreStatsModal";
import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { EarningsElement, StatsElement } from "./StatsElement";
import { TaskSelector } from "./TaskSelector"; import { TaskSelector } from "./TaskSelector";
import { TravelModal } from "./TravelModal";
interface IProps { interface IProps {
sleeve: Sleeve; sleeve: Sleeve;
@ -131,10 +127,9 @@ export function SleeveElem(props: IProps): React.ReactElement {
} }
return ( return (
<Box component={Paper} sx={{ width: "auto" }}> <>
<Box sx={{ m: 1 }}> <Paper sx={{ p: 1, display: "grid", gridTemplateColumns: "1fr 1fr", width: "auto", gap: 1 }}>
<Box display="grid" sx={{ gridTemplateColumns: "1fr 1fr", width: "100%", gap: 1 }}> <span>
<Box>
<StatsElement sleeve={props.sleeve} /> <StatsElement sleeve={props.sleeve} />
<Box display="grid" sx={{ gridTemplateColumns: "1fr 1fr", width: "100%" }}> <Box display="grid" sx={{ gridTemplateColumns: "1fr 1fr", width: "100%" }}>
<Button onClick={() => setStatsOpen(true)}>More Stats</Button> <Button onClick={() => setStatsOpen(true)}>More Stats</Button>
@ -151,9 +146,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
</span> </span>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
title={ title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""
}
> >
<span> <span>
<Button <Button
@ -166,24 +159,25 @@ export function SleeveElem(props: IProps): React.ReactElement {
</span> </span>
</Tooltip> </Tooltip>
</Box> </Box>
</Box> </span>
<Box> <span>
<EarningsElement sleeve={props.sleeve} /> <EarningsElement sleeve={props.sleeve} />
<Box>
<TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} /> <TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} />
<Button onClick={setTask} sx={{ width: "100%" }}> <Button onClick={setTask} sx={{ width: "100%" }}>
Set Task Set Task
</Button> </Button>
<Typography>{desc}</Typography> <Typography>{desc}</Typography>
<Typography> <Typography>
{props.sleeve.currentTask === SleeveTaskType.Crime && {props.sleeve.currentTask === SleeveTaskType.Crime && (
createProgressBarText({ <ProgressBar
progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime, variant="determinate"
totalTicks: 25, value={(props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime) * 100}
})} color="primary"
/>
)}
</Typography> </Typography>
</Box> </span>
</Box> </Paper>
<MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} /> <MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} />
<MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} /> <MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} />
<TravelModal <TravelModal
@ -197,8 +191,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
onClose={() => setAugmentationsOpen(false)} onClose={() => setAugmentationsOpen(false)}
sleeve={props.sleeve} sleeve={props.sleeve}
/> />
</Box> </>
</Box>
</Box>
); );
} }

13
src/ui/React/Progress.tsx Normal file

@ -0,0 +1,13 @@
import LinearProgress from "@mui/material/LinearProgress";
import { Theme } from "@mui/material/styles";
import withStyles from "@mui/styles/withStyles";
export const ProgressBar = withStyles((theme: Theme) => ({
root: {
backgroundColor: theme.palette.background.paper,
},
bar: {
transition: "none",
backgroundColor: theme.palette.primary.main,
},
}))(LinearProgress);

@ -16,13 +16,14 @@ interface IProps {
name: string; name: string;
color: string; color: string;
classes?: any; classes?: any;
data: ITableRowData; data?: ITableRowData;
children?: React.ReactElement; children?: React.ReactElement;
} }
export const StatsRow = ({ name, color, classes = useStyles(), children, data }: IProps): React.ReactElement => { export const StatsRow = ({ name, color, classes = useStyles(), children, data }: IProps): React.ReactElement => {
let content; let content = "";
if (data) {
if (data.content !== undefined) { if (data.content !== undefined) {
content = data.content; content = data.content;
} else if (data.level !== undefined && data.exp !== undefined) { } else if (data.level !== undefined && data.exp !== undefined) {
@ -30,6 +31,7 @@ export const StatsRow = ({ name, color, classes = useStyles(), children, data }:
} else if (data.level !== undefined && data.exp === undefined) { } else if (data.level !== undefined && data.exp === undefined) {
content = `${formatNumber(data.level, 0)}`; content = `${formatNumber(data.level, 0)}`;
} }
}
return ( return (
<TableRow> <TableRow>

@ -1,26 +1,46 @@
import React, { useState, useEffect } from "react"; import { Box, Container, Paper, Table, TableBody, Tooltip } from "@mui/material";
import { use } from "./Context"; import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { uniqueId } from "lodash";
import React, { useEffect, useState } from "react";
import { Companies } from "../Company/Companies";
import { Company } from "../Company/Company";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Factions } from "../Faction/Factions";
import { LocationName } from "../Locations/data/LocationNames";
import { Locations } from "../Locations/Locations";
import { Settings } from "../Settings/Settings";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { use } from "./Context";
import { numeralWrapper } from "./numeralFormat"; import { numeralWrapper } from "./numeralFormat";
import { Money } from "./React/Money";
import { MoneyRate } from "./React/MoneyRate";
import { ProgressBar } from "./React/Progress";
import { Reputation } from "./React/Reputation"; import { Reputation } from "./React/Reputation";
import { ReputationRate } from "./React/ReputationRate"; import { ReputationRate } from "./React/ReputationRate";
import { MoneyRate } from "./React/MoneyRate"; import { StatsRow } from "./React/StatsRow";
import { Money } from "./React/Money";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Factions } from "../Faction/Factions";
import { Company } from "../Company/Company";
import { Companies } from "../Company/Companies";
import { Locations } from "../Locations/Locations";
import { LocationName } from "../Locations/data/LocationNames";
import Typography from "@mui/material/Typography";
import Grid from "@mui/material/Grid";
import Button from "@mui/material/Button";
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
interface IWorkInfo {
buttons: {
cancel: () => void;
unfocus?: () => void;
};
title: string | React.ReactElement;
description?: string | React.ReactElement;
gains?: (string | React.ReactElement)[];
progress?: {
elapsed?: number;
remaining?: number;
percentage?: number;
};
stopText: string;
stopTooltip?: string | React.ReactElement;
}
export function WorkInProgressRoot(): React.ReactElement { export function WorkInProgressRoot(): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -35,18 +55,103 @@ export function WorkInProgressRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router(); const router = use.Router();
if (player.workType == CONSTANTS.WorkTypeFaction) { const expGains = [
player.workHackExpGained > 0 ? (
<StatsRow
name="Hacking Exp"
color={Settings.theme.hack}
data={{
content: `${numeralWrapper.formatExp(player.workHackExpGained)} (${numeralWrapper.formatExp(
player.workHackExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
player.workStrExpGained > 0 ? (
<StatsRow
name="Strength Exp"
color={Settings.theme.combat}
data={{
content: `${numeralWrapper.formatExp(player.workStrExpGained)} (${numeralWrapper.formatExp(
player.workStrExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
player.workDefExpGained > 0 ? (
<StatsRow
name="Defense Exp"
color={Settings.theme.combat}
data={{
content: `${numeralWrapper.formatExp(player.workDefExpGained)} (${numeralWrapper.formatExp(
player.workDefExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
player.workDexExpGained > 0 ? (
<StatsRow
name="Dexterity Exp"
color={Settings.theme.combat}
data={{
content: `${numeralWrapper.formatExp(player.workDexExpGained)} (${numeralWrapper.formatExp(
player.workDexExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
player.workAgiExpGained > 0 ? (
<StatsRow
name="Agility Exp"
color={Settings.theme.combat}
data={{
content: `${numeralWrapper.formatExp(player.workAgiExpGained)} (${numeralWrapper.formatExp(
player.workAgiExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
player.workChaExpGained > 0 ? (
<StatsRow
name="Charisma Exp"
color={Settings.theme.cha}
data={{
content: `${numeralWrapper.formatExp(player.workChaExpGained)} (${numeralWrapper.formatExp(
player.workChaExpGainRate * CYCLES_PER_SEC,
)} / sec)`,
}}
/>
) : (
<></>
),
];
let workInfo: IWorkInfo | null;
switch (player.workType) {
case CONSTANTS.WorkTypeFaction: {
const faction = Factions[player.currentWorkFactionName]; const faction = Factions[player.currentWorkFactionName];
if (!faction) { if (!faction) {
return ( workInfo = {
<> buttons: {
<Typography variant="h4" color="primary"> cancel: () => router.toFactions(),
You have not joined {player.currentWorkFactionName || "(Faction not found)"} yet or cannot work at this },
time, please try again if you think this should have worked title:
</Typography> `You have not joined ${player.currentWorkFactionName || "(Faction not found)"} at this time,` +
<Button onClick={() => router.toFactions()}>Back to Factions</Button> " please try again if you think this should have worked",
</>
); stopText: "Back to Factions",
};
} }
function cancel(): void { function cancel(): void {
@ -57,81 +162,54 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toFaction(faction); router.toFaction(faction);
player.stopFocusing(); player.stopFocusing();
} }
return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> workInfo = {
<Grid item> buttons: {
cancel: cancel,
unfocus: unfocus,
},
title: (
<>
You are currently {player.currentWorkFactionDescription} for your faction <b>{faction.name}</b>
</>
),
description: (
<>
Current Faction Reputation: <Reputation reputation={faction.playerReputation} />
</>
),
gains: [
player.workMoneyGained > 0 ? (
<StatsRow name="Money" color={Settings.theme.money}>
<Typography> <Typography>
You are currently {player.currentWorkFactionDescription} for your faction {faction.name} <Money money={player.workMoneyGained} /> (
<br /> <MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />)
(Current Faction Reputation: <Reputation reputation={faction.playerReputation} />
). <br />
You have been doing this for {convertTimeMsToTimeElapsedString(player.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
<Reputation reputation={player.workRepGained} /> (
<ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />) reputation for this faction <br />
<br />
{player.workHackExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workHackExpGained)} (
{numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br />
</>
)}
<br />
{player.workStrExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workStrExpGained)} (
{numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
</>
)}
{player.workDefExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDefExpGained)} (
{numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
</>
)}
{player.workDexExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDexExpGained)} (
{numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
</>
)}
{player.workAgiExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workAgiExpGained)} (
{numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br />
</>
)}
<br />
{player.workChaExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workChaExpGained)} (
{numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br />
</>
)}
<br />
You will automatically finish after working for 20 hours. You can cancel earlier if you wish.
<br />
There is no penalty for cancelling earlier.
</Typography> </Typography>
</Grid> </StatsRow>
<Grid item> ) : (
<Button sx={{ mx: 2 }} onClick={cancel}> <></>
Stop Faction Work ),
</Button> <StatsRow name="Faction Reputation" color={Settings.theme.rep}>
<Button onClick={unfocus}>Do something else simultaneously</Button> <Typography>
</Grid> <Reputation reputation={player.workRepGained} /> (
</Grid> <ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />)
); </Typography>
</StatsRow>,
...expGains,
],
progress: {
elapsed: player.timeWorked,
},
stopText: "Stop Faction work",
};
break;
} }
case CONSTANTS.WorkTypeStudyClass: {
const className = player.className; const className = player.className;
if (player.className !== "") {
function cancel(): void { function cancel(): void {
player.finishClass(true); player.finishClass(true);
router.toCity(); router.toCity();
@ -144,89 +222,59 @@ export function WorkInProgressRoot(): React.ReactElement {
let stopText = ""; let stopText = "";
if ( if (
className == CONSTANTS.ClassGymStrength || className === CONSTANTS.ClassGymStrength ||
className == CONSTANTS.ClassGymDefense || className === CONSTANTS.ClassGymDefense ||
className == CONSTANTS.ClassGymDexterity || className === CONSTANTS.ClassGymDexterity ||
className == CONSTANTS.ClassGymAgility className === CONSTANTS.ClassGymAgility
) { ) {
stopText = "Stop training at gym"; stopText = "Stop training at gym";
} else { } else {
stopText = "Stop taking course"; stopText = "Stop taking course";
} }
return ( workInfo = {
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> buttons: {
<Grid item> cancel: cancel,
unfocus: unfocus,
},
title: (
<>
You are currently <b>{className}</b>
</>
),
gains: [
<StatsRow name="Total Cost" color={Settings.theme.money}>
<Typography> <Typography>
You have been {className} for {convertTimeMsToTimeElapsedString(player.timeWorked)} <Money money={-player.workMoneyGained} /> (<MoneyRate money={player.workMoneyLossRate * CYCLES_PER_SEC} />
<br /> )
<br />
This has cost you: <br />
<Money money={-player.workMoneyGained} /> (<MoneyRate money={player.workMoneyLossRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
You have gained: <br />
{player.workHackExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workHackExpGained)} (
{numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br />
</>
)}
{player.workStrExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workStrExpGained)} (
{numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
</>
)}
{player.workDefExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDefExpGained)} (
{numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
</>
)}
{player.workDexExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDexExpGained)} (
{numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
</>
)}
{player.workAgiExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workAgiExpGained)} (
{numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br />
</>
)}
{player.workChaExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workChaExpGained)} (
{numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br />
</>
)}
You may cancel at any time
</Typography> </Typography>
</Grid> </StatsRow>,
<Grid item> ...expGains,
<Button sx={{ mx: 2 }} onClick={cancel}> ],
{stopText} progress: {
</Button> elapsed: player.timeWorked,
<Button onClick={unfocus}>Do something else simultaneously</Button> },
</Grid>
</Grid> stopText: stopText,
); };
break;
} }
if (player.workType == CONSTANTS.WorkTypeCompany) { case CONSTANTS.WorkTypeCompany: {
const comp = Companies[player.companyName]; const comp = Companies[player.companyName];
if (comp == null || !(comp instanceof Company)) { if (comp == null || !(comp instanceof Company)) {
return ( workInfo = {
<> buttons: {
<Typography variant="h4" color="primary"> cancel: () => router.toTerminal(),
You cannot work for {player.companyName || "(Company not found)"} at this time, please try again if you },
think this should have worked title:
</Typography> `You cannot work for ${player.companyName || "(Company not found)"} at this time,` +
<Button onClick={() => router.toTerminal()}>Back to Terminal</Button> " please try again if you think this should have worked",
</>
); stopText: "Back to Terminal",
};
} }
const companyRep = comp.playerReputation; const companyRep = comp.playerReputation;
@ -245,84 +293,51 @@ export function WorkInProgressRoot(): React.ReactElement {
const penalty = player.cancelationPenalty(); const penalty = player.cancelationPenalty();
const penaltyString = penalty === 0.5 ? "half" : "three-quarters"; const penaltyString = penalty === 0.5 ? "half" : "three-quarters";
return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> workInfo = {
<Grid item> buttons: {
cancel: cancel,
unfocus: unfocus,
},
title: (
<>
You are currently working as a <b>{position}</b> at <b>{player.companyName}</b>
</>
),
description: (
<>
Current Company Reputation: <Reputation reputation={companyRep} />
</>
),
gains: [
<StatsRow name="Money" color={Settings.theme.money}>
<Typography> <Typography>
You are currently working as a {position} at {player.companyName} (Current Company Reputation:{" "} <Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />)
<Reputation reputation={companyRep} />)<br />
<br />
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
<Reputation reputation={player.workRepGained} /> (
<ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />) reputation for this company <br />
<br />
{player.workHackExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workHackExpGained)} (
{`${numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
) hacking exp <br />
</>
)}
<br />
{player.workStrExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workStrExpGained)} (
{`${numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
) strength exp <br />
</>
)}
{player.workDefExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDefExpGained)} (
{`${numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
) defense exp <br />
</>
)}
{player.workDexExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDexExpGained)} (
{`${numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
) dexterity exp <br />
</>
)}
{player.workAgiExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workAgiExpGained)} (
{`${numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
) agility exp <br />
</>
)}
<br />
{player.workChaExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workChaExpGained)} (
{`${numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
) charisma exp <br />
</>
)}
<br />
You will automatically finish after working for 8 hours. You can cancel earlier if you wish, but you will
only gain {penaltyString} of the reputation you've earned so far.
</Typography> </Typography>
</Grid> </StatsRow>,
<Grid item> <StatsRow name="Company Reputation" color={Settings.theme.rep}>
<Button sx={{ mx: 2 }} onClick={cancel}> <Typography>
Stop Working <Reputation reputation={player.workRepGained} /> (
</Button> <ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />)
<Button onClick={unfocus}>Do something else simultaneously</Button> </Typography>
</Grid> </StatsRow>,
</Grid> ...expGains,
); ],
progress: {
elapsed: player.timeWorked,
},
stopText: "Stop working",
stopTooltip:
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish" +
` but you will only gain ${penaltyString} of the reputation you've earned so far.`,
};
break;
} }
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) { case CONSTANTS.WorkTypeCompanyPartTime: {
function cancel(): void { function cancel(): void {
player.finishWorkPartTime(true); player.finishWorkPartTime(true);
router.toJob(); router.toJob();
@ -339,126 +354,74 @@ export function WorkInProgressRoot(): React.ReactElement {
companyRep = comp.playerReputation; companyRep = comp.playerReputation;
const position = player.jobs[player.companyName]; const position = player.jobs[player.companyName];
return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> workInfo = {
<Grid item> buttons: {
cancel: cancel,
unfocus: unfocus,
},
title: (
<>
You are currently working as a <b>{position}</b> at <b>{player.companyName}</b>
</>
),
description: (
<>
Current Company Reputation: <Reputation reputation={companyRep} />
</>
),
gains: [
<StatsRow name="Money" color={Settings.theme.money}>
<Typography>
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />)
</Typography>
</StatsRow>,
<StatsRow name="Company Reputation" color={Settings.theme.rep}>
<Typography> <Typography>
You are currently working as a {position} at {player.companyName} (Current Company Reputation:{" "}
<Reputation reputation={companyRep} />)<br />
<br />
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
<Reputation reputation={player.workRepGained} /> ( <Reputation reputation={player.workRepGained} /> (
<ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} /> <ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />)
) reputation for this company <br />
<br />
{player.workHackExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workHackExpGained)} (
{`${numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
) hacking exp <br />
</>
)}
<br />
{player.workStrExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workStrExpGained)} (
{`${numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
) strength exp <br />
</>
)}
{player.workDefExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDefExpGained)} (
{`${numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
) defense exp <br />
</>
)}
{player.workDexExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workDexExpGained)} (
{`${numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
) dexterity exp <br />
</>
)}
{player.workAgiExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workAgiExpGained)} (
{`${numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
) agility exp <br />
</>
)}
<br />
{player.workChaExpGained > 0 && (
<>
{numeralWrapper.formatExp(player.workChaExpGained)} (
{`${numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
) charisma exp <br />
</>
)}
<br />
You will automatically finish after working for 8 hours. You can cancel earlier if you wish, and there will
be no penalty because this is a part-time job.
</Typography> </Typography>
</Grid> </StatsRow>,
<Grid item> ...expGains,
<Button sx={{ mx: 2 }} onClick={cancel}> ],
Stop Working progress: {
</Button> elapsed: player.timeWorked,
<Button onClick={unfocus}>Do something else simultaneously</Button> },
</Grid>
</Grid> stopText: "Stop working",
); stopTooltip:
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish" +
" and there will be no penalty because this is a part-time job.",
};
break;
} }
if (player.crimeType !== "") { case CONSTANTS.WorkTypeCrime: {
const percent = Math.round((player.timeWorked / player.timeNeededToCompleteWork) * 100); const completion = Math.round((player.timeWorked / player.timeNeededToCompleteWork) * 100);
let numBars = Math.round(percent / 5);
if (numBars < 0) {
numBars = 0;
}
if (numBars > 20) {
numBars = 20;
}
// const progressBar = "[" + Array(numBars + 1).join("|") + Array(20 - numBars + 1).join(" ") + "]";
const progressBar = createProgressBarText({ progress: (numBars + 1) / 20, totalTicks: 20 });
return ( workInfo = {
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> buttons: {
<Grid item> cancel: () => {
<Typography>
<Typography>You are attempting to {player.crimeType}.</Typography>
<br />
<Typography>
Time remaining: {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorked)}
</Typography>
<br />
<pre>{progressBar}</pre>
</Typography>
</Grid>
<Grid item>
<Button
onClick={() => {
router.toLocation(Locations[LocationName.Slums]); router.toLocation(Locations[LocationName.Slums]);
player.finishCrime(true); player.finishCrime(true);
}} },
> },
Cancel crime title: `You are attempting to ${player.crimeType}`,
</Button>
</Grid> progress: {
</Grid> remaining: player.timeNeededToCompleteWork - player.timeWorked,
); percentage: completion,
},
stopText: "Cancel crime",
};
break;
} }
if (player.createProgramName !== "") { case CONSTANTS.WorkTypeCreateProgram: {
function cancel(): void { function cancel(): void {
player.finishCreateProgramWork(true); player.finishCreateProgramWork(true);
router.toTerminal(); router.toTerminal();
@ -467,31 +430,33 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toTerminal(); router.toTerminal();
player.stopFocusing(); player.stopFocusing();
} }
return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> const completion = (player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100;
<Grid item>
<Typography> workInfo = {
You are currently working on coding {player.createProgramName}.<br /> buttons: {
<br /> cancel: cancel,
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)} unfocus: unfocus,
<br /> },
<br /> title: (
The program is {((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)} <>
% complete. <br /> You are currently working on coding <b>{player.createProgramName}</b>
If you cancel, your work will be saved and you can come back to complete the program later. </>
</Typography> ),
</Grid>
<Grid item> progress: {
<Button sx={{ mx: 2 }} onClick={cancel}> elapsed: player.timeWorked,
Cancel work on creating program percentage: completion,
</Button> },
<Button onClick={unfocus}>Do something else simultaneously</Button>
</Grid> stopText: "Stop creating program",
</Grid> stopTooltip: "Your work will be saved and you can return to complete the program later.",
); };
break;
} }
if (player.graftAugmentationName !== "") { case CONSTANTS.WorkTypeGraftAugmentation: {
function cancel(): void { function cancel(): void {
player.finishGraftAugmentationWork(true); player.finishGraftAugmentationWork(true);
router.toTerminal(); router.toTerminal();
@ -500,34 +465,111 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toTerminal(); router.toTerminal();
player.stopFocusing(); player.stopFocusing();
} }
return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> const completion = (player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100;
<Grid item>
<Typography> workInfo = {
You are currently working on grafting {player.graftAugmentationName}. buttons: {
<br /> cancel: cancel,
<br /> unfocus: unfocus,
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)} },
<br /> title: (
<br /> <>
The augmentation is{" "} You are currently working on grafting <b>{player.graftAugmentationName}</b>
{((player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)}% done being </>
crafted. ),
<br />
If you cancel, your work will <b>not</b> be saved, and the money you spent will <b>not</b> be returned. progress: {
</Typography> elapsed: player.timeWorked,
</Grid> percentage: completion,
<Grid item> },
<Button sx={{ mx: 2 }} onClick={cancel}>
Cancel work on grafting Augmentation stopText: "Stop grafting",
</Button> stopTooltip: (
<Button onClick={unfocus}>Do something else simultaneously</Button> <>
</Grid> If you cancel, your work will <b>not</b> be saved, and the money you spent will <b>not</b> be returned
</Grid> </>
); ),
};
break;
} }
if (!player.workType) router.toTerminal(); default:
router.toTerminal();
workInfo = null;
}
if (workInfo === null) {
return <></>; return <></>;
}
const tooltipInfo =
typeof workInfo?.stopTooltip === "string" ? (
<Typography>{workInfo.stopTooltip}</Typography>
) : (
workInfo.stopTooltip || <></>
);
return (
<Container
maxWidth="md"
sx={{ display: "flex", flexDirection: "column", justifyContent: "center", height: "calc(100vh - 16px)" }}
>
<Paper sx={{ p: 1, mb: 1 }}>
<Typography variant="h6">{workInfo.title}</Typography>
<Typography>{workInfo.description}</Typography>
{workInfo.gains && (
<Table sx={{ mt: 1 }}>
<TableBody>
{workInfo.gains.map((row) => (
<React.Fragment key={uniqueId()}>{row}</React.Fragment>
))}
</TableBody>
</Table>
)}
</Paper>
<Paper sx={{ mb: 1, p: 1 }}>
{workInfo.progress !== undefined && (
<Box sx={{ mb: 1 }}>
<Box
display="grid"
sx={{
gridTemplateColumns: `repeat(${Object.keys(workInfo.progress).length}, 1fr)`,
width: "100%",
justifyItems: "center",
textAlign: "center",
}}
>
{workInfo.progress.elapsed !== undefined && (
<Typography>{convertTimeMsToTimeElapsedString(workInfo.progress.elapsed)} elapsed</Typography>
)}
{workInfo.progress.remaining !== undefined && (
<Typography>{convertTimeMsToTimeElapsedString(workInfo.progress.remaining)} remaining</Typography>
)}
{workInfo.progress.percentage !== undefined && (
<Typography>{workInfo.progress.percentage.toFixed(2)}% done</Typography>
)}
</Box>
{workInfo.progress.percentage !== undefined && (
<ProgressBar variant="determinate" value={workInfo.progress.percentage} color="primary" />
)}
</Box>
)}
<Box display="grid" sx={{ gridTemplateColumns: `repeat(${Object.keys(workInfo.buttons).length}, 1fr)` }}>
{workInfo.stopTooltip ? (
<Tooltip title={tooltipInfo}>
<Button onClick={workInfo.buttons.cancel}>{workInfo.stopText}</Button>
</Tooltip>
) : (
<Button onClick={workInfo.buttons.cancel}>{workInfo.stopText}</Button>
)}
{workInfo.buttons.unfocus && (
<Button onClick={workInfo.buttons.unfocus}>Do something else simultaneously</Button>
)}
</Box>
</Paper>
</Container>
);
} }