From 3813d03fb644ceac61fbd5141f4b8e713cef7c04 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:09:37 +0700 Subject: [PATCH] MISC: Rework intelligence override (#2575) --- src/BitNode/ui/BitNodeAdvancedOptions.tsx | 27 ++- src/Constants.ts | 2 +- src/DevMenu/ui/StatsDev.tsx | 4 + src/PersonObjects/Person.ts | 24 ++- .../Player/PlayerObjectGeneralMethods.ts | 1 + src/PersonObjects/Sleeve/Sleeve.ts | 2 + src/PersonObjects/formulas/intelligence.ts | 8 +- src/RedPill.tsx | 1 + src/ui/React/CharacterOverview.tsx | 16 +- src/utils/SaveDataMigrationUtils.ts | 4 + test/jest/Migration/Migration.test.ts | 116 +++++++++---- .../Migration/save-files/v2.8.1_500int.gz | Bin 0 -> 9127 bytes .../v2.8.1_500int_override_1000int.gz | Bin 0 -> 8745 bytes .../v2.8.1_500int_override_100int.gz | Bin 0 -> 8773 bytes test/jest/Netscript/Singularity.test.ts | 158 ++++++++++++++++-- test/jest/__snapshots__/FullSave.test.ts.snap | 9 + 16 files changed, 295 insertions(+), 77 deletions(-) create mode 100644 test/jest/Migration/save-files/v2.8.1_500int.gz create mode 100644 test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz create mode 100644 test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz diff --git a/src/BitNode/ui/BitNodeAdvancedOptions.tsx b/src/BitNode/ui/BitNodeAdvancedOptions.tsx index c67ba0c11..8c91396c7 100644 --- a/src/BitNode/ui/BitNodeAdvancedOptions.tsx +++ b/src/BitNode/ui/BitNodeAdvancedOptions.tsx @@ -289,26 +289,35 @@ function IntelligenceOverride({ tooltip={ <> - The Intelligence bonuses for you and your Sleeves will be limited by this value. For example: + Your intelligence and your Sleeves' intelligence will be temporarily set to this value if it is lower than + their current values. For example: - You will still gain Intelligence experience as normal. Intelligence Override only affects the Intelligence - bonus. + Note that you still gain intelligence experience as normal. +
+ For example, suppose you have 1e6 intelligence exp (intelligence skill = 242) and set the intelligence + override to 100. At the start of the BitNode, your intelligence skill will be set to 100 (equivalent to + ~11255.318 intelligence exp). +
+ If you gain 500e3 intelligence exp during the BitNode, your intelligence skill will increase to 220 (total + intelligence exp = 11255 + 500e3 = 511255). After performing bitflume, the exp gained during the BitNode is + added to your original exp. Your intelligence skill will then become 255 (total intelligence exp = 1e6 + + 500e3 = 1.5e6).

- The "effective" Intelligence will be shown in the character overview. If the effective value is different - from the original value, you can hover your mouse over it to see the original value. + The overridden intelligence will be shown in the character overview. You can hover your mouse over it to see + the original value. } diff --git a/src/Constants.ts b/src/Constants.ts index 498229049..f128866f1 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -7,7 +7,7 @@ export const CONSTANTS = { VersionString: "3.0.0dev", isDevBranch: true, isInTestEnvironment: globalThis.process?.env?.JEST_WORKER_ID !== undefined, - VersionNumber: 46, + VersionNumber: 47, /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then diff --git a/src/DevMenu/ui/StatsDev.tsx b/src/DevMenu/ui/StatsDev.tsx index 7298dae19..d1f13bd0b 100644 --- a/src/DevMenu/ui/StatsDev.tsx +++ b/src/DevMenu/ui/StatsDev.tsx @@ -76,6 +76,7 @@ function setStatLevel(stat: string, level: number): void { break; case "Intelligence": Player.exp.intelligence = 0; + Player.persistentIntelligenceData.exp = 0; Player.gainIntelligenceExp(calculateExp(level, 1)); break; } @@ -107,6 +108,7 @@ function resetAllExp(): void { Player.exp.agility = 0; Player.exp.charisma = 0; Player.exp.intelligence = 0; + Player.persistentIntelligenceData.exp = 0; Player.updateSkillLevels(); } @@ -133,6 +135,7 @@ function resetExperience(stat: string): () => void { break; case "Intelligence": Player.exp.intelligence = 0; + Player.persistentIntelligenceData.exp = 0; break; } Player.updateSkillLevels(); @@ -155,6 +158,7 @@ function enableIntelligence(): void { function disableIntelligence(): void { Player.exp.intelligence = 0; Player.skills.intelligence = 0; + Player.persistentIntelligenceData.exp = 0; Player.updateSkillLevels(); } diff --git a/src/PersonObjects/Person.ts b/src/PersonObjects/Person.ts index cc64be8af..741886f7d 100644 --- a/src/PersonObjects/Person.ts +++ b/src/PersonObjects/Person.ts @@ -10,7 +10,7 @@ import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { CONSTANTS } from "../Constants"; import { Player } from "../Player"; import { defaultMultipliers } from "./Multipliers"; -import { calculateSkill } from "./formulas/skill"; +import { calculateExp, calculateSkill } from "./formulas/skill"; // Base class representing a person-like object export abstract class Person implements IPerson { @@ -34,6 +34,10 @@ export abstract class Person implements IPerson { intelligence: 0, }; + persistentIntelligenceData = { + exp: 0, + }; + mults = defaultMultipliers(); /** Augmentations */ @@ -142,6 +146,23 @@ export abstract class Person implements IPerson { ); } + overrideIntelligence(): void { + const persistentIntelligenceSkill = this.calculateSkill(this.persistentIntelligenceData.exp, 1); + // Reset exp and skill to the persistent values if there is no limit (intelligenceOverride) or the limit is greater + // than or equal to the persistent skill. + if ( + Player.bitNodeOptions.intelligenceOverride === undefined || + Player.bitNodeOptions.intelligenceOverride >= persistentIntelligenceSkill + ) { + this.exp.intelligence = this.persistentIntelligenceData.exp; + this.skills.intelligence = persistentIntelligenceSkill; + return; + } + // Limit exp and skill based on intelligenceOverride only if it's smaller than the persistent skill. + this.exp.intelligence = calculateExp(Player.bitNodeOptions.intelligenceOverride, 1); + this.skills.intelligence = Player.bitNodeOptions.intelligenceOverride; + } + gainIntelligenceExp(exp: number): void { if (isNaN(exp)) { console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); @@ -154,6 +175,7 @@ export abstract class Person implements IPerson { if (Player.sourceFileLvl(5) > 0 || this.skills.intelligence > 0 || Player.bitNodeN === 5) { this.exp.intelligence += exp; this.skills.intelligence = Math.floor(this.calculateSkill(this.exp.intelligence, 1)); + this.persistentIntelligenceData.exp += exp; } } diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts index 36de6b3fd..2f612b792 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts @@ -140,6 +140,7 @@ export function prestigeAugmentation(this: PlayerObject): void { export function prestigeSourceFile(this: PlayerObject): void { this.entropy = 0; this.prestigeAugmentation(); + this.overrideIntelligence(); this.karma = 0; // Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists) this.sleeves.forEach((sleeve) => sleeve.prestige()); diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 90beb390f..d0fefd774 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -251,6 +251,8 @@ export class Sleeve extends Person implements SleevePerson { this.shock = 100; this.storedCycles = 0; this.sync = Math.max(this.memory, 1); + + this.overrideIntelligence(); } /** diff --git a/src/PersonObjects/formulas/intelligence.ts b/src/PersonObjects/formulas/intelligence.ts index 5b0400e62..6af662555 100644 --- a/src/PersonObjects/formulas/intelligence.ts +++ b/src/PersonObjects/formulas/intelligence.ts @@ -1,9 +1,3 @@ -import { Player } from "@player"; - export function calculateIntelligenceBonus(intelligence: number, weight = 1): number { - const effectiveIntelligence = - Player.bitNodeOptions.intelligenceOverride !== undefined - ? Math.min(Player.bitNodeOptions.intelligenceOverride, intelligence) - : intelligence; - return 1 + (weight * Math.pow(effectiveIntelligence, 0.8)) / 600; + return 1 + (weight * Math.pow(intelligence, 0.8)) / 600; } diff --git a/src/RedPill.tsx b/src/RedPill.tsx index 217c0aebc..4423d2c28 100644 --- a/src/RedPill.tsx +++ b/src/RedPill.tsx @@ -66,6 +66,7 @@ export function enterBitNode( } else if (Player.sourceFileLvl(5) === 0 && newBitNode !== 5) { Player.skills.intelligence = 0; Player.exp.intelligence = 0; + Player.persistentIntelligenceData.exp = 0; } if (newBitNode === 5 && Player.skills.intelligence === 0) { Player.skills.intelligence = 1; diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index a432182f6..13801943f 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -71,15 +71,15 @@ export function Val({ name, color }: ValProps): React.ReactElement { return clearSubscription; }, [name]); - if ( - name === "Int" && - Player.bitNodeOptions.intelligenceOverride !== undefined && - Player.bitNodeOptions.intelligenceOverride < Player.skills.intelligence - ) { + if (name === "Int" && Player.bitNodeOptions.intelligenceOverride !== undefined) { return ( - + - {formatSkill(Player.bitNodeOptions.intelligenceOverride)} + {formatSkill(Player.skills.intelligence)} * @@ -140,7 +140,7 @@ export function CharacterOverview({ parentOpen, save, killScripts }: OverviewPro const theme = useTheme(); return ( <> - +
diff --git a/src/utils/SaveDataMigrationUtils.ts b/src/utils/SaveDataMigrationUtils.ts index af331cd8d..f6d44832c 100644 --- a/src/utils/SaveDataMigrationUtils.ts +++ b/src/utils/SaveDataMigrationUtils.ts @@ -625,6 +625,10 @@ Error: ${e}`, for (const faction of [...Player.factions, ...Player.factionInvitations]) { Player.factionRumors.add(faction); } + for (const person of [Player, ...Player.sleeves]) { + person.persistentIntelligenceData.exp = person.exp.intelligence; + person.overrideIntelligence(); + } showAPIBreaks("3.0.0", breakingChanges300); } } diff --git a/test/jest/Migration/Migration.test.ts b/test/jest/Migration/Migration.test.ts index aa0cfc4ce..7c2e06c3f 100644 --- a/test/jest/Migration/Migration.test.ts +++ b/test/jest/Migration/Migration.test.ts @@ -1,46 +1,34 @@ import { Player } from "@player"; import fs from "node:fs"; import type { ScriptFilePath } from "../../../src/Paths/ScriptFilePath"; -import { loadGame } from "../../../src/SaveObject"; +import { loadGame, saveObject } from "../../../src/SaveObject"; import * as db from "../../../src/db"; import * as FileUtils from "../../../src/utils/FileUtils"; +import type { SaveData } from "../../../src/types"; +import { calculateExp } from "../../../src/PersonObjects/formulas/skill"; + +async function loadGameFromSaveData(saveData: SaveData) { + // Simulate loading the data in IndexedDB + const mockedLoad = jest.spyOn(db, "load"); + // We must use structuredClone(saveData) instead of saveData; otherwise, the check of mockedDownload won't catch wrong + // changes in evaluateVersionCompatibility (e.g., unexpectedly mutating saveData before passing it to + // downloadContentAsFile). + mockedLoad.mockReturnValue(Promise.resolve(structuredClone(saveData))); + + // Simulate saving the data in IndexedDB + jest.spyOn(db, "save").mockImplementation(() => Promise.resolve()); + + const mockedDownload = jest.spyOn(FileUtils, "downloadContentAsFile").mockImplementation(() => {}); + + await loadGame(saveData); + + return mockedDownload; +} describe("v3", () => { test("v2.8.1 to v3.0.0", async () => { const saveData = new Uint8Array(fs.readFileSync("test/jest/Migration/save-files/v2.8.1.gz")); - - // Simulate loading the data in IndexedDB - const mockedLoad = jest.spyOn(db, "load"); - /** - * We must use structuredClone(saveData) instead of saveData; otherwise, the check of mockedDownload won't catch - * wrong changes in evaluateVersionCompatibility (e.g., unexpectedly mutating saveData before passing it to - * downloadContentAsFile). - */ - mockedLoad.mockReturnValue(Promise.resolve(structuredClone(saveData))); - - const mockedDownload = jest.spyOn(FileUtils, "downloadContentAsFile"); - - const originalConsoleError = console.error; - const originalConsoleWarning = console.warn; - const consoleError = jest.spyOn(console, "error").mockImplementation((...data: unknown[]) => { - if (Array.isArray(data) && data.length > 0 && (data[0] === "There was no Darknet savedata" || data[0] === "")) { - return; - } - originalConsoleError(...data); - }); - const consoleWarning = jest.spyOn(console, "warn").mockImplementation((...data: unknown[]) => { - if ( - Array.isArray(data) && - data.length > 0 && - (data[0] === "Encountered the following issue while loading Darknet savedata:" || data[0] === "Savedata:") - ) { - return; - } - originalConsoleWarning(...data); - }); - await loadGame(await db.load()); - consoleError.mockRestore(); - consoleWarning.mockRestore(); + const mockedDownload = await loadGameFromSaveData(saveData); // Check if auto-migration works expect( @@ -57,4 +45,64 @@ describe("v3", () => { // Check if evaluateVersionCompatibility correctly loads the data in IndexedDB and passes it to downloadContentAsFile expect(mockedDownload).toHaveBeenCalledWith(saveData, "bitburnerSave_backup_2.8.1_1756913326.json.gz"); }); + + test.each([ + ["test/jest/Migration/save-files/v2.8.1_500int.gz", 1773597870229, 1773597871370, undefined, 500, 300], + ["test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz", 1773597926723, 1773597928370, 100, 500, 300], + ["test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz", 1773597951205, 1773597953370, 1000, 500, 300], + ])("%s", async (path, lastSave, lastUpdate, intelligenceOverride, playerInt, sleeveInt) => { + const saveData = new Uint8Array(fs.readFileSync(path)); + const mockedDownload = await loadGameFromSaveData(saveData); + expect(Player.lastSave).toStrictEqual(lastSave); + + const playerIntExp = + intelligenceOverride !== undefined && intelligenceOverride < playerInt + ? calculateExp(intelligenceOverride, 1) + : calculateExp(playerInt, 1); + const sleeveIntExp = + intelligenceOverride !== undefined && intelligenceOverride < sleeveInt + ? calculateExp(intelligenceOverride, 1) + : calculateExp(sleeveInt, 1); + const persistentPlayerIntExp = calculateExp(playerInt, 1); + const persistentSleeveIntExp = calculateExp(sleeveInt, 1); + + // Check if Player.exp.intelligence and Player.persistentIntelligenceData.exp are migrated. + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(intelligenceOverride); + expect(Player.exp.intelligence).toStrictEqual(playerIntExp); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(persistentPlayerIntExp); + for (const sleeve of Player.sleeves) { + expect(sleeve.exp.intelligence).toStrictEqual(sleeveIntExp); + expect(sleeve.persistentIntelligenceData.exp).toStrictEqual(persistentSleeveIntExp); + } + + const expGain = 1e9; + // Gain exp and check if it is accumulated correctly. + Player.gainIntelligenceExp(expGain); + expect(Player.exp.intelligence).toStrictEqual(playerIntExp + expGain); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(persistentPlayerIntExp + expGain); + for (const sleeve of Player.sleeves) { + sleeve.gainIntelligenceExp(expGain); + expect(sleeve.exp.intelligence).toStrictEqual(sleeveIntExp + expGain); + expect(sleeve.persistentIntelligenceData.exp).toStrictEqual(persistentSleeveIntExp + expGain); + } + + // Save and reload. + await saveObject.saveGame(); + await loadGameFromSaveData(await saveObject.getSaveData()); + expect(Player.lastSave).not.toStrictEqual(lastSave); + + // Check if gained exp is saved correctly. + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(intelligenceOverride); + expect(Player.exp.intelligence).toStrictEqual(playerIntExp + expGain); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(persistentPlayerIntExp + expGain); + for (const sleeve of Player.sleeves) { + expect(sleeve.exp.intelligence).toStrictEqual(sleeveIntExp + expGain); + expect(sleeve.persistentIntelligenceData.exp).toStrictEqual(persistentSleeveIntExp + expGain); + } + + expect(mockedDownload).toHaveBeenCalledWith( + saveData, + `bitburnerSave_backup_2.8.1_${Math.round(lastUpdate / 1000)}.json.gz`, + ); + }); }); diff --git a/test/jest/Migration/save-files/v2.8.1_500int.gz b/test/jest/Migration/save-files/v2.8.1_500int.gz new file mode 100644 index 0000000000000000000000000000000000000000..d65a2d77d7891bea3c7a7556a7921b9d072d08b4 GIT binary patch literal 9127 zcmV;YBUs!YiwFP!000003hiBObDKE3{wp4znRC8i4?>8SeoNh?&9hC~iPPP4w&`>f zkZm>~`4N!ByVL)EMgq)>6~MI}Kgo1Ev#lioH$s>0_gnuAy@c`Mo8j9axlK7Ge2VYM zr`x}XmkhO`j}tt6^Uv^eh*w$H@Xhd_8+p;q@Xbx$k&V9@YB%zVo8g;(Zib6x_P3XE zPH1v7d;@gtW*Ff|`OhEP%`mDtmS>!)`G9xr5>tBxv;X$&> zf8%)&mcP6O=0P0c{AWND5{AK?P)}TsbopfRxNJUFuRm6=Jyvf#RKZiX?4e^! z*DbQCR1T?r#WAn|K(4FmU*AJ9rt;}qy4&wiSuU*u+U`|XL$6|p*ee77k&@IQc5CXPg8qF&>j`|Pw7~kU7A-N}E>u`@n zOH5baIeBs-X4p#tM%#uwFn-rQM;;F%@;y#HkHm5Npw|1`hS(?B_W3=+kKf}2M*%H% zplWa4^L>s3s_4cPhpRY2gYHoaXDz@OnW86LwE17HsC*k8;6@ zms;O=fn#&}F1u=m!nH> z9B0)0Bc?wL2Q$V87x}spL)MLxa{dQrDfNqcToFI@$lxNNL6k;=De=+@KRTx#Cpad9 z3;ZbitM@*SmYm&_!8A$zRmt#*=J%^;aLsW*%dXQ$vDD^dgQ6=E6U@DZYTVm$Sq!c) zz3Un?{hW_@PblFy5g*bI=zK6?G~w7w^6p8tBMi>)z5ELLk004*j ziRZCY${t1gU+h+X0&(>n=4*aU)95o{%aHsQgrQ`vqBUe*fr6XiHOKcP9GnQAzLH;l zy(s|OEq+c}$~|(@STh%8*EPr9-Nw?X<4Fn>I1VT)euOwKR)HsH$g59K9kP0{Z{`2a zv0MC8a2UZgc2KFVev9Za2WhueP%#sL73FYh(ki|*O$c!^NK~0ump$s zzr|i%43WD>aREyf>ltZocDh^xm05rLYu2A@)|Vk(B|$`{0rkj7949BKloXMRY+ct? zBi;sy;Q#eeVRot5Q>2Pu+YmXx1tvr&?-rZ*Q~iZt<4@Q0%e_AKc(6=9bLKY)tWVC@ z>S~$F<`<#Gz~g8!7SJu*g=5Fn9oMprZY@Tow5W6xg-W6|XS1PjY|d{}ols19O<7QU zgbh_-U8qy_tUHYyBTIIibRYT6BIA9&DgcZreOlz z5va0|3A2rw6MwSuLb*Lz4gx~3h*#8;Z}~&}wPF3*uzqb=zc#F6ZCJ>)Y=~?FS&sQ@ z!}_&h{o1g8ZCL-&8N`c^84XPiZ)T`>c`G$xz{VPVLtnI5+|3oV)vhKQ2m;3K}Jn9+`s^j0C{(fC=~)rr|o(%g!&aPd|PuA9KH>+b~gr(ep@EfW|(e$#|- zfrW6wq6ky}Lm*bds#vMQ&hTaF4xDJ)|Ah=r9<>vy8Lfr zmFE`n8S~PrnUc_ivt_{>ax?s=Wr3o9sw?i(J>fj?d#=3oYU3nMc;F=;SVTTgzGPA(b-PsOs`nWuywLPU6Dc&gX z2~C1okoy<_c3f8{rsX4qOtB#hwc$w^<|d)iYn?eG>Kjf@M%|2`8~Mt8Y?)3NC&flN zOO~G*Pd+UPty`Q={3xJT!F(YvO?XN&n_WV2MC$&KMWVb(u$04~H5PwtIW&kTj8fty z#J^_ceIxvMg(D%V7;*!QV>~DMp0bd}&Uh3jm?r_9j~1Bvw~XD5!yw7_6~^b7=61x( zf;_QC2A?PLu}jY897p*bFTebDIwp@9@n?)jX^7*jp|rR4S($Q8=GMkf^pYgG5%TNJ zkm|ZGH&zK0=F>PyXEXUT4(1Uac`S}d5_s8=zZi+%7_?B_cp zQtuDRbw?GGITf9tw4AXy24kcIWifV*O1%NfCoy+ADIQ-%+}=PXrMmyD1z)b}Empki zs+PQLZLWC;Rf`^4NVj#v(QQ~?{w^>q8`(N=475Ka7|_`SgCzGUG(=>M_W}rWM?jQ> zbTcY6w#W7u?imvoWR4FEixH;&2>$}c@QrC92Repn%UG5QFaWSjwSM!{ROW77?a{{~nygG4leP!vV;t;|1wRx1pg!7wu7`(k+ElWx+PMWGw&KV9BWHgo*>k*4P=F z{jg%08_LctSuyI5B1}898>z1j&Z(aYjrfqzxl3^{nlqtMYgGYrmWCcrBOJ#hiC+Ph z93_?)4x6vW*rf(Fe9Okl>C$k~DO49DQt$ z%_mF-oh;{Om@LR>5%@l#qX))AzXMB`e|&ld&hz2{vInijwH()Ukls+*UBfgCXaghb z*%Pw?j-fz^)q)T&#QZLh*7KcUV(*BOl8$ag9*xo1`dLMOli^5rPn?X{Y!pNh<03#W zS#1DONa=G*XmlLc{J!_1{#-7a!XDsD|>6(UvfaP9>ug_kCMcUF; zsF1B2h6FV#9K(hXAziN5Dk_lWKXwo_6Xp*+TV_00LF$8L-&l~-%=dBXLLR|rwQRyc8YwDL*aQr!hmJ7UmARB zviOoc5WZk(Oh&UHp)#`R#bbY+V!PS8!4VCm;lnlogpOgCHpUG5mV+P^8vxj=5mB1+ znk=!+h**v+WqPJ~S{Z#|^Xw=FbC==^(OKD)|v6+*fVi(_Z( zV)sJEEq z8amJ+(i>qz2Gn%}IkvS&E4r)VAE?cUUVf=xGQj z9T7c(!PtExdVtkCfbL0DT?_JriX+0~*kRKl>Nb#4-+M*DK(;bO&Q^uU8K%-w0HFxz zc2Uz(;6Q|s>+I2(<{+pL*xFE4aSI$5R24${gn8Rzr>~<@q4UUaQ{iAAMQJPw#&kgH zq9E>>oHZp^syobHKA4meo~@Gb3}uKn0>2uaWR+3?e+Yzbl^a6u{Iqhx{UNMt0%X_u|;IwODZu0sAn z(DpY;<5waB+RErPTNS;gPJwk?9YWi7nj)ZKAjdS#RuOP(W~DG|J8jm5ISxgsHE-mt zZa0eeZw)WBBfOf_bO(l7Qjgkq?)dwhj=@CGad3M>B+BU1AdSkP&pbvgGt#{xdxWJF z{Vb*Er;G!4pbJeCAA04QnsK8LiUaOoYuxCz7IJ0OPh0kZ z>XRKAT~B1Rbm&RO*br&wd)AXqGFmcUQ%TEVObX?$QcH1q`3O=P7%ZiMLG7Zokl`3k zvqFPm0%+*AVeZq%WT^6D+vK^Jg`xocuCQ@-%}glB=o2|7Y6JDOgezpaCoOiuMmNJj zNc_=^Gnxd1cQCAJM)hF2#2y(;4=wJ!CN*hiACs*kOGnUd1_jxcsRL1oVUJo8P#7BQ zHXl=I5jw7-R15d-5%S=S^}S~_gbDsc4;VbBFiKWqxGRxSugL1{DB~-Eob5x|MW=rX&kaK56z2*)0x7%LV2;Cq3+J^sdp;jU#t#jm^F|Y6cE% z9h~}JQaAW4sKXvflfc8_ArrRAeHx9HEDXF?!G;_q8*+MkkAz9Vv`o`(re_-nh|{&5 zHf!CM(w&V|5kzgZfJ(3WamBlucfUivbI1DF*n5ELCA-Ii3*rlSJfBIS9^%1MY9jcC)1N@?M^*eU7g<~^2(yW4vmo(L&A z9ir;mqP(+|p}~93(gS)^YmVe5^I6+eUOskqH5LHf1i%KcS>@iaWcB;@BKO-$X|}=! z*BX`(l1Jeb-#sp2*XrCALi*@A;$VoMwZlD$osZ=4&>;vc5hqWKQX0GhZ0uaQ(Kajq z3;>$>sSZGfqZ@lwV1Ocn2(>9No8cnIMTjzdr86Bult=7Y%)UR9xmvKnq)Ey_028NC z6eJm5vxTg^+Ii$b;(o8Uz#$pSU=%cO=gr?h1huqzz6*#nB}X;zI@?ye>wB zl#B?e7!gpKq%GYtpy4=8kB&Gx#W9T58lqb=B4n#9(rt+jb-U-W!$+&M^wV+X&a{1$ON}^$8rE@ zJy~T-^q4?l9WdMcp|zdvxZtUg(kJ}d64gd_VLqHBBXPnVNrEB-t}WU?u0k4%Q?o|_ z9f>HK5yzc5uvgi84e|_gd0PF!lJ!Uu1~fZOqpmYn82gJy^RqfFeNK#UM7Z3(`PrXu2j>MD12!uYUk`|+zu1Dt zjE)#j7O&xSmEGeKblWhHP?VaMI!CvlBaWrqU$GykkOJ*Cb3|1WKCU66p_6w=fTqkI z|5+2nPjazs!@sBUm7axxHDISSG`)B%?ra%^IxsDLqhzp+#PQ>xHS&8~Q--NPVyi?M zuVRn!qqD9x8QpOWjyTcsXV0{J8Z@g|yvtxpyp#vYK@%qAv6|`aYkXnOp~0x!S947 zDL-g=lR{&9`Os0uF<8nt24y+~v@I7xw^H4O+&r?njFwp>M5oK}h2w4(J6cU-Qr zCZk&yvd4P=lahOvsk>f~!H1lH1D%RY=6FO&At{zzbZz2$@koKn^Gu-XJQJWk&jcaM zf{xK(GSG1WauI6NgpBQwVyIk6W-E_j(K6k`!LGa34KC^E#EJvPwm7xn=OnOgODRkQ zVb?ifhsMy0$BLz_vTdoVY^&2$4G5v`ZeRsD(6G&RtZZv*b5w5E)+p?P`5#vtt6FV4 zPB+Pp#p-EcQ`rl)Jaq1oBp(BM_e{0s;vl>yd?boDK4!{)!zWM*t}h>0${=k^8KkXt zE7?eOtd`|e+V>=`0Y&;ZYCBk~VaC}KUe(b*eXy%(Lg=`g2fLnxbH;;Y5e430nlSHf zaLut#_~1PAUKYo@eg(kROrV*LhLGmC8bFpNTmwLdnu#>v>KZ_f=7_(b26PPoOEV13 zaWr5Ua=2~C%WVx9NCTz@zV$vOc31YhX-MlVDIs|9z9ZECm60*{B*<8bNcHF3J~sRp zRpac7-$9@<{~fCG-+@x$vmGeThiyhgxk#K2YqUkJZ!5*B3Y1%IQ5{s*{mnVuzG%d* zoVg&QTZR>Oe7CQiq_^wqk}*FdgSVI;4mJxGy@rfIrR7j%I8=(M_9iC<86$lf1^Slf zQ*L4aX7_*c3|Gc{8Yk&&_u53ep5I!bq+3cA5$L@SZ&lP8-!3rlj4}Umx~A=b7?H_m zTz?HfibK1)pqk`#2Q>k4d{XbzEt8?^C+n%e0-R130=6w& zqVO)Yp+zh>%roD{aWPY+q}vo-Y2X+hBiC^Ou($CT)g49#)BuDUupABO zt_B_9{4p)f0h(yBH4AADlKvy%II?ZcG&RH4V4GV=+smto&@SNY;tUf`ECe+RYQV8H z08LG#QbVA@?1dalGo<^=a5Mnz%moIK27qQm4VXZ)fxN^88qiG*TA~XeQvZQSwKQ>pr|5As$JBu9icSCxaghr(;G*aAsBMU=9kv`-mc#97%TXk#QSvZK zcz`<(HkeAn1|Lc}`YqA|ONEo+XU>89!07x;90Yew@PeiB z0juY}7E|huRwJlFPj`gNmO?(<)@roC9Nz^z7{zJ4*g5BHXY4en>GseGZ?)O?ScCrh z@CVHCZvhVmQ$dJuTOVG&U)xz;hm zI9$r@oA>gB;Z>Lk=B&%+`bm&An6g>22KF-;TeW(|TrDLC-$yh^$VfL{!&y|jT4gQU zG))UNkCL|>3jhnY)h67QJ2{G^W!RR`->~Q$Idi#AOn3USBTjGY>-g&();4DjM%va`wAP)cd2ThrN6N>ge&t!A?*2tz`HG};$G*|pxSf}lP{ zo6y+Ue;C$9K}*3{4iBJO+0NH?X7$eOrmEK{VixzAVQDK4kTTm6q?YV~aOr~B7nIyx z?P&^%`glnN59(P%dJEIqnwY~&X?H0OMsr5z@`CFH8BOs0Yp_7?Tv5i=)%F6T2QS+8 z(07$y(^@#XULgM%0VjqFbQ^3&Wn8lahl4-xDjpmL=dD+8_1VxJPBqHOQJ&(oe#ygWS6f0ZJ9I=tb@_=f$}TeDr2tEug@ z()zi6yRk=W*YegiS~`Iu4%3Ex`J#aoIh5*3AKErnLs4z&Dy8(GrQ^#ZC|^99c1uBS zKxlu!K~Pb@qpf_$S}i!Pa#qz&I>ks=IHG1@XuyxC*+s{k7B&B}SRsE%hw%`?&s~ zjE3$|1bJj=U%k9pu6FyaeH6YVj+C+Fg5=zAO%4y+0D$$9CmyGrSG5RoD*_gxT+b#XGCoS&cYq_XVt zo)0X!Bdd6`7#z`;m;}!7;P4dZ} z;3f8g@=WLp{P>a06|E8to6XmRM*+oQtxNP>k%%Qk>=Y-<;KcWN=4ihehJq%s^GbSZ z!^AW4)%7JNf5hT_Y6B`SE!Nk29Fb2{^i4C(BH^j*OP5PI{pso?okxTwa;KdzIt#=E zN$+iWr+jpd!E<^aBqUZ0xF~n=X4f^x_ay9iz}pb}-tA|KF#GyZ zjjtJw6UBf63He&||G-@AKvibYPSg?nS!1qIPnTDZQPhV(Iea7jd z7p6YBs%>g#oJIM>7vs+3Y2cGv%*DnkchmfnT;m|TBGf0m0#uO63dw>*xn8*BL4^4# z6r5VFmjBXcdd?ALPu}R&8U^RfJKU@zPCbvrvEn{?PyIf5sW+E*v#c!t@FDxARf_WW%6oE}yjo+0s{FkII%Aca zSnh1yoV7s9TenoE1!h9e0|~>*<)&$_o3j?f$dbYy7C}M^LbGSwhS)1^m(S56ldJyA z_n`^1TS-=3Tets}PG8yG__XT*X_7FSPch6a3lUz5Ju%-jvYA>>77g!1 zy=*RSBbvc_0qE6cBmX9j4?M4IhHFA}aqo<&uhw#07*O(o`Is{t1!4A{|02m-jsqHBFv_&S&5hv0DG!Lq1b+}ewL!!vio&N4GH z3ejv;Nkv|7BVzceA0&)lmpg_K6Xmn-<59Ly<#%2XE;q1z`|(&5v0S^xo=5U^@D@!@ zPcqQmX3znr;*V^ZWi9Va@h6|L6-{tnpeVsvKeF7(y9}L#CK(H?wZ;iAfH^sJf$ikW zZcZL^|FvmZF3Q&eze|_RJKT}g1LJxb?d+A( zpj-gW8CYUU!hD*u7ch{4jQq7oD7Btu`z;faW9?Qy*29D1BjooxdH2pbI}=y_kh0!jq|3e{s1y^6T(zzP62Z z+dGmFFFB`v@w^JN%SN2FK%nyA3UXvDRoVJ6nB)AM!^W-Okr__IrxdFz)}s zakf}kzF%Zrla$Ad7h+=ZZ5!QhMIE1FVcwQU5CuND<~RrgI)6vJAj09FgtMC3EQSbc zBBp*kB}>e4rUe!Q(wLmmc|ZvfEB}&h%v`>|PSe>ec&xqnZ)Ej0pnfj5{3Df{-^IDZ zjOH4J38lUhzQ?7OG1P`1ah#k!E*VeWGMdK2H$!0C5IKfnxCmKN@4=MZ#qWbzMeix? z4P|e`=rH40F1NKZ9>nqylLhAUYy#IRb3;v%XG%X!!%!eL<($wY zZxmZpkh#c7l8K9Pj;t)C^XtuUoc$-8_ww&=T1nj7{5iQ2Q8cf$_nYA#8C#cJmf)I} lTsE9Bo{y6qOB~1Rfj{zi)+ZIrAHyF%{vRt!Zbmv90RZ%|vJC(L literal 0 HcmV?d00001 diff --git a/test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz b/test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz new file mode 100644 index 0000000000000000000000000000000000000000..1c78bec1166afb1a37ebf41579321f5fc6d85d5b GIT binary patch literal 8745 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 literal 0 HcmV?d00001 diff --git a/test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz b/test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz new file mode 100644 index 0000000000000000000000000000000000000000..98f3162c09bda7a69831ca8561b63f1484a0c1a6 GIT binary patch literal 8773 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 { initGameEnvironment(); }); +function testIntelligenceOverride( + ns: NSFull, + prestigeAPI: "b1tflum3" | "destroyW0r1dD43m0n", + expectSuccessPrestige: () => void, + setUpBeforePrestige = () => {}, +): void { + Player.sourceFiles.set(5, 1); + // Start without exp. + expect(Player.exp.intelligence).toStrictEqual(0); + expect(Player.skills.intelligence).toStrictEqual(1); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(0); + // Gain 1e6 exp (skill = 242). + Player.gainIntelligenceExp(1e6); + expect(Player.exp.intelligence).toStrictEqual(1e6); + expect(Player.skills.intelligence).toStrictEqual(242); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6); + + // Prestige and check if intelligenceOverride works (exp is set to 11255, skill = 100, and + // persistentIntelligenceData.exp is still 1e6). + const intelligenceExpGainOnPrestige = prestigeAPI === "destroyW0r1dD43m0n" ? 300 : 0; + setUpBeforePrestige(); + ns.singularity[prestigeAPI](nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: 100, + }); + expectSuccessPrestige(); + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(100); + expect(Player.exp.intelligence).toStrictEqual(11255.317546552918 + intelligenceExpGainOnPrestige); + expect(Player.skills.intelligence).toStrictEqual(100); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige); + + // Gain 500e3 exp. + const intExpGain = 500e3; + Player.gainIntelligenceExp(intExpGain); + // Check if int gain is accumulated correctly in both Player.exp.intelligence and + // Player.persistentIntelligenceData.exp. + expect(Player.exp.intelligence).toStrictEqual(11255.317546552918 + intelligenceExpGainOnPrestige + intExpGain); + expect(Player.skills.intelligence).toStrictEqual(220); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige + intExpGain); + + // Prestige and check if int gain is still retained correctly. + setUpBeforePrestige(); + ns.singularity[prestigeAPI](nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: undefined, + }); + expectSuccessPrestige(); + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(undefined); + expect(Player.exp.intelligence).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 2 + intExpGain); + expect(Player.skills.intelligence).toStrictEqual(255); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 2 + intExpGain); + + // Prestige with intelligenceOverride set higher than the persistent int skill and check if the int skill is + // incorrectly set to that value. + setUpBeforePrestige(); + ns.singularity[prestigeAPI](nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: 1000, + }); + expectSuccessPrestige(); + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(1000); + expect(Player.exp.intelligence).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 3 + intExpGain); + expect(Player.skills.intelligence).toStrictEqual(255); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 3 + intExpGain); + + // Start testing another scenario. + // Set the initial state (int exp = 1e6, skill = 242) and bitflume. + Player.exp.intelligence = 1e6; + Player.skills.intelligence = 242; + Player.persistentIntelligenceData.exp = 1e6; + ns.singularity.b1tflum3(nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: undefined, + }); + + // Double-check the initial state. + expect(Player.exp.intelligence).toStrictEqual(1e6); + expect(Player.skills.intelligence).toStrictEqual(242); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6); + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(undefined); + + // Limit int skill to 100. + setUpBeforePrestige(); + ns.singularity[prestigeAPI](nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: 100, + }); + expectSuccessPrestige(); + + // Check if int is overridden correctly. + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(100); + expect(Player.exp.intelligence).toStrictEqual(11255.317546552918 + intelligenceExpGainOnPrestige); + expect(Player.skills.intelligence).toStrictEqual(100); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige); + + // Limit int skill to 1000. + setUpBeforePrestige(); + ns.singularity[prestigeAPI](nextBN, undefined, { + ...ns.getResetInfo().bitNodeOptions, + intelligenceOverride: 1000, + }); + expectSuccessPrestige(); + + // The limit is higher than the persistent int skill, so it's not applied. Exp and skill are reset back to the initial + // state, plus the int exp gained from prestige. + expect(Player.bitNodeOptions.intelligenceOverride).toStrictEqual(1000); + expect(Player.exp.intelligence).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 2); + expect(Player.skills.intelligence).toStrictEqual(242); + expect(Player.persistentIntelligenceData.exp).toStrictEqual(1e6 + intelligenceExpGainOnPrestige * 2); +} + +function setUpBeforeDestroyingWD(): void { + Player.queueAugmentation(AugmentationName.TheRedPill); + installAugmentations(); + Player.gainHackingExp(1e100); + const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon); + wdServer.hasAdminRights = true; + Player.startBladeburner(); + setNumBlackOpsComplete(blackOpsArray.length); +} + describe("b1tflum3", () => { beforeEach(() => { setupBasicTestingEnvironment(); - Player.queueAugmentation(AugmentationName.TheRedPill); + Player.queueAugmentation(AugmentationName.Targeting1); installAugmentations(); Player.gainHackingExp(1e100); - const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon); - wdServer.hasAdminRights = true; }); // Make sure that the player is in the next BN without SF rewards. const expectSucceedInB1tflum3 = () => { expect(Player.bitNodeN).toStrictEqual(nextBN); expect(Player.augmentations.length).toStrictEqual(0); + expect(Player.exp.hacking).toStrictEqual(0); expect(Player.sourceFileLvl(1)).toStrictEqual(0); }; @@ -79,15 +199,20 @@ describe("b1tflum3", () => { }); expectSucceedInB1tflum3(); }); + test("intelligenceOverride", () => { + testIntelligenceOverride(getNS(), "b1tflum3", expectSucceedInB1tflum3); + }); }); + // Make sure that the player is still in the same BN without SF rewards. + const expectFailToB1tflum3 = () => { + expect(Player.bitNodeN).toStrictEqual(1); + expect(Player.augmentations.length).toStrictEqual(1); + expect(Player.augmentations[0].name).toStrictEqual(AugmentationName.Targeting1); + expect(Player.exp.hacking).toStrictEqual(1e100); + expect(Player.sourceFileLvl(1)).toStrictEqual(0); + }; describe("Failure", () => { - // Make sure that the player is still in the same BN without SF rewards. - const expectFailToB1tflum3 = () => { - expect(Player.bitNodeN).toStrictEqual(1); - expect(Player.augmentations.length).toStrictEqual(1); - expect(Player.sourceFileLvl(1)).toStrictEqual(0); - }; test("Invalid intelligenceOverride", () => { const ns = getNS(); expect(() => { @@ -114,13 +239,7 @@ describe("b1tflum3", () => { describe("destroyW0r1dD43m0n", () => { beforeEach(() => { setupBasicTestingEnvironment(); - Player.queueAugmentation(AugmentationName.TheRedPill); - installAugmentations(); - Player.gainHackingExp(1e100); - const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon); - wdServer.hasAdminRights = true; - Player.startBladeburner(); - setNumBlackOpsComplete(blackOpsArray.length); + setUpBeforeDestroyingWD(); }); describe("Success", () => { @@ -162,6 +281,9 @@ describe("destroyW0r1dD43m0n", () => { }); expectSucceedInDestroyingWD(); }); + test("intelligenceOverride", () => { + testIntelligenceOverride(getNS(), "destroyW0r1dD43m0n", expectSucceedInDestroyingWD, setUpBeforeDestroyingWD); + }); }); describe("Failure", () => { @@ -488,6 +610,7 @@ describe("purchaseProgram", () => { const spiedExceptionAlert = jest.spyOn(exceptionAlertModule, "exceptionAlert"); const ns = getNS(); expect(Player.hasProgram(CompletedProgramName.darkscape)).toStrictEqual(false); + // @ts-expect-error - Intentionally use lowercase program name expect(ns.singularity.purchaseProgram(CompletedProgramName.darkscape.toLowerCase())).toStrictEqual(true); expect(Player.hasProgram(CompletedProgramName.darkscape)).toStrictEqual(true); expect(spiedExceptionAlert).not.toHaveBeenCalled(); @@ -507,6 +630,7 @@ describe("purchaseProgram", () => { }); test("Invalid program name", () => { const ns = getNS(); + // @ts-expect-error - Intentionally use invalid program name expect(ns.singularity.purchaseProgram("InvalidProgram.exe")).toStrictEqual(false); }); test("Not enough money", () => { diff --git a/test/jest/__snapshots__/FullSave.test.ts.snap b/test/jest/__snapshots__/FullSave.test.ts.snap index de155d47d..4062c8341 100644 --- a/test/jest/__snapshots__/FullSave.test.ts.snap +++ b/test/jest/__snapshots__/FullSave.test.ts.snap @@ -534,6 +534,9 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "work_money": 1, }, "numPeopleKilled": 0, + "persistentIntelligenceData": { + "exp": 0, + }, "playtimeSinceLastAug": 0, "playtimeSinceLastBitnode": 0, "purchasedServers": [], @@ -614,6 +617,9 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "strength_exp": 1, "work_money": 1, }, + "persistentIntelligenceData": { + "exp": 0, + }, "queuedAugmentations": [], "shock": 100, "skills": { @@ -686,6 +692,9 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "strength_exp": 1, "work_money": 1, }, + "persistentIntelligenceData": { + "exp": 0, + }, "queuedAugmentations": [], "shock": 100, "skills": {