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'}>
+
+
+ Install Augmentations
+
+
+
+ It's always a good idea to backup/export your save!}>
+
+ Backup Save {exportBonusStr()}
+
+
+
+
+ {player.queuedAugmentations.length > 0 ? (
+
+
+
+
+ ) : (
+
+ No Augmentations have been purchased yet
+
+ )}
-
- Purchased Augmentations
-
-
- 'I never asked for this'}>
-
-
- Install Augmentations
-
-
-
- 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!}>
-
- Backup Save {exportBonusStr()}
-
-
-
+
+ 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 (
- <>
-
- Sort in Order
-
-
-
- Sort by Acquirement Time
-
-
-
- {player.entropy > 0 &&
- (() => {
- const [open, setOpen] = useState(false);
-
- return (
-
- setOpen((old) => !old)}>
-
- Entropy Virus - Level {player.entropy}
-
- }
- />
- {open ? (
-
- ) : (
-
- )}
+
+
+ Installed Augmentations
+
+
+
+ Sort in Order
+
+
+
+
+ Sort by Time of Acquirement
+
+
+
+
+ {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:
-
-
-
-
-
-
-
-
-
- >
- }
- endAdornment={
- <>
-
-
-
- >
- }
- >
- {Object.values(AugmentationNames).map((aug) => (
-
- {aug}
-
- ))}
-
-
-
-
-
+
+
+
+
+
+
+
+ >
+ }
+ endAdornment={
+ <>
+
+
+
+ >
+ }
+ >
+ {Object.values(AugmentationNames).map((aug) => (
+
+ {aug}
+
+ ))}
+
+
+ Clear Queued Augmentations
+
);
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}
);