// Root React Component for the Corporation UI import React, { useMemo, useState, useEffect } from "react"; import { Theme, useTheme } from "@mui/material/styles"; import makeStyles from "@mui/styles/makeStyles"; import createStyles from "@mui/styles/createStyles"; import { numeralWrapper } from "../numeralFormat"; import { Reputation } from "./Reputation"; import { KillScriptsModal } from "./KillScriptsModal"; import { convertTimeMsToTimeElapsedString, formatNumber } from "../../utils/StringHelperFunctions"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableRow from "@mui/material/TableRow"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import IconButton from "@mui/material/IconButton"; import SaveIcon from "@mui/icons-material/Save"; import ClearAllIcon from "@mui/icons-material/ClearAll"; import { Settings } from "../../Settings/Settings"; import { Router } from "../GameRoot"; import { Page } from "../Router"; import { Player } from "@player"; import { StatsProgressOverviewCell } from "./StatsProgressBar"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { Box, Tooltip } from "@mui/material"; import { isClassWork } from "../../Work/ClassWork"; import { CONSTANTS } from "../../Constants"; import { isCreateProgramWork } from "../../Work/CreateProgramWork"; import { isGraftingWork } from "../../Work/GraftingWork"; import { isFactionWork } from "../../Work/FactionWork"; import { ReputationRate } from "./ReputationRate"; import { isCompanyWork } from "../../Work/CompanyWork"; import { isCrimeWork } from "../../Work/CrimeWork"; interface IProps { parentOpen: boolean; save: () => void; killScripts: () => void; } function Bladeburner(): React.ReactElement { const classes = useStyles(); const bladeburner = Player.bladeburner; if (bladeburner === null) return <>; const action = bladeburner.getTypeAndNameFromActionId(bladeburner.action); if (action.type === "Idle") return <>; return ( <> {useMemo( () => ( Bladeburner: ), [classes.cellNone], )} {useMemo( () => ( {action.type}: {action.name} ), [classes.cellNone, action.type, action.name], )} ); } interface WorkInProgressOverviewProps { tooltip: React.ReactNode; header: React.ReactNode; children: React.ReactNode; } const onClickFocusWork = (): void => { Player.startFocusing(); Router.toPage(Page.Work); }; function WorkInProgressOverview({ tooltip, children, header }: WorkInProgressOverviewProps): React.ReactElement { const classes = useStyles(); return ( <> {tooltip}}> {header} {children} {useMemo( () => ( ), [classes.cellNone], )} ); } function Work(): React.ReactElement { if (Player.currentWork === null || Player.focus) return <>; let details = <>; let header = <>; let innerText = <>; if (isCrimeWork(Player.currentWork)) { const crime = Player.currentWork.getCrime(); const perc = (Player.currentWork.unitCompleted / crime.time) * 100; details = <>{Player.currentWork.crimeType}; header = <>You are attempting to {Player.currentWork.crimeType}; innerText = <>{perc.toFixed(2)}%; } if (isClassWork(Player.currentWork)) { details = <>{Player.currentWork.getClass().youAreCurrently}; header = <>You are {Player.currentWork.getClass().youAreCurrently}; innerText = <>{convertTimeMsToTimeElapsedString(Player.currentWork.cyclesWorked * CONSTANTS._idleSpeed)}; } if (isCreateProgramWork(Player.currentWork)) { const create = Player.currentWork; details = <>Coding {create.programName}; header = <>Creating a program; innerText = ( <> {create.programName} {((create.unitCompleted / create.unitNeeded()) * 100).toFixed(2)}% ); } if (isGraftingWork(Player.currentWork)) { const graft = Player.currentWork; details = <>Grafting {graft.augmentation}; header = <>Grafting an Augmentation; innerText = ( <> {((graft.unitCompleted / graft.unitNeeded()) * 100).toFixed(2)}% done ); } if (isFactionWork(Player.currentWork)) { const factionWork = Player.currentWork; header = ( <> Working for {factionWork.factionName} ); innerText = ( <> rep
( ) ); } if (isCompanyWork(Player.currentWork)) { const companyWork = Player.currentWork; details = ( <> {Player.jobs[companyWork.companyName]} at {companyWork.companyName} ); header = ( <> Working at {companyWork.companyName} ); innerText = ( <> rep
( ) ); } return ( {innerText} ); } const useStyles = makeStyles((theme: Theme) => createStyles({ workCell: { textAlign: "center", maxWidth: "200px", borderBottom: "none", padding: 0, margin: 0, }, workHeader: { fontSize: "0.9rem", }, workSubtitles: { fontSize: "0.8rem", }, cellNone: { borderBottom: "none", padding: 0, margin: 0, }, cell: { padding: 0, margin: 0, }, hp: { color: theme.colors.hp, }, money: { color: theme.colors.money, }, hack: { color: theme.colors.hack, }, combat: { color: theme.colors.combat, }, cha: { color: theme.colors.cha, }, int: { color: theme.colors.int, }, }), ); export { useStyles as characterOverviewStyles }; function rowWithHook(name: string, value: string, className: string, cellNone: string): React.ReactElement { return useMemo( () => ( {name}  {value} {/*Hook for player scripts*/} ), [name, value, className, cellNone], ); } function statItem( name: string, value: number, className: string, cellNone: string, themeColor: React.CSSProperties["color"], exp: number, mult: number, bitNodeMult: number, ): React.ReactElement[] { return [ rowWithHook(name, formatNumber(value, 0), className, cellNone), useMemo(() => { const progress = Player.calculateSkillProgress(exp, mult * bitNodeMult); return ( {!Settings.DisableOverviewProgressBars && ( )} ); }, [Settings.DisableOverviewProgressBars, themeColor, exp, mult, bitNodeMult]), ]; } export function CharacterOverview({ parentOpen, save, killScripts }: IProps): React.ReactElement { const [killOpen, setKillOpen] = useState(false); const setRerender = useState(false)[1]; // Don't rerender while the overview is closed. useEffect(() => { if (parentOpen) { const id = setInterval(() => setRerender((old) => !old), 600); return () => clearInterval(id); } return () => null; }, [parentOpen]); const classes = useStyles(); const theme = useTheme(); const hackingProgress = Player.calculateSkillProgress( Player.exp.hacking, Player.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier, ); return ( <> {rowWithHook( "HP", numeralWrapper.formatHp(Player.hp.current) + "\u00a0/\u00a0" + numeralWrapper.formatHp(Player.hp.max), classes.hp, classes.cellNone, )} {rowWithHook("Money", numeralWrapper.formatMoney(Player.money), classes.money, classes.cellNone)} {useMemo( // Hack is a special-case, because of its overview-hack-hook placement () => ( Hack  {formatNumber(Player.skills.hacking, 0)} ), [Player.skills.hacking, classes.hack, classes.cellNone], )} {useMemo( () => ( {!Settings.DisableOverviewProgressBars && ( )} ), [Settings.DisableOverviewProgressBars, Player.exp.hacking, Player.mults.hacking, theme.colors.hack], )} {useMemo( () => ( {/*Hook for player scripts*/} ), [classes.cell, classes.hack], )} {statItem( "Str", Player.skills.strength, classes.combat, classes.cellNone, theme.colors.combat, Player.exp.strength, Player.mults.strength, BitNodeMultipliers.StrengthLevelMultiplier, )} {statItem( "Def", Player.skills.defense, classes.combat, classes.cellNone, theme.colors.combat, Player.exp.defense, Player.mults.defense, BitNodeMultipliers.DefenseLevelMultiplier, )} {statItem( "Dex", Player.skills.dexterity, classes.combat, classes.cellNone, theme.colors.combat, Player.exp.dexterity, Player.mults.dexterity, BitNodeMultipliers.DexterityLevelMultiplier, )} {statItem( "Agi", Player.skills.agility, classes.combat, classes.cellNone, theme.colors.combat, Player.exp.agility, Player.mults.agility, BitNodeMultipliers.AgilityLevelMultiplier, )} {statItem( "Cha", Player.skills.charisma, classes.cha, classes.cellNone, theme.colors.cha, Player.exp.charisma, Player.mults.charisma, BitNodeMultipliers.CharismaLevelMultiplier, )} {Player.skills.intelligence !== 0 && statItem( "Int", Player.skills.intelligence, classes.int, classes.cellNone, theme.colors.int, Player.exp.intelligence, 1, 1, )} {useMemo( () => ( {/*Hook for player scripts*/} {/*Hook for player scripts*/} {/*Hook for player scripts*/} ), [classes.cell, classes.hack], )}
{useMemo( () => ( setKillOpen(true)}> ), [Settings.theme.welllight, save, Settings.AutosaveInterval], )} setKillOpen(false)} killScripts={killScripts} /> ); }