mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
MISC: Rework intelligence override (#2575)
This commit is contained in:
@@ -289,26 +289,35 @@ function IntelligenceOverride({
|
||||
tooltip={
|
||||
<>
|
||||
<Typography component="div">
|
||||
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:
|
||||
<ul>
|
||||
<li>
|
||||
If your Intelligence is 1000 and you set this value to 500, the "effective" Intelligence, which is used
|
||||
for bonus calculation, is only 500.
|
||||
If your intelligence is 1000 and you set this value to 500, your intelligence will be temporarily set to
|
||||
500.
|
||||
</li>
|
||||
<li>
|
||||
If a Sleeve's Intelligence is 200 and you set this value to 500, the "effective" Intelligence, which is
|
||||
used for bonus calculation, is still 200.
|
||||
If a Sleeve's intelligence is 200 and you set this value to 500, that Sleeve's intelligence is still
|
||||
200.
|
||||
</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
<Typography>
|
||||
You will still gain Intelligence experience as normal. Intelligence Override only affects the Intelligence
|
||||
bonus.
|
||||
Note that you still gain intelligence experience as normal.
|
||||
<br />
|
||||
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).
|
||||
<br />
|
||||
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).
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
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.
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (
|
||||
<Tooltip title={`Intelligence: ${formatSkill(Player.skills.intelligence)}`}>
|
||||
<Tooltip
|
||||
title={`Persistent Intelligence: ${formatSkill(
|
||||
Player.calculateSkill(Player.persistentIntelligenceData.exp, 1),
|
||||
)}`}
|
||||
>
|
||||
<Typography color={color}>
|
||||
{formatSkill(Player.bitNodeOptions.intelligenceOverride)}
|
||||
{formatSkill(Player.skills.intelligence)}
|
||||
<sup>*</sup>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
@@ -140,7 +140,7 @@ export function CharacterOverview({ parentOpen, save, killScripts }: OverviewPro
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<>
|
||||
<Table sx={{ display: "block", m: 1 }}>
|
||||
<Table sx={{ display: "block", p: 1 }}>
|
||||
<TableBody>
|
||||
<DataRow name="HP" showBar={false} color={theme.colors.hp} cellType={"cellNone"} />
|
||||
<DataRow name="Money" showBar={false} color={theme.colors.money} cellType={"cell"} />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
BIN
test/jest/Migration/save-files/v2.8.1_500int.gz
Normal file
BIN
test/jest/Migration/save-files/v2.8.1_500int.gz
Normal file
Binary file not shown.
BIN
test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz
Normal file
BIN
test/jest/Migration/save-files/v2.8.1_500int_override_1000int.gz
Normal file
Binary file not shown.
BIN
test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz
Normal file
BIN
test/jest/Migration/save-files/v2.8.1_500int_override_100int.gz
Normal file
Binary file not shown.
@@ -15,7 +15,7 @@ import { CompanyPositions } from "../../../src/Company/CompanyPositions";
|
||||
import { getTorRouter } from "../../../src/Server/ServerHelpers";
|
||||
import * as exceptionAlertModule from "../../../src/utils/helpers/exceptionAlert";
|
||||
|
||||
const nextBN = 3;
|
||||
const nextBN = 4;
|
||||
|
||||
function setNumBlackOpsComplete(value: number): void {
|
||||
if (!Player.bladeburner) {
|
||||
@@ -48,19 +48,139 @@ beforeAll(() => {
|
||||
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", () => {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user