// 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(
() => (
),
[classes.cell, classes.hack],
)}
{useMemo(
() => (
setKillOpen(true)}>
),
[Settings.theme.welllight, save, Settings.AutosaveInterval],
)}
setKillOpen(false)} killScripts={killScripts} />
>
);
}