TYPESAFETY: Strict internal typing for AugmentationName (#608)

This commit is contained in:
Snarling 2023-06-16 17:52:42 -04:00 committed by GitHub
parent 12b5c00d14
commit a4b826683e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 2649 additions and 3221 deletions

@ -28,7 +28,7 @@ import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router"; import { Page } from "../ui/Router";
import data from "./AchievementData.json"; import data from "./AchievementData.json";
import { isClassWork } from "../Work/ClassWork"; import { isClassWork } from "../Work/ClassWork";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { workerScripts } from "../Netscript/WorkerScripts"; import { workerScripts } from "../Netscript/WorkerScripts";
import { getRecordValues } from "../Types/Record"; import { getRecordValues } from "../Types/Record";
@ -384,7 +384,7 @@ export const achievements: Record<string, Achievement> = {
Icon: "donation", Icon: "donation",
Condition: () => Condition: () =>
Object.values(Factions).some( Object.values(Factions).some(
(f) => f.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction), (f) => f.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction),
), ),
}, },
TRAVEL: { TRAVEL: {

@ -1,35 +1,24 @@
// Class definition for a single Augmentation object // Class definition for a single Augmentation object
import * as React from "react";
import { AugmentationName, CompletedProgramName, FactionName } from "@enums";
import { Faction } from "../Faction/Faction";
import { Factions } from "../Faction/Factions";
import { formatPercent } from "../ui/formatNumber";
import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { Player } from "@player"; import { Player } from "@player";
import { CONSTANTS } from "../Constants"; import { AugmentationName, CompletedProgramName, FactionName } from "@enums";
import { StaticAugmentations } from "./StaticAugmentations"; import { formatPercent } from "../ui/formatNumber";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { getBaseAugmentationPriceMultiplier, getGenericAugmentationPriceMultiplier } from "./AugmentationHelpers";
import { initSoAAugmentations } from "./data/AugmentationCreator";
import { Multipliers, defaultMultipliers } from "../PersonObjects/Multipliers"; import { Multipliers, defaultMultipliers } from "../PersonObjects/Multipliers";
import { getRecordKeys } from "../Types/Record";
export interface AugmentationCosts { export interface AugmentationCosts {
moneyCost: number; moneyCost: number;
repCost: number; repCost: number;
} }
export interface IConstructorParams { export interface AugmentationCtorParams {
info: string | JSX.Element; info: string;
stats?: JSX.Element | null; stats?: string;
isSpecial?: boolean; isSpecial?: boolean;
moneyCost: number; moneyCost: number;
name: string; name: AugmentationName;
prereqs?: string[]; prereqs?: AugmentationName[];
repCost: number; repCost: number;
factions: string[]; factions: FactionName[];
hacking?: number; hacking?: number;
strength?: number; strength?: number;
@ -66,85 +55,42 @@ export interface IConstructorParams {
programs?: CompletedProgramName[]; programs?: CompletedProgramName[];
} }
function generateStatsDescription(mults: Multipliers, programs?: string[], startingMoney?: number): JSX.Element { function generateStatsDescription(mults: Multipliers, programs?: string[], startingMoney?: number): string {
// For a percentage that is <10, show x.xx%, otherwise show xx.x% // For a percentage that is <10, show x.xx%, otherwise show xx.x%
const f = (x: number) => formatPercent(x, x - 1 < 0.1 ? 2 : 1); const f = (x: number) => formatPercent(x, x - 1 < 0.1 ? 2 : 1);
let desc = <>Effects:</>; let desc = "Effects:";
// Skills
if ( if (
mults.hacking !== 1 && mults.hacking !== 1 &&
mults.hacking == mults.strength && mults.hacking === mults.strength &&
mults.hacking == mults.defense && mults.hacking === mults.defense &&
mults.hacking == mults.dexterity && mults.hacking === mults.dexterity &&
mults.hacking == mults.agility && mults.hacking === mults.agility &&
mults.hacking == mults.charisma mults.hacking === mults.charisma
) { ) {
desc = ( desc += `\n+${f(mults.hacking - 1)} all skills`;
<>
{desc}
<br />+{f(mults.hacking - 1)} all skills
</>
);
} else { } else {
if (mults.hacking !== 1) // Not allskills
desc = ( if (mults.hacking !== 1) desc += `\n+${f(mults.hacking - 1)} hacking skill`;
<>
{desc}
<br />+{f(mults.hacking - 1)} hacking skill
</>
);
if ( if (
mults.strength !== 1 && mults.strength !== 1 &&
mults.strength == mults.defense && mults.strength === mults.defense &&
mults.strength == mults.dexterity && mults.strength === mults.dexterity &&
mults.strength == mults.agility mults.strength === mults.agility
) { ) {
desc = ( desc += `\n+${f(mults.strength - 1)} combat skills`;
<>
{desc}
<br />+{f(mults.strength - 1)} combat skills
</>
);
} else { } else {
if (mults.strength !== 1) // Not all combat
desc = ( if (mults.strength !== 1) desc += `\n+${f(mults.strength - 1)} strength skill`;
<> if (mults.defense !== 1) desc += `\n+${f(mults.defense - 1)} defense skill`;
{desc} if (mults.dexterity !== 1) desc += `\n+${f(mults.dexterity - 1)} dexterity skill`;
<br />+{f(mults.strength - 1)} strength skill if (mults.agility !== 1) desc += `\n+${f(mults.agility - 1)} agility skill`;
</>
);
if (mults.defense !== 1)
desc = (
<>
{desc}
<br />+{f(mults.defense - 1)} defense skill
</>
);
if (mults.dexterity !== 1)
desc = (
<>
{desc}
<br />+{f(mults.dexterity - 1)} dexterity skill
</>
);
if (mults.agility !== 1)
desc = (
<>
{desc}
<br />+{f(mults.agility - 1)} agility skill
</>
);
} }
if (mults.charisma !== 1) if (mults.charisma !== 1) desc += `\n+${f(mults.charisma - 1)} charisma skill`;
desc = (
<>
{desc}
<br />+{f(mults.charisma - 1)} charisma skill
</>
);
} }
// Skill XP
if ( if (
mults.hacking_exp !== 1 && mults.hacking_exp !== 1 &&
mults.hacking_exp === mults.strength_exp && mults.hacking_exp === mults.strength_exp &&
@ -153,214 +99,66 @@ function generateStatsDescription(mults: Multipliers, programs?: string[], start
mults.hacking_exp === mults.agility_exp && mults.hacking_exp === mults.agility_exp &&
mults.hacking_exp === mults.charisma_exp mults.hacking_exp === mults.charisma_exp
) { ) {
desc = ( desc += `\n+${f(mults.hacking_exp - 1)} exp for all skills`;
<>
{desc}
<br />+{f(mults.hacking_exp - 1)} exp for all skills
</>
);
} else { } else {
if (mults.hacking_exp !== 1) // Not allskillxp
desc = ( if (mults.hacking_exp !== 1) desc += `\n+${f(mults.hacking_exp - 1)} hacking exp`;
<>
{desc}
<br />+{f(mults.hacking_exp - 1)} hacking exp
</>
);
if ( if (
mults.strength_exp !== 1 && mults.strength_exp !== 1 &&
mults.strength_exp === mults.defense_exp && mults.strength_exp === mults.defense_exp &&
mults.strength_exp === mults.dexterity_exp && mults.strength_exp === mults.dexterity_exp &&
mults.strength_exp === mults.agility_exp mults.strength_exp === mults.agility_exp
) { ) {
desc = ( desc += `\n+${f(mults.strength_exp - 1)} combat exp`;
<>
{desc}
<br />+{f(mults.strength_exp - 1)} combat exp
</>
);
} else { } else {
if (mults.strength_exp !== 1) // Not all combat
desc = ( if (mults.strength_exp !== 1) desc += `\n+${f(mults.strength_exp - 1)} strength exp`;
<> if (mults.defense_exp !== 1) desc += `\n+${f(mults.defense_exp - 1)} defense exp`;
{desc} if (mults.dexterity_exp !== 1) desc += `\n+${f(mults.dexterity_exp - 1)} dexterity exp`;
<br />+{f(mults.strength_exp - 1)} strength exp if (mults.agility_exp !== 1) desc += `\n+${f(mults.agility_exp - 1)} agility exp`;
</>
);
if (mults.defense_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.defense_exp - 1)} defense exp
</>
);
if (mults.dexterity_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.dexterity_exp - 1)} dexterity exp
</>
);
if (mults.agility_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.agility_exp - 1)} agility exp
</>
);
} }
if (mults.charisma_exp !== 1) if (mults.charisma_exp !== 1) desc += `\n+${f(mults.charisma_exp - 1)} charisma exp`;
desc = (
<>
{desc}
<br />+{f(mults.charisma_exp - 1)} charisma exp
</>
);
} }
if (mults.hacking_speed !== 1) if (mults.hacking_speed !== 1) desc += `\n+${f(mults.hacking_speed - 1)} faster hack(), grow(), and weaken()`;
desc = ( if (mults.hacking_chance !== 1) desc += `\n+${f(mults.hacking_chance - 1)} hack() success chance`;
<> if (mults.hacking_money !== 1) desc += `\n+${f(mults.hacking_money - 1)} hack() power`;
{desc} if (mults.hacking_grow !== 1) desc += `\n+${f(mults.hacking_grow - 1)} grow() power`;
<br />+{f(mults.hacking_speed - 1)} faster hack(), grow(), and weaken()
</>
);
if (mults.hacking_chance !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacking_chance - 1)} hack() success chance
</>
);
if (mults.hacking_money !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacking_money - 1)} hack() power
</>
);
if (mults.hacking_grow !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacking_grow - 1)} grow() power
</>
);
if (mults.faction_rep !== 1 && mults.faction_rep === mults.company_rep) { // Reputation
desc = ( if (mults.faction_rep !== 1 && mults.faction_rep === mults.company_rep)
<> desc += `\n+${f(mults.faction_rep - 1)} reputation from factions and companies`;
{desc} else {
<br />+{f(mults.faction_rep - 1)} reputation from factions and companies // Not all reputation
</> if (mults.faction_rep !== 1) desc += `\n+${f(mults.faction_rep - 1)} reputation from factions`;
); if (mults.company_rep !== 1) desc += `\n+${f(mults.company_rep - 1)} reputation from companies`;
} else {
if (mults.faction_rep !== 1)
desc = (
<>
{desc}
<br />+{f(mults.faction_rep - 1)} reputation from factions
</>
);
if (mults.company_rep !== 1)
desc = (
<>
{desc}
<br />+{f(mults.company_rep - 1)} reputation from companies
</>
);
} }
if (mults.crime_money !== 1) if (mults.crime_money !== 1) desc += `\n+${f(mults.crime_money - 1)} crime money`;
desc = ( if (mults.crime_success !== 1) desc += `\n+${f(mults.crime_success - 1)} crime success rate`;
<> if (mults.work_money !== 1) desc += `\n+${f(mults.work_money - 1)} work money`;
{desc}
<br />+{f(mults.crime_money - 1)} crime money
</>
);
if (mults.crime_success !== 1)
desc = (
<>
{desc}
<br />+{f(mults.crime_success - 1)} crime success rate
</>
);
if (mults.work_money !== 1)
desc = (
<>
{desc}
<br />+{f(mults.work_money - 1)} work money
</>
);
if (mults.hacknet_node_money !== 1) // Hacknet: costs are negative
desc = ( if (mults.hacknet_node_money !== 1) desc += `\n+${f(mults.hacknet_node_money - 1)} hacknet production`;
<> if (mults.hacknet_node_purchase_cost !== 1) {
{desc} desc += `\n-${f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost`;
<br />+{f(mults.hacknet_node_money - 1)} hacknet production }
</> if (mults.hacknet_node_level_cost !== 1) {
); desc += `\n-${f(-(mults.hacknet_node_level_cost - 1))} hacknet nodes upgrade cost`;
if (mults.hacknet_node_purchase_cost !== 1) }
desc = ( // Bladeburner
<> if (mults.bladeburner_max_stamina !== 1) desc += `\n+${f(mults.bladeburner_max_stamina - 1)} Bladeburner Max Stamina`;
{desc} if (mults.bladeburner_stamina_gain !== 1) {
<br />-{f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost desc += `\n+${f(mults.bladeburner_stamina_gain - 1)} Bladeburner Stamina gain`;
</> }
); if (mults.bladeburner_analysis !== 1) {
if (mults.hacknet_node_level_cost !== 1) desc += `\n+${f(mults.bladeburner_analysis - 1)} Bladeburner Field Analysis effectiveness`;
desc = ( }
<> if (mults.bladeburner_success_chance !== 1) {
{desc} desc += `\n+${f(mults.bladeburner_success_chance - 1)} Bladeburner Contracts and Operations success chance`;
<br />-{f(-(mults.hacknet_node_level_cost - 1))} hacknet nodes upgrade cost }
</> if (startingMoney) desc += `\nStart with ${startingMoney} after installing Augmentations.`;
); if (programs) desc += `\nStart with ${programs.join(" and ")} after installing Augmentations.`;
if (mults.bladeburner_max_stamina !== 1)
desc = (
<>
{desc}
<br />+{f(mults.bladeburner_max_stamina - 1)} Bladeburner Max Stamina
</>
);
if (mults.bladeburner_stamina_gain !== 1)
desc = (
<>
{desc}
<br />+{f(mults.bladeburner_stamina_gain - 1)} Bladeburner Stamina gain
</>
);
if (mults.bladeburner_analysis !== 1)
desc = (
<>
{desc}
<br />+{f(mults.bladeburner_analysis - 1)} Bladeburner Field Analysis effectiveness
</>
);
if (mults.bladeburner_success_chance !== 1)
desc = (
<>
{desc}
<br />+{f(mults.bladeburner_success_chance - 1)} Bladeburner Contracts and Operations success chance
</>
);
if (startingMoney)
desc = (
<>
{desc}
<br />
Start with <Money money={startingMoney} /> after installing Augmentations.
</>
);
if (programs)
desc = (
<>
{desc}
<br />
Start with {programs.join(" and ")} after installing Augmentations.
</>
);
return desc; return desc;
} }
@ -372,36 +170,28 @@ export class Augmentation {
baseRepRequirement = 0; baseRepRequirement = 0;
// Description of what this Aug is and what it does // Description of what this Aug is and what it does
info: string | JSX.Element; info: string;
// Description of the stats, often autogenerated, sometimes manually written. // Description of the stats, often autogenerated, sometimes manually written.
stats: JSX.Element | null; stats: string;
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs) // Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial = false; isSpecial = false;
// Name of Augmentation // Name of Augmentation
name = ""; name: AugmentationName;
// Array of names of all prerequisites // Array of names of all prerequisites
prereqs: string[] = []; prereqs: AugmentationName[] = [];
// Multipliers given by this Augmentation. Must match the property name in // Multipliers given by this Augmentation. Must match the property name in
// The Player/Person classes // The Player/Person classes
mults: Multipliers = defaultMultipliers(); mults: Multipliers = defaultMultipliers();
// Factions that offer this aug. // Factions that offer this aug.
factions: string[] = []; factions: FactionName[] = [];
constructor( constructor(params: AugmentationCtorParams) {
params: IConstructorParams = {
info: "",
moneyCost: 0,
name: "",
repCost: 0,
factions: [],
},
) {
this.name = params.name; this.name = params.name;
this.info = params.info; this.info = params.info;
this.prereqs = params.prereqs ? params.prereqs : []; this.prereqs = params.prereqs ? params.prereqs : [];
@ -417,95 +207,9 @@ export class Augmentation {
} }
// Set multipliers // Set multipliers
if (params.hacking) { for (const multName of getRecordKeys(this.mults)) {
this.mults.hacking = params.hacking; const mult = params[multName];
} if (mult) this.mults[multName] = mult;
if (params.strength) {
this.mults.strength = params.strength;
}
if (params.defense) {
this.mults.defense = params.defense;
}
if (params.dexterity) {
this.mults.dexterity = params.dexterity;
}
if (params.agility) {
this.mults.agility = params.agility;
}
if (params.charisma) {
this.mults.charisma = params.charisma;
}
if (params.hacking_exp) {
this.mults.hacking_exp = params.hacking_exp;
}
if (params.strength_exp) {
this.mults.strength_exp = params.strength_exp;
}
if (params.defense_exp) {
this.mults.defense_exp = params.defense_exp;
}
if (params.dexterity_exp) {
this.mults.dexterity_exp = params.dexterity_exp;
}
if (params.agility_exp) {
this.mults.agility_exp = params.agility_exp;
}
if (params.charisma_exp) {
this.mults.charisma_exp = params.charisma_exp;
}
if (params.hacking_chance) {
this.mults.hacking_chance = params.hacking_chance;
}
if (params.hacking_speed) {
this.mults.hacking_speed = params.hacking_speed;
}
if (params.hacking_money) {
this.mults.hacking_money = params.hacking_money;
}
if (params.hacking_grow) {
this.mults.hacking_grow = params.hacking_grow;
}
if (params.company_rep) {
this.mults.company_rep = params.company_rep;
}
if (params.faction_rep) {
this.mults.faction_rep = params.faction_rep;
}
if (params.crime_money) {
this.mults.crime_money = params.crime_money;
}
if (params.crime_success) {
this.mults.crime_success = params.crime_success;
}
if (params.work_money) {
this.mults.work_money = params.work_money;
}
if (params.hacknet_node_money) {
this.mults.hacknet_node_money = params.hacknet_node_money;
}
if (params.hacknet_node_purchase_cost) {
this.mults.hacknet_node_purchase_cost = params.hacknet_node_purchase_cost;
}
if (params.hacknet_node_ram_cost) {
this.mults.hacknet_node_ram_cost = params.hacknet_node_ram_cost;
}
if (params.hacknet_node_core_cost) {
this.mults.hacknet_node_core_cost = params.hacknet_node_core_cost;
}
if (params.hacknet_node_level_cost) {
this.mults.hacknet_node_level_cost = params.hacknet_node_level_cost;
}
if (params.bladeburner_max_stamina) {
this.mults.bladeburner_max_stamina = params.bladeburner_max_stamina;
}
if (params.bladeburner_stamina_gain) {
this.mults.bladeburner_stamina_gain = params.bladeburner_stamina_gain;
}
if (params.bladeburner_analysis) {
this.mults.bladeburner_analysis = params.bladeburner_analysis;
}
if (params.bladeburner_success_chance) {
this.mults.bladeburner_success_chance = params.bladeburner_success_chance;
} }
if (params.stats === undefined) if (params.stats === undefined)
@ -513,89 +217,14 @@ export class Augmentation {
else this.stats = params.stats; else this.stats = params.stats;
} }
// Adds this Augmentation to the specified Factions /** Get the current level of an augmentation before buying. Currently only relevant for NFG. */
addToFactions(factionList: string[]): void {
for (let i = 0; i < factionList.length; ++i) {
const faction: Faction | null = Factions[factionList[i]];
if (faction == null) {
console.warn(`In Augmentation.addToFactions(), could not find faction with this name: ${factionList[i]}`);
continue;
}
faction.augmentations.push(this.name);
}
}
getCost(): AugmentationCosts {
const augmentationReference = StaticAugmentations[this.name];
let moneyCost = augmentationReference.baseCost;
let repCost = augmentationReference.baseRepRequirement;
if (augmentationReference.name === AugmentationName.NeuroFluxGovernor) {
let nextLevel = this.getLevel();
--nextLevel;
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
moneyCost = augmentationReference.baseCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
moneyCost *= getBaseAugmentationPriceMultiplier();
}
} else if (augmentationReference.factions.includes(FactionName.ShadowsOfAnarchy)) {
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
const soaAugCount = soaAugmentationNames.filter((augmentationName) =>
Player.hasAugmentation(augmentationName),
).length;
moneyCost = augmentationReference.baseCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount);
if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) {
repCost = augmentationReference.baseRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount);
}
} else {
moneyCost =
augmentationReference.baseCost *
getGenericAugmentationPriceMultiplier() *
BitNodeMultipliers.AugmentationMoneyCost;
repCost = augmentationReference.baseRepRequirement * BitNodeMultipliers.AugmentationRepCost;
}
return { moneyCost, repCost };
}
getLevel(): number { getLevel(): number {
// Get current Neuroflux level based on Player's augmentations // Only NFG currently has levels, all others will be level 0 before purchase
if (this.name === AugmentationName.NeuroFluxGovernor) { if (this.name !== AugmentationName.NeuroFluxGovernor) return 0;
let currLevel = 0; // Owned NFG has the level baked in
for (let i = 0; i < Player.augmentations.length; ++i) { const ownedNFGLevel = Player.augmentations.find((aug) => aug.name === this.name)?.level ?? 0;
if (Player.augmentations[i].name === AugmentationName.NeuroFluxGovernor) { // Queued NFG is queued multiple times for each level purchased
currLevel = Player.augmentations[i].level; const queuedNFGLevel = Player.queuedAugmentations.filter((aug) => aug.name === this.name).length;
} return ownedNFGLevel + queuedNFGLevel;
}
// Account for purchased but uninstalled Augmentations
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationName.NeuroFluxGovernor) {
++currLevel;
}
}
return currLevel + 1;
}
return 0;
}
// Adds this Augmentation to all Factions
addToAllFactions(): void {
for (const faction of Object.values(Factions)) {
if (!faction.getInfo().special) faction.augmentations.push(this.name);
}
}
// Serialize the current object to a JSON save state.
toJSON(): IReviverValue {
return Generic_toJSON("Augmentation", this);
}
// Initializes a Augmentation object from a JSON save state.
static fromJSON(value: IReviverValue): Augmentation {
return Generic_fromJSON(Augmentation, value.data);
} }
} }
constructorsForReviver.Augmentation = Augmentation;

@ -1,53 +1,17 @@
import { Augmentation } from "./Augmentation"; import { Augmentation } from "./Augmentation";
import { StaticAugmentations } from "./StaticAugmentations"; import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName } from "@enums";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Factions, factionExists } from "../Faction/Factions";
import { Player } from "@player"; import { Player } from "@player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import {
initBladeburnerAugmentations,
initChurchOfTheMachineGodAugmentations,
initGeneralAugmentations,
initSoAAugmentations,
initNeuroFluxGovernor,
initUnstableCircadianModulator,
} from "./data/AugmentationCreator";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router"; import { Page } from "../ui/Router";
import { mergeMultipliers } from "../PersonObjects/Multipliers"; import { mergeMultipliers } from "../PersonObjects/Multipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
export function AddToStaticAugmentations(aug: Augmentation): void {
const name = aug.name;
StaticAugmentations[name] = aug;
}
function createAugmentations(): void {
[
initNeuroFluxGovernor(),
initUnstableCircadianModulator(),
...initGeneralAugmentations(),
...initSoAAugmentations(),
...(factionExists(FactionName.Bladeburners) ? initBladeburnerAugmentations() : []),
...(factionExists(FactionName.ChurchOfTheMachineGod) ? initChurchOfTheMachineGodAugmentations() : []),
].map(resetAugmentation);
}
function resetFactionAugmentations(): void {
for (const faction of Object.values(Factions)) faction.augmentations = [];
}
function initAugmentations(): void {
resetFactionAugmentations();
for (const augName of Object.getOwnPropertyNames(StaticAugmentations)) delete StaticAugmentations[augName];
createAugmentations();
Player.reapplyAllAugmentations();
}
export function getBaseAugmentationPriceMultiplier(): number { export function getBaseAugmentationPriceMultiplier(): number {
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)]; return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
@ -56,18 +20,8 @@ export function getGenericAugmentationPriceMultiplier(): number {
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length); return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);
} }
//Resets an Augmentation during (re-initialization) export function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
function resetAugmentation(aug: Augmentation): void { const staticAugmentation = Augmentations[aug.name];
aug.addToFactions(aug.factions);
const name = aug.name;
if (augmentationExists(name)) {
delete StaticAugmentations[name];
}
AddToStaticAugmentations(aug);
}
function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
const staticAugmentation = StaticAugmentations[aug.name];
// Apply multipliers // Apply multipliers
Player.mults = mergeMultipliers(Player.mults, staticAugmentation.mults); Player.mults = mergeMultipliers(Player.mults, staticAugmentation.mults);
@ -93,7 +47,7 @@ function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void
} }
} }
function installAugmentations(force?: boolean): boolean { export function installAugmentations(force?: boolean): boolean {
if (Player.queuedAugmentations.length == 0 && !force) { if (Player.queuedAugmentations.length == 0 && !force) {
dialogBoxCreate("You have not purchased any Augmentations to install!"); dialogBoxCreate("You have not purchased any Augmentations to install!");
return false; return false;
@ -108,7 +62,7 @@ function installAugmentations(force?: boolean): boolean {
} }
for (let i = 0; i < Player.queuedAugmentations.length; ++i) { for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
const ownedAug = Player.queuedAugmentations[i]; const ownedAug = Player.queuedAugmentations[i];
const aug = StaticAugmentations[ownedAug.name]; const aug = Augmentations[ownedAug.name];
if (aug == null) { if (aug == null) {
console.error(`Invalid augmentation: ${ownedAug.name}`); console.error(`Invalid augmentation: ${ownedAug.name}`);
continue; continue;
@ -137,13 +91,59 @@ function installAugmentations(force?: boolean): boolean {
return true; return true;
} }
function augmentationExists(name: string): boolean {
return Object.hasOwn(StaticAugmentations, name);
}
export function isRepeatableAug(aug: Augmentation | string): boolean { export function isRepeatableAug(aug: Augmentation | string): boolean {
const augName = typeof aug === "string" ? aug : aug.name; const augName = typeof aug === "string" ? aug : aug.name;
return augName === AugmentationName.NeuroFluxGovernor; return augName === AugmentationName.NeuroFluxGovernor;
} }
export { installAugmentations, initAugmentations, applyAugmentation, augmentationExists }; export interface AugmentationCosts {
moneyCost: number;
repCost: number;
}
export function getAugCost(aug: Augmentation): AugmentationCosts {
let moneyCost = aug.baseCost;
let repCost = aug.baseRepRequirement;
switch (aug.name) {
// Special cost for NFG
case AugmentationName.NeuroFluxGovernor: {
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, aug.getLevel());
repCost = aug.baseRepRequirement * multiplier * currentNodeMults.AugmentationRepCost;
moneyCost = aug.baseCost * multiplier * currentNodeMults.AugmentationMoneyCost;
moneyCost *= getBaseAugmentationPriceMultiplier() ** Player.queuedAugmentations.length;
break;
}
// SOA Augments use a unique cost method
case AugmentationName.BeautyOfAphrodite:
case AugmentationName.ChaosOfDionysus:
case AugmentationName.FloodOfPoseidon:
case AugmentationName.HuntOfArtemis:
case AugmentationName.KnowledgeOfApollo:
case AugmentationName.MightOfAres:
case AugmentationName.TrickeryOfHermes:
case AugmentationName.WKSharmonizer:
case AugmentationName.WisdomOfAthena: {
const soaAugmentationNames = [
AugmentationName.BeautyOfAphrodite,
AugmentationName.ChaosOfDionysus,
AugmentationName.FloodOfPoseidon,
AugmentationName.HuntOfArtemis,
AugmentationName.KnowledgeOfApollo,
AugmentationName.MightOfAres,
AugmentationName.TrickeryOfHermes,
AugmentationName.WKSharmonizer,
AugmentationName.WisdomOfAthena,
];
const soaAugCount = soaAugmentationNames.filter((augName) => Player.hasAugmentation(augName)).length;
moneyCost = aug.baseCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount);
repCost = aug.baseRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount);
break;
}
// Standard cost
default:
moneyCost = aug.baseCost * getGenericAugmentationPriceMultiplier() * currentNodeMults.AugmentationMoneyCost;
repCost = aug.baseRepRequirement * currentNodeMults.AugmentationRepCost;
}
return { moneyCost, repCost };
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,119 @@
// This is in a separate file from the normal augmentation helpers to limit import impact on Augmentations.ts
import { Multipliers } from "@nsdefs";
import { FactionName } from "@enums";
import { AugmentationCtorParams } from "./Augmentation";
import { getRecordKeys } from "../Types/Record";
import { WHRNG } from "../Casino/RNG";
export function getUnstableCircadianModulatorParams(): Omit<AugmentationCtorParams, "name"> {
//Time-Based Augment Test
const randomBonuses = getRandomBonus();
const UnstableCircadianModulatorParams: Omit<AugmentationCtorParams, "name"> = {
moneyCost: 5e9,
repCost: 3.625e5,
info:
"An experimental nanobot injection. Its unstable nature leads to " +
"unpredictable results based on your circadian rhythm.",
factions: [FactionName.SpeakersForTheDead],
};
getRecordKeys(randomBonuses.bonuses).forEach(
(key) => (UnstableCircadianModulatorParams[key] = randomBonuses.bonuses[key]),
);
return UnstableCircadianModulatorParams;
}
interface CircadianBonus {
bonuses: Partial<Multipliers>;
description: string;
}
function getRandomBonus(): CircadianBonus {
const bonuses = [
{
bonuses: {
hacking_chance: 1.25,
hacking_speed: 1.1,
hacking_money: 1.25,
hacking_grow: 1.1,
},
description:
"Increases the player's hacking chance by 25%.\n" +
"Increases the player's hacking speed by 10%.\n" +
"Increases the amount of money the player's gains from hacking by 25%.\n" +
"Improves grow() by 10%.",
},
{
bonuses: {
hacking: 1.15,
hacking_exp: 2,
},
description:
"Increases the player's hacking skill by 15%.\n" +
"Increases the player's hacking experience gain rate by 100%.",
},
{
bonuses: {
strength: 1.25,
strength_exp: 2,
defense: 1.25,
defense_exp: 2,
dexterity: 1.25,
dexterity_exp: 2,
agility: 1.25,
agility_exp: 2,
},
description:
"Increases all of the player's combat stats by 25%.\n" +
"Increases all of the player's combat stat experience gain rate by 100%.",
},
{
bonuses: {
charisma: 1.5,
charisma_exp: 2,
},
description:
"This augmentation increases the player's charisma by 50%.\n" +
"Increases the player's charisma experience gain rate by 100%.",
},
{
bonuses: {
hacknet_node_money: 1.2,
hacknet_node_purchase_cost: 0.85,
hacknet_node_ram_cost: 0.85,
hacknet_node_core_cost: 0.85,
hacknet_node_level_cost: 0.85,
},
description:
"Increases the amount of money produced by Hacknet Nodes by 20%.\n" +
"Decreases all costs related to Hacknet Node by 15%.",
},
{
bonuses: {
company_rep: 1.25,
faction_rep: 1.15,
work_money: 1.7,
},
description:
"Increases the amount of money the player gains from working by 70%.\n" +
"Increases the amount of reputation the player gains when working for a company by 25%.\n" +
"Increases the amount of reputation the player gains for a faction by 15%.",
},
{
bonuses: {
crime_success: 2,
crime_money: 2,
},
description:
"Increases the player's crime success rate by 100%.\n" +
"Increases the amount of money the player gains from crimes by 100%.",
},
];
const randomNumber = new WHRNG(Math.floor(Date.now() / 3600000));
for (let i = 0; i < 5; i++) randomNumber.step();
return bonuses[Math.floor(bonuses.length * randomNumber.random())];
}

@ -1,8 +1,10 @@
import type { AugmentationName } from "./Enums";
export class PlayerOwnedAugmentation { export class PlayerOwnedAugmentation {
level = 1; level = 1;
name = ""; name: AugmentationName;
constructor(name = "") { constructor(name: AugmentationName) {
this.name = name; this.name = name;
} }
} }

@ -1,3 +0,0 @@
import { Augmentation } from "./Augmentation";
export const StaticAugmentations: Record<string, Augmentation> = {};

File diff suppressed because it is too large Load Diff

@ -20,7 +20,7 @@ import { Settings } from "../../Settings/Settings";
import { ConfirmationModal } from "../../ui/React/ConfirmationModal"; import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
import { Player } from "@player"; import { Player } from "@player";
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
import { StaticAugmentations } from "../StaticAugmentations"; import { Augmentations } from "../Augmentations";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { formatNumberNoSuffix } from "../../ui/formatNumber"; import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { Info } from "@mui/icons-material"; import { Info } from "@mui/icons-material";
@ -49,9 +49,7 @@ const NeuroFluxDisplay = (): React.ReactElement => {
<Typography variant="h5" color={Settings.theme.info}> <Typography variant="h5" color={Settings.theme.info}>
NeuroFlux Governor - Level {level} NeuroFlux Governor - Level {level}
</Typography> </Typography>
<Typography color={Settings.theme.info}> <Typography color={Settings.theme.info}>{Augmentations[AugmentationName.NeuroFluxGovernor].stats}</Typography>
{StaticAugmentations[AugmentationName.NeuroFluxGovernor].stats}
</Typography>
<Typography color={Settings.theme.info}> <Typography color={Settings.theme.info}>
The power of {AugmentationName.NeuroFluxGovernor} increases with blood donations from players in real life. The power of {AugmentationName.NeuroFluxGovernor} increases with blood donations from players in real life.
Learn more <Link onClick={openBloodDonation}>here</Link> Learn more <Link onClick={openBloodDonation}>here</Link>

@ -13,7 +13,7 @@ import React, { useState } from "react";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Player } from "@player"; import { Player } from "@player";
import { StaticAugmentations } from "../StaticAugmentations"; import { Augmentations } from "../Augmentations";
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
import { useRerender } from "../../ui/React/hooks"; import { useRerender } from "../../ui/React/hooks";
@ -73,7 +73,7 @@ export function InstalledAugmentations(): React.ReactElement {
</Typography> </Typography>
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}> <Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
{(() => { {(() => {
const aug = StaticAugmentations[selectedAug.name]; const aug = Augmentations[selectedAug.name];
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info; const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
const tooltip = ( const tooltip = (

@ -2,16 +2,16 @@ import { DoubleArrow } from "@mui/icons-material";
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material"; import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { Multipliers, defaultMultipliers, mergeMultipliers } from "../../PersonObjects/Multipliers"; import { Multipliers, defaultMultipliers, mergeMultipliers } from "../../PersonObjects/Multipliers";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Player } from "@player"; import { Player } from "@player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { formatPercent } from "../../ui/formatNumber"; import { formatPercent } from "../../ui/formatNumber";
import { StaticAugmentations } from "../StaticAugmentations"; import { Augmentations } from "../Augmentations";
function calculateAugmentedStats(): Multipliers { function calculateAugmentedStats(): Multipliers {
let augP: Multipliers = defaultMultipliers(); let augP: Multipliers = defaultMultipliers();
for (const aug of Player.queuedAugmentations) { for (const aug of Player.queuedAugmentations) {
const augObj = StaticAugmentations[aug.name]; const augObj = Augmentations[aug.name];
augP = mergeMultipliers(augP, augObj.mults); augP = mergeMultipliers(augP, augObj.mults);
} }
return augP; return augP;
@ -101,7 +101,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Hacking Money", mult: "Hacking Money",
current: Player.mults.hacking_money, current: Player.mults.hacking_money,
augmented: Player.mults.hacking_money * mults.hacking_money, augmented: Player.mults.hacking_money * mults.hacking_money,
bnMult: BitNodeMultipliers.ScriptHackMoney, bnMult: currentNodeMults.ScriptHackMoney,
}, },
{ {
mult: "Hacking Growth", mult: "Hacking Growth",
@ -112,13 +112,13 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Hacking Level", mult: "Hacking Level",
current: Player.mults.hacking, current: Player.mults.hacking,
augmented: Player.mults.hacking * mults.hacking, augmented: Player.mults.hacking * mults.hacking,
bnMult: BitNodeMultipliers.HackingLevelMultiplier, bnMult: currentNodeMults.HackingLevelMultiplier,
}, },
{ {
mult: "Hacking Experience", mult: "Hacking Experience",
current: Player.mults.hacking_exp, current: Player.mults.hacking_exp,
augmented: Player.mults.hacking_exp * mults.hacking_exp, augmented: Player.mults.hacking_exp * mults.hacking_exp,
bnMult: BitNodeMultipliers.HackExpGain, bnMult: currentNodeMults.HackExpGain,
}, },
].map((data: MultiplierListItemData) => ].map((data: MultiplierListItemData) =>
Object.defineProperty(data, "color", { Object.defineProperty(data, "color", {
@ -130,7 +130,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Strength Level", mult: "Strength Level",
current: Player.mults.strength, current: Player.mults.strength,
augmented: Player.mults.strength * mults.strength, augmented: Player.mults.strength * mults.strength,
bnMult: BitNodeMultipliers.StrengthLevelMultiplier, bnMult: currentNodeMults.StrengthLevelMultiplier,
}, },
{ {
mult: "Strength Experience", mult: "Strength Experience",
@ -141,7 +141,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Defense Level", mult: "Defense Level",
current: Player.mults.defense, current: Player.mults.defense,
augmented: Player.mults.defense * mults.defense, augmented: Player.mults.defense * mults.defense,
bnMult: BitNodeMultipliers.DefenseLevelMultiplier, bnMult: currentNodeMults.DefenseLevelMultiplier,
}, },
{ {
mult: "Defense Experience", mult: "Defense Experience",
@ -152,7 +152,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Dexterity Level", mult: "Dexterity Level",
current: Player.mults.dexterity, current: Player.mults.dexterity,
augmented: Player.mults.dexterity * mults.dexterity, augmented: Player.mults.dexterity * mults.dexterity,
bnMult: BitNodeMultipliers.DexterityLevelMultiplier, bnMult: currentNodeMults.DexterityLevelMultiplier,
}, },
{ {
mult: "Dexterity Experience", mult: "Dexterity Experience",
@ -163,7 +163,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Agility Level", mult: "Agility Level",
current: Player.mults.agility, current: Player.mults.agility,
augmented: Player.mults.agility * mults.agility, augmented: Player.mults.agility * mults.agility,
bnMult: BitNodeMultipliers.AgilityLevelMultiplier, bnMult: currentNodeMults.AgilityLevelMultiplier,
}, },
{ {
mult: "Agility Experience", mult: "Agility Experience",
@ -179,7 +179,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Charisma Level", mult: "Charisma Level",
current: Player.mults.charisma, current: Player.mults.charisma,
augmented: Player.mults.charisma * mults.charisma, augmented: Player.mults.charisma * mults.charisma,
bnMult: BitNodeMultipliers.CharismaLevelMultiplier, bnMult: currentNodeMults.CharismaLevelMultiplier,
color: Settings.theme.cha, color: Settings.theme.cha,
}, },
{ {
@ -194,7 +194,7 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Hacknet Node Production", mult: "Hacknet Node Production",
current: Player.mults.hacknet_node_money, current: Player.mults.hacknet_node_money,
augmented: Player.mults.hacknet_node_money * mults.hacknet_node_money, augmented: Player.mults.hacknet_node_money * mults.hacknet_node_money,
bnMult: BitNodeMultipliers.HacknetNodeMoney, bnMult: currentNodeMults.HacknetNodeMoney,
}, },
{ {
mult: "Hacknet Node Purchase Cost", mult: "Hacknet Node Purchase Cost",
@ -226,14 +226,14 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Faction Reputation Gain", mult: "Faction Reputation Gain",
current: Player.mults.faction_rep, current: Player.mults.faction_rep,
augmented: Player.mults.faction_rep * mults.faction_rep, augmented: Player.mults.faction_rep * mults.faction_rep,
bnMult: BitNodeMultipliers.FactionWorkRepGain, bnMult: currentNodeMults.FactionWorkRepGain,
color: Settings.theme.combat, color: Settings.theme.combat,
}, },
{ {
mult: "Salary", mult: "Salary",
current: Player.mults.work_money, current: Player.mults.work_money,
augmented: Player.mults.work_money * mults.work_money, augmented: Player.mults.work_money * mults.work_money,
bnMult: BitNodeMultipliers.CompanyWorkMoney, bnMult: currentNodeMults.CompanyWorkMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
{ {
@ -246,12 +246,12 @@ export function PlayerMultipliers(): React.ReactElement {
mult: "Crime Money", mult: "Crime Money",
current: Player.mults.crime_money, current: Player.mults.crime_money,
augmented: Player.mults.crime_money * mults.crime_money, augmented: Player.mults.crime_money * mults.crime_money,
bnMult: BitNodeMultipliers.CrimeMoney, bnMult: currentNodeMults.CrimeMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
]; ];
if (Player.canAccessBladeburner() && BitNodeMultipliers.BladeburnerRank > 0) { if (Player.canAccessBladeburner() && currentNodeMults.BladeburnerRank > 0) {
rightColData.push( rightColData.push(
{ {
mult: "Bladeburner Success Chance", mult: "Bladeburner Success Chance",

@ -11,8 +11,9 @@ import { Settings } from "../../Settings/Settings";
import { formatMoney, formatReputation } from "../../ui/formatNumber"; import { formatMoney, formatReputation } from "../../ui/formatNumber";
import { Augmentation } from "../Augmentation"; import { Augmentation } from "../Augmentation";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { StaticAugmentations } from "../StaticAugmentations"; import { Augmentations } from "../Augmentations";
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal"; import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
import { getAugCost } from "../AugmentationHelpers";
interface IPreReqsProps { interface IPreReqsProps {
aug: Augmentation; aug: Augmentation;
@ -126,8 +127,8 @@ const Requirement = (props: IReqProps): React.ReactElement => {
}; };
interface IPurchasableAugsProps { interface IPurchasableAugsProps {
augNames: string[]; augNames: AugmentationName[];
ownedAugNames: string[]; ownedAugNames: AugmentationName[];
canPurchase: (aug: Augmentation) => boolean; canPurchase: (aug: Augmentation) => boolean;
purchaseAugmentation: (aug: Augmentation, showModal: (open: boolean) => void) => void; purchaseAugmentation: (aug: Augmentation, showModal: (open: boolean) => void) => void;
@ -144,10 +145,10 @@ export const PurchasableAugmentations = (props: IPurchasableAugsProps): React.Re
disableGutters disableGutters
sx={{ mx: 0, display: "grid", gridTemplateColumns: "repeat(1, 1fr)", gap: 0.75 }} sx={{ mx: 0, display: "grid", gridTemplateColumns: "repeat(1, 1fr)", gap: 0.75 }}
> >
{props.augNames.map((augName: string) => ( {props.augNames.map((augName) => (
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={false} /> <PurchasableAugmentation key={augName} parent={props} augName={augName} owned={false} />
))} ))}
{props.ownedAugNames.map((augName: string) => ( {props.ownedAugNames.map((augName) => (
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={true} /> <PurchasableAugmentation key={augName} parent={props} augName={augName} owned={true} />
))} ))}
</Container> </Container>
@ -156,16 +157,17 @@ export const PurchasableAugmentations = (props: IPurchasableAugsProps): React.Re
interface IPurchasableAugProps { interface IPurchasableAugProps {
parent: IPurchasableAugsProps; parent: IPurchasableAugsProps;
augName: string; augName: AugmentationName;
owned: boolean; owned: boolean;
} }
export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement { export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const aug = StaticAugmentations[props.augName]; const aug = Augmentations[props.augName];
if (!aug) return <></>; if (!aug) return <></>;
const augCosts = aug.getCost(); const augLevel = aug.getLevel();
const augCosts = getAugCost(aug);
const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost; const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost;
const repCost = augCosts.repCost; const repCost = augCosts.repCost;
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info; const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
@ -209,8 +211,8 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
title={ title={
<> <>
<Typography variant="h5"> <Typography variant="h5">
{props.augName} {aug.name}
{props.augName === AugmentationName.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`} {aug.name === AugmentationName.NeuroFluxGovernor && ` - Level ${augLevel + 1}`}
</Typography> </Typography>
<Typography>{description}</Typography> <Typography>{description}</Typography>
</> </>
@ -227,7 +229,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
}} }}
> >
{aug.name} {aug.name}
{aug.name === AugmentationName.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`} {aug.name === AugmentationName.NeuroFluxGovernor && ` - Level ${augLevel + 1}`}
</Typography> </Typography>
</Tooltip> </Tooltip>

@ -3,7 +3,7 @@ import React from "react";
import { Augmentation } from "../Augmentation"; import { Augmentation } from "../Augmentation";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { purchaseAugmentation } from "../../Faction/FactionHelpers"; import { purchaseAugmentation } from "../../Faction/FactionHelpers";
import { isRepeatableAug } from "../AugmentationHelpers"; import { getAugCost, isRepeatableAug } from "../AugmentationHelpers";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { Player } from "@player"; import { Player } from "@player";
@ -33,7 +33,7 @@ export function PurchaseAugmentationModal({ aug, faction, onClose, open }: IProp
<br /> <br />
<br /> <br />
Would you like to purchase the {aug.name} Augmentation for&nbsp; Would you like to purchase the {aug.name} Augmentation for&nbsp;
<Money money={aug.getCost().moneyCost} />? <Money money={getAugCost(aug).moneyCost} />?
<br /> <br />
<br /> <br />
</Typography> </Typography>

@ -5,7 +5,7 @@
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material"; import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { Player } from "@player"; import { Player } from "@player";
import { StaticAugmentations } from "../StaticAugmentations"; import { Augmentations } from "../Augmentations";
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
export function PurchasedAugmentations(): React.ReactElement { export function PurchasedAugmentations(): React.ReactElement {
@ -20,10 +20,10 @@ export function PurchasedAugmentations(): React.ReactElement {
} }
for (let i = 0; i < Player.queuedAugmentations.length; i++) { for (let i = 0; i < Player.queuedAugmentations.length; i++) {
const ownedAug = Player.queuedAugmentations[i]; const ownedAug = Player.queuedAugmentations[i];
let displayName = ownedAug.name; let displayName: string = ownedAug.name;
if (ownedAug.name === AugmentationName.NeuroFluxGovernor && i !== nfgIndex) continue; if (ownedAug.name === AugmentationName.NeuroFluxGovernor && i !== nfgIndex) continue;
const aug = StaticAugmentations[ownedAug.name]; const aug = Augmentations[ownedAug.name];
let level = null; let level = null;
if (ownedAug.name === AugmentationName.NeuroFluxGovernor) { if (ownedAug.name === AugmentationName.NeuroFluxGovernor) {

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Player } from "@player"; import { Player } from "@player";
import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNodeMultipliers";
import { CityName, FactionName } from "@enums"; import { CityName, FactionName } from "@enums";
import { BitNodeMultipliers, replaceCurrentNodeMults } from "./BitNodeMultipliers";
class BitNode { class BitNode {
// A short description, or tagline, about the BitNode // A short description, or tagline, about the BitNode
@ -447,82 +447,16 @@ export function initBitNodes() {
); );
} }
export const defaultMultipliers: IBitNodeMultipliers = { export const defaultMultipliers = new BitNodeMultipliers();
HackingLevelMultiplier: 1,
StrengthLevelMultiplier: 1,
DefenseLevelMultiplier: 1,
DexterityLevelMultiplier: 1,
AgilityLevelMultiplier: 1,
CharismaLevelMultiplier: 1,
ServerGrowthRate: 1,
ServerMaxMoney: 1,
ServerStartingMoney: 1,
ServerStartingSecurity: 1,
ServerWeakenRate: 1,
HomeComputerRamCost: 1,
PurchasedServerCost: 1,
PurchasedServerSoftcap: 1,
PurchasedServerLimit: 1,
PurchasedServerMaxRam: 1,
CompanyWorkMoney: 1,
CrimeMoney: 1,
HacknetNodeMoney: 1,
ManualHackMoney: 1,
ScriptHackMoney: 1,
ScriptHackMoneyGain: 1,
CodingContractMoney: 1,
ClassGymExpGain: 1,
CompanyWorkExpGain: 1,
CrimeExpGain: 1,
FactionWorkExpGain: 1,
HackExpGain: 1,
FactionPassiveRepGain: 1,
FactionWorkRepGain: 1,
RepToDonateToFaction: 1,
AugmentationMoneyCost: 1,
AugmentationRepCost: 1,
InfiltrationMoney: 1,
InfiltrationRep: 1,
FourSigmaMarketDataCost: 1,
FourSigmaMarketDataApiCost: 1,
CorporationValuation: 1,
CorporationSoftcap: 1,
CorporationDivisions: 1,
BladeburnerRank: 1,
BladeburnerSkillCost: 1,
GangSoftcap: 1,
GangUniqueAugs: 1,
DaedalusAugsRequirement: 30,
StaneksGiftPowerMultiplier: 1,
StaneksGiftExtraSize: 0,
WorldDaemonDifficulty: 1,
};
Object.freeze(defaultMultipliers); Object.freeze(defaultMultipliers);
export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultipliers { export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultipliers {
const mults = Object.assign({}, defaultMultipliers);
switch (n) { switch (n) {
case 1: { case 1: {
return mults; return new BitNodeMultipliers();
} }
case 2: { case 2: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.8, HackingLevelMultiplier: 0.8,
ServerGrowthRate: 0.8, ServerGrowthRate: 0.8,
@ -546,7 +480,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 3: { case 3: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.8, HackingLevelMultiplier: 0.8,
ServerGrowthRate: 0.2, ServerGrowthRate: 0.2,
@ -578,7 +512,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 4: { case 4: {
return Object.assign(mults, { return new BitNodeMultipliers({
ServerMaxMoney: 0.1125, ServerMaxMoney: 0.1125,
ServerStartingMoney: 0.75, ServerStartingMoney: 0.75,
@ -606,7 +540,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 5: { case 5: {
return Object.assign(mults, { return new BitNodeMultipliers({
ServerStartingSecurity: 2, ServerStartingSecurity: 2,
ServerStartingMoney: 0.5, ServerStartingMoney: 0.5,
@ -635,7 +569,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 6: { case 6: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35, HackingLevelMultiplier: 0.35,
ServerMaxMoney: 0.2, ServerMaxMoney: 0.2,
@ -669,7 +603,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 7: { case 7: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35, HackingLevelMultiplier: 0.35,
ServerMaxMoney: 0.2, ServerMaxMoney: 0.2,
@ -711,7 +645,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 8: { case 8: {
return Object.assign(mults, { return new BitNodeMultipliers({
PurchasedServerSoftcap: 4, PurchasedServerSoftcap: 4,
CompanyWorkMoney: 0, CompanyWorkMoney: 0,
@ -739,7 +673,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 9: { case 9: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.5, HackingLevelMultiplier: 0.5,
StrengthLevelMultiplier: 0.45, StrengthLevelMultiplier: 0.45,
DefenseLevelMultiplier: 0.45, DefenseLevelMultiplier: 0.45,
@ -780,7 +714,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 10: { case 10: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35, HackingLevelMultiplier: 0.35,
StrengthLevelMultiplier: 0.4, StrengthLevelMultiplier: 0.4,
DefenseLevelMultiplier: 0.4, DefenseLevelMultiplier: 0.4,
@ -823,7 +757,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 11: { case 11: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.6, HackingLevelMultiplier: 0.6,
ServerGrowthRate: 0.2, ServerGrowthRate: 0.2,
@ -861,8 +795,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
const inc = Math.pow(1.02, lvl); const inc = Math.pow(1.02, lvl);
const dec = 1 / inc; const dec = 1 / inc;
return Object.assign(mults, { return new BitNodeMultipliers({
DaedalusAugsRequirement: Math.floor(Math.min(mults.DaedalusAugsRequirement + inc, 40)), DaedalusAugsRequirement: Math.floor(Math.min(defaultMultipliers.DaedalusAugsRequirement + inc, 40)),
HackingLevelMultiplier: dec, HackingLevelMultiplier: dec,
StrengthLevelMultiplier: dec, StrengthLevelMultiplier: dec,
@ -929,7 +863,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}); });
} }
case 13: { case 13: {
return Object.assign(mults, { return new BitNodeMultipliers({
HackingLevelMultiplier: 0.25, HackingLevelMultiplier: 0.25,
StrengthLevelMultiplier: 0.7, StrengthLevelMultiplier: 0.7,
DefenseLevelMultiplier: 0.7, DefenseLevelMultiplier: 0.7,
@ -982,5 +916,5 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
} }
export function initBitNodeMultipliers(): void { export function initBitNodeMultipliers(): void {
Object.assign(BitNodeMultipliers, getBitNodeMultipliers(Player.bitNodeN, Player.sourceFileLvl(Player.bitNodeN) + 1)); replaceCurrentNodeMults(getBitNodeMultipliers(Player.bitNodeN, Player.sourceFileLvl(Player.bitNodeN) + 1));
} }

@ -1,168 +1,173 @@
import { defaultMultipliers } from "./BitNode"; import { PartialRecord, getRecordEntries } from "../Types/Record";
/** /**
* Bitnode multipliers influence the difficulty of different aspects of the game. * Bitnode multipliers influence the difficulty of different aspects of the game.
* Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the * Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the
* player toward the intended strategy. Unless they really want to play the long, slow game of waiting... * player toward the intended strategy. Unless they really want to play the long, slow game of waiting...
*/ */
export interface IBitNodeMultipliers { export class BitNodeMultipliers {
/** Influences how quickly the player's agility level (not exp) scales */ /** Influences how quickly the player's agility level (not exp) scales */
AgilityLevelMultiplier: number; AgilityLevelMultiplier = 1;
/** Influences the base cost to purchase an augmentation. */ /** Influences the base cost to purchase an augmentation. */
AugmentationMoneyCost: number; AugmentationMoneyCost = 1;
/** Influences the base rep the player must have with a faction to purchase an augmentation. */ /** Influences the base rep the player must have with a faction to purchase an augmentation. */
AugmentationRepCost: number; AugmentationRepCost = 1;
/** Influences how quickly the player can gain rank within Bladeburner. */ /** Influences how quickly the player can gain rank within Bladeburner. */
BladeburnerRank: number; BladeburnerRank = 1;
/** Influences the cost of skill levels from Bladeburner. */ /** Influences the cost of skill levels from Bladeburner. */
BladeburnerSkillCost: number; BladeburnerSkillCost = 1;
/** Influences how quickly the player's charisma level (not exp) scales */ /** Influences how quickly the player's charisma level (not exp) scales */
CharismaLevelMultiplier: number; CharismaLevelMultiplier = 1;
/** Influences the experience gained for each ability when a player completes a class. */ /** Influences the experience gained for each ability when a player completes a class. */
ClassGymExpGain: number; ClassGymExpGain = 1;
/**Influences the amount of money gained from completing Coding Contracts. */ /**Influences the amount of money gained from completing Coding Contracts. */
CodingContractMoney: number; CodingContractMoney = 1;
/** Influences the experience gained for each ability when the player completes working their job. */ /** Influences the experience gained for each ability when the player completes working their job. */
CompanyWorkExpGain: number; CompanyWorkExpGain = 1;
/** Influences how much money the player earns when completing working their job. */ /** Influences how much money the player earns when completing working their job. */
CompanyWorkMoney: number; CompanyWorkMoney = 1;
/** Influences the valuation of corporations created by the player. */ /** Influences the valuation of corporations created by the player. */
CorporationValuation: number; CorporationValuation = 1;
/** Influences the base experience gained for each ability when the player commits a crime. */ /** Influences the base experience gained for each ability when the player commits a crime. */
CrimeExpGain: number; CrimeExpGain = 1;
/** Influences the base money gained when the player commits a crime. */ /** Influences the base money gained when the player commits a crime. */
CrimeMoney: number; CrimeMoney = 1;
/** Influences how many Augmentations you need in order to get invited to the Daedalus faction */ /** Influences how many Augmentations you need in order to get invited to the Daedalus faction */
DaedalusAugsRequirement: number; DaedalusAugsRequirement = 30;
/** Influences how quickly the player's defense level (not exp) scales */ /** Influences how quickly the player's defense level (not exp) scales */
DefenseLevelMultiplier: number; DefenseLevelMultiplier = 1;
/** Influences how quickly the player's dexterity level (not exp) scales */ /** Influences how quickly the player's dexterity level (not exp) scales */
DexterityLevelMultiplier: number; DexterityLevelMultiplier = 1;
/** Influences how much rep the player gains in each faction simply by being a member. */ /** Influences how much rep the player gains in each faction simply by being a member. */
FactionPassiveRepGain: number; FactionPassiveRepGain = 1;
/** Influences the experience gained for each ability when the player completes work for a Faction. */ /** Influences the experience gained for each ability when the player completes work for a Faction. */
FactionWorkExpGain: number; FactionWorkExpGain = 1;
/** Influences how much rep the player gains when performing work for a faction. */ /** Influences how much rep the player gains when performing work for a faction. */
FactionWorkRepGain: number; FactionWorkRepGain = 1;
/** Influences how much it costs to unlock the stock market's 4S Market Data API */ /** Influences how much it costs to unlock the stock market's 4S Market Data API */
FourSigmaMarketDataApiCost: number; FourSigmaMarketDataApiCost = 1;
/** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */ /** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */
FourSigmaMarketDataCost: number; FourSigmaMarketDataCost = 1;
/** Reduces gangs earning. */ /** Reduces gangs earning. */
GangSoftcap: number; GangSoftcap = 1;
/** Percentage of unique augs that the gang has. */ /** Percentage of unique augs that the gang has. */
GangUniqueAugs: number; GangUniqueAugs = 1;
/** Influences the experienced gained when hacking a server. */ /** Influences the experienced gained when hacking a server. */
HackExpGain: number; HackExpGain = 1;
/** Influences how quickly the player's hacking level (not experience) scales */ /** Influences how quickly the player's hacking level (not experience) scales */
HackingLevelMultiplier: number; HackingLevelMultiplier = 1;
/** /**
* Influences how much money is produced by Hacknet Nodes. * Influences how much money is produced by Hacknet Nodes.
* Influences the hash rate of Hacknet Servers (unlocked in BitNode-9) * Influences the hash rate of Hacknet Servers (unlocked in BitNode-9)
*/ */
HacknetNodeMoney: number; HacknetNodeMoney = 1;
/** Influences how much money it costs to upgrade your home computer's RAM */ /** Influences how much money it costs to upgrade your home computer's RAM */
HomeComputerRamCost: number; HomeComputerRamCost = 1;
/** Influences how much money is gained when the player infiltrates a company. */ /** Influences how much money is gained when the player infiltrates a company. */
InfiltrationMoney: number; InfiltrationMoney = 1;
/** Influences how much rep the player can gain from factions when selling stolen documents and secrets */ /** Influences how much rep the player can gain from factions when selling stolen documents and secrets */
InfiltrationRep: number; InfiltrationRep = 1;
/** /**
* Influences how much money can be stolen from a server when the player performs a hack against it through * Influences how much money can be stolen from a server when the player performs a hack against it through
* the Terminal. * the Terminal.
*/ */
ManualHackMoney: number; ManualHackMoney = 1;
/** Influence how much it costs to purchase a server */ /** Influence how much it costs to purchase a server */
PurchasedServerCost: number; PurchasedServerCost = 1;
/** Influence how much it costs to purchase a server */ /** Influence how much it costs to purchase a server */
PurchasedServerSoftcap: number; PurchasedServerSoftcap = 1;
/** Influences the maximum number of purchased servers you can have */ /** Influences the maximum number of purchased servers you can have */
PurchasedServerLimit: number; PurchasedServerLimit = 1;
/** Influences the maximum allowed RAM for a purchased server */ /** Influences the maximum allowed RAM for a purchased server */
PurchasedServerMaxRam: number; PurchasedServerMaxRam = 1;
/** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */ /** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */
RepToDonateToFaction: number; RepToDonateToFaction = 1;
/** Influences how much money can be stolen from a server when a script performs a hack against it. */ /** Influences how much money can be stolen from a server when a script performs a hack against it. */
ScriptHackMoney: number; ScriptHackMoney = 1;
/** /**
* The amount of money actually gained when script hack a server. This is * The amount of money actually gained when script hack a server. This is
* different than the above because you can reduce the amount of money but * different than the above because you can reduce the amount of money but
* not gain that same amount. * not gain that same amount.
*/ */
ScriptHackMoneyGain: number; ScriptHackMoneyGain = 1;
/** Influences the growth percentage per cycle against a server. */ /** Influences the growth percentage per cycle against a server. */
ServerGrowthRate: number; ServerGrowthRate = 1;
/** Influences the maximum money that a server can grow to. */ /** Influences the maximum money that a server can grow to. */
ServerMaxMoney: number; ServerMaxMoney = 1;
/** Influences the initial money that a server starts with. */ /** Influences the initial money that a server starts with. */
ServerStartingMoney: number; ServerStartingMoney = 1;
/** Influences the initial security level (hackDifficulty) of a server. */ /** Influences the initial security level (hackDifficulty) of a server. */
ServerStartingSecurity: number; ServerStartingSecurity = 1;
/** Influences the weaken amount per invocation against a server. */ /** Influences the weaken amount per invocation against a server. */
ServerWeakenRate: number; ServerWeakenRate = 1;
/** Influences how quickly the player's strength level (not exp) scales */ /** Influences how quickly the player's strength level (not exp) scales */
StrengthLevelMultiplier: number; StrengthLevelMultiplier = 1;
/** Influences the power of the gift. */ /** Influences the power of the gift. */
StaneksGiftPowerMultiplier: number; StaneksGiftPowerMultiplier = 1;
/** Influences the size of the gift. */ /** Influences the size of the gift. */
StaneksGiftExtraSize: number; StaneksGiftExtraSize = 0;
/** Influences the hacking skill required to backdoor the world daemon. */ /** Influences the hacking skill required to backdoor the world daemon. */
WorldDaemonDifficulty: number; WorldDaemonDifficulty = 1;
/** Influences profits from corporation dividends and selling shares. */ /** Influences profits from corporation dividends and selling shares. */
CorporationSoftcap: number; CorporationSoftcap = 1;
/** Influences number of divisions a corporation can have. */ /** Influences number of divisions a corporation can have. */
CorporationDivisions: number; CorporationDivisions = 1;
// Index signature constructor(a: PartialRecord<keyof BitNodeMultipliers, number> = {}) {
[key: string]: number; for (const [key, value] of getRecordEntries(a)) this[key] = value;
}
} }
/** The multipliers that are influenced by current Bitnode progression. */ /** The multipliers currently in effect */
export const BitNodeMultipliers = Object.assign({}, defaultMultipliers); export let currentNodeMults = new BitNodeMultipliers();
export function replaceCurrentNodeMults(mults: BitNodeMultipliers) {
currentNodeMults = mults;
}

@ -1,14 +1,16 @@
import React from "react";
import { uniqueId } from "lodash";
import { Box, Collapse, ListItemButton, ListItemText, Paper, Table, TableBody, Typography } from "@mui/material";
import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore"; import ExpandMore from "@mui/icons-material/ExpandMore";
import { Box, Collapse, ListItemButton, ListItemText, Paper, Table, TableBody, Typography } from "@mui/material";
import { uniqueId } from "lodash"; import { Player } from "@player";
import React from "react";
import { SpecialServers } from "../../Server/data/SpecialServers"; import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Player } from "@player";
import { StatsRow } from "../../ui/React/StatsRow"; import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode"; import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNodeMultipliers";
import { PartialRecord, getRecordEntries } from "../../Types/Record";
interface IProps { interface IProps {
n: number; n: number;
@ -60,8 +62,8 @@ export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElem
); );
}; };
type IBNMultRows = Record< type IBNMultRows = PartialRecord<
string, keyof BitNodeMultipliers,
{ {
name: string; name: string;
content?: string; content?: string;
@ -72,11 +74,11 @@ type IBNMultRows = Record<
interface IBNMultTableProps { interface IBNMultTableProps {
sectionName: string; sectionName: string;
rowData: IBNMultRows; rowData: IBNMultRows;
mults: IBitNodeMultipliers; mults: BitNodeMultipliers;
} }
const BNMultTable = (props: IBNMultTableProps): React.ReactElement => { const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
const rowsArray = Object.entries(props.rowData) const rowsArray = getRecordEntries(props.rowData)
.filter(([key]) => props.mults[key] !== defaultMultipliers[key]) .filter(([key]) => props.mults[key] !== defaultMultipliers[key])
.map(([key, value]) => ( .map(([key, value]) => (
<StatsRow <StatsRow
@ -101,7 +103,7 @@ const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
interface IMultsProps { interface IMultsProps {
n: number; n: number;
mults: IBitNodeMultipliers; mults: BitNodeMultipliers;
} }
function GeneralMults({ mults }: IMultsProps): React.ReactElement { function GeneralMults({ mults }: IMultsProps): React.ReactElement {

@ -21,7 +21,7 @@ import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber"; import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
@ -1281,7 +1281,7 @@ export class Bladeburner {
action.setMaxLevel(BladeburnerConstants.ContractSuccessesPerLevel); action.setMaxLevel(BladeburnerConstants.ContractSuccessesPerLevel);
} }
if (action.rankGain) { if (action.rankGain) {
const gain = addOffset(action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank, 10); const gain = addOffset(action.rankGain * rewardMultiplier * currentNodeMults.BladeburnerRank, 10);
this.changeRank(person, gain); this.changeRank(person, gain);
if (isOperation && this.logging.ops) { if (isOperation && this.logging.ops) {
this.log( this.log(
@ -1365,7 +1365,7 @@ export class Bladeburner {
this.blackops[action.name] = true; this.blackops[action.name] = true;
let rankGain = 0; let rankGain = 0;
if (action.rankGain) { if (action.rankGain) {
rankGain = addOffset(action.rankGain * BitNodeMultipliers.BladeburnerRank, 10); rankGain = addOffset(action.rankGain * currentNodeMults.BladeburnerRank, 10);
this.changeRank(person, rankGain); this.changeRank(person, rankGain);
} }
teamLossMax = Math.ceil(teamCount / 2); teamLossMax = Math.ceil(teamCount / 2);
@ -1481,7 +1481,7 @@ export class Bladeburner {
} }
const hackingExpGain = 20 * person.mults.hacking_exp; const hackingExpGain = 20 * person.mults.hacking_exp;
const charismaExpGain = 20 * person.mults.charisma_exp; const charismaExpGain = 20 * person.mults.charisma_exp;
const rankGain = 0.1 * BitNodeMultipliers.BladeburnerRank; const rankGain = 0.1 * currentNodeMults.BladeburnerRank;
retValue.hackExp = hackingExpGain; retValue.hackExp = hackingExpGain;
retValue.chaExp = charismaExpGain; retValue.chaExp = charismaExpGain;
retValue.intExp = BladeburnerConstants.BaseIntGain; retValue.intExp = BladeburnerConstants.BaseIntGain;

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
interface ISkillParams { interface ISkillParams {
name: string; name: string;
@ -138,10 +138,10 @@ export class Skill {
//be possible for it to run with them. For the sake of not crashing the game, //be possible for it to run with them. For the sake of not crashing the game,
const recursiveMode = (currentLevel: number, count: number): number => { const recursiveMode = (currentLevel: number, count: number): number => {
if (count <= 1) { if (count <= 1) {
return Math.floor((this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost); return Math.floor((this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost);
} else { } else {
const thisUpgrade = Math.floor( const thisUpgrade = Math.floor(
(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost, (this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost,
); );
return this.calculateCost(currentLevel + 1, count - 1) + thisUpgrade; return this.calculateCost(currentLevel + 1, count - 1) + thisUpgrade;
} }
@ -161,7 +161,7 @@ export class Skill {
//(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost //(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost
//being repeated for increasing currentLevel //being repeated for increasing currentLevel
const preMult = (count * (2 * this.baseCost + this.costInc * (2 * currentLevel + count + 1))) / 2; const preMult = (count * (2 * this.baseCost + this.costInc * (2 * currentLevel + count + 1))) / 2;
const unFloored = preMult * BitNodeMultipliers.BladeburnerSkillCost - count / 2; const unFloored = preMult * currentNodeMults.BladeburnerSkillCost - count / 2;
return Math.floor(unFloored); return Math.floor(unFloored);
} }
} }

@ -6,7 +6,7 @@ import { CorpUpgrades } from "./data/CorporationUpgrades";
import * as corpConstants from "./data/Constants"; import * as corpConstants from "./data/Constants";
import { Division } from "./Division"; import { Division } from "./Division";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { showLiterature } from "../Literature/LiteratureHelpers"; import { showLiterature } from "../Literature/LiteratureHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
@ -28,7 +28,7 @@ export class Corporation {
/** Map keyed by division name */ /** Map keyed by division name */
divisions = new JSONMap<string, Division>(); divisions = new JSONMap<string, Division>();
maxDivisions = 20 * BitNodeMultipliers.CorporationDivisions; maxDivisions = 20 * currentNodeMults.CorporationDivisions;
//Financial stats //Financial stats
funds = 150e9; funds = 150e9;
@ -44,7 +44,7 @@ export class Corporation {
shareSaleCooldown = 0; // Game cycles until player can sell shares again shareSaleCooldown = 0; // Game cycles until player can sell shares again
issueNewSharesCooldown = 0; // Game cycles until player can issue shares again issueNewSharesCooldown = 0; // Game cycles until player can issue shares again
dividendRate = 0; dividendRate = 0;
dividendTax = 1 - BitNodeMultipliers.CorporationSoftcap + 0.15; dividendTax = 1 - currentNodeMults.CorporationSoftcap + 0.15;
issuedShares = 0; issuedShares = 0;
sharePrice = 0; sharePrice = 0;
storedCycles = 0; storedCycles = 0;
@ -188,7 +188,7 @@ export class Corporation {
val *= Math.pow(1.1, this.divisions.size); val *= Math.pow(1.1, this.divisions.size);
val -= val % 1e6; //Round down to nearest millionth val -= val % 1e6; //Round down to nearest millionth
} }
return val * BitNodeMultipliers.CorporationValuation; return val * currentNodeMults.CorporationValuation;
} }
determineValuation(): void { determineValuation(): void {

@ -9,9 +9,9 @@ import { CalculateEffect } from "./formulas/effect";
import { StaneksGiftEvents } from "./StaneksGiftEvents"; import { StaneksGiftEvents } from "./StaneksGiftEvents";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { StanekConstants } from "./data/Constants"; import { StanekConstants } from "./data/Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { defaultMultipliers, mergeMultipliers, Multipliers, scaleMultipliers } from "../PersonObjects/Multipliers"; import { defaultMultipliers, mergeMultipliers, Multipliers, scaleMultipliers } from "../PersonObjects/Multipliers";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { getKeyList } from "../utils/helpers/getKeyList"; import { getKeyList } from "../utils/helpers/getKeyList";
export class StaneksGift extends BaseGift { export class StaneksGift extends BaseGift {
@ -23,7 +23,7 @@ export class StaneksGift extends BaseGift {
} }
baseSize(): number { baseSize(): number {
return StanekConstants.BaseSize + BitNodeMultipliers.StaneksGiftExtraSize + Player.sourceFileLvl(13); return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.sourceFileLvl(13);
} }
width(): number { width(): number {
@ -226,7 +226,7 @@ export class StaneksGift extends BaseGift {
sleeve.resetMultipliers(); sleeve.resetMultipliers();
//reapplying augmentation's multiplier //reapplying augmentation's multiplier
for (let i = 0; i < sleeve.augmentations.length; ++i) { for (let i = 0; i < sleeve.augmentations.length; ++i) {
const aug = StaticAugmentations[sleeve.augmentations[i].name]; const aug = Augmentations[sleeve.augmentations[i].name];
sleeve.applyAugmentation(aug); sleeve.applyAugmentation(aug);
} }
//applying stanek multiplier //applying stanek multiplier

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
export function CalculateEffect(highestCharge: number, numCharge: number, power: number, boost: number): number { export function CalculateEffect(highestCharge: number, numCharge: number, power: number, boost: number): number {
return ( return (
@ -7,6 +7,6 @@ export function CalculateEffect(highestCharge: number, numCharge: number, power:
Math.pow((numCharge + 1) / 5, 0.07) * Math.pow((numCharge + 1) / 5, 0.07) *
power * power *
boost * boost *
BitNodeMultipliers.StaneksGiftPowerMultiplier currentNodeMults.StaneksGiftPowerMultiplier
); );
} }

@ -15,10 +15,10 @@ import {
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
export function Augmentations(): React.ReactElement { export function Augmentations(): React.ReactElement {
const [augmentation, setAugmentation] = useState("Augmented Targeting I"); const [augmentation, setAugmentation] = useState(AugmentationName.Targeting1);
function setAugmentationDropdown(event: SelectChangeEvent): void { function setAugmentationDropdown(event: SelectChangeEvent): void {
setAugmentation(event.target.value); setAugmentation(event.target.value as AugmentationName);
} }
function queueAug(): void { function queueAug(): void {
Player.queueAugmentation(augmentation); Player.queueAugmentation(augmentation);

@ -1,6 +1,8 @@
import type { AugmentationName } from "@enums";
import { FactionInfo, FactionInfos } from "./FactionInfo"; import { FactionInfo, FactionInfos } from "./FactionInfo";
import { favorToRep, repToFavor } from "./formulas/favor"; import { favorToRep, repToFavor } from "./formulas/favor";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getEnumHelper } from "../utils/EnumHelper";
export class Faction { export class Faction {
/** /**
@ -10,7 +12,7 @@ export class Faction {
alreadyInvited = false; alreadyInvited = false;
/** Holds names of all augmentations that this Faction offers */ /** Holds names of all augmentations that this Faction offers */
augmentations: string[] = []; augmentations: AugmentationName[] = [];
/** Amount of favor the player has with this faction. */ /** Amount of favor the player has with this faction. */
favor = 0; favor = 0;
@ -67,7 +69,11 @@ export class Faction {
/** Initializes a Faction object from a JSON save state. */ /** Initializes a Faction object from a JSON save state. */
static fromJSON(value: IReviverValue): Faction { static fromJSON(value: IReviverValue): Faction {
return Generic_fromJSON(Faction, value.data); const faction = Generic_fromJSON(Faction, value.data);
// Remove invalid augs from faction. Augs are repopulated with correct augs during any reset.
const augHelper = getEnumHelper("AugmentationName");
faction.augmentations = faction.augmentations.filter((augName) => augHelper.isMember(augName));
return faction;
} }
} }

@ -1,8 +1,8 @@
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { Augmentation } from "../Augmentation/Augmentation"; import { Augmentation } from "../Augmentation/Augmentation";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Faction } from "./Faction"; import { Faction } from "./Faction";
import { Factions } from "./Factions"; import { Factions } from "./Factions";
@ -18,6 +18,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal"; import { InvitationEvent } from "./ui/InvitationModal";
import { SFC32RNG } from "../Casino/RNG"; import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork"; import { isFactionWork } from "../Work/FactionWork";
import { getAugCost } from "../Augmentation/AugmentationHelpers";
export function inviteToFaction(faction: Faction): void { export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name); Player.receiveInvite(faction.name);
@ -55,7 +56,7 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string { export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const hasPrereqs = hasAugmentationPrereqs(aug); const hasPrereqs = hasAugmentationPrereqs(aug);
const augCosts = aug.getCost(); const augCosts = getAugCost(aug);
if (!hasPrereqs) { if (!hasPrereqs) {
const txt = `You must first purchase or install ${aug.prereqs const txt = `You must first purchase or install ${aug.prereqs
.filter((req) => !Player.hasAugmentation(req)) .filter((req) => !Player.hasAugmentation(req))
@ -127,21 +128,21 @@ export function processPassiveFactionRepGain(numCycles: number): void {
const fRep = getFactionFieldWorkRepGain(Player, faction.favor); const fRep = getFactionFieldWorkRepGain(Player, faction.favor);
const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1 / 120); const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1 / 120);
faction.playerReputation += rate * numCycles * Player.mults.faction_rep * BitNodeMultipliers.FactionPassiveRepGain; faction.playerReputation += rate * numCycles * Player.mults.faction_rep * currentNodeMults.FactionPassiveRepGain;
} }
} }
export const getFactionAugmentationsFiltered = (faction: Faction): string[] => { export const getFactionAugmentationsFiltered = (faction: Faction): AugmentationName[] => {
// If player has a gang with this faction, return (almost) all augmentations // If player has a gang with this faction, return (almost) all augmentations
if (Player.hasGangWith(faction.name)) { if (Player.hasGangWith(faction.name)) {
let augs = Object.values(StaticAugmentations); let augs = Object.values(Augmentations);
// Remove special augs // Remove special augs
augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationName.CongruityImplant); augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationName.CongruityImplant);
if (Player.bitNodeN === 2) { if (Player.bitNodeN === 2) {
// TRP is not available outside of BN2 for Gangs // TRP is not available outside of BN2 for Gangs
augs.push(StaticAugmentations[AugmentationName.TheRedPill]); augs.push(Augmentations[AugmentationName.TheRedPill]);
} }
const rng = SFC32RNG(`BN${Player.bitNodeN}.${Player.sourceFileLvl(Player.bitNodeN)}`); const rng = SFC32RNG(`BN${Player.bitNodeN}.${Player.sourceFileLvl(Player.bitNodeN)}`);
@ -156,7 +157,7 @@ export const getFactionAugmentationsFiltered = (faction: Faction): string[] => {
return true; return true;
} }
return rng() >= 1 - BitNodeMultipliers.GangUniqueAugs; return rng() >= 1 - currentNodeMults.GangUniqueAugs;
}; };
augs = augs.filter(uniqueFilter); augs = augs.filter(uniqueFilter);

@ -6,6 +6,8 @@ import { Faction } from "./Faction";
import { FactionInfos } from "./FactionInfo"; import { FactionInfos } from "./FactionInfo";
import { Reviver } from "../utils/JSONReviver"; import { Reviver } from "../utils/JSONReviver";
import { getRecordValues } from "../Types/Record";
import { Augmentations, initCircadianModulator } from "../Augmentation/Augmentations";
export let Factions: Record<string, Faction> = {}; export let Factions: Record<string, Faction> = {};
@ -47,4 +49,16 @@ function resetFaction(newFactionObject: Faction): void {
delete Factions[factionName]; delete Factions[factionName];
} }
AddToFactions(newFactionObject); AddToFactions(newFactionObject);
// All factions are added, this is a good place to add augs back to factions.
initCircadianModulator();
for (const aug of getRecordValues(Augmentations)) {
for (const factionName of aug.factions) {
const faction = Factions[factionName];
if (!faction) {
console.error(`Faction ${factionName} did not exist while adding augs to factions`);
continue;
}
faction.augmentations.push(aug.name);
}
}
} }

@ -1,7 +1,7 @@
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Person as IPerson } from "@nsdefs"; import { Person as IPerson } from "@nsdefs";
export function repFromDonation(amt: number, person: IPerson): number { export function repFromDonation(amt: number, person: IPerson): number {
return (amt / CONSTANTS.DonateMoneyToRepDivisor) * person.mults.faction_rep * BitNodeMultipliers.FactionWorkRepGain; return (amt / CONSTANTS.DonateMoneyToRepDivisor) * person.mults.faction_rep * currentNodeMults.FactionWorkRepGain;
} }

@ -1,8 +1,8 @@
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material"; import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import React from "react"; import React from "react";
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers"; import { getAugCost, getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations"; import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
@ -25,11 +25,11 @@ interface IProps {
export function AugmentationsPage(props: IProps): React.ReactElement { export function AugmentationsPage(props: IProps): React.ReactElement {
const rerender = useRerender(); const rerender = useRerender();
function getAugs(): string[] { function getAugs(): AugmentationName[] {
return getFactionAugmentationsFiltered(props.faction); return getFactionAugmentationsFiltered(props.faction);
} }
function getAugsSorted(): string[] { function getAugsSorted(): AugmentationName[] {
switch (Settings.PurchaseAugmentationsOrder) { switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: { case PurchaseAugmentationsOrderSetting.Cost: {
return getAugsSortedByCost(); return getAugsSortedByCost();
@ -45,26 +45,26 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
} }
} }
function getAugsSortedByCost(): string[] { function getAugsSortedByCost(): AugmentationName[] {
const augs = getAugs(); const augs = getAugs();
augs.sort((augName1, augName2) => { augs.sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = StaticAugmentations[augName2]; aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost().moneyCost - aug2.getCost().moneyCost; return getAugCost(aug1).moneyCost - getAugCost(aug2).moneyCost;
}); });
return augs; return augs;
} }
function getAugsSortedByPurchasable(): string[] { function getAugsSortedByPurchasable(): AugmentationName[] {
const augs = getAugs(); const augs = getAugs();
function canBuy(augName: string): boolean { function canBuy(augName: AugmentationName): boolean {
const aug = StaticAugmentations[augName]; const aug = Augmentations[augName];
const augCosts = aug.getCost(); const augCosts = getAugCost(aug);
const repCost = augCosts.repCost; const repCost = augCosts.repCost;
const hasReq = props.faction.playerReputation >= repCost; const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug); const hasRep = hasAugmentationPrereqs(aug);
@ -72,43 +72,43 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
return hasCost && hasReq && hasRep; return hasCost && hasReq && hasRep;
} }
const buy = augs.filter(canBuy).sort((augName1, augName2) => { const buy = augs.filter(canBuy).sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = StaticAugmentations[augName2]; aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost().moneyCost - aug2.getCost().moneyCost; return getAugCost(aug1).moneyCost - getAugCost(aug2).moneyCost;
}); });
const cantBuy = augs const cantBuy = augs
.filter((aug) => !canBuy(aug)) .filter((aug) => !canBuy(aug))
.sort((augName1, augName2) => { .sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = StaticAugmentations[augName2]; aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost().repCost - aug2.getCost().repCost; return getAugCost(aug1).repCost - getAugCost(aug2).repCost;
}); });
return buy.concat(cantBuy); return buy.concat(cantBuy);
} }
function getAugsSortedByReputation(): string[] { function getAugsSortedByReputation(): AugmentationName[] {
const augs = getAugs(); const augs = getAugs();
augs.sort((augName1, augName2) => { augs.sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = StaticAugmentations[augName2]; aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost().repCost - aug2.getCost().repCost; return getAugCost(aug1).repCost - getAugCost(aug2).repCost;
}); });
return augs; return augs;
} }
function getAugsSortedByDefault(): string[] { function getAugsSortedByDefault(): AugmentationName[] {
return getAugs(); return getAugs();
} }
@ -123,7 +123,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
aug === AugmentationName.NeuroFluxGovernor || aug === AugmentationName.NeuroFluxGovernor ||
(!Player.augmentations.some((a) => a.name === aug) && !Player.queuedAugmentations.some((a) => a.name === aug)), (!Player.augmentations.some((a) => a.name === aug) && !Player.queuedAugmentations.some((a) => a.name === aug)),
); );
const owned = augs.filter((aug: string) => !purchasable.includes(aug)); const owned = augs.filter((aug) => !purchasable.includes(aug));
const multiplierComponent = const multiplierComponent =
props.faction.name !== FactionName.ShadowsOfAnarchy ? ( props.faction.name !== FactionName.ShadowsOfAnarchy ? (
@ -213,7 +213,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
augNames={purchasable} augNames={purchasable}
ownedAugNames={owned} ownedAugNames={owned}
canPurchase={(aug) => { canPurchase={(aug) => {
const costs = aug.getCost(); const costs = getAugCost(aug);
return ( return (
hasAugmentationPrereqs(aug) && hasAugmentationPrereqs(aug) &&
props.faction.playerReputation >= costs.repCost && props.faction.playerReputation >= costs.repCost &&

@ -12,7 +12,7 @@ import { Option } from "./Option";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
@ -107,7 +107,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
// Flags for whether special options (gang, sleeve purchases, donate, etc.) // Flags for whether special options (gang, sleeve purchases, donate, etc.)
// should be shown // should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate; const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = faction.name === FactionName.TheCovenant && Player.bitNodeN === 10; const canPurchaseSleeves = faction.name === FactionName.TheCovenant && Player.bitNodeN === 10;

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import { GangMemberTask } from "../GangMemberTask"; import { GangMemberTask } from "../GangMemberTask";
@ -24,7 +24,7 @@ export function calculateRespectGain(gang: FormulaGang, member: GangMember, task
statWeight -= 4 * task.difficulty; statWeight -= 4 * task.difficulty;
if (statWeight <= 0) return 0; if (statWeight <= 0) return 0;
const territoryMult = Math.max(0.005, Math.pow(gang.territory * 100, task.territory.respect) / 100); const territoryMult = Math.max(0.005, Math.pow(gang.territory * 100, task.territory.respect) / 100);
const territoryPenalty = (0.2 * gang.territory + 0.8) * BitNodeMultipliers.GangSoftcap; const territoryPenalty = (0.2 * gang.territory + 0.8) * currentNodeMults.GangSoftcap;
if (isNaN(territoryMult) || territoryMult <= 0) return 0; if (isNaN(territoryMult) || territoryMult <= 0) return 0;
const respectMult = calculateWantedPenalty(gang); const respectMult = calculateWantedPenalty(gang);
return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty); return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty);
@ -68,7 +68,7 @@ export function calculateMoneyGain(gang: FormulaGang, member: GangMember, task:
const territoryMult = Math.max(0.005, Math.pow(gang.territory * 100, task.territory.money) / 100); const territoryMult = Math.max(0.005, Math.pow(gang.territory * 100, task.territory.money) / 100);
if (isNaN(territoryMult) || territoryMult <= 0) return 0; if (isNaN(territoryMult) || territoryMult <= 0) return 0;
const respectMult = calculateWantedPenalty(gang); const respectMult = calculateWantedPenalty(gang);
const territoryPenalty = (0.2 * gang.territory + 0.8) * BitNodeMultipliers.GangSoftcap; const territoryPenalty = (0.2 * gang.territory + 0.8) * currentNodeMults.GangSoftcap;
return Math.pow(5 * task.baseMoney * statWeight * territoryMult * respectMult, territoryPenalty); return Math.pow(5 * task.baseMoney * statWeight * territoryMult * respectMult, territoryPenalty);
} }

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
import { Person as IPerson } from "@nsdefs"; import { Person as IPerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence"; import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { Server as IServer } from "@nsdefs"; import { Server as IServer } from "@nsdefs";
@ -32,7 +32,7 @@ export function calculateHackingExpGain(server: IServer, person: IPerson): numbe
const diffFactor = 0.3; const diffFactor = 0.3;
let expGain = baseExpGain; let expGain = baseExpGain;
expGain += baseDifficulty * diffFactor; expGain += baseDifficulty * diffFactor;
return expGain * person.mults.hacking_exp * BitNodeMultipliers.HackExpGain; return expGain * person.mults.hacking_exp * currentNodeMults.HackExpGain;
} }
/** /**
@ -49,7 +49,7 @@ export function calculatePercentMoneyHacked(server: IServer, person: IPerson): n
const difficultyMult = (100 - hackDifficulty) / 100; const difficultyMult = (100 - hackDifficulty) / 100;
const skillMult = (person.skills.hacking - (requiredHackingSkill - 1)) / person.skills.hacking; const skillMult = (person.skills.hacking - (requiredHackingSkill - 1)) / person.skills.hacking;
const percentMoneyHacked = const percentMoneyHacked =
(difficultyMult * skillMult * person.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor; (difficultyMult * skillMult * person.mults.hacking_money * currentNodeMults.ScriptHackMoney) / balanceFactor;
return Math.min(1, Math.max(percentMoneyHacked, 0)); return Math.min(1, Math.max(percentMoneyHacked, 0));
} }

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { HacknetNodeConstants } from "../data/Constants"; import { HacknetNodeConstants } from "../data/Constants";
export function calculateMoneyGainRate(level: number, ram: number, cores: number, mult: number): number { export function calculateMoneyGainRate(level: number, ram: number, cores: number, mult: number): number {
@ -7,7 +7,7 @@ export function calculateMoneyGainRate(level: number, ram: number, cores: number
const levelMult = level * gainPerLevel; const levelMult = level * gainPerLevel;
const ramMult = Math.pow(1.035, ram - 1); const ramMult = Math.pow(1.035, ram - 1);
const coresMult = (cores + 5) / 6; const coresMult = (cores + 5) / 6;
return levelMult * ramMult * coresMult * mult * BitNodeMultipliers.HacknetNodeMoney; return levelMult * ramMult * coresMult * mult * currentNodeMults.HacknetNodeMoney;
} }
export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number {

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { HacknetServerConstants } from "../data/Constants"; import { HacknetServerConstants } from "../data/Constants";
export function calculateHashGainRate( export function calculateHashGainRate(
@ -13,7 +13,7 @@ export function calculateHashGainRate(
const coreMultiplier = 1 + (cores - 1) / 5; const coreMultiplier = 1 + (cores - 1) / 5;
const ramRatio = 1 - ramUsed / maxRam; const ramRatio = 1 - ramUsed / maxRam;
return baseGain * ramMultiplier * coreMultiplier * ramRatio * mult * BitNodeMultipliers.HacknetNodeMoney; return baseGain * ramMultiplier * coreMultiplier * ramRatio * mult * currentNodeMults.HacknetNodeMoney;
} }
export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number {

@ -1,5 +1,5 @@
import { Player } from "@player"; import { Player } from "@player";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { LocationsMetadata } from "../../Locations/data/LocationsMetadata"; import { LocationsMetadata } from "../../Locations/data/LocationsMetadata";
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
@ -13,7 +13,7 @@ export function calculateSellInformationCashReward(reward: number, maxLevel: num
3e3 * 3e3 *
levelBonus * levelBonus *
(Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.5 : 1) * (Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.5 : 1) *
BitNodeMultipliers.InfiltrationMoney currentNodeMults.InfiltrationMoney
); );
} }
@ -26,7 +26,7 @@ export function calculateTradeInformationRepReward(reward: number, maxLevel: num
30 * 30 *
levelBonus * levelBonus *
(Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.5 : 1) * (Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.5 : 1) *
BitNodeMultipliers.InfiltrationRep currentNodeMults.InfiltrationRep
); );
} }

@ -11,7 +11,7 @@ import { Money } from "../../ui/React/Money";
import { formatRam } from "../../ui/formatNumber"; import { formatRam } from "../../ui/formatNumber";
import { MathJax } from "better-react-mathjax"; import { MathJax } from "better-react-mathjax";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
interface IProps { interface IProps {
rerender: () => void; rerender: () => void;
@ -30,7 +30,7 @@ export function RamButton(props: IProps): React.ReactElement {
props.rerender(); props.rerender();
} }
const bnMult = BitNodeMultipliers.HomeComputerRamCost === 1 ? "" : `\\cdot ${BitNodeMultipliers.HomeComputerRamCost}`; const bnMult = currentNodeMults.HomeComputerRamCost === 1 ? "" : `\\cdot ${currentNodeMults.HomeComputerRamCost}`;
return ( return (
<Tooltip <Tooltip

@ -34,7 +34,7 @@ import { HacknetNode } from "../../Hacknet/HacknetNode";
import { HacknetServer } from "../../Hacknet/HacknetServer"; import { HacknetServer } from "../../Hacknet/HacknetServer";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot"; import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
interface SpecialLocationProps { interface SpecialLocationProps {
loc: Location; loc: Location;
@ -75,7 +75,7 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
} }
function renderBladeburner(): React.ReactElement { function renderBladeburner(): React.ReactElement {
if (!Player.canAccessBladeburner() || BitNodeMultipliers.BladeburnerRank === 0) { if (!Player.canAccessBladeburner() || currentNodeMults.BladeburnerRank === 0) {
return <></>; return <></>;
} }
const text = Player.bladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division"; const text = Player.bladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
@ -314,7 +314,7 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
return renderGrafting(); return renderGrafting();
} }
case LocationName.Sector12CityHall: { case LocationName.Sector12CityHall: {
return (BitNodeMultipliers.CorporationSoftcap < 0.15 && <></>) || <CreateCorporation />; return (currentNodeMults.CorporationSoftcap < 0.15 && <></>) || <CreateCorporation />;
} }
case LocationName.Sector12NSA: { case LocationName.Sector12NSA: {
return renderBladeburner(); return renderBladeburner();

@ -17,7 +17,7 @@ import {
} from "../Hacking"; } from "../Hacking";
import { netscriptCanHack } from "../Hacking/netscriptCanHack"; import { netscriptCanHack } from "../Hacking/netscriptCanHack";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { influenceStockThroughServerHack } from "../StockMarket/PlayerInfluencing"; import { influenceStockThroughServerHack } from "../StockMarket/PlayerInfluencing";
import { PortNumber } from "../NetscriptPort"; import { PortNumber } from "../NetscriptPort";
@ -506,9 +506,9 @@ function hack(
server.moneyAvailable = 0; server.moneyAvailable = 0;
} }
let moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain; let moneyGained = moneyDrained * currentNodeMults.ScriptHackMoneyGain;
if (manual) { if (manual) {
moneyGained = moneyDrained * BitNodeMultipliers.ManualHackMoney; moneyGained = moneyDrained * currentNodeMults.ManualHackMoney;
} }
Player.gainMoney(moneyGained, "hacking"); Player.gainMoney(moneyGained, "hacking");

@ -1,6 +1,6 @@
import $ from "jquery"; import $ from "jquery";
import { vsprintf, sprintf } from "sprintf-js"; import { vsprintf, sprintf } from "sprintf-js";
import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { import {
calculateHackingChance, calculateHackingChance,
@ -407,7 +407,7 @@ export const ns: InternalAPI<NSFull> = {
ctx.workerScript.scriptRef.onlineExpGained += expGain; ctx.workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain); Player.gainHackingExp(expGain);
// Account for hidden multiplier in Server.weaken() // Account for hidden multiplier in Server.weaken()
return Promise.resolve(weakenAmt * BitNodeMultipliers.ServerWeakenRate); return Promise.resolve(weakenAmt * currentNodeMults.ServerWeakenRate);
}); });
}, },
weakenAnalyze: weakenAnalyze:
@ -416,7 +416,7 @@ export const ns: InternalAPI<NSFull> = {
const threads = helpers.number(ctx, "threads", _threads); const threads = helpers.number(ctx, "threads", _threads);
const cores = helpers.number(ctx, "cores", _cores); const cores = helpers.number(ctx, "cores", _cores);
const coreBonus = 1 + (cores - 1) / 16; const coreBonus = 1 + (cores - 1) / 16;
return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate; return CONSTANTS.ServerWeakenAmount * threads * coreBonus * currentNodeMults.ServerWeakenRate;
}, },
share: (ctx) => () => { share: (ctx) => () => {
helpers.log(ctx, () => "Sharing this computer."); helpers.log(ctx, () => "Sharing this computer.");
@ -967,7 +967,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
getBitNodeMultipliers: getBitNodeMultipliers:
(ctx) => (ctx) =>
(_n = Player.bitNodeN, _lvl = Player.sourceFileLvl(Player.bitNodeN) + 1): IBitNodeMultipliers => { (_n = Player.bitNodeN, _lvl = Player.sourceFileLvl(Player.bitNodeN) + 1) => {
if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5) if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5)
throw helpers.makeRuntimeErrorMsg(ctx, "Requires Source-File 5 to run."); throw helpers.makeRuntimeErrorMsg(ctx, "Requires Source-File 5 to run.");
const n = Math.round(helpers.number(ctx, "n", _n)); const n = Math.round(helpers.number(ctx, "n", _n));
@ -1679,7 +1679,7 @@ export const ns: InternalAPI<NSFull> = {
}); });
}, },
getFavorToDonate: () => () => { getFavorToDonate: () => () => {
return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); return Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction);
}, },
getPlayer: () => () => { getPlayer: () => () => {
const data = { const data = {

@ -4,7 +4,7 @@ import type { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { Player } from "@player"; import { Player } from "@player";
import { Bladeburner } from "../Bladeburner/Bladeburner"; import { Bladeburner } from "../Bladeburner/Bladeburner";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { BlackOperation } from "../Bladeburner/BlackOperation"; import { BlackOperation } from "../Bladeburner/BlackOperation";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { getEnumHelper } from "../utils/EnumHelper"; import { getEnumHelper } from "../utils/EnumHelper";
@ -139,7 +139,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const action = getBladeburnerActionObject(ctx, type, name); const action = getBladeburnerActionObject(ctx, type, name);
const level = _level === undefined ? action.level : helpers.number(ctx, "level", _level); const level = _level === undefined ? action.level : helpers.number(ctx, "level", _level);
const rewardMultiplier = Math.pow(action.rewardFac, level - 1); const rewardMultiplier = Math.pow(action.rewardFac, level - 1);
return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank; return action.rankGain * rewardMultiplier * currentNodeMults.BladeburnerRank;
}, },
getActionCountRemaining: (ctx) => (_type, _name) => { getActionCountRemaining: (ctx) => (_type, _name) => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
@ -299,7 +299,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
}, },
joinBladeburnerDivision: (ctx) => () => { joinBladeburnerDivision: (ctx) => () => {
if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) { if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) {
if (BitNodeMultipliers.BladeburnerRank === 0) { if (currentNodeMults.BladeburnerRank === 0) {
return false; // Disabled in this bitnode return false; // Disabled in this bitnode
} }
if (Player.bladeburner) { if (Player.bladeburner) {

@ -56,7 +56,7 @@ import { IndustriesData, IndustryResearchTrees } from "../Corporation/data/Indus
import * as corpConstants from "../Corporation/data/Constants"; import * as corpConstants from "../Corporation/data/Constants";
import { ResearchMap } from "../Corporation/ResearchMap"; import { ResearchMap } from "../Corporation/ResearchMap";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { getEnumHelper } from "../utils/EnumHelper"; import { getEnumHelper } from "../utils/EnumHelper";
@ -70,7 +70,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
if (!player.canAccessCorporation() || player.corporation) return false; if (!player.canAccessCorporation() || player.corporation) return false;
if (!corporationName) return false; if (!corporationName) return false;
if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3"); if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3");
if (BitNodeMultipliers.CorporationSoftcap < 0.15) if (currentNodeMults.CorporationSoftcap < 0.15)
throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`); throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`);
if (selfFund) { if (selfFund) {

@ -1,16 +1,17 @@
import type { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import type { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
import { CityName } from "@enums";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers";
import { Player } from "@player"; import { Player } from "@player";
import { Grafting as IGrafting } from "@nsdefs"; import { Grafting as IGrafting } from "@nsdefs";
import { AugmentationName, CityName } from "@enums";
import { Augmentations } from "../Augmentation/Augmentations";
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router"; import { Page } from "../ui/Router";
import { GraftingWork } from "../Work/GraftingWork"; import { GraftingWork } from "../Work/GraftingWork";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { augmentationExists } from "../Augmentation/AugmentationHelpers"; import { getEnumHelper } from "../utils/EnumHelper";
export function NetscriptGrafting(): InternalAPI<IGrafting> { export function NetscriptGrafting(): InternalAPI<IGrafting> {
const checkGraftingAPIAccess = (ctx: NetscriptContext): void => { const checkGraftingAPIAccess = (ctx: NetscriptContext): void => {
@ -22,27 +23,26 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
} }
}; };
const isValidGraftingAugName = (augName: string) => const isValidGraftingAugName = (augName: AugmentationName) => getGraftingAvailableAugs().includes(augName);
getGraftingAvailableAugs().includes(augName) && augmentationExists(augName);
return { return {
getAugmentationGraftPrice: (ctx) => (_augName) => { getAugmentationGraftPrice: (ctx) => (_augName) => {
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
if (!isValidGraftingAugName(augName)) { if (!isValidGraftingAugName(augName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(Augmentations[augName]);
return graftableAug.cost; return graftableAug.cost;
}, },
getAugmentationGraftTime: (ctx) => (_augName) => { getAugmentationGraftTime: (ctx) => (_augName) => {
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
if (!isValidGraftingAugName(augName)) { if (!isValidGraftingAugName(augName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(Augmentations[augName]);
return calculateGraftingTimeWithBonus(graftableAug); return calculateGraftingTimeWithBonus(graftableAug);
}, },
@ -55,7 +55,7 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
graftAugmentation: graftAugmentation:
(ctx) => (ctx) =>
(_augName, _focus = true) => { (_augName, _focus = true) => {
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const focus = !!_focus; const focus = !!_focus;
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
if (Player.city !== CityName.NewTokyo) { if (Player.city !== CityName.NewTokyo) {
@ -68,7 +68,7 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
const wasFocusing = Player.focus; const wasFocusing = Player.focus;
const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const craftableAug = new GraftableAugmentation(Augmentations[augName]);
if (Player.money < craftableAug.cost) { if (Player.money < craftableAug.cost) {
helpers.log(ctx, () => `You don't have enough money to craft ${augName}`); helpers.log(ctx, () => `You don't have enough money to craft ${augName}`);
return false; return false;

@ -1,5 +1,4 @@
import type { Singularity as ISingularity } from "@nsdefs"; import type { Singularity as ISingularity } from "@nsdefs";
import type { Augmentation } from "../Augmentation/Augmentation";
import type { Company } from "../Company/Company"; import type { Company } from "../Company/Company";
import type { Faction } from "../Faction/Faction"; import type { Faction } from "../Faction/Faction";
@ -16,8 +15,8 @@ import {
} from "@enums"; } from "@enums";
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers"; import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
import { startWorkerScript } from "../NetscriptWorker"; import { startWorkerScript } from "../NetscriptWorker";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers"; import { getAugCost, installAugmentations } from "../Augmentation/AugmentationHelpers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { calculateAchievements } from "../Achievements/Achievements"; import { calculateAchievements } from "../Achievements/Achievements";
@ -31,7 +30,7 @@ import { Locations } from "../Locations/Locations";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber"; import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Companies } from "../Company/Companies"; import { Companies } from "../Company/Companies";
import { companiesMetadata } from "../Company/data/CompaniesMetadata"; import { companiesMetadata } from "../Company/data/CompaniesMetadata";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
@ -59,14 +58,6 @@ import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { root } from "../Paths/Directory"; import { root } from "../Paths/Directory";
export function NetscriptSingularity(): InternalAPI<ISingularity> { export function NetscriptSingularity(): InternalAPI<ISingularity> {
const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation {
if (!augmentationExists(name)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid augmentation: '${name}'`);
}
return StaticAugmentations[name];
};
const getFaction = function (ctx: NetscriptContext, name: string): Faction { const getFaction = function (ctx: NetscriptContext, name: string): Faction {
if (!factionExists(name)) { if (!factionExists(name)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid faction name: '${name}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid faction name: '${name}`);
@ -127,40 +118,40 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}, },
getAugmentationPrereq: (ctx) => (_augName) => { getAugmentationPrereq: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
return aug.prereqs.slice(); return aug.prereqs.slice();
}, },
getAugmentationBasePrice: (ctx) => (_augName) => { getAugmentationBasePrice: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
return aug.baseCost * BitNodeMultipliers.AugmentationMoneyCost; return aug.baseCost * currentNodeMults.AugmentationMoneyCost;
}, },
getAugmentationPrice: (ctx) => (_augName) => { getAugmentationPrice: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
return aug.getCost().moneyCost; return getAugCost(aug).moneyCost;
}, },
getAugmentationRepReq: (ctx) => (_augName) => { getAugmentationRepReq: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
return aug.getCost().repCost; return getAugCost(aug).repCost;
}, },
getAugmentationStats: (ctx) => (_augName) => { getAugmentationStats: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
return Object.assign({}, aug.mults); return Object.assign({}, aug.mults);
}, },
purchaseAugmentation: (ctx) => (_facName, _augName) => { purchaseAugmentation: (ctx) => (_facName, _augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = helpers.string(ctx, "facName", _facName);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const fac = getFaction(ctx, facName); const fac = getFaction(ctx, facName);
const aug = getAugmentation(ctx, augName); const aug = Augmentations[augName];
const augs = getFactionAugmentationsFiltered(fac); const augs = getFactionAugmentationsFiltered(fac);
@ -190,7 +181,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
} }
} }
if (fac.playerReputation < aug.getCost().repCost) { if (fac.playerReputation < getAugCost(aug).repCost) {
helpers.log(ctx, () => `You do not have enough reputation with '${fac.name}'.`); helpers.log(ctx, () => `You do not have enough reputation with '${fac.name}'.`);
return false; return false;
} }
@ -1036,7 +1027,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
helpers.log(ctx, () => `You do not have enough money to donate ${formatMoney(amt)} to '${facName}'`); helpers.log(ctx, () => `You do not have enough money to donate ${formatMoney(amt)} to '${facName}'`);
return false; return false;
} }
const repNeededToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); const repNeededToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction);
if (faction.favor < repNeededToDonate) { if (faction.favor < repNeededToDonate) {
helpers.log( helpers.log(
ctx, ctx,

@ -1,9 +1,9 @@
import { Player } from "@player"; import type { Augmentation } from "../Augmentation/Augmentation";
import type { Sleeve as NetscriptSleeve } from "@nsdefs"; import type { Sleeve as NetscriptSleeve } from "@nsdefs";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { findCrime } from "../Crime/CrimeHelpers";
import { Augmentation } from "../Augmentation/Augmentation";
import { Player } from "@player";
import { Augmentations } from "../Augmentation/Augmentations";
import { findCrime } from "../Crime/CrimeHelpers";
import { getEnumHelper } from "../utils/EnumHelper"; import { getEnumHelper } from "../utils/EnumHelper";
import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper";
import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork"; import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork";
@ -11,6 +11,7 @@ import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionW
import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyWork"; import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyWork";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { getAugCost } from "../Augmentation/AugmentationHelpers";
export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> { export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext) { const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
@ -198,7 +199,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
}, },
purchaseSleeveAug: (ctx) => (_sleeveNumber, _augName) => { purchaseSleeveAug: (ctx) => (_sleeveNumber, _augName) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
@ -206,7 +207,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`); throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`);
} }
const aug = StaticAugmentations[augName]; const aug = Augmentations[augName];
if (!aug) { if (!aug) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
@ -215,15 +216,15 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
}, },
getSleeveAugmentationPrice: (ctx) => (_augName) => { getSleeveAugmentationPrice: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug: Augmentation = StaticAugmentations[augName]; const aug: Augmentation = Augmentations[augName];
return aug.baseCost; return aug.baseCost;
}, },
getSleeveAugmentationRepReq: (ctx) => (_augName) => { getSleeveAugmentationRepReq: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug: Augmentation = StaticAugmentations[augName]; const aug: Augmentation = Augmentations[augName];
return aug.getCost().repCost; return getAugCost(aug).repCost;
}, },
setToBladeburnerAction: (ctx) => (_sleeveNumber, _action, _contract?) => { setToBladeburnerAction: (ctx) => (_sleeveNumber, _action, _contract?) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);

@ -1,14 +1,16 @@
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
import { GraftableAugmentation } from "./GraftableAugmentation";
import { Player } from "@player"; import { Player } from "@player";
import { AugmentationName, FactionName } from "@enums";
import { Augmentations } from "../../Augmentation/Augmentations";
import { calculateIntelligenceBonus } from "../formulas/intelligence"; import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { GraftableAugmentation } from "./GraftableAugmentation";
import { getRecordEntries } from "../../Types/Record";
export const getGraftingAvailableAugs = (): string[] => { export const getGraftingAvailableAugs = (): AugmentationName[] => {
const augs: string[] = []; const augs: AugmentationName[] = [];
for (const [augName, aug] of Object.entries(StaticAugmentations)) { for (const [augName, aug] of getRecordEntries(Augmentations)) {
if (Player.factions.includes("Bladeburners")) { if (Player.factions.includes(FactionName.Bladeburners)) {
if (aug.isSpecial && !aug.factions.includes("Bladeburners")) continue; if (aug.isSpecial && !aug.factions.includes(FactionName.Bladeburners)) continue;
} else { } else {
if (aug.isSpecial) continue; if (aug.isSpecial) continue;
} }

@ -1,13 +1,14 @@
import type { Augmentation } from "../../../Augmentation/Augmentation"; import type { Augmentation } from "../../../Augmentation/Augmentation";
import { Player } from "@player";
import { AugmentationName, LocationName } from "@enums";
import React, { useState } from "react"; import React, { useState } from "react";
import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material"; import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material";
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material"; import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
import { Player } from "@player";
import { AugmentationName, LocationName } from "@enums";
import { GraftingWork } from "../../../Work/GraftingWork"; import { GraftingWork } from "../../../Work/GraftingWork";
import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations"; import { Augmentations } from "../../../Augmentation/Augmentations";
import { CONSTANTS } from "../../../Constants"; import { CONSTANTS } from "../../../Constants";
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers"; import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
import { Locations } from "../../../Locations/Locations"; import { Locations } from "../../../Locations/Locations";
@ -25,7 +26,7 @@ import { useRerender } from "../../../ui/React/hooks";
export const GraftableAugmentations = (): Record<string, GraftableAugmentation> => { export const GraftableAugmentations = (): Record<string, GraftableAugmentation> => {
const gAugs: Record<string, GraftableAugmentation> = {}; const gAugs: Record<string, GraftableAugmentation> = {};
for (const aug of Object.values(StaticAugmentations)) { for (const aug of Object.values(Augmentations)) {
const name = aug.name; const name = aug.name;
const graftableAug = new GraftableAugmentation(aug); const graftableAug = new GraftableAugmentation(aug);
gAugs[name] = graftableAug; gAugs[name] = graftableAug;
@ -66,10 +67,10 @@ export const GraftingRoot = (): React.ReactElement => {
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs()[0]); const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs()[0]);
const [graftOpen, setGraftOpen] = useState(false); const [graftOpen, setGraftOpen] = useState(false);
const selectedAugmentation = StaticAugmentations[selectedAug]; const selectedAugmentation = Augmentations[selectedAug];
const rerender = useRerender(200); const rerender = useRerender(200);
const getAugsSorted = (): string[] => { const getAugsSorted = (): AugmentationName[] => {
const augs = getGraftingAvailableAugs(); const augs = getGraftingAvailableAugs();
switch (Settings.PurchaseAugmentationsOrder) { switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: case PurchaseAugmentationsOrderSetting.Cost:

@ -1,6 +1,6 @@
import { Person } from "./Person"; import { Person } from "./Person";
import { calculateSkill } from "./formulas/skill"; import { calculateSkill } from "./formulas/skill";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Player } from "@player"; import { Player } from "@player";
import { WorkStats } from "@nsdefs"; import { WorkStats } from "@nsdefs";
@ -14,10 +14,7 @@ export function gainHackingExp(this: Person, exp: number): void {
this.exp.hacking = 0; this.exp.hacking = 0;
} }
this.skills.hacking = calculateSkill( this.skills.hacking = calculateSkill(this.exp.hacking, this.mults.hacking * currentNodeMults.HackingLevelMultiplier);
this.exp.hacking,
this.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier,
);
} }
export function gainStrengthExp(this: Person, exp: number): void { export function gainStrengthExp(this: Person, exp: number): void {
@ -32,7 +29,7 @@ export function gainStrengthExp(this: Person, exp: number): void {
this.skills.strength = calculateSkill( this.skills.strength = calculateSkill(
this.exp.strength, this.exp.strength,
this.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier, this.mults.strength * currentNodeMults.StrengthLevelMultiplier,
); );
} }
@ -46,10 +43,7 @@ export function gainDefenseExp(this: Person, exp: number): void {
this.exp.defense = 0; this.exp.defense = 0;
} }
this.skills.defense = calculateSkill( this.skills.defense = calculateSkill(this.exp.defense, this.mults.defense * currentNodeMults.DefenseLevelMultiplier);
this.exp.defense,
this.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier,
);
const ratio = this.hp.current / this.hp.max; const ratio = this.hp.current / this.hp.max;
this.hp.max = Math.floor(10 + this.skills.defense / 10); this.hp.max = Math.floor(10 + this.skills.defense / 10);
this.hp.current = Math.round(this.hp.max * ratio); this.hp.current = Math.round(this.hp.max * ratio);
@ -67,7 +61,7 @@ export function gainDexterityExp(this: Person, exp: number): void {
this.skills.dexterity = calculateSkill( this.skills.dexterity = calculateSkill(
this.exp.dexterity, this.exp.dexterity,
this.mults.dexterity * BitNodeMultipliers.DexterityLevelMultiplier, this.mults.dexterity * currentNodeMults.DexterityLevelMultiplier,
); );
} }
@ -81,10 +75,7 @@ export function gainAgilityExp(this: Person, exp: number): void {
this.exp.agility = 0; this.exp.agility = 0;
} }
this.skills.agility = calculateSkill( this.skills.agility = calculateSkill(this.exp.agility, this.mults.agility * currentNodeMults.AgilityLevelMultiplier);
this.exp.agility,
this.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier,
);
} }
export function gainCharismaExp(this: Person, exp: number): void { export function gainCharismaExp(this: Person, exp: number): void {
@ -99,7 +90,7 @@ export function gainCharismaExp(this: Person, exp: number): void {
this.skills.charisma = calculateSkill( this.skills.charisma = calculateSkill(
this.exp.charisma, this.exp.charisma,
this.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier, this.mults.charisma * currentNodeMults.CharismaLevelMultiplier,
); );
} }
@ -164,33 +155,29 @@ export function regenerateHp(this: Person, amt: number): void {
export function updateSkillLevels(this: Person): void { export function updateSkillLevels(this: Person): void {
this.skills.hacking = Math.max( this.skills.hacking = Math.max(
1, 1,
Math.floor(this.calculateSkill(this.exp.hacking, this.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier)), Math.floor(this.calculateSkill(this.exp.hacking, this.mults.hacking * currentNodeMults.HackingLevelMultiplier)),
); );
this.skills.strength = Math.max( this.skills.strength = Math.max(
1, 1,
Math.floor( Math.floor(this.calculateSkill(this.exp.strength, this.mults.strength * currentNodeMults.StrengthLevelMultiplier)),
this.calculateSkill(this.exp.strength, this.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier),
),
); );
this.skills.defense = Math.max( this.skills.defense = Math.max(
1, 1,
Math.floor(this.calculateSkill(this.exp.defense, this.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier)), Math.floor(this.calculateSkill(this.exp.defense, this.mults.defense * currentNodeMults.DefenseLevelMultiplier)),
); );
this.skills.dexterity = Math.max( this.skills.dexterity = Math.max(
1, 1,
Math.floor( Math.floor(
this.calculateSkill(this.exp.dexterity, this.mults.dexterity * BitNodeMultipliers.DexterityLevelMultiplier), this.calculateSkill(this.exp.dexterity, this.mults.dexterity * currentNodeMults.DexterityLevelMultiplier),
), ),
); );
this.skills.agility = Math.max( this.skills.agility = Math.max(
1, 1,
Math.floor(this.calculateSkill(this.exp.agility, this.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier)), Math.floor(this.calculateSkill(this.exp.agility, this.mults.agility * currentNodeMults.AgilityLevelMultiplier)),
); );
this.skills.charisma = Math.max( this.skills.charisma = Math.max(
1, 1,
Math.floor( Math.floor(this.calculateSkill(this.exp.charisma, this.mults.charisma * currentNodeMults.CharismaLevelMultiplier)),
this.calculateSkill(this.exp.charisma, this.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier),
),
); );
const ratio: number = Math.min(this.hp.current / this.hp.max, 1); const ratio: number = Math.min(this.hp.current / this.hp.max, 1);

@ -26,6 +26,7 @@ import { cyrb53 } from "../../utils/StringHelperFunctions";
import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { Person } from "../Person"; import { Person } from "../Person";
import { getEnumHelper } from "../../utils/EnumHelper";
export class PlayerObject extends Person implements IPlayer { export class PlayerObject extends Person implements IPlayer {
// Player-specific properties // Player-specific properties
@ -167,9 +168,19 @@ export class PlayerObject extends Person implements IPlayer {
/** Initializes a PlayerObject object from a JSON save state. */ /** Initializes a PlayerObject object from a JSON save state. */
static fromJSON(value: IReviverValue): PlayerObject { static fromJSON(value: IReviverValue): PlayerObject {
const player = Generic_fromJSON(PlayerObject, value.data); const player = Generic_fromJSON(PlayerObject, value.data);
// Any statistics that could be infinite would be serialized as null (JSON.stringify(Infinity) is "null")
player.hp = { current: player.hp?.current ?? 10, max: player.hp?.max ?? 10 }; player.hp = { current: player.hp?.current ?? 10, max: player.hp?.max ?? 10 };
player.money ??= 0; player.money ??= 0;
// Just remove from the save file any augs that have invalid name
player.augmentations = player.augmentations.filter((ownedAug) =>
getEnumHelper("AugmentationName").isMember(ownedAug.name),
);
player.queuedAugmentations = player.queuedAugmentations.filter((ownedAug) =>
getEnumHelper("AugmentationName").isMember(ownedAug.name),
);
player.updateSkillLevels(); player.updateSkillLevels();
// Converstion code for Player.sourceFiles is here instead of normal save conversion area because it needs
// to happen earlier for use in the savegame comparison tool.
if (Array.isArray(player.sourceFiles)) { if (Array.isArray(player.sourceFiles)) {
// Expect pre-2.3 sourcefile format here. // Expect pre-2.3 sourcefile format here.
type OldSourceFiles = { n: number; lvl: number }[]; type OldSourceFiles = { n: number; lvl: number }[];

@ -1,10 +1,11 @@
import { AugmentationName, CityName, CompletedProgramName, FactionName, LocationName, ToastVariant } from "@enums";
import type { PlayerObject } from "./PlayerObject"; import type { PlayerObject } from "./PlayerObject";
import type { ProgramFilePath } from "../../Paths/ProgramFilePath"; import type { ProgramFilePath } from "../../Paths/ProgramFilePath";
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers"; import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, CityName, CompletedProgramName, FactionName, LocationName, ToastVariant } from "@enums"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CodingContractRewardType, ICodingContractReward } from "../../CodingContracts"; import { CodingContractRewardType, ICodingContractReward } from "../../CodingContracts";
import { Company } from "../../Company/Company"; import { Company } from "../../Company/Company";
import { Companies } from "../../Company/Companies"; import { Companies } from "../../Company/Companies";
@ -548,22 +549,16 @@ export function reapplyAllAugmentations(this: PlayerObject, resetMultipliers = t
this.resetMultipliers(); this.resetMultipliers();
} }
for (let i = 0; i < this.augmentations.length; ++i) { for (const playerAug of this.augmentations) {
//Compatibility with new version
if (this.augmentations[i].name === "HacknetNode NIC Architecture Neural-Upload") {
this.augmentations[i].name = "Hacknet Node NIC Architecture Neural-Upload";
}
const playerAug = this.augmentations[i];
const augName = playerAug.name; const augName = playerAug.name;
if (augName == AugmentationName.NeuroFluxGovernor) { if (augName == AugmentationName.NeuroFluxGovernor) {
for (let j = 0; j < playerAug.level; ++j) { for (let i = 0; i < playerAug.level; ++i) {
applyAugmentation(this.augmentations[i], true); applyAugmentation(playerAug, true);
} }
continue; continue;
} }
applyAugmentation(this.augmentations[i], true); applyAugmentation(playerAug, true);
} }
this.updateSkillLevels(); this.updateSkillLevels();
@ -644,7 +639,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] {
!daedalusFac.isBanned && !daedalusFac.isBanned &&
!daedalusFac.isMember && !daedalusFac.isMember &&
!daedalusFac.alreadyInvited && !daedalusFac.alreadyInvited &&
numAugmentations >= BitNodeMultipliers.DaedalusAugsRequirement && numAugmentations >= currentNodeMults.DaedalusAugsRequirement &&
this.money >= 100000000000 && this.money >= 100000000000 &&
(this.skills.hacking >= 2500 || (this.skills.hacking >= 2500 ||
(this.skills.strength >= 1500 && (this.skills.strength >= 1500 &&
@ -1080,7 +1075,7 @@ export function setBitNodeNumber(this: PlayerObject, n: number): void {
this.bitNodeN = n; this.bitNodeN = n;
} }
export function queueAugmentation(this: PlayerObject, name: string): void { export function queueAugmentation(this: PlayerObject, name: AugmentationName): void {
for (const aug of this.queuedAugmentations) { for (const aug of this.queuedAugmentations) {
if (aug.name == name) { if (aug.name == name) {
console.warn(`tried to queue ${name} twice, this may be a bug`); console.warn(`tried to queue ${name} twice, this may be a bug`);
@ -1152,7 +1147,7 @@ export function gainCodingContractReward(
} }
case CodingContractRewardType.Money: case CodingContractRewardType.Money:
default: { default: {
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney; const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney;
this.gainMoney(moneyGain, "codingcontract"); this.gainMoney(moneyGain, "codingcontract");
return `Gained ${formatMoney(moneyGain)}`; return `Gained ${formatMoney(moneyGain)}`;
} }

@ -1,7 +1,7 @@
// Server and HacknetServer-related methods for the Player class (PlayerObject) // Server and HacknetServer-related methods for the Player class (PlayerObject)
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { HacknetServer } from "../../Hacknet/HacknetServer"; import { HacknetServer } from "../../Hacknet/HacknetServer";
@ -35,7 +35,7 @@ export function getUpgradeHomeRamCost(this: PlayerObject): number {
//Calculate cost //Calculate cost
//Have cost increase by some percentage each time RAM has been upgraded //Have cost increase by some percentage each time RAM has been upgraded
const mult = Math.pow(1.58, numUpgrades); const mult = Math.pow(1.58, numUpgrades);
const cost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHome * mult * BitNodeMultipliers.HomeComputerRamCost; const cost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHome * mult * currentNodeMults.HomeComputerRamCost;
return cost; return cost;
} }

@ -7,14 +7,16 @@
* Sleeves are unlocked in BitNode-10. * Sleeves are unlocked in BitNode-10.
*/ */
import type { SleevePerson } from "@nsdefs";
import type { Augmentation } from "../../Augmentation/Augmentation";
import type { Company } from "../../Company/Company";
import type { CompanyPosition } from "../../Company/CompanyPosition";
import type { SleeveWork } from "./Work/Work";
import { Player } from "@player"; import { Player } from "@player";
import { Person } from "../Person"; import { Person } from "../Person";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Companies } from "../../Company/Companies"; import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions"; import { CompanyPositions } from "../../Company/CompanyPositions";
import { Contracts } from "../../Bladeburner/data/Contracts"; import { Contracts } from "../../Bladeburner/data/Contracts";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
@ -24,7 +26,6 @@ import { Factions } from "../../Faction/Factions";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
import { formatPercent } from "../../ui/formatNumber"; import { formatPercent } from "../../ui/formatNumber";
import { SleeveWork } from "./Work/Work";
import { SleeveClassWork } from "./Work/SleeveClassWork"; import { SleeveClassWork } from "./Work/SleeveClassWork";
import { SleeveSynchroWork } from "./Work/SleeveSynchroWork"; import { SleeveSynchroWork } from "./Work/SleeveSynchroWork";
import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork"; import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork";
@ -35,8 +36,8 @@ import { SleeveSupportWork } from "./Work/SleeveSupportWork";
import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork"; import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork";
import { SleeveCrimeWork } from "./Work/SleeveCrimeWork"; import { SleeveCrimeWork } from "./Work/SleeveCrimeWork";
import * as sleeveMethods from "./SleeveMethods"; import * as sleeveMethods from "./SleeveMethods";
import { SleevePerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "../formulas/intelligence"; import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { getEnumHelper } from "../../utils/EnumHelper";
export class Sleeve extends Person implements SleevePerson { export class Sleeve extends Person implements SleevePerson {
currentWork: SleeveWork | null = null; currentWork: SleeveWork | null = null;
@ -475,8 +476,17 @@ export class Sleeve extends Person implements SleevePerson {
/** Initializes a Sleeve object from a JSON save state. */ /** Initializes a Sleeve object from a JSON save state. */
static fromJSON(value: IReviverValue): Sleeve { static fromJSON(value: IReviverValue): Sleeve {
if (!value.data.hp?.current || !value.data.hp?.max) value.data.hp = { current: 10, max: 10 }; const sleeve = Generic_fromJSON(Sleeve, value.data);
return Generic_fromJSON(Sleeve, value.data); if (!sleeve.hp?.current || !sleeve.hp?.max) sleeve.hp = { current: 10, max: 10 };
// Remove any invalid aug names on game load
sleeve.augmentations = sleeve.augmentations.filter((ownedAug) =>
getEnumHelper("AugmentationName").isMember(ownedAug.name),
);
sleeve.queuedAugmentations = sleeve.queuedAugmentations.filter((ownedAug) =>
getEnumHelper("AugmentationName").isMember(ownedAug.name),
);
return sleeve;
} }
} }

@ -2,10 +2,11 @@ import { Player } from "@player";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { Sleeve } from "./Sleeve"; import { Sleeve } from "./Sleeve";
import { Augmentation } from "../../Augmentation/Augmentation"; import { Augmentation } from "../../Augmentation/Augmentation";
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { mergeMultipliers, Multipliers } from "../Multipliers"; import { mergeMultipliers, Multipliers } from "../Multipliers";
import { getFactionAugmentationsFiltered } from "../../Faction/FactionHelpers"; import { getFactionAugmentationsFiltered } from "../../Faction/FactionHelpers";
import { getAugCost } from "../../Augmentation/AugmentationHelpers";
/** Updates this object's multipliers for the given augmentation */ /** Updates this object's multipliers for the given augmentation */
export function applyAugmentation(this: Sleeve, aug: Augmentation): void { export function applyAugmentation(this: Sleeve, aug: Augmentation): void {
@ -61,10 +62,10 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
const gangAugs = getFactionAugmentationsFiltered(fac); const gangAugs = getFactionAugmentationsFiltered(fac);
for (const augName of gangAugs) { for (const augName of gangAugs) {
const aug = StaticAugmentations[augName]; const aug = Augmentations[augName];
if (!isAvailableForSleeve(aug)) continue; if (!isAvailableForSleeve(aug)) continue;
if (fac.playerReputation > aug.getCost().repCost) { if (fac.playerReputation > getAugCost(aug).repCost) {
availableAugs.push(aug); availableAugs.push(aug);
} }
} }
@ -77,10 +78,10 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
if (!fac) continue; if (!fac) continue;
for (const augName of fac.augmentations) { for (const augName of fac.augmentations) {
const aug = StaticAugmentations[augName]; const aug = Augmentations[augName];
if (!isAvailableForSleeve(aug)) continue; if (!isAvailableForSleeve(aug)) continue;
if (fac.playerReputation > aug.getCost().repCost) { if (fac.playerReputation > getAugCost(aug).repCost) {
availableAugs.push(aug); availableAugs.push(aug);
} }
} }
@ -88,7 +89,7 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
// Add the stanek sleeve aug // Add the stanek sleeve aug
if (!ownedAugNames.includes(AugmentationName.ZOE) && Player.factions.includes(FactionName.ChurchOfTheMachineGod)) { if (!ownedAugNames.includes(AugmentationName.ZOE) && Player.factions.includes(FactionName.ChurchOfTheMachineGod)) {
const aug = StaticAugmentations[AugmentationName.ZOE]; const aug = Augmentations[AugmentationName.ZOE];
availableAugs.push(aug); availableAugs.push(aug);
} }

@ -1,5 +1,5 @@
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { CalculateShareMult } from "../../NetworkShare/Share"; import { CalculateShareMult } from "../../NetworkShare/Share";
import { Person as IPerson } from "@nsdefs"; import { Person as IPerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "./intelligence"; import { calculateIntelligenceBonus } from "./intelligence";
@ -9,7 +9,7 @@ function mult(favor: number): number {
if (isNaN(favorMult)) { if (isNaN(favorMult)) {
favorMult = 1; favorMult = 1;
} }
return favorMult * BitNodeMultipliers.FactionWorkRepGain; return favorMult * currentNodeMults.FactionWorkRepGain;
} }
export function getHackingWorkRepGain(p: IPerson, favor: number): number { export function getHackingWorkRepGain(p: IPerson, favor: number): number {

@ -1,6 +1,4 @@
import { AugmentationName, CityName, CompletedProgramName, FactionName, LiteratureName } from "@enums"; import { AugmentationName, CityName, CompletedProgramName, FactionName, LiteratureName } from "@enums";
import { StaticAugmentations } from "./Augmentation/StaticAugmentations";
import { augmentationExists, initAugmentations } from "./Augmentation/AugmentationHelpers";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Companies, initCompanies } from "./Company/Companies"; import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData"; import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData";
@ -51,15 +49,15 @@ export function prestigeAugmentation(): void {
AddToAllServers(homeComp); AddToAllServers(homeComp);
prestigeHomeComputer(homeComp); prestigeHomeComputer(homeComp);
if (augmentationExists(AugmentationName.Neurolink) && Player.hasAugmentation(AugmentationName.Neurolink, true)) { if (Player.hasAugmentation(AugmentationName.Neurolink, true)) {
homeComp.programs.push(CompletedProgramName.ftpCrack); homeComp.programs.push(CompletedProgramName.ftpCrack);
homeComp.programs.push(CompletedProgramName.relaySmtp); homeComp.programs.push(CompletedProgramName.relaySmtp);
} }
if (augmentationExists(AugmentationName.CashRoot) && Player.hasAugmentation(AugmentationName.CashRoot, true)) { if (Player.hasAugmentation(AugmentationName.CashRoot, true)) {
Player.setMoney(1e6); Player.setMoney(1e6);
homeComp.programs.push(CompletedProgramName.bruteSsh); homeComp.programs.push(CompletedProgramName.bruteSsh);
} }
if (augmentationExists(AugmentationName.PCMatrix) && Player.hasAugmentation(AugmentationName.PCMatrix, true)) { if (Player.hasAugmentation(AugmentationName.PCMatrix, true)) {
homeComp.programs.push(CompletedProgramName.deepScan1); homeComp.programs.push(CompletedProgramName.deepScan1);
homeComp.programs.push(CompletedProgramName.autoLink); homeComp.programs.push(CompletedProgramName.autoLink);
} }
@ -86,7 +84,7 @@ export function prestigeAugmentation(): void {
initFactions(); // Factions must be initialized before augmentations initFactions(); // Factions must be initialized before augmentations
Player.factionInvitations = Player.factionInvitations.concat(maintainMembership); Player.factionInvitations = Player.factionInvitations.concat(maintainMembership);
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
Player.hp.current = Player.hp.max; Player.hp.current = Player.hp.max;
initCompanies(); initCompanies();
@ -136,7 +134,7 @@ export function prestigeAugmentation(): void {
} }
// Red Pill // Red Pill
if (augmentationExists(AugmentationName.TheRedPill) && Player.hasAugmentation(AugmentationName.TheRedPill, true)) { if (Player.hasAugmentation(AugmentationName.TheRedPill, true)) {
const WorldDaemon = GetServer(SpecialServers.WorldDaemon); const WorldDaemon = GetServer(SpecialServers.WorldDaemon);
const DaedalusServer = GetServer(SpecialServers.DaedalusServer); const DaedalusServer = GetServer(SpecialServers.DaedalusServer);
if (WorldDaemon && DaedalusServer) { if (WorldDaemon && DaedalusServer) {
@ -145,10 +143,7 @@ export function prestigeAugmentation(): void {
} }
} }
if ( if (Player.hasAugmentation(AugmentationName.StaneksGift1, true)) {
augmentationExists(AugmentationName.StaneksGift1) &&
Player.hasAugmentation(AugmentationName.StaneksGift1, true)
) {
joinFaction(Factions[FactionName.ChurchOfTheMachineGod]); joinFaction(Factions[FactionName.ChurchOfTheMachineGod]);
} }
@ -205,11 +200,6 @@ export function prestigeSourceFile(isFlume: boolean): void {
Terminal.finishAction(true); Terminal.finishAction(true);
} }
// Delete all Augmentations
for (const name of Object.getOwnPropertyNames(StaticAugmentations)) {
delete StaticAugmentations[name];
}
// Give levels of NeuroFluxGovernor for Source-File 12. Must be done here before Augmentations are recalculated // Give levels of NeuroFluxGovernor for Source-File 12. Must be done here before Augmentations are recalculated
if (Player.sourceFileLvl(12) > 0) { if (Player.sourceFileLvl(12) > 0) {
Player.augmentations.push({ Player.augmentations.push({
@ -220,7 +210,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
// Re-initialize things - This will update any changes // Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations initFactions(); // Factions must be initialized before augmentations
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
initCompanies(); initCompanies();

@ -7,7 +7,7 @@ import { Player } from "@player";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { formatMoney } from "../ui/formatNumber"; import { formatMoney } from "../ui/formatNumber";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { BitFlumeEvent } from "../BitNode/ui/BitFlumeModal"; import { BitFlumeEvent } from "../BitNode/ui/BitFlumeModal";
import { calculateHackingTime, calculateGrowTime, calculateWeakenTime } from "../Hacking"; import { calculateHackingTime, calculateGrowTime, calculateWeakenTime } from "../Hacking";
import { CompletedProgramName, FactionName } from "@enums"; import { CompletedProgramName, FactionName } from "@enums";
@ -286,7 +286,7 @@ export const Programs: Record<CompletedProgramName, Program> = {
name: CompletedProgramName.flight, name: CompletedProgramName.flight,
create: null, create: null,
run: (): void => { run: (): void => {
const numAugReq = BitNodeMultipliers.DaedalusAugsRequirement; const numAugReq = currentNodeMults.DaedalusAugsRequirement;
const fulfilled = const fulfilled =
Player.augmentations.length >= numAugReq && Player.money > 1e11 && Player.skills.hacking >= 2500; Player.augmentations.length >= numAugReq && Player.money > 1e11 && Player.skills.hacking >= 2500;
if (!fulfilled) { if (!fulfilled) {

@ -28,7 +28,6 @@ import { save } from "./db";
import { AwardNFG, v1APIBreak } from "./utils/v1APIBreak"; import { AwardNFG, v1APIBreak } from "./utils/v1APIBreak";
import { AugmentationName, FactionName, LocationName, ToastVariant } from "@enums"; import { AugmentationName, FactionName, LocationName, ToastVariant } from "@enums";
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { pushGameSaved } from "./Electron"; import { pushGameSaved } from "./Electron";
import { defaultMonacoTheme } from "./ScriptEditor/ui/themes"; import { defaultMonacoTheme } from "./ScriptEditor/ui/themes";
import { Faction } from "./Faction/Faction"; import { Faction } from "./Faction/Faction";
@ -364,7 +363,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
if (typeof ver !== "number") return; if (typeof ver !== "number") return;
if (ver < 2) { if (ver < 2) {
AwardNFG(10); AwardNFG(10);
initAugmentations(); Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
} }
if (ver < 3) { if (ver < 3) {
@ -447,7 +446,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
]; ];
v22PlayerBreak(); v22PlayerBreak();
initAugmentations(); Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
} }
@ -679,17 +678,6 @@ function evaluateVersionCompatibility(ver: string | number): void {
} }
//2.3 hotfix changes and 2.3.1 changes //2.3 hotfix changes and 2.3.1 changes
if (ver < 32) { if (ver < 32) {
// Due to a bug from before 2.3, some scripts have the wrong server listed. In 2.3 this caused issues.
for (const server of GetAllServers()) {
for (const script of server.scripts.values()) {
if (script.server !== server.hostname) {
console.warn(
`Detected script ${script.filename} on ${server.hostname} with incorrect server property: ${script.server}. Repairing.`,
);
script.server = server.hostname;
}
}
}
// Sanitize corporation exports // Sanitize corporation exports
let anyExportsFailed = false; let anyExportsFailed = false;
if (Player.corporation) { if (Player.corporation) {
@ -723,6 +711,19 @@ Error: ${e}`);
"Some material exports failed to validate while loading and have been removed. See console for more info.", "Some material exports failed to validate while loading and have been removed. See console for more info.",
); );
} }
if (ver < 33) {
// 2.3.2 fixed what should be the last issue with scripts having the wrong server assigned..
for (const server of GetAllServers()) {
for (const script of server.scripts.values()) {
if (script.server !== server.hostname) {
console.warn(
`Detected script ${script.filename} on ${server.hostname} with incorrect server property: ${script.server}. Repairing.`,
);
script.server = server.hostname;
}
}
}
}
} }
function loadGame(saveString: string): boolean { function loadGame(saveString: string): boolean {

@ -9,7 +9,7 @@ import { createRandomIp } from "../utils/IPAddress";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Reviver } from "../utils/JSONReviver"; import { Reviver } from "../utils/JSONReviver";
import { SpecialServers } from "./data/SpecialServers"; import { SpecialServers } from "./data/SpecialServers";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { IPAddress, isIPAddress } from "../Types/strings"; import { IPAddress, isIPAddress } from "../Types/strings";
import "../Script/RunningScript"; // For reviver side-effect import "../Script/RunningScript"; // For reviver side-effect
@ -159,7 +159,7 @@ export function initForeignServers(homeComputer: Server): void {
} }
if (server.hostname === SpecialServers.WorldDaemon) { if (server.hostname === SpecialServers.WorldDaemon) {
server.requiredHackingSkill *= BitNodeMultipliers.WorldDaemonDifficulty; server.requiredHackingSkill *= currentNodeMults.WorldDaemonDifficulty;
} }
AddToAllServers(server); AddToAllServers(server);
if (metadata.networkLayer !== undefined) { if (metadata.networkLayer !== undefined) {

@ -1,7 +1,7 @@
// Class representing a single hackable Server // Class representing a single hackable Server
import { BaseServer } from "./BaseServer"; import { BaseServer } from "./BaseServer";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { createRandomString } from "../utils/helpers/createRandomString"; import { createRandomString } from "../utils/helpers/createRandomString";
import { createRandomIp } from "../utils/IPAddress"; import { createRandomIp } from "../utils/IPAddress";
@ -72,12 +72,12 @@ export class Server extends BaseServer {
/* Hacking information (only valid for "foreign" aka non-purchased servers) */ /* Hacking information (only valid for "foreign" aka non-purchased servers) */
this.requiredHackingSkill = params.requiredHackingSkill != null ? params.requiredHackingSkill : 1; this.requiredHackingSkill = params.requiredHackingSkill != null ? params.requiredHackingSkill : 1;
const baseMoney = params.moneyAvailable ?? 0; const baseMoney = params.moneyAvailable ?? 0;
this.moneyAvailable = baseMoney * BitNodeMultipliers.ServerStartingMoney; this.moneyAvailable = baseMoney * currentNodeMults.ServerStartingMoney;
this.moneyMax = 25 * baseMoney * BitNodeMultipliers.ServerMaxMoney; this.moneyMax = 25 * baseMoney * currentNodeMults.ServerMaxMoney;
//Hack Difficulty is synonymous with server security. Base Difficulty = Starting difficulty //Hack Difficulty is synonymous with server security. Base Difficulty = Starting difficulty
const realDifficulty = const realDifficulty =
params.hackDifficulty != null ? params.hackDifficulty * BitNodeMultipliers.ServerStartingSecurity : 1; params.hackDifficulty != null ? params.hackDifficulty * currentNodeMults.ServerStartingSecurity : 1;
this.hackDifficulty = Math.min(realDifficulty, 100); this.hackDifficulty = Math.min(realDifficulty, 100);
this.baseDifficulty = this.hackDifficulty; this.baseDifficulty = this.hackDifficulty;
this.minDifficulty = Math.min(Math.max(1, Math.round(realDifficulty / 3)), 100); this.minDifficulty = Math.min(Math.max(1, Math.round(realDifficulty / 3)), 100);
@ -141,7 +141,7 @@ export class Server extends BaseServer {
/** Lowers the server's security level (difficulty) by the specified amount) */ /** Lowers the server's security level (difficulty) by the specified amount) */
weaken(amt: number): void { weaken(amt: number): void {
this.hackDifficulty -= amt * BitNodeMultipliers.ServerWeakenRate; this.hackDifficulty -= amt * currentNodeMults.ServerWeakenRate;
this.capDifficulty(); this.capDifficulty();
} }

@ -3,7 +3,7 @@ import { Server, IConstructorParams } from "./Server";
import { BaseServer } from "./BaseServer"; import { BaseServer } from "./BaseServer";
import { calculateServerGrowth } from "./formulas/grow"; import { calculateServerGrowth } from "./formulas/grow";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Player } from "@player"; import { Player } from "@player";
import { CompletedProgramName, LiteratureName } from "@enums"; import { CompletedProgramName, LiteratureName } from "@enums";
@ -66,7 +66,7 @@ export function numCycleForGrowth(server: IServer, growth: number, cores = 1): n
(Math.log(ajdGrowthRate) * (Math.log(ajdGrowthRate) *
Player.mults.hacking_grow * Player.mults.hacking_grow *
serverGrowthPercentage * serverGrowthPercentage *
BitNodeMultipliers.ServerGrowthRate * currentNodeMults.ServerGrowthRate *
coreBonus); coreBonus);
return cycles; return cycles;
@ -105,7 +105,7 @@ export function numCycleForGrowthCorrected(
const serverGrowthPercentage = server.serverGrowth / 100.0; const serverGrowthPercentage = server.serverGrowth / 100.0;
const coreMultiplier = 1 + (cores - 1) / 16; const coreMultiplier = 1 + (cores - 1) / 16;
const threadMultiplier = const threadMultiplier =
serverGrowthPercentage * person.mults.hacking_grow * coreMultiplier * BitNodeMultipliers.ServerGrowthRate; serverGrowthPercentage * person.mults.hacking_grow * coreMultiplier * currentNodeMults.ServerGrowthRate;
/* To understand what is done below we need to do some math. I hope the explanation is clear enough. /* To understand what is done below we need to do some math. I hope the explanation is clear enough.
* First of, the names will be shortened for ease of manipulation: * First of, the names will be shortened for ease of manipulation:

@ -5,7 +5,7 @@
import { AddToAllServers, createUniqueRandomIp, GetServer, renameServer } from "./AllServers"; import { AddToAllServers, createUniqueRandomIp, GetServer, renameServer } from "./AllServers";
import { safelyCreateUniqueServer } from "./ServerHelpers"; import { safelyCreateUniqueServer } from "./ServerHelpers";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Player } from "@player"; import { Player } from "@player";
@ -34,8 +34,8 @@ export function getPurchaseServerCost(ram: number): number {
return ( return (
sanitizedRam * sanitizedRam *
CONSTANTS.BaseCostFor1GBOfRamServer * CONSTANTS.BaseCostFor1GBOfRamServer *
BitNodeMultipliers.PurchasedServerCost * currentNodeMults.PurchasedServerCost *
Math.pow(BitNodeMultipliers.PurchasedServerSoftcap, upg) Math.pow(currentNodeMults.PurchasedServerSoftcap, upg)
); );
} }
@ -86,11 +86,11 @@ export const renamePurchasedServer = (hostname: string, newName: string): void =
}; };
export function getPurchaseServerLimit(): number { export function getPurchaseServerLimit(): number {
return Math.round(CONSTANTS.PurchasedServerLimit * BitNodeMultipliers.PurchasedServerLimit); return Math.round(CONSTANTS.PurchasedServerLimit * currentNodeMults.PurchasedServerLimit);
} }
export function getPurchaseServerMaxRam(): number { export function getPurchaseServerMaxRam(): number {
const ram = Math.round(CONSTANTS.PurchasedServerMaxRam * BitNodeMultipliers.PurchasedServerMaxRam); const ram = Math.round(CONSTANTS.PurchasedServerMaxRam * currentNodeMults.PurchasedServerMaxRam);
// Round this to the nearest power of 2 // Round this to the nearest power of 2
return 1 << (31 - Math.clz32(ram)); return 1 << (31 - Math.clz32(ram));

@ -1,5 +1,5 @@
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Person as IPerson, Server as IServer } from "@nsdefs"; import { Person as IPerson, Server as IServer } from "@nsdefs";
export function calculateServerGrowth(server: IServer, threads: number, p: IPerson, cores = 1): number { export function calculateServerGrowth(server: IServer, threads: number, p: IPerson, cores = 1): number {
@ -17,7 +17,7 @@ export function calculateServerGrowth(server: IServer, threads: number, p: IPers
//Calculate adjusted server growth rate based on parameters //Calculate adjusted server growth rate based on parameters
const serverGrowthPercentage = server.serverGrowth / 100; const serverGrowthPercentage = server.serverGrowth / 100;
const numServerGrowthCyclesAdjusted = const numServerGrowthCyclesAdjusted =
numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; numServerGrowthCycles * serverGrowthPercentage * currentNodeMults.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles //Apply serverGrowth for the calculated number of growth cycles
const coreBonus = 1 + (cores - 1) / 16; const coreBonus = 1 + (cores - 1) / 16;

@ -1,12 +1,12 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
export function getStockMarket4SDataCost(): number { export function getStockMarket4SDataCost(): number {
return CONSTANTS.MarketData4SCost * BitNodeMultipliers.FourSigmaMarketDataCost; return CONSTANTS.MarketData4SCost * currentNodeMults.FourSigmaMarketDataCost;
} }
export function getStockMarket4STixApiCost(): number { export function getStockMarket4STixApiCost(): number {
return CONSTANTS.MarketDataTixApi4SCost * BitNodeMultipliers.FourSigmaMarketDataApiCost; return CONSTANTS.MarketDataTixApi4SCost * currentNodeMults.FourSigmaMarketDataApiCost;
} }
export function getStockMarketWseCost(): number { export function getStockMarketWseCost(): number {

@ -74,7 +74,7 @@ import { wget } from "./commands/wget";
import { hash } from "../hash/hash"; import { hash } from "../hash/hash";
import { apr1 } from "./commands/apr1"; import { apr1 } from "./commands/apr1";
import { changelog } from "./commands/changelog"; import { changelog } from "./commands/changelog";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Engine } from "../engine"; import { Engine } from "../engine";
import { Directory, resolveDirectory, root } from "../Paths/Directory"; import { Directory, resolveDirectory, root } from "../Paths/Directory";
import { FilePath, isFilePath, resolveFilePath } from "../Paths/FilePath"; import { FilePath, isFilePath, resolveFilePath } from "../Paths/FilePath";
@ -213,7 +213,7 @@ export class Terminal {
Engine.Counters.checkFactionInvitations = 0; Engine.Counters.checkFactionInvitations = 0;
Engine.checkCounters(); Engine.checkCounters();
let moneyGained = calculatePercentMoneyHacked(server, Player) * BitNodeMultipliers.ManualHackMoney; let moneyGained = calculatePercentMoneyHacked(server, Player) * currentNodeMults.ManualHackMoney;
moneyGained = Math.floor(server.moneyAvailable * moneyGained); moneyGained = Math.floor(server.moneyAvailable * moneyGained);
if (moneyGained <= 0) { if (moneyGained <= 0) {

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Crime } from "../Crime/Crime"; import { Crime } from "../Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats, multWorkStats } from "./WorkStats"; import { newWorkStats, scaleWorkStats, WorkStats, multWorkStats } from "./WorkStats";
import { Person as IPerson } from "@nsdefs"; import { Person as IPerson } from "@nsdefs";
@ -55,9 +55,9 @@ export function calculateCrimeWorkStats(person: IPerson, crime: Crime): WorkStat
intExp: crime.intelligence_exp, intExp: crime.intelligence_exp,
}), }),
person.mults, person.mults,
person.mults.crime_money * BitNodeMultipliers.CrimeMoney, person.mults.crime_money * currentNodeMults.CrimeMoney,
), ),
BitNodeMultipliers.CrimeExpGain, currentNodeMults.CrimeExpGain,
false, false,
); );
return gains; return gains;
@ -77,7 +77,7 @@ export const calculateFactionRep = (person: IPerson, type: FactionWorkType, favo
export function calculateFactionExp(person: IPerson, type: FactionWorkType): WorkStats { export function calculateFactionExp(person: IPerson, type: FactionWorkType): WorkStats {
return scaleWorkStats( return scaleWorkStats(
multWorkStats(FactionWorkStats[type], person.mults), multWorkStats(FactionWorkStats[type], person.mults),
BitNodeMultipliers.FactionWorkExpGain / gameCPS, currentNodeMults.FactionWorkExpGain / gameCPS,
); );
} }
@ -119,7 +119,7 @@ export const calculateCompanyWorkStats = (
const gains = scaleWorkStats( const gains = scaleWorkStats(
multWorkStats( multWorkStats(
{ {
money: companyPosition.baseSalary * company.salaryMultiplier * bn11Mult * BitNodeMultipliers.CompanyWorkMoney, money: companyPosition.baseSalary * company.salaryMultiplier * bn11Mult * currentNodeMults.CompanyWorkMoney,
hackExp: companyPosition.hackingExpGain, hackExp: companyPosition.hackingExpGain,
strExp: companyPosition.strengthExpGain, strExp: companyPosition.strengthExpGain,
defExp: companyPosition.defenseExpGain, defExp: companyPosition.defenseExpGain,
@ -130,7 +130,7 @@ export const calculateCompanyWorkStats = (
worker.mults, worker.mults,
worker.mults.work_money, worker.mults.work_money,
), ),
company.expMultiplier * BitNodeMultipliers.CompanyWorkExpGain, company.expMultiplier * currentNodeMults.CompanyWorkExpGain,
false, false,
); );

@ -9,17 +9,17 @@ import { applyAugmentation } from "../Augmentation/AugmentationHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation"; import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { Augmentations } from "../Augmentation/Augmentations";
export const isGraftingWork = (w: Work | null): w is GraftingWork => w !== null && w.type === WorkType.GRAFTING; export const isGraftingWork = (w: Work | null): w is GraftingWork => w !== null && w.type === WorkType.GRAFTING;
interface GraftingWorkParams { interface GraftingWorkParams {
augmentation: string; augmentation: AugmentationName;
singularity: boolean; singularity: boolean;
} }
export class GraftingWork extends Work { export class GraftingWork extends Work {
augmentation: string; augmentation: AugmentationName;
unitCompleted: number; unitCompleted: number;
constructor(params?: GraftingWorkParams) { constructor(params?: GraftingWorkParams) {
@ -31,7 +31,7 @@ export class GraftingWork extends Work {
} }
unitNeeded(): number { unitNeeded(): number {
return new GraftableAugmentation(StaticAugmentations[this.augmentation]).time; return new GraftableAugmentation(Augmentations[this.augmentation]).time;
} }
process(cycles: number): boolean { process(cycles: number): boolean {

@ -1,5 +1,4 @@
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationName, ToastVariant } from "@enums"; import { AugmentationName, ToastVariant } from "@enums";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { initSourceFiles } from "./SourceFile/SourceFiles"; import { initSourceFiles } from "./SourceFile/SourceFiles";
@ -231,7 +230,7 @@ const Engine: {
if (loadGame(saveString)) { if (loadGame(saveString)) {
FormatsNeedToChange.emit(); FormatsNeedToChange.emit();
initBitNodeMultipliers(); initBitNodeMultipliers();
initAugmentations(); // Also calls Player.reapplyAllAugmentations() Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
if (Player.hasWseAccount) { if (Player.hasWseAccount) {
initSymbolToStockMap(); initSymbolToStockMap();
@ -377,7 +376,7 @@ const Engine: {
initForeignServers(Player.getHomeComputer()); initForeignServers(Player.getHomeComputer());
initCompanies(); initCompanies();
initFactions(); initFactions();
initAugmentations(); Player.reapplyAllAugmentations();
// Start interactive tutorial // Start interactive tutorial
iTutorialStart(); iTutorialStart();

@ -1,8 +1,8 @@
import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material"; import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material";
import { MoreHoriz, Info } from "@mui/icons-material"; import { MoreHoriz, Info } from "@mui/icons-material";
import React, { useState } from "react"; import React, { useState } from "react";
import { BitNodes, defaultMultipliers, getBitNodeMultipliers } from "../BitNode/BitNode"; import { BitNodes } from "../BitNode/BitNode";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { BitNodeMultipliersDisplay } from "../BitNode/ui/BitnodeMultipliersDescription"; import { BitNodeMultipliersDisplay } from "../BitNode/ui/BitnodeMultipliersDescription";
import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { HacknetServerConstants } from "../Hacknet/data/Constants";
import { getPurchaseServerLimit } from "../Server/ServerPurchases"; import { getPurchaseServerLimit } from "../Server/ServerPurchases";
@ -15,7 +15,6 @@ import { Modal } from "./React/Modal";
import { Money } from "./React/Money"; import { Money } from "./React/Money";
import { StatsRow } from "./React/StatsRow"; import { StatsRow } from "./React/StatsRow";
import { StatsTable } from "./React/StatsTable"; import { StatsTable } from "./React/StatsTable";
import { isEqual } from "lodash";
import { useRerender } from "./React/hooks"; import { useRerender } from "./React/hooks";
interface EmployersModalProps { interface EmployersModalProps {
@ -211,12 +210,7 @@ export function CharacterStats(): React.ReactElement {
timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]); timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]);
let showBitNodeMults = false; let showBitNodeMults = false;
if (Player.sourceFileLvl(5) > 0) { if (Player.sourceFileLvl(5) > 0) showBitNodeMults = true;
const n = Player.bitNodeN;
const maxSfLevel = n === 12 ? Infinity : 3;
const mults = getBitNodeMultipliers(n, Math.min(Player.sourceFileLvl(n) + 1, maxSfLevel));
showBitNodeMults = !isEqual(mults, defaultMultipliers);
}
return ( return (
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}> <Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
<Typography variant="h4">Stats</Typography> <Typography variant="h4">Stats</Typography>
@ -355,12 +349,12 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Hacking Money", mult: "Hacking Money",
value: Player.mults.hacking_money, value: Player.mults.hacking_money,
effValue: Player.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney, effValue: Player.mults.hacking_money * currentNodeMults.ScriptHackMoney,
}, },
{ {
mult: "Hacking Growth", mult: "Hacking Growth",
value: Player.mults.hacking_grow, value: Player.mults.hacking_grow,
effValue: Player.mults.hacking_grow * BitNodeMultipliers.ServerGrowthRate, effValue: Player.mults.hacking_grow * currentNodeMults.ServerGrowthRate,
}, },
]} ]}
color={Settings.theme.hack} color={Settings.theme.hack}
@ -370,12 +364,12 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Hacking Level", mult: "Hacking Level",
value: Player.mults.hacking, value: Player.mults.hacking,
effValue: Player.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier, effValue: Player.mults.hacking * currentNodeMults.HackingLevelMultiplier,
}, },
{ {
mult: "Hacking Experience", mult: "Hacking Experience",
value: Player.mults.hacking_exp, value: Player.mults.hacking_exp,
effValue: Player.mults.hacking_exp * BitNodeMultipliers.HackExpGain, effValue: Player.mults.hacking_exp * currentNodeMults.HackExpGain,
}, },
]} ]}
color={Settings.theme.hack} color={Settings.theme.hack}
@ -385,7 +379,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Strength Level", mult: "Strength Level",
value: Player.mults.strength, value: Player.mults.strength,
effValue: Player.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier, effValue: Player.mults.strength * currentNodeMults.StrengthLevelMultiplier,
}, },
{ {
mult: "Strength Experience", mult: "Strength Experience",
@ -399,7 +393,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Defense Level", mult: "Defense Level",
value: Player.mults.defense, value: Player.mults.defense,
effValue: Player.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier, effValue: Player.mults.defense * currentNodeMults.DefenseLevelMultiplier,
}, },
{ {
mult: "Defense Experience", mult: "Defense Experience",
@ -413,7 +407,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Dexterity Level", mult: "Dexterity Level",
value: Player.mults.dexterity, value: Player.mults.dexterity,
effValue: Player.mults.dexterity * BitNodeMultipliers.DexterityLevelMultiplier, effValue: Player.mults.dexterity * currentNodeMults.DexterityLevelMultiplier,
}, },
{ {
mult: "Dexterity Experience", mult: "Dexterity Experience",
@ -427,7 +421,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Agility Level", mult: "Agility Level",
value: Player.mults.agility, value: Player.mults.agility,
effValue: Player.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier, effValue: Player.mults.agility * currentNodeMults.AgilityLevelMultiplier,
}, },
{ {
mult: "Agility Experience", mult: "Agility Experience",
@ -441,7 +435,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Charisma Level", mult: "Charisma Level",
value: Player.mults.charisma, value: Player.mults.charisma,
effValue: Player.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier, effValue: Player.mults.charisma * currentNodeMults.CharismaLevelMultiplier,
}, },
{ {
mult: "Charisma Experience", mult: "Charisma Experience",
@ -459,7 +453,7 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Hacknet Node Production", mult: "Hacknet Node Production",
value: Player.mults.hacknet_node_money, value: Player.mults.hacknet_node_money,
effValue: Player.mults.hacknet_node_money * BitNodeMultipliers.HacknetNodeMoney, effValue: Player.mults.hacknet_node_money * currentNodeMults.HacknetNodeMoney,
}, },
{ {
mult: "Hacknet Node Purchase Cost", mult: "Hacknet Node Purchase Cost",
@ -490,13 +484,13 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Faction Reputation Gain", mult: "Faction Reputation Gain",
value: Player.mults.faction_rep, value: Player.mults.faction_rep,
effValue: Player.mults.faction_rep * BitNodeMultipliers.FactionWorkRepGain, effValue: Player.mults.faction_rep * currentNodeMults.FactionWorkRepGain,
color: Settings.theme.rep, color: Settings.theme.rep,
}, },
{ {
mult: "Salary", mult: "Salary",
value: Player.mults.work_money, value: Player.mults.work_money,
effValue: Player.mults.work_money * BitNodeMultipliers.CompanyWorkMoney, effValue: Player.mults.work_money * currentNodeMults.CompanyWorkMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
]} ]}
@ -511,13 +505,13 @@ export function CharacterStats(): React.ReactElement {
{ {
mult: "Crime Money", mult: "Crime Money",
value: Player.mults.crime_money, value: Player.mults.crime_money,
effValue: Player.mults.crime_money * BitNodeMultipliers.CrimeMoney, effValue: Player.mults.crime_money * currentNodeMults.CrimeMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
]} ]}
color={Settings.theme.combat} color={Settings.theme.combat}
/> />
{Player.canAccessBladeburner() && BitNodeMultipliers.BladeburnerRank > 0 && ( {Player.canAccessBladeburner() && currentNodeMults.BladeburnerRank > 0 && (
<MultiplierTable <MultiplierTable
rows={[ rows={[
{ {

@ -24,7 +24,7 @@ interface IProps {
export function AugmentationAccordion(props: IProps): React.ReactElement { export function AugmentationAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
let displayName = props.aug.name; let displayName: string = props.aug.name;
if (props.level != null) { if (props.level != null) {
if (props.aug.name === AugmentationName.NeuroFluxGovernor) { if (props.aug.name === AugmentationName.NeuroFluxGovernor) {
displayName += ` - Level ${props.level}`; displayName += ` - Level ${props.level}`;

@ -24,7 +24,7 @@ import { Router } from "../GameRoot";
import { Page } from "../Router"; import { Page } from "../Router";
import { Player } from "@player"; import { Player } from "@player";
import { StatsProgressOverviewCell } from "./StatsProgressBar"; import { StatsProgressOverviewCell } from "./StatsProgressBar";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Box, Tooltip } from "@mui/material"; import { Box, Tooltip } from "@mui/material";
@ -73,12 +73,12 @@ const formattedVals: Record<RowName, () => string> = {
const skillMultUpdaters: Record<SkillRowName, () => number> = { const skillMultUpdaters: Record<SkillRowName, () => number> = {
//Used by skill bars to calculate the mult //Used by skill bars to calculate the mult
Hack: () => Player.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier, Hack: () => Player.mults.hacking * currentNodeMults.HackingLevelMultiplier,
Str: () => Player.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier, Str: () => Player.mults.strength * currentNodeMults.StrengthLevelMultiplier,
Def: () => Player.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier, Def: () => Player.mults.defense * currentNodeMults.DefenseLevelMultiplier,
Dex: () => Player.mults.dexterity * BitNodeMultipliers.DexterityLevelMultiplier, Dex: () => Player.mults.dexterity * currentNodeMults.DexterityLevelMultiplier,
Agi: () => Player.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier, Agi: () => Player.mults.agility * currentNodeMults.AgilityLevelMultiplier,
Cha: () => Player.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier, Cha: () => Player.mults.charisma * currentNodeMults.CharismaLevelMultiplier,
Int: () => 1, Int: () => 1,
}; };

@ -28,13 +28,17 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
if (this.isMember(toValidate)) return toValidate; if (this.isMember(toValidate)) return toValidate;
// assertString is just called so if the user didn't even pass in a string, they get a different error message // assertString is just called so if the user didn't even pass in a string, they get a different error message
assertString(ctx, argName, toValidate); assertString(ctx, argName, toValidate);
// Don't display all possibilities for large enums
let allowableValues = `Allowable values: ${this.valueArray.map((val) => `"${val}"`).join(", ")}`;
if (this.valueArray.length > 10) {
console.warn(
`Provided value ${toValidate} was not a valid option for enum type ${this.name}.\n${allowableValues}`,
);
allowableValues = `See the developer console for allowable values.`;
}
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
`Argument ${argName} should be a ${ `Argument ${argName} should be a ${this.name} enum member.\nProvided value: "${toValidate}".\n${allowableValues}`,
this.name
} enum member.\nProvided value: "${toValidate}".\nAllowable values: ${this.valueArray
.map((val) => `"${val}"`)
.join(", ")}`,
); );
} }
/** Provides case insensitivty and ignores spaces and dashes, and can always match the input */ /** Provides case insensitivty and ignores spaces and dashes, and can always match the input */

@ -1,7 +1,6 @@
import { AugmentationName } from "@enums"; import { AugmentationName } from "@enums";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { Player } from "@player"; import { Player } from "@player";
import { Script } from "../Script/Script";
import { GetAllServers } from "../Server/AllServers"; import { GetAllServers } from "../Server/AllServers";
import { resolveTextFilePath } from "../Paths/TextFilePath"; import { resolveTextFilePath } from "../Paths/TextFilePath";
import { resolveScriptFilePath } from "../Paths/ScriptFilePath"; import { resolveScriptFilePath } from "../Paths/ScriptFilePath";
@ -137,7 +136,7 @@ export function v1APIBreak(): void {
console.error(`Unexpected error resolving backup path for ${script.filename}`); console.error(`Unexpected error resolving backup path for ${script.filename}`);
continue; continue;
} }
server.scripts.set(filename, new Script(filename, script.code, script.server)); server.writeToScriptFile(filename, script.code);
script.code = convert(script.code); script.code = convert(script.code);
} }
} }