diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx
index 13fc2b919..db5b9e721 100644
--- a/src/ui/WorkInProgressRoot.tsx
+++ b/src/ui/WorkInProgressRoot.tsx
@@ -1,23 +1,24 @@
-import React, { useState, useEffect } from "react";
-import { use } from "./Context";
+import { Box, Container, Paper, Table, TableBody, Tooltip } from "@mui/material";
+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 { 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 { Money } from "./React/Money";
+import { MoneyRate } from "./React/MoneyRate";
+import { ProgressBar } from "./React/Progress";
import { Reputation } from "./React/Reputation";
import { ReputationRate } from "./React/ReputationRate";
-import { MoneyRate } from "./React/MoneyRate";
-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";
+import { StatsRow } from "./React/StatsRow";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@@ -32,6 +33,7 @@ interface IWorkInfo {
gains?: (string | React.ReactElement)[];
progress?: {
elapsed?: number;
+ remaining?: number;
percentage?: number;
};
@@ -53,18 +55,102 @@ export function WorkInProgressRoot(): React.ReactElement {
const player = use.Player();
const router = use.Router();
+ const expGains = [
+ player.workHackExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ player.workStrExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ player.workDefExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ player.workDexExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ player.workAgiExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ player.workChaExpGained > 0 ? (
+
+ ) : (
+ <>>
+ ),
+ ];
+
+ let workInfo: IWorkInfo | null;
+
if (player.workType == CONSTANTS.WorkTypeFaction) {
const faction = Factions[player.currentWorkFactionName];
if (!faction) {
- return (
- <>
-
- 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
-
-
- >
- );
+ workInfo = {
+ buttons: {
+ cancel: () => router.toFactions(),
+ },
+ title:
+ `You have not joined ${player.currentWorkFactionName || "(Faction not found)"} at this time,` +
+ " please try again if you think this should have worked",
+
+ stopText: "Back to Factions",
+ };
}
function cancel(): void {
@@ -75,81 +161,45 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toFaction(faction);
player.stopFocusing();
}
- return (
-
-
-
- You are currently {player.currentWorkFactionDescription} for your faction {faction.name}
-
- (Current Faction Reputation:
- ).
- You have been doing this for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- You have earned:
-
- (){" "}
-
-
- (
- ) reputation for this faction
-
- {player.workHackExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workHackExpGained)} (
- {numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp
- >
- )}
-
- {player.workStrExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workStrExpGained)} (
- {numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp
- >
- )}
- {player.workDefExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDefExpGained)} (
- {numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp
- >
- )}
- {player.workDexExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDexExpGained)} (
- {numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp
- >
- )}
- {player.workAgiExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workAgiExpGained)} (
- {numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp
- >
- )}
-
- {player.workChaExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workChaExpGained)} (
- {numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp
- >
- )}
-
- You will automatically finish after working for 20 hours. You can cancel earlier if you wish.
-
- There is no penalty for cancelling earlier.
-
-
-
-
-
-
-
- );
- }
- const className = player.className;
- if (player.className !== "") {
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently {player.currentWorkFactionDescription} for your faction {faction.name}
+ >
+ ),
+
+ description: (
+ <>
+ Current Faction Reputation:
+ >
+ ),
+ gains: [
+
+
+ ()
+
+ ,
+
+
+ (
+ )
+
+ ,
+ ...expGains,
+ ],
+ progress: {
+ elapsed: player.timeWorked,
+ },
+
+ stopText: "Stop Faction work",
+ };
+ } else if (player.className !== "") {
+ const className = player.className;
function cancel(): void {
player.finishClass(true);
router.toCity();
@@ -172,79 +222,44 @@ export function WorkInProgressRoot(): React.ReactElement {
stopText = "Stop taking course";
}
- return (
-
-
-
- You have been {className} for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- This has cost you:
- (){" "}
-
-
- You have gained:
- {player.workHackExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workHackExpGained)} (
- {numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp
- >
- )}
- {player.workStrExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workStrExpGained)} (
- {numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp
- >
- )}
- {player.workDefExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDefExpGained)} (
- {numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp
- >
- )}
- {player.workDexExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDexExpGained)} (
- {numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp
- >
- )}
- {player.workAgiExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workAgiExpGained)} (
- {numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp
- >
- )}
- {player.workChaExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workChaExpGained)} (
- {numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp
- >
- )}
- You may cancel at any time
-
-
-
-
-
-
-
- );
- }
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently {className}
+ >
+ ),
- if (player.workType == CONSTANTS.WorkTypeCompany) {
+ gains: [
+
+
+ ()
+
+ ,
+ ...expGains,
+ ],
+ progress: {
+ elapsed: player.timeWorked,
+ },
+
+ stopText: stopText,
+ };
+ } else if (player.workType == CONSTANTS.WorkTypeCompany) {
const comp = Companies[player.companyName];
if (comp == null || !(comp instanceof Company)) {
- return (
- <>
-
- You cannot work for {player.companyName || "(Company not found)"} at this time, please try again if you
- think this should have worked
-
-
- >
- );
+ workInfo = {
+ buttons: {
+ cancel: () => router.toTerminal(),
+ },
+ title:
+ `You cannot work for ${player.companyName || "(Company not found)"} at this time,` +
+ " please try again if you think this should have worked",
+
+ stopText: "Back to Terminal",
+ };
}
const companyRep = comp.playerReputation;
@@ -263,84 +278,47 @@ export function WorkInProgressRoot(): React.ReactElement {
const penalty = player.cancelationPenalty();
const penaltyString = penalty === 0.5 ? "half" : "three-quarters";
- return (
-
-
-
- You are currently working as a {position} at {player.companyName} (Current Company Reputation:{" "}
- )
-
- You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- You have earned:
-
- (){" "}
-
-
- (
- ) reputation for this company
-
- {player.workHackExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workHackExpGained)} (
- {`${numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) hacking exp
- >
- )}
-
- {player.workStrExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workStrExpGained)} (
- {`${numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) strength exp
- >
- )}
- {player.workDefExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDefExpGained)} (
- {`${numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) defense exp
- >
- )}
- {player.workDexExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDexExpGained)} (
- {`${numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) dexterity exp
- >
- )}
- {player.workAgiExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workAgiExpGained)} (
- {`${numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) agility exp
- >
- )}
-
- {player.workChaExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workChaExpGained)} (
- {`${numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) charisma exp
- >
- )}
-
- 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.
-
-
-
-
-
-
-
- );
- }
- if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently working as a {position} at {player.companyName}
+ >
+ ),
+
+ description: (
+ <>
+ Current Company Reputation:
+ >
+ ),
+ gains: [
+
+
+ ()
+
+ ,
+
+
+ (
+ )
+
+ ,
+ ...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.`,
+ };
+ } else if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
function cancel(): void {
player.finishWorkPartTime(true);
router.toJob();
@@ -357,126 +335,67 @@ export function WorkInProgressRoot(): React.ReactElement {
companyRep = comp.playerReputation;
const position = player.jobs[player.companyName];
- return (
-
-
+
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently working as a {position} at {player.companyName}
+ >
+ ),
+
+ description: (
+ <>
+ Current Company Reputation:
+ >
+ ),
+ gains: [
+
+
+ ()
+
+ ,
+
- You are currently working as a {position} at {player.companyName} (Current Company Reputation:{" "}
- )
-
- You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- You have earned:
-
- (){" "}
-
- (
-
- ) reputation for this company
-
- {player.workHackExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workHackExpGained)} (
- {`${numeralWrapper.formatExp(player.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) hacking exp
- >
- )}
-
- {player.workStrExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workStrExpGained)} (
- {`${numeralWrapper.formatExp(player.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) strength exp
- >
- )}
- {player.workDefExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDefExpGained)} (
- {`${numeralWrapper.formatExp(player.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) defense exp
- >
- )}
- {player.workDexExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workDexExpGained)} (
- {`${numeralWrapper.formatExp(player.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) dexterity exp
- >
- )}
- {player.workAgiExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workAgiExpGained)} (
- {`${numeralWrapper.formatExp(player.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) agility exp
- >
- )}
-
- {player.workChaExpGained > 0 && (
- <>
- {numeralWrapper.formatExp(player.workChaExpGained)} (
- {`${numeralWrapper.formatExp(player.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
- ) charisma exp
- >
- )}
-
- 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.
+ )
-
-
-
-
-
-
- );
- }
+ ,
+ ...expGains,
+ ],
+ progress: {
+ elapsed: player.timeWorked,
+ },
- if (player.crimeType !== "") {
- const percent = 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 });
+ 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.",
+ };
+ } else if (player.crimeType !== "") {
+ const completion = Math.round((player.timeWorked / player.timeNeededToCompleteWork) * 100);
- return (
-
-
-
- You are attempting to {player.crimeType}.
-
+ workInfo = {
+ buttons: {
+ cancel: () => {
+ router.toLocation(Locations[LocationName.Slums]);
+ player.finishCrime(true);
+ },
+ },
+ title: `You are attempting to ${player.crimeType}`,
-
- Time remaining: {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorked)}
-
+ progress: {
+ elapsed: player.timeWorked,
+ remaining: player.timeNeededToCompleteWork - player.timeWorked,
+ percentage: completion,
+ },
-
-
{progressBar}
-
-
-
-
-
-
- );
- }
-
- if (player.createProgramName !== "") {
+ stopText: "Cancel crime",
+ };
+ } else if (player.createProgramName !== "") {
function cancel(): void {
player.finishCreateProgramWork(true);
router.toTerminal();
@@ -485,31 +404,29 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toTerminal();
player.stopFocusing();
}
- return (
-
-
-
- You are currently working on coding {player.createProgramName}.
-
- You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- The program is {((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}
- % complete.
- If you cancel, your work will be saved and you can come back to complete the program later.
-
-
-
-
-
-
-
- );
- }
- if (player.graftAugmentationName !== "") {
+ const completion = (player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100;
+
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently working on coding {player.createProgramName}
+ >
+ ),
+
+ progress: {
+ elapsed: player.timeWorked,
+ percentage: completion,
+ },
+
+ stopText: "Stop creating program",
+ stopTooltip: "Your work will be saved and you can return to complete the program later.",
+ };
+ } else if (player.graftAugmentationName !== "") {
function cancel(): void {
player.finishGraftAugmentationWork(true);
router.toTerminal();
@@ -518,33 +435,50 @@ export function WorkInProgressRoot(): React.ReactElement {
router.toTerminal();
player.stopFocusing();
}
- return (
-
-
-
- You are currently working on grafting {player.graftAugmentationName}.
-
-
- You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
-
-
- The augmentation is{" "}
- {((player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)}% done being
- crafted.
-
- If you cancel, your work will not be saved, and the money you spent will not be returned.
-
-
-
-
-
-
-
- );
+
+ const completion = (player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100;
+
+ workInfo = {
+ buttons: {
+ cancel: cancel,
+ unfocus: unfocus,
+ },
+ title: (
+ <>
+ You are currently working on grafting {player.graftAugmentationName}
+ >
+ ),
+
+ progress: {
+ elapsed: player.timeWorked,
+ percentage: completion,
+ },
+
+ stopText: "Stop grafting",
+ stopTooltip: (
+ <>
+ If you cancel, you work will not be saved, and the money you spent will not be returned
+ >
+ ),
+ };
+ } else if (!player.workType) {
+ router.toTerminal();
+ workInfo = null;
+ } else {
+ workInfo = null;
}
+ if (workInfo === null) {
+ return <>>;
+ }
+
+ const tooltipInfo =
+ typeof workInfo?.stopTooltip === "string" ? (
+ {workInfo.stopTooltip}
+ ) : (
+ workInfo.stopTooltip || <>>
+ );
+
return (
{workInfo.title}{workInfo.description}
+ {workInfo.gains && (
+