diff --git a/src/Augmentation/ui/AugmentationsRoot.tsx b/src/Augmentation/ui/AugmentationsRoot.tsx index 9f7ad401d..fcb5bd861 100644 --- a/src/Augmentation/ui/AugmentationsRoot.tsx +++ b/src/Augmentation/ui/AugmentationsRoot.tsx @@ -7,7 +7,7 @@ import React, { useState, useEffect } from "react"; import { InstalledAugmentations } from "./InstalledAugmentations"; import { PlayerMultipliers } from "./PlayerMultipliers"; import { PurchasedAugmentations } from "./PurchasedAugmentations"; -import { SourceFiles } from "./SourceFiles"; +import { SourceFilesElement } from "./SourceFiles"; import { canGetBonus } from "../../ExportBonus"; import { use } from "../../ui/Context"; @@ -16,8 +16,55 @@ import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; import Box from "@mui/material/Box"; +import Paper from "@mui/material/Paper"; +import Container from "@mui/material/Container"; import { Settings } from "../../Settings/Settings"; import { ConfirmationModal } from "../../ui/React/ConfirmationModal"; +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { AugmentationNames } from "../data/AugmentationNames"; +import { Augmentations } from "../Augmentations"; +import { CONSTANTS } from "../../Constants"; +import { formatNumber } from "../../utils/StringHelperFunctions"; +import { Info } from "@mui/icons-material"; + +interface NFGDisplayProps { + player: IPlayer; +} + +const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => { + const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0; + + return level > 0 ? ( + + + NeuroFlux Governor - Level {level} + + {Augmentations[AugmentationNames.NeuroFluxGovernor].stats} + + ) : ( + <> + ); +}; + +interface EntropyDisplayProps { + player: IPlayer; +} + +const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => { + return player.entropy > 0 ? ( + + + Entropy Virus - Level {player.entropy} + + + All multipliers decreased by: {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% + (multiplicative) + + + ) : ( + <> + ); +}; interface IProps { exportGameFn: () => void; @@ -55,81 +102,113 @@ export function AugmentationsRoot(props: IProps): React.ReactElement { } return ( - <> + Augmentations - - - Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to - install them. - - WARNING: Installing your Augmentations resets most of your progress, including: -
- - Stats/Skill levels and Experience - - Money - - Scripts on every computer but your home computer - - Purchased servers - - Hacknet Nodes - - Faction/Company reputation - - Stocks -
- - Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations - you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but you - will lose all programs besides NUKE.exe) - + + + + Purchased Augmentations + + + Below is a list of all Augmentations you have purchased but not yet installed. Click the button + below to install them. + + + WARNING: Installing your Augmentations resets most of your progress, including: + +
+ - Stats/Skill levels and Experience + - Money + - Scripts on every computer but your home computer + - Purchased servers + - Hacknet Nodes + - Faction/Company reputation + - Stocks +
+ + Installing Augmentations lets you start over with the perks and benefits granted by all of the + Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your + home computer (but you will lose all programs besides NUKE.exe) + + + } + > + +
+
+ setInstallOpen(false)} + onConfirm={props.installAugmentationsFn} + confirmationText={ + <> + Installing will reset +
+
- money +
- skill / experience +
- every server except home +
- factions and reputation +
+
+ You will keep: +
+
- All scripts on home +
- home ram and cores +
+
+ It is recommended to install several Augmentations at once. Preferably everything from any faction of + your choosing. + + } + /> + + 'I never asked for this'}> + + + + + It's always a good idea to backup/export your save!}> + + + +
+ {player.queuedAugmentations.length > 0 ? ( + + + + + ) : ( + + No Augmentations have been purchased yet + + )}
- - Purchased Augmentations - - - 'I never asked for this'}> - - - - - setInstallOpen(false)} - onConfirm={props.installAugmentationsFn} - confirmationText={ - <> - Installing will reset -
-
- money -
- skill / experience -
- every server except home -
- factions and reputation -
-
- You will keep: -
-
- All scripts on home -
- home ram and cores -
-
- It is recommended to install several Augmentations at once. Preferably everything from any faction of your - choosing. - - } - /> - It's always a good idea to backup/export your save!}> - - - + + e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) + + +!!(player.entropy > 0) + }, 1fr)`, + gap: 1, + }} + > + + - Installed Augmentations - - - List of all Augmentations that have been installed. You have gained the effects of these. - + + - - - + +
); } diff --git a/src/Augmentation/ui/InstalledAugmentations.tsx b/src/Augmentation/ui/InstalledAugmentations.tsx index eccfd94ec..d200ebfa6 100644 --- a/src/Augmentation/ui/InstalledAugmentations.tsx +++ b/src/Augmentation/ui/InstalledAugmentations.tsx @@ -5,28 +5,23 @@ * It also contains 'configuration' buttons that allow you to change how the * Augs/SF's are displayed */ +import { Box, ListItemButton, Paper, Typography } from "@mui/material"; +import Button from "@mui/material/Button"; +import List from "@mui/material/List"; +import Tooltip from "@mui/material/Tooltip"; import React, { useState } from "react"; - -import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion"; -import { Augmentations } from "../Augmentations"; -import { AugmentationNames } from "../data/AugmentationNames"; - +import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { Settings } from "../../Settings/Settings"; import { use } from "../../ui/Context"; -import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; -import Button from "@mui/material/Button"; -import Tooltip from "@mui/material/Tooltip"; -import List from "@mui/material/List"; -import { ExpandLess, ExpandMore } from "@mui/icons-material"; -import { Box, Paper, ListItemButton, ListItemText, Typography, Collapse } from "@mui/material"; -import { CONSTANTS } from "../../Constants"; -import { formatNumber } from "../../utils/StringHelperFunctions"; +import { Augmentations } from "../Augmentations"; +import { AugmentationNames } from "../data/AugmentationNames"; export function InstalledAugmentations(): React.ReactElement { const setRerender = useState(true)[1]; const player = use.Player(); + const sourceAugs = player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor); - const sourceAugs = player.augmentations.slice(); + const [selectedAug, setSelectedAug] = useState(sourceAugs[0]); if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) { sourceAugs.sort((aug1, aug2) => { @@ -49,59 +44,60 @@ export function InstalledAugmentations(): React.ReactElement { } return ( - <> - - - - - - - - {player.entropy > 0 && - (() => { - const [open, setOpen] = useState(false); - - return ( - - setOpen((old) => !old)}> - - Entropy Virus - Level {player.entropy} - - } - /> - {open ? ( - - ) : ( - - )} + + + Installed Augmentations + + + + + + + + + + {sourceAugs.length > 0 ? ( + + + + {sourceAugs.map((k, i) => ( + setSelectedAug(k)} selected={selectedAug === k}> + {k.name} - - - - All multipliers decreased by:{" "} - {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% (multiplicative) - - - - - ); - })()} + ))} + + + + + {selectedAug.name} + + + {(() => { + const aug = Augmentations[selectedAug.name]; - {sourceAugs.map((e) => { - const aug = Augmentations[e.name]; - - let level = null; - if (e.name === AugmentationNames.NeuroFluxGovernor) { - level = e.level; - } - - return ; - })} - - + const info = typeof aug.info === "string" ? {aug.info} : aug.info; + const tooltip = ( + <> + {info} +
+
+ {aug.stats} + + ); + return tooltip; + })()} +
+
+ + ) : ( + + No Augmentations have been installed yet + + )} + ); } diff --git a/src/Augmentation/ui/PlayerMultipliers.tsx b/src/Augmentation/ui/PlayerMultipliers.tsx index be2c1bf47..b5ec5edd7 100644 --- a/src/Augmentation/ui/PlayerMultipliers.tsx +++ b/src/Augmentation/ui/PlayerMultipliers.tsx @@ -1,20 +1,22 @@ /** * React component for displaying the player's multipliers on the Augmentation UI page */ +import { DoubleArrow } from "@mui/icons-material"; +import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material"; import * as React from "react"; - +import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { Player } from "../../Player"; +import { Settings } from "../../Settings/Settings"; +import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { numeralWrapper } from "../../ui/numeralFormat"; import { Augmentations } from "../Augmentations"; -import { Table, TableCell } from "../../ui/React/Table"; -import TableBody from "@mui/material/TableBody"; -import TableRow from "@mui/material/TableRow"; -import Typography from "@mui/material/Typography"; -import Box from "@mui/material/Box"; -import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; -function calculateAugmentedStats(): any { - const augP: any = {}; +interface IAugmentedStats { + [index: string]: number; +} + +function calculateAugmentedStats(): IAugmentedStats { + const augP: IAugmentedStats = {}; for (const aug of Player.queuedAugmentations) { const augObj = Augmentations[aug.name]; for (const mult of Object.keys(augObj.mults)) { @@ -25,268 +27,249 @@ function calculateAugmentedStats(): any { return augP; } -function Improvements({ r, m }: { r: number; m: number }): React.ReactElement { - if (r) { - return ( - <> - -  {"=>"}  - - - - {numeralWrapper.formatPercentage(r)} - - - - ); - } - return <>; -} - -interface IBN5StatsProps { +interface IBitNodeModifiedStatsProps { base: number; mult: number; + color: string; } -function BN5Stat(props: IBN5StatsProps): React.ReactElement { - if (props.mult === 1) return <>; - return <>({numeralWrapper.formatPercentage(props.base * props.mult)}); -} +function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement { + // If player doesn't have SF5 or if the property isn't affected by BitNode mults + if (props.mult === 1 || SourceFileFlags[5] === 0) + return {numeralWrapper.formatPercentage(props.base)}; -function MultiplierTable({ rows }: { rows: [string, number, number, number][] }): React.ReactElement { return ( - - - {rows.map((r: any) => ( - - - {r[0]} multiplier:  - - - - {numeralWrapper.formatPercentage(r[1])} - - - - - ))} - -
+ + {numeralWrapper.formatPercentage(props.base)}{" "} + {numeralWrapper.formatPercentage(props.base * props.mult)} + ); } +type MultiplierListItemData = [ + multiplier: string, + currentValue: number, + augmentedValue: number, + bitNodeMultiplier: number, + color: string, +]; + +interface IMultiplierListProps { + rows: MultiplierListItemData[]; +} + +function MultiplierList(props: IMultiplierListProps): React.ReactElement { + const listItems = props.rows + .map((data) => { + const [multiplier, currentValue, augmentedValue, bitNodeMultiplier, color] = data; + + if (!isNaN(augmentedValue)) { + return ( + + + {multiplier} + + } + secondary={ + + + + + + } + disableTypography + /> + + ); + } + return; + }) + .filter((i) => i !== undefined); + + return listItems.length > 0 ? {listItems} : <>; +} + export function PlayerMultipliers(): React.ReactElement { const mults = calculateAugmentedStats(); - function BladeburnerMults(): React.ReactElement { - if (!Player.canAccessBladeburner()) return <>; - return ( - <> - -
- + // Column data is a bit janky, so it's set up here to allow for + // easier logic in setting up the layout + const leftColData: MultiplierListItemData[] = [ + ...[ + ["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1], + ["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1], + ["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1], + ["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1], + [ + "Hacking Level ", + Player.hacking_mult, + Player.hacking_mult * mults.hacking_mult, + BitNodeMultipliers.HackingLevelMultiplier, + ], + [ + "Hacking Experience ", + Player.hacking_exp_mult, + Player.hacking_exp_mult * mults.hacking_exp_mult, + BitNodeMultipliers.HackExpGain, + ], + ].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.hack])), + ...[ + [ + "Strength Level ", + Player.strength_mult, + Player.strength_mult * mults.strength_mult, + BitNodeMultipliers.StrengthLevelMultiplier, + ], + ["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1], + [ + "Defense Level ", + Player.defense_mult, + Player.defense_mult * mults.defense_mult, + BitNodeMultipliers.DefenseLevelMultiplier, + ], + ["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1], + [ + "Dexterity Level ", + Player.dexterity_mult, + Player.dexterity_mult * mults.dexterity_mult, + BitNodeMultipliers.DexterityLevelMultiplier, + ], + ["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult, 1], + [ + "Agility Level ", + Player.agility_mult, + Player.agility_mult * mults.agility_mult, + BitNodeMultipliers.AgilityLevelMultiplier, + ], + ["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1], + ].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.combat])), + [ + "Charisma Level ", + Player.charisma_mult, + Player.charisma_mult * mults.charisma_mult, + BitNodeMultipliers.CharismaLevelMultiplier, + Settings.theme.cha, + ], + [ + "Charisma Experience ", + Player.charisma_exp_mult, + Player.charisma_exp_mult * mults.charisma_exp_mult, + 1, + Settings.theme.cha, + ], + ]; + const rightColData: MultiplierListItemData[] = [ + ...[ + [ + "Hacknet Node production ", + Player.hacknet_node_money_mult, + Player.hacknet_node_money_mult * mults.hacknet_node_money_mult, + BitNodeMultipliers.HacknetNodeMoney, + ], + [ + "Hacknet Node purchase cost ", + Player.hacknet_node_purchase_cost_mult, + Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult, + 1, + ], + [ + "Hacknet Node RAM upgrade cost ", + Player.hacknet_node_ram_cost_mult, + Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult, + 1, + ], + [ + "Hacknet Node Core purchase cost ", + Player.hacknet_node_core_cost_mult, + Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult, + 1, + ], + [ + "Hacknet Node level upgrade cost ", + Player.hacknet_node_level_cost_mult, + Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult, + 1, + ], + ["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1], + [ + "Faction reputation gain ", + Player.faction_rep_mult, + Player.faction_rep_mult * mults.faction_rep_mult, + BitNodeMultipliers.FactionWorkRepGain, + ], + ].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])), + [ + "Salary ", + Player.work_money_mult, + Player.work_money_mult * mults.work_money_mult, + BitNodeMultipliers.CompanyWorkMoney, + Settings.theme.money, + ], + [ + "Crime success ", + Player.crime_success_mult, + Player.crime_success_mult * mults.crime_success_mult, + 1, + Settings.theme.combat, + ], + [ + "Crime money ", + Player.crime_money_mult, + Player.crime_money_mult * mults.crime_money_mult, + BitNodeMultipliers.CrimeMoney, + Settings.theme.money, + ], + ]; + + if (Player.canAccessBladeburner()) { + rightColData.push( + ...[ + [ + "Bladeburner Success Chance", + Player.bladeburner_success_chance_mult, + Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult, + 1, + ], + [ + "Bladeburner Max Stamina", + Player.bladeburner_max_stamina_mult, + Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult, + 1, + ], + [ + "Bladeburner Stamina Gain", + Player.bladeburner_stamina_gain_mult, + Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult, + 1, + ], + [ + "Bladeburner Field Analysis", + Player.bladeburner_analysis_mult, + Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult, + 1, + ], + ].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])), ); } + const hasLeftImprovements = +!!(leftColData.filter((item) => item[2] !== 0).length > 0), + hasRightImprovements = +!!(rightColData.filter((item) => item[2] !== 0).length > 0); + return ( - <> - Multipliers - - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- + + + + ); } diff --git a/src/Augmentation/ui/PurchasedAugmentations.tsx b/src/Augmentation/ui/PurchasedAugmentations.tsx index a8aa13b55..f3982f035 100644 --- a/src/Augmentation/ui/PurchasedAugmentations.tsx +++ b/src/Augmentation/ui/PurchasedAugmentations.tsx @@ -2,14 +2,11 @@ * React component for displaying all of the player's purchased (but not installed) * Augmentations on the Augmentations UI. */ +import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material"; import * as React from "react"; - +import { Player } from "../../Player"; import { Augmentations } from "../Augmentations"; import { AugmentationNames } from "../data/AugmentationNames"; -import { Player } from "../../Player"; - -import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion"; -import List from "@mui/material/List"; export function PurchasedAugmentations(): React.ReactElement { const augs: React.ReactElement[] = []; @@ -23,14 +20,48 @@ export function PurchasedAugmentations(): React.ReactElement { } for (let i = 0; i < Player.queuedAugmentations.length; i++) { const ownedAug = Player.queuedAugmentations[i]; + let displayName = ownedAug.name; + if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue; const aug = Augmentations[ownedAug.name]; + let level = null; if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) { level = ownedAug.level; + displayName += ` - Level ${level}`; } - augs.push(); + + augs.push( + + {(() => { + const info = typeof aug.info === "string" ? {aug.info} : aug.info; + const tooltip = ( + <> + {info} +
+
+ {aug.stats} + + ); + return tooltip; + })()} + + } + enterNextDelay={500} + key={displayName} + > + +
, + ); } - return {augs}; + return ( + + + {augs} + + + ); } diff --git a/src/Augmentation/ui/SourceFiles.tsx b/src/Augmentation/ui/SourceFiles.tsx index 1293f2298..f649d3890 100644 --- a/src/Augmentation/ui/SourceFiles.tsx +++ b/src/Augmentation/ui/SourceFiles.tsx @@ -1,21 +1,159 @@ -import React from "react"; -import { SourceFileMinus1 } from "./SourceFileMinus1"; -import { OwnedSourceFiles } from "./OwnedSourceFiles"; -import List from "@mui/material/List"; - -import Typography from "@mui/material/Typography"; +import { ListItemButton, ListItemText, Paper } from "@mui/material"; import Box from "@mui/material/Box"; +import List from "@mui/material/List"; +import Typography from "@mui/material/Typography"; +import React, { useState } from "react"; +import { Exploit, ExploitName } from "../../Exploits/Exploit"; +import { Player } from "../../Player"; +import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; +import { Settings } from "../../Settings/Settings"; +import { SourceFile } from "../../SourceFile/SourceFile"; +import { SourceFiles } from "../../SourceFile/SourceFiles"; + +interface SfMinus1 { + info: React.ReactElement; + n: number; + name: string; + lvl: number; +} + +const safeGetSf = (sfNum: number): SourceFile | SfMinus1 | null => { + if (sfNum === -1) { + const sfMinus1: SfMinus1 = { + info: ( + <> + This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem. +
+
+ It increases all of the player's multipliers by 0.1% +
+
+ You have found the following exploits: +
+
+ {Player.exploits.map((c: Exploit) => ( + + * {ExploitName(c)} +
+
+ ))} + + ), + lvl: Player.exploits.length, + n: -1, + name: "Source-File -1: Exploits in the BitNodes", + }; + return sfMinus1; + } + + const srcFileKey = "SourceFile" + sfNum; + const sfObj = SourceFiles[srcFileKey]; + if (sfObj == null) { + console.error(`Invalid source file number: ${sfNum}`); + return null; + } + return sfObj; +}; + +const getMaxLevel = (sfObj: SourceFile | SfMinus1): string | number => { + let maxLevel; + switch (sfObj.n) { + case 12: + maxLevel = "∞"; + break; + case -1: + maxLevel = Object.keys(Exploit).length; + break; + default: + maxLevel = "3"; + } + return maxLevel; +}; + +export function SourceFilesElement(): React.ReactElement { + const sourceSfs = Player.sourceFiles.slice(); + const exploits = Player.exploits; + // Create a fake SF for -1, if "owned" + if (exploits.length > 0) { + sourceSfs.unshift({ + n: -1, + lvl: exploits.length, + }); + } + + if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) { + sourceSfs.sort((sf1, sf2) => { + return sf1.n - sf2.n; + }); + } + + if (sourceSfs.length === 0) { + return <>; + } + + const [selectedSf, setSelectedSf] = useState(sourceSfs[0]); -export function SourceFiles(): React.ReactElement { return ( - <> - Source Files - - - - - - - + + + Source Files + + + + + {sourceSfs.map((e, i) => { + const sfObj = safeGetSf(e.n); + if (!sfObj) return; + + const maxLevel = getMaxLevel(sfObj); + + return ( + setSelectedSf(e)} + selected={selectedSf.n === e.n} + sx={{ py: 0 }} + > + {sfObj.name}} + secondary={ + + Level {e.lvl} / {maxLevel} + + } + /> + + ); + })} + + + + + {safeGetSf(selectedSf.n)?.name} + + + {(() => { + const sfObj = safeGetSf(selectedSf.n); + if (!sfObj) return; + + const maxLevel = getMaxLevel(sfObj); + + return ( + <> + Level {selectedSf.lvl} / {maxLevel} +
+
+ {sfObj.info} + + ); + })()} +
+
+
+
); } diff --git a/src/DevMenu/ui/Augmentations.tsx b/src/DevMenu/ui/Augmentations.tsx index 3927ac5fd..be569e1d3 100644 --- a/src/DevMenu/ui/Augmentations.tsx +++ b/src/DevMenu/ui/Augmentations.tsx @@ -1,19 +1,18 @@ +import { Clear, ExpandMore, Reply, ReplyAll } from "@mui/icons-material"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Button, + IconButton, + MenuItem, + Select, + SelectChangeEvent, + Typography, +} from "@mui/material"; import React, { useState } from "react"; - -import Accordion from "@mui/material/Accordion"; -import AccordionSummary from "@mui/material/AccordionSummary"; -import AccordionDetails from "@mui/material/AccordionDetails"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; - -import Select, { SelectChangeEvent } from "@mui/material/Select"; -import { IPlayer } from "../../PersonObjects/IPlayer"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; -import Typography from "@mui/material/Typography"; -import MenuItem from "@mui/material/MenuItem"; -import IconButton from "@mui/material/IconButton"; -import ReplyAllIcon from "@mui/icons-material/ReplyAll"; -import ReplyIcon from "@mui/icons-material/Reply"; -import ClearIcon from "@mui/icons-material/Clear"; +import { IPlayer } from "../../PersonObjects/IPlayer"; interface IProps { player: IPlayer; @@ -39,50 +38,46 @@ export function Augmentations(props: IProps): React.ReactElement { props.player.augmentations = []; } + function clearQueuedAugs(): void { + props.player.queuedAugmentations = []; + } + return ( - }> + }> Augmentations - - - - - - - -
- Aug: - - -
+ +
); diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 9821d8a14..1122f73fa 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -94,7 +94,7 @@ export const GraftingRoot = (): React.ReactElement => { Graft Augmentations {getAvailableAugs(player).length > 0 ? ( - + {getAvailableAugs(player).map((k, i) => ( setSelectedAug(k)} selected={selectedAug === k}> {k} diff --git a/src/ui/CharacterStats.tsx b/src/ui/CharacterStats.tsx index 0ee401d47..0c24fb734 100644 --- a/src/ui/CharacterStats.tsx +++ b/src/ui/CharacterStats.tsx @@ -1,144 +1,80 @@ -import React, { useState, useEffect } from "react"; - -import { numeralWrapper } from "./numeralFormat"; -import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; -import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; -import { getPurchaseServerLimit } from "../Server/ServerPurchases"; -import { HacknetServerConstants } from "../Hacknet/data/Constants"; -import { StatsTable } from "./React/StatsTable"; -import { Money } from "./React/Money"; -import { use } from "./Context"; -import { MoneySourceTracker } from "../utils/MoneySourceTracker"; +import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material"; +import { MoreHoriz, Info } from "@mui/icons-material"; +import React, { useEffect, useState } from "react"; import { BitNodes } from "../BitNode/BitNode"; - -import Typography from "@mui/material/Typography"; -import Box from "@mui/material/Box"; -import IconButton from "@mui/material/IconButton"; -import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; +import { HacknetServerConstants } from "../Hacknet/data/Constants"; +import { getPurchaseServerLimit } from "../Server/ServerPurchases"; +import { Settings } from "../Settings/Settings"; +import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; +import { MoneySourceTracker } from "../utils/MoneySourceTracker"; +import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; +import { use } from "./Context"; +import { numeralWrapper } from "./numeralFormat"; import { Modal } from "./React/Modal"; +import { Money } from "./React/Money"; +import { StatsRow } from "./React/StatsRow"; +import { StatsTable } from "./React/StatsTable"; -import TableBody from "@mui/material/TableBody"; -import { Table, TableCell } from "./React/Table"; -import TableRow from "@mui/material/TableRow"; - -function LastEmployer(): React.ReactElement { - const player = use.Player(); - if (player.companyName) { - return Employer at which you last worked: {player.companyName}; - } - return <>; +interface EmployersModalProps { + open: boolean; + onClose: () => void; } -function LastJob(): React.ReactElement { +const EmployersModal = ({ open, onClose }: EmployersModalProps): React.ReactElement => { const player = use.Player(); - if (player.companyName !== "") { - return Job you last worked: {player.jobs[player.companyName]}; - } - return <>; -} - -function Employers(): React.ReactElement { - const player = use.Player(); - if (player.jobs && Object.keys(player.jobs).length !== 0) - return ( + return ( + <> - All Employers: - + All Employers
    {Object.keys(player.jobs).map((j) => ( - * {j} + * {j} ))}
- ); - return <>; -} - -function Hacknet(): React.ReactElement { - const player = use.Player(); - // Can't import HacknetHelpers for some reason. - if (!(player.bitNodeN === 9 || SourceFileFlags[9] > 0)) { - return ( - <> - {`Hacknet Nodes owned: ${player.hacknetNodes.length}`} -
- - ); - } else { - return ( - <> - {`Hacknet Servers owned: ${player.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`} -
- - ); - } -} - -function Intelligence(): React.ReactElement { - const player = use.Player(); - if (player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0)) { - return ( - - - Intelligence:  - - - {numeralWrapper.formatSkill(player.intelligence)}  - - - ({numeralWrapper.formatExp(player.intelligence_exp)} exp) - - - ); - } - return <>; -} - -function MultiplierTable(props: any): React.ReactElement { - function bn5Stat(r: any): JSX.Element { - if (SourceFileFlags[5] > 0 && r.length > 2 && r[1] != r[2]) { - return ( - - ({numeralWrapper.formatPercentage(r[2])}) - - ); - } - return <>; - } - return ( - <> - - - {props.rows.map((r: any) => ( - - - {`${r[0]} multiplier:`}  - - - {numeralWrapper.formatPercentage(r[1])} - - {bn5Stat(r)} - - ))} - -
- +
); +}; + +interface MultTableProps { + rows: (string | number)[][]; + color: string; + noMargin?: boolean; } -function BladeburnerMults(): React.ReactElement { - const player = use.Player(); - if (!player.canAccessBladeburner()) return <>; +function MultiplierTable(props: MultTableProps): React.ReactElement { return ( - + + + {props.rows.map((data) => { + const mult = data[0] as string, + value = data[1] as number, + modded = data[2] as number | null; + + if (modded && modded !== value && SourceFileFlags[5] > 0) { + return ( + + <> + + {numeralWrapper.formatPercentage(value)}{" "} + {numeralWrapper.formatPercentage(modded)} + + + + ); + } + return ( + + ); + })} + +
); } @@ -146,15 +82,17 @@ function CurrentBitNode(): React.ReactElement { const player = use.Player(); if (player.sourceFiles.length > 0) { const index = "BitNode" + player.bitNodeN; + const currentSourceFile = player.sourceFiles.find((sourceFile) => sourceFile.n == player.bitNodeN); + const lvl = currentSourceFile ? currentSourceFile.lvl : 0; return ( - <> - - BitNode {player.bitNodeN}: {BitNodes[index].name} - - - {BitNodes[index].info} - - + + + + BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl}) + + {BitNodes[index].info} + + ); } @@ -262,6 +200,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement { export function CharacterStats(): React.ReactElement { const player = use.Player(); const [moneyOpen, setMoneyOpen] = useState(false); + const [employersOpen, setEmployersOpen] = useState(false); const setRerender = useState(false)[1]; function rerender(): void { setRerender((old) => !old); @@ -273,229 +212,307 @@ export function CharacterStats(): React.ReactElement { }, []); const timeRows = [ - ["Time played since last Augmentation:", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)], + ["Since last Augmentation installation", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)], ]; if (player.sourceFiles.length > 0) { - timeRows.push([ - "Time played since last Bitnode destroyed:", - convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode), - ]); + timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode)]); } - timeRows.push(["Total Time played:", convertTimeMsToTimeElapsedString(player.totalPlaytime)]); + timeRows.push(["Total", convertTimeMsToTimeElapsedString(player.totalPlaytime)]); return ( - <> - General - - Current City: {player.city} - - - - - - Money: - setMoneyOpen(true)}> - - - - -
+ Stats - - - - - - Hacking:  - - - {numeralWrapper.formatSkill(player.hacking)}  - - - ({numeralWrapper.formatExp(player.hacking_exp)} exp) - - - - - Strength:  - - - {numeralWrapper.formatSkill(player.strength)}  - - - ({numeralWrapper.formatExp(player.strength_exp)} exp) - - - - - Defense:  - - - {numeralWrapper.formatSkill(player.defense)}  - - - ({numeralWrapper.formatExp(player.defense_exp)} exp) - - - - - Dexterity:  - - - {numeralWrapper.formatSkill(player.dexterity)}  - - - ({numeralWrapper.formatExp(player.dexterity_exp)} exp) - - - - - Agility:  - - - {numeralWrapper.formatSkill(player.agility)}  - - - ({numeralWrapper.formatExp(player.agility_exp)} exp) - - - - - Charisma:  - - - {numeralWrapper.formatSkill(player.charisma)}  - - - ({numeralWrapper.formatExp(player.charisma_exp)} exp) - - - - -
-
+ + + General + + + + + <> + + setMoneyOpen(true)} sx={{ p: 0 }}> + + + + + {player.companyName ? ( + <> + + + + ) : ( + <> + )} + {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 || SourceFileFlags[9] > 0 ? ` / ${HacknetServerConstants.MaxServers}` : "" + }`, + }} + /> + + +
+
+ + Skills + + + + + + + + + {player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0) && ( + + )} + +
+
-
- Multipliers - - -
- -
+ + + + Multipliers + {SourceFileFlags[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() && ( + + )} + +
+
-
- Misc - - {`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`} - - {`Augmentations installed: ${player.augmentations.length}`} - + + + Time Played + + + {timeRows.map(([name, content]) => ( + + ))} + +
+
-
setMoneyOpen(false)} /> - + setEmployersOpen(false)} /> +
); } diff --git a/src/ui/React/StatsRow.tsx b/src/ui/React/StatsRow.tsx index eed5c7184..405abcc26 100644 --- a/src/ui/React/StatsRow.tsx +++ b/src/ui/React/StatsRow.tsx @@ -17,9 +17,10 @@ interface IProps { color: string; classes?: any; data: ITableRowData; + children?: React.ReactElement; } -export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps): React.ReactElement => { +export const StatsRow = ({ name, color, classes = useStyles(), children, data }: IProps): React.ReactElement => { let content; if (data.content !== undefined) { @@ -36,7 +37,8 @@ export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps): {name} - {content} + {content ? {content} : <>} + {children} );