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 && ( + + + {workInfo.gains.map((row) => ( + {row} + ))} + +
+ )} {workInfo.progress !== undefined && ( @@ -563,11 +506,15 @@ export function WorkInProgressRoot(): React.ReactElement { gridTemplateColumns: `repeat(${Object.keys(workInfo.progress).length}, 1fr)`, width: "100%", justifyItems: "center", + textAlign: "center", }} > {workInfo.progress.elapsed !== undefined && ( {convertTimeMsToTimeElapsedString(workInfo.progress.elapsed)} elapsed )} + {workInfo.progress.remaining !== undefined && ( + {convertTimeMsToTimeElapsedString(workInfo.progress.remaining)} remaining + )} {workInfo.progress.percentage !== undefined && ( {workInfo.progress.percentage.toFixed(2)}% done )}