From 5cb0d559dfcb11eb837fbbd6bb52389fa2971996 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:45:18 +0700 Subject: [PATCH] UI: Consistently calculate BitNode "level" (#2645) --- src/BitNode/BitNodeUtils.ts | 9 +++++++++ src/BitNode/ui/BitnodeMultipliersDescription.tsx | 5 ++--- src/BitNode/ui/PortalModal.tsx | 3 ++- src/SaveObject.ts | 8 ++++++-- src/ui/CharacterStats.tsx | 5 ++--- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/BitNode/BitNodeUtils.ts b/src/BitNode/BitNodeUtils.ts index c21d25115..0071a5a0a 100644 --- a/src/BitNode/BitNodeUtils.ts +++ b/src/BitNode/BitNodeUtils.ts @@ -93,3 +93,12 @@ export function finishBitNode() { } wd.backdoorInstalled = true; } + +/** + * BitNode level is not something that is stored, but rather calculated from the current BN and SF level. The concept + * appeared because saying "Enter BN1.2" is shorter than saying "Enter BN1 with SF1.1". This is how we display it in the + * BitVerse UI and other places. This function is used to consistently calculate this "level". + */ +export function getBitNodeLevel(bn = Player.bitNodeN, sfLevel = Player.activeSourceFileLvl(bn)): number { + return Math.min(sfLevel + 1, bn === 12 ? Number.MAX_VALUE : 3); +} diff --git a/src/BitNode/ui/BitnodeMultipliersDescription.tsx b/src/BitNode/ui/BitnodeMultipliersDescription.tsx index 72772bdef..67b50e00f 100644 --- a/src/BitNode/ui/BitnodeMultipliersDescription.tsx +++ b/src/BitNode/ui/BitnodeMultipliersDescription.tsx @@ -20,7 +20,7 @@ import { StatsRow } from "../../ui/React/StatsRow"; import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode"; import { BitNodeMultipliers } from "../BitNodeMultipliers"; import { PartialRecord, getRecordEntries } from "../../Types/Record"; -import { canAccessBitNodeFeature } from "../BitNodeUtils"; +import { canAccessBitNodeFeature, getBitNodeLevel } from "../BitNodeUtils"; interface IProps { n: number; @@ -56,8 +56,7 @@ export const BitNodeMultipliersDisplay = ({ n, level, hideMultsIfCannotAccessFea // If not, then we have to assume that we want the next level up from the // current node's source file, so we get the min of that, the SF's max level, // or if it's BN12, ∞ - const maxSfLevel = n === 12 ? Number.MAX_VALUE : 3; - const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.activeSourceFileLvl(n) + 1, maxSfLevel)); + const mults = getBitNodeMultipliers(n, level ?? getBitNodeLevel(n)); return ( diff --git a/src/BitNode/ui/PortalModal.tsx b/src/BitNode/ui/PortalModal.tsx index 66c767bf6..89e7f8103 100644 --- a/src/BitNode/ui/PortalModal.tsx +++ b/src/BitNode/ui/PortalModal.tsx @@ -10,6 +10,7 @@ import Button from "@mui/material/Button"; import { BitNodeMultiplierDescription } from "./BitnodeMultipliersDescription"; import { BitNodeAdvancedOptions } from "./BitNodeAdvancedOptions"; import { JSONMap } from "../../Types/Jsonable"; +import { getBitNodeLevel } from "../BitNodeUtils"; interface IProps { open: boolean; @@ -37,7 +38,7 @@ export function PortalModal(props: IProps): React.ReactElement { const bitNode = BitNodes[bitNodeKey]; if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`); const maxSourceFileLevel = props.n === 12 ? "∞" : "3"; - const newLevel = Math.min(props.level + 1, props.n === 12 ? Number.MAX_VALUE : 3); + const newLevel = getBitNodeLevel(props.n, props.level); let currentSourceFiles = new Map(Player.sourceFiles); if (!props.flume) { diff --git a/src/SaveObject.ts b/src/SaveObject.ts index 10772e65c..4305fa4f9 100644 --- a/src/SaveObject.ts +++ b/src/SaveObject.ts @@ -36,6 +36,7 @@ import { loadInfiltrations } from "./Infiltration/SaveLoadInfiltration"; import { InfiltrationState } from "./Infiltration/formulas/game"; import { hasDarknetAccess } from "./DarkNet/utils/darknetAuthUtils"; import { loadSettings } from "./Settings/SettingsUtils"; +import { getBitNodeLevel } from "./BitNode/BitNodeUtils"; /* SaveObject.js * Defines the object used to save/load games @@ -270,7 +271,7 @@ class BitburnerSaveObject implements BitburnerSaveObjectType { * - Base64 format: save file uses .json extension. Save data is the base64-encoded json save string. */ const extension = canUseBinaryFormat() ? "json.gz" : "json"; - return `bitburnerSave_${epochTime}_BN${bn}x${Player.sourceFileLvl(bn) + 1}.${extension}`; + return `bitburnerSave_${epochTime}_BN${bn}x${getBitNodeLevel()}.${extension}`; } async exportGame(): Promise { @@ -430,7 +431,10 @@ class BitburnerSaveObject implements BitburnerSaveObjectType { achievements: importedPlayer.achievements?.length ?? 0, bitNode: importedPlayer.bitNodeN, - bitNodeLevel: importedPlayer.sourceFileLvl(importedPlayer.bitNodeN) + 1, + bitNodeLevel: getBitNodeLevel( + importedPlayer.bitNodeN, + importedPlayer.activeSourceFileLvl(importedPlayer.bitNodeN), + ), sourceFiles: [...importedPlayer.sourceFiles].reduce((total, [__bn, lvl]) => (total += lvl), 0), exploits: importedPlayer.exploits.length, diff --git a/src/ui/CharacterStats.tsx b/src/ui/CharacterStats.tsx index 823eae207..ec9d1fadb 100644 --- a/src/ui/CharacterStats.tsx +++ b/src/ui/CharacterStats.tsx @@ -17,7 +17,7 @@ import { StatsRow } from "./React/StatsRow"; import { StatsTable } from "./React/StatsTable"; import { useCycleRerender } from "./React/hooks"; import { getMaxRep } from "../Go/effects/effect"; -import { canAccessBitNodeFeature, knowAboutBitverse } from "../BitNode/BitNodeUtils"; +import { canAccessBitNodeFeature, getBitNodeLevel, knowAboutBitverse } from "../BitNode/BitNodeUtils"; interface EmployersModalProps { open: boolean; @@ -103,11 +103,10 @@ function MultiplierTable(props: MultTableProps): React.ReactElement { function CurrentBitNode(): React.ReactElement { if (knowAboutBitverse()) { const index = "BitNode" + Player.bitNodeN; - const lvl = Math.min(Player.sourceFileLvl(Player.bitNodeN) + 1, Player.bitNodeN === 12 ? Number.MAX_VALUE : 3); return ( - BitNode {Player.bitNodeN}: {BitNodes[index].name} (Level {lvl}) + BitNode {Player.bitNodeN}: {BitNodes[index].name} (Level {getBitNodeLevel()}) {BitNodes[index].info}