more work

This commit is contained in:
Olivier Gagnon
2021-09-17 21:30:02 -04:00
parent 45f2f85a30
commit 907314e76b
33 changed files with 234 additions and 325 deletions

View File

@ -1,128 +0,0 @@
@import "theme";
.active-scripts-list {
list-style-type: none;
}
.active-scripts-container {
> p {
margin: 6px;
padding: 4px;
}
.accordion-header {
> pre {
color: white;
}
}
}
.active-scripts-server-header {
background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none;
&:after {
content: "\02795"; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&.active,
&:hover {
background-color: #555;
}
}
.active-scripts-server-header.active {
&:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&:hover {
background-color: #666;
}
}
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none;
div,
ul,
ul > li {
background-color: #555;
}
}
.active-scripts-script-header {
background-color: #555;
border: none;
color: var(--my-font-color);
cursor: pointer;
display: block;
outline: none;
padding: 4px 25px 4px 10px;
position: relative;
text-align: left;
width: auto;
&:after {
content: "\02795"; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
&:hover,
&.active:hover {
background-color: #666;
}
&.active {
background-color: #555;
}
}
.active-scripts-script-panel {
background-color: #555;
display: none;
font-size: 14px;
margin-bottom: 6px;
padding: 0 18px;
width: auto;
pre,
h2,
ul,
li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}

View File

@ -4,7 +4,6 @@ import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import React from "react";
import { TTheme as Theme } from "./ui/React/Theme";
import { General } from "./DevMenu/ui/General";
import { Stats } from "./DevMenu/ui/Stats";
@ -30,7 +29,6 @@ interface IProps {
export function DevMenuRoot(props: IProps): React.ReactElement {
return (
<Theme>
<>
<h1>Development Menu - Only meant to be used for testing/debugging</h1>
<General player={props.player} router={props.router} />
@ -56,6 +54,5 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
<TimeSkip player={props.player} engine={props.engine} />
</>
</Theme>
);
}

View File

@ -40,7 +40,7 @@ export function Augmentations(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Augmentations</h2>
</AccordionSummary>

View File

@ -55,7 +55,7 @@ export function Bladeburner(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Bladeburner</h2>
</AccordionSummary>

View File

@ -25,7 +25,7 @@ export function CodingContracts(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Coding Contracts</h2>
</AccordionSummary>

View File

@ -69,7 +69,7 @@ export function Companies(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Companies</h2>
</AccordionSummary>

View File

@ -67,7 +67,7 @@ export function Corporation(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Corporation</h2>
</AccordionSummary>

View File

@ -97,7 +97,7 @@ export function Factions(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Factions</h2>
</AccordionSummary>

View File

@ -36,7 +36,7 @@ export function Gang(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Gang</h2>
</AccordionSummary>

View File

@ -43,7 +43,7 @@ export function General(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>General</h2>
</AccordionSummary>

View File

@ -35,7 +35,7 @@ export function Programs(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Programs</h2>
</AccordionSummary>

View File

@ -75,7 +75,7 @@ export function Servers(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Servers</h2>
</AccordionSummary>

View File

@ -38,7 +38,7 @@ export function Sleeves(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Sleeves</h2>
</AccordionSummary>

View File

@ -51,7 +51,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Source-Files</h2>
</AccordionSummary>

View File

@ -136,7 +136,7 @@ export function Stats(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Experience / Stats</h2>
</AccordionSummary>

View File

@ -80,7 +80,7 @@ export function StockMarket(): React.ReactElement {
);
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Stock Market</h2>
</AccordionSummary>

View File

@ -28,7 +28,7 @@ export function TimeSkip(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Time skip</h2>
</AccordionSummary>

View File

@ -55,7 +55,9 @@ export function GangStats(props: IProps): React.ReactElement {
</p>
<br />
<div>
<p style={{ display: "inline-block" }}>Money gain rate: {MoneyRate(5 * props.gang.moneyGainRate)}</p>
<p style={{ display: "inline-block" }}>
Money gain rate: <MoneyRate money={5 * props.gang.moneyGainRate} />
</p>
</div>
<br />
<p className="tooltip" style={{ display: "inline-block" }}>

View File

@ -28,7 +28,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
const tasks = props.gang.getAllTaskNames();
const data = [
[`Money:`, MoneyRate(5 * props.member.calculateMoneyGain(props.gang))],
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(props.gang)} />],
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(props.gang))} / sec`],
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(props.gang))} / sec`],
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],

View File

@ -144,7 +144,8 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
<Money money={node.totalMoneyGenerated} player={props.player} /> ({MoneyRate(node.moneyGainRatePerSecond)})
<Money money={node.totalMoneyGenerated} player={props.player} /> (
<MoneyRate money={node.moneyGainRatePerSecond} />)
</span>
</div>
<div className={"row"}>

View File

@ -25,7 +25,7 @@ export function PlayerInfo(props: IProps): React.ReactElement {
if (hasServers) {
prod = HashRate(props.totalProduction);
} else {
prod = MoneyRate(props.totalProduction);
prod = <MoneyRate money={props.totalProduction} />;
}
return (

View File

@ -175,7 +175,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
// });
} else {
data = [
[`Money:`, MoneyRate(5 * props.sleeve.gainRatesForTask.money)],
[`Money:`, <MoneyRate money={5 * props.sleeve.gainRatesForTask.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / s`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / s`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / s`],

View File

@ -8,7 +8,6 @@ import "../css/mainmenu.scss";
import "../css/characteroverview.scss";
import "../css/terminal.scss";
import "../css/scripteditor.scss";
import "../css/activescripts.scss";
import "../css/hacknetnodes.scss";
import "../css/menupages.scss";
import "../css/augmentations.scss";

View File

@ -8,10 +8,10 @@ import { ScriptProduction } from "./ScriptProduction";
import { ServerAccordions } from "./ServerAccordions";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography";
type IProps = {
p: IPlayer;
workerScripts: Map<number, WorkerScript>;
};
@ -27,15 +27,15 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
}, []);
return (
<div className="active-scripts-container">
<p>
<>
<Typography>
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the servers
on which they are running.
</p>
</Typography>
<ScriptProduction {...props} />
<ServerAccordions {...props} />
</div>
</>
);
}

View File

@ -5,16 +5,44 @@
import * as React from "react";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../React/Money";
import { MoneyRate } from "../React/MoneyRate";
import { use } from "../Context";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
type IProps = {
p: IPlayer;
workerScripts: Map<number, WorkerScript>;
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
cell: {
borderBottom: "none",
padding: theme.spacing(1),
margin: theme.spacing(1),
whiteSpace: "nowrap",
},
size: {
width: "1px",
},
}),
);
export function ScriptProduction(props: IProps): React.ReactElement {
const prodRateSinceLastAug = props.p.scriptProdSinceLastAug / (props.p.playtimeSinceLastAug / 1000);
const player = use.Player();
const classes = useStyles();
const prodRateSinceLastAug = player.scriptProdSinceLastAug / (player.playtimeSinceLastAug / 1000);
let onlineProduction = 0;
for (const ws of props.workerScripts.values()) {
@ -22,27 +50,29 @@ export function ScriptProduction(props: IProps): React.ReactElement {
}
return (
<p id="active-scripts-total-prod">
Total online production of Active scripts:&nbsp;
<span className="money-gold">
<span id="active-scripts-total-production-active">
<Money money={onlineProduction} />
</span>{" "}
/ sec
</span>
<br />
Total online production since last Aug installation:&nbsp;
<span id="active-scripts-total-prod-aug-total" className="money-gold">
<Money money={props.p.scriptProdSinceLastAug} />
</span>
&nbsp;(
<span className="money-gold">
<span id="active-scripts-total-prod-aug-avg" className="money-gold">
<Money money={prodRateSinceLastAug} />
</span>{" "}
/ sec
</span>
)
</p>
<Table size="small" classes={{ root: classes.size }}>
<TableBody>
<TableRow>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography variant="body2">Total online production of Active scripts:</Typography>
</TableCell>
<TableCell align="left" classes={{ root: classes.cell }}>
<Typography variant="body2">
<Money money={player.scriptProdSinceLastAug} />
</Typography>
</TableCell>
</TableRow>
<TableRow style={{ width: "1px" }}>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography variant="body2">Total online production since last Aug installation:</Typography>
</TableCell>
<TableCell align="left" classes={{ root: classes.cell }}>
<Typography variant="body2">
(<MoneyRate money={prodRateSinceLastAug} />)
</Typography>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}

View File

@ -4,7 +4,11 @@
*/
import * as React from "react";
import { BBAccordion } from "../React/BBAccordion";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { ServerAccordionContent } from "./ServerAccordionContent";
import { BaseServer } from "../../Server/BaseServer";
@ -34,9 +38,15 @@ export function ServerAccordion(props: IProps): React.ReactElement {
const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
return (
<BBAccordion
headerContent={<pre>{headerTxt}</pre>}
panelContent={<ServerAccordionContent workerScripts={props.workerScripts} />}
/>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography style={{ whiteSpace: "pre-wrap" }} color="primary">
{headerTxt}
</Typography>
</AccordionSummary>
<AccordionDetails>
<ServerAccordionContent workerScripts={props.workerScripts} />
</AccordionDetails>
</Accordion>
);
}

View File

@ -14,11 +14,13 @@ export function ServerAccordionContent(props: IProps): React.ReactElement {
return <ServerAccordionContentPaginated workerScripts={props.workerScripts} />;
}
const scripts = props.workerScripts.map((ws) => {
return (
<ul>
{props.workerScripts.map((ws) => {
return <WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />;
});
return <ul>{scripts}</ul>;
})}
</ul>
);
}
export function ServerAccordionContentPaginated(props: IProps): React.ReactElement {

View File

@ -2,7 +2,7 @@
* React Component for rendering the Accordion elements for all servers
* on which scripts are running
*/
import * as React from "react";
import React, { useState, useEffect } from "react";
import { ServerAccordion } from "./ServerAccordion";
@ -18,7 +18,7 @@ interface IServerData {
}
interface IServerToScriptsMap {
[key: string]: IServerData;
[key: string]: IServerData | undefined;
}
type IProps = {
@ -31,72 +31,47 @@ type IState = {
const subscriberId = "ActiveScriptsUI";
export class ServerAccordions extends React.Component<IProps, IState> {
serverToScriptMap: IServerToScriptsMap = {};
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
};
this.updateServerToScriptsMap();
this.rerender = this.rerender.bind(this);
export function ServerAccordions(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
componentDidMount(): void {
useEffect(() => {
WorkerScriptStartStopEventEmitter.addSubscriber({
cb: this.rerender,
cb: rerender,
id: subscriberId,
});
}
return () => WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
}, []);
componentWillUnmount(): void {
WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
}
updateServerToScriptsMap(): void {
const map: IServerToScriptsMap = {};
for (const ws of this.props.workerScripts.values()) {
const serverToScriptMap: IServerToScriptsMap = {};
for (const ws of props.workerScripts.values()) {
const server = getServer(ws.serverIp);
if (server == null) {
console.warn(`WorkerScript has invalid IP address: ${ws.serverIp}`);
continue;
}
if (map[server.hostname] == null) {
map[server.hostname] = {
let data = serverToScriptMap[server.hostname];
if (data === undefined) {
serverToScriptMap[server.hostname] = {
server: server,
workerScripts: [],
};
data = serverToScriptMap[server.hostname];
}
map[server.hostname].workerScripts.push(ws);
if (data !== undefined) data.workerScripts.push(ws);
}
this.serverToScriptMap = map;
}
rerender(): void {
this.updateServerToScriptsMap();
this.setState((prevState) => {
return { rerenderFlag: !prevState.rerenderFlag };
});
}
render(): React.ReactNode {
const elems = Object.keys(this.serverToScriptMap).map((serverName) => {
const data = this.serverToScriptMap[serverName];
return <ServerAccordion key={serverName} server={data.server} workerScripts={data.workerScripts} />;
});
return (
<ul className="active-scripts-list" id="active-scripts-list">
{elems}
{Object.values(serverToScriptMap).map((data) => {
return (
data && <ServerAccordion key={data.server.hostname} server={data.server} workerScripts={data.workerScripts} />
);
})}
</ul>
);
}
}

View File

@ -6,8 +6,15 @@ import * as React from "react";
import { numeralWrapper } from "../numeralFormat";
import { BBAccordion } from "../React/BBAccordion";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { AccordionButton } from "../React/AccordionButton";
import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScript } from "../../Netscript/WorkerScript";
@ -41,12 +48,11 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const offlineEps = scriptRef.offlineExpGained / scriptRef.offlineRunningTime;
return (
<BBAccordion
headerClass="active-scripts-script-header"
headerContent={<>{props.workerScript.name}</>}
panelClass="active-scripts-script-panel"
panelContent={
<>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography color="primary">{props.workerScript.name}</Typography>
</AccordionSummary>
<AccordionDetails>
<pre>Threads: {numeralWrapper.formatThreads(props.workerScript.scriptRef.threads)}</pre>
<pre>Args: {arrayToString(props.workerScript.args)}</pre>
<pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
@ -68,10 +74,13 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
</pre>
<pre>{Array(26).join(" ") + numeralWrapper.formatExp(offlineEps) + " hacking exp / second"}</pre>
<AccordionButton onClick={logClickHandler} text="Log" />
<AccordionButton onClick={killScriptClickHandler} text="Kill Script" />
</>
}
/>
<Button onClick={logClickHandler}>
<Typography>Log</Typography>
</Button>
<IconButton onClick={killScriptClickHandler}>
<DeleteIcon color="error" />
</IconButton>
</AccordionDetails>
</Accordion>
);
}

View File

@ -278,7 +278,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
) : page === Page.CreateScript ? (
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
) : page === Page.ActiveScripts ? (
<ActiveScriptsRoot p={player} workerScripts={workerScripts} />
<ActiveScriptsRoot workerScripts={workerScripts} />
) : page === Page.Hacknet ? (
<HacknetRoot player={player} />
) : page === Page.CreateProgram ? (

View File

@ -2,6 +2,6 @@ import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
export function MoneyRate(money: number): JSX.Element {
export function MoneyRate({ money }: { money: number }): JSX.Element {
return <Money money={`${numeralWrapper.formatMoney(money)} / sec`} />;
}

View File

@ -19,6 +19,10 @@ export const colors = {
secondary: "#888",
secondarydark: "#666",
warninglight: "#ff0",
warning: "#cc0",
warningdark: "#990",
welllight: "#444",
well: "#222",
white: "#fff",
@ -49,6 +53,11 @@ export const theme = createTheme({
main: colors.error,
dark: colors.errordark,
},
warning: {
light: colors.warninglight,
main: colors.warning,
dark: colors.warningdark,
},
background: {
paper: colors.well,
},
@ -66,7 +75,6 @@ export const theme = createTheme({
backgroundColor: colors.well,
},
input: {
color: colors.primary,
"&::placeholder": {
userSelect: "none",
color: colors.primarydark,
@ -106,22 +114,22 @@ export const theme = createTheme({
},
},
},
MuiButton: {
styleOverrides: {
root: {
backgroundColor: "#333",
border: "1px solid " + colors.well,
// color: colors.primary,
margin: "5px",
padding: "3px 5px",
"&:hover": {
backgroundColor: colors.black,
},
// MuiButton: {
// styleOverrides: {
// root: {
// backgroundColor: "#333",
// border: "1px solid " + colors.well,
// // color: colors.primary,
// margin: "5px",
// padding: "3px 5px",
// "&:hover": {
// backgroundColor: colors.black,
// },
borderRadius: 0,
},
},
},
// borderRadius: 0,
// },
// },
// },
MuiSelect: {
styleOverrides: {
icon: {

View File

@ -47,7 +47,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} ({ReputationRate(player.workRepGainRate * CYCLES_PER_SEC)}) reputation for
this faction <br />
@ -112,7 +113,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
<br />
This has cost you: <br />
<Money money={-player.workMoneyGained} /> ({MoneyRate(player.workMoneyLossRate * CYCLES_PER_SEC)}) <br />
<Money money={-player.workMoneyGained} /> (<MoneyRate money={player.workMoneyLossRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
You have gained: <br />
{numeralWrapper.formatExp(player.workHackExpGained)} (
@ -173,7 +175,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} ({ReputationRate(player.workRepGainRate * CYCLES_PER_SEC)}) reputation for
this company <br />
@ -241,7 +244,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} (
{Reputation(`${numeralWrapper.formatExp(player.workRepGainRate * CYCLES_PER_SEC)} / sec`)}