import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material"; import { MoreHoriz, Info } from "@mui/icons-material"; import React, { useState } from "react"; import { BitNodes } from "../BitNode/BitNode"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliersDisplay } from "../BitNode/ui/BitnodeMultipliersDescription"; import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { getPurchaseServerLimit } from "../Server/ServerPurchases"; import { Settings } from "../Settings/Settings"; import { MoneySourceTracker } from "../utils/MoneySourceTracker"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { Player } from "@player"; import { formatPercent, formatNumber } from "./formatNumber"; import { Modal } from "./React/Modal"; import { Money } from "./React/Money"; import { StatsRow } from "./React/StatsRow"; import { StatsTable } from "./React/StatsTable"; import { useRerender } from "./React/hooks"; import { getMaxFavor } from "../Go/effects/effect"; interface EmployersModalProps { open: boolean; onClose: () => void; } const EmployersModal = ({ open, onClose }: EmployersModalProps): React.ReactElement => { return ( <> All Employers ); }; interface IMultRow { // The name of the multiplier mult: string; // The player's raw multiplier value value: number; // The player's effective multiplier value, affected by BitNode mults effValue?: number; // The text color for the row color?: string; // Whether to format as percent or scalar isNumber?: boolean; } interface MultTableProps { rows: IMultRow[]; color: string; noMargin?: boolean; } function MultiplierTable(props: MultTableProps): React.ReactElement { return ( {props.rows.map((data) => { const { mult, value, effValue = null, color = props.color } = data; if (effValue !== null && effValue !== value && Player.sourceFileLvl(5) > 0) { return ( <> {data.isNumber ? ( formatNumber(value, 0) ) : ( <> {formatPercent(value)} {formatPercent(effValue)} )} ); } return ( ); })}
); } function CurrentBitNode(): React.ReactElement { if (Player.sourceFiles.size > 0) { const index = "BitNode" + Player.bitNodeN; const lvl = Math.min(Player.sourceFileLvl(Player.bitNodeN) + 1, Player.bitNodeN === 12 ? Infinity : 3); return ( BitNode {Player.bitNodeN}: {BitNodes[index].name} (Level {lvl}) {BitNodes[index].info} ); } return <>; } interface IMoneyModalProps { open: boolean; onClose: () => void; } function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement { function convertMoneySourceTrackerToString(src: MoneySourceTracker): React.ReactElement { const parts: [string, JSX.Element][] = [[`Total:`, ]]; if (src.augmentations) { parts.push([`Augmentations:`, ]); } if (src.bladeburner) { parts.push([`Bladeburner:`, ]); } if (src.casino) { parts.push([`Casino:`, ]); } if (src.codingcontract) { parts.push([`Coding Contracts:`, ]); } if (src.work) { parts.push([`Company Work:`, ]); } if (src.class) { parts.push([`Class:`, ]); } if (src.corporation) { parts.push([`Corporation:`, ]); } if (src.crime) { parts.push([`Crimes:`, ]); } if (src.gang) { parts.push([`Gang:`, ]); } if (src.gang_expenses) { parts.push([`Gang Expenses:`, ]); } if (src.hacking) { parts.push([`Hacking:`, ]); } if (src.hacknet) { parts.push([`Hacknet:`, ]); } if (src.hacknet_expenses) { parts.push([`Hacknet Expenses:`, ]); } if (src.hospitalization) { parts.push([`Hospitalization:`, ]); } if (src.infiltration) { parts.push([`Infiltration:`, ]); } if (src.servers) { parts.push([`Servers:`, ]); } if (src.stock) { parts.push([`Stock Market:`, ]); } if (src.sleeves) { parts.push([`Sleeves:`, ]); } if (src.other) { parts.push([`Other:`, ]); } return ; } let content = ( <> Money earned since you last installed Augmentations
{convertMoneySourceTrackerToString(Player.moneySourceA)} ); if (Player.sourceFiles.size > 0) { content = ( <> {content}

Money earned in this BitNode
{convertMoneySourceTrackerToString(Player.moneySourceB)} ); } return ( {content} ); } export function CharacterStats(): React.ReactElement { const [moneyOpen, setMoneyOpen] = useState(false); const [employersOpen, setEmployersOpen] = useState(false); useRerender(200); const timeRows = [ ["Since last Augmentation installation", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastAug)], ]; if (Player.sourceFiles.size > 0) { timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastBitnode)]); } timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]); let showBitNodeMults = false; if (Player.sourceFileLvl(5) > 0) showBitNodeMults = true; return ( Stats General <> setMoneyOpen(true)} sx={{ p: 0 }}> {Player.jobs && Object.keys(Player.jobs).length !== 0 ? ( <> {Object.keys(Player.jobs).length} total setEmployersOpen(true)} sx={{ p: 0 }}> ) : ( <> )} 0 ? "Servers" : "Nodes"} owned`} color={Settings.theme.primary} data={{ content: `${Player.hacknetNodes.length}${ Player.bitNodeN === 9 || Player.sourceFileLvl(9) > 0 ? ` / ${HacknetServerConstants.MaxServers}` : "" }`, }} />
Skills {Player.skills.intelligence > 0 && (Player.bitNodeN === 5 || Player.sourceFileLvl(5) > 0) && ( )}
Multipliers {Player.sourceFileLvl(5) > 0 && ( Displays your current multipliers.

When there is a dim number next to a multiplier, that means that the multiplier in question is being affected by BitNode multipliers.

The dim number is the raw multiplier, and the undimmed number is the effective multiplier, as dictated by the BitNode.
} > )} {Player.canAccessBladeburner() && currentNodeMults.BladeburnerRank > 0 && ( )} {(Player.sourceFileLvl(14) || Player.bitNodeN === 14) && ( )}
Time Played {timeRows.map(([name, content]) => ( ))}
{showBitNodeMults && ( BitNode Multipliers )} setMoneyOpen(false)} /> setEmployersOpen(false)} />
); }