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

@ -1,35 +1,24 @@
// 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 { CONSTANTS } from "../Constants";
import { StaticAugmentations } from "./StaticAugmentations";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { getBaseAugmentationPriceMultiplier, getGenericAugmentationPriceMultiplier } from "./AugmentationHelpers";
import { initSoAAugmentations } from "./data/AugmentationCreator";
import { AugmentationName, CompletedProgramName, FactionName } from "@enums";
import { formatPercent } from "../ui/formatNumber";
import { Multipliers, defaultMultipliers } from "../PersonObjects/Multipliers";
import { getRecordKeys } from "../Types/Record";
export interface AugmentationCosts {
moneyCost: number;
repCost: number;
}
export interface IConstructorParams {
info: string | JSX.Element;
stats?: JSX.Element | null;
export interface AugmentationCtorParams {
info: string;
stats?: string;
isSpecial?: boolean;
moneyCost: number;
name: string;
prereqs?: string[];
name: AugmentationName;
prereqs?: AugmentationName[];
repCost: number;
factions: string[];
factions: FactionName[];
hacking?: number;
strength?: number;
@ -66,85 +55,42 @@ export interface IConstructorParams {
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%
const f = (x: number) => formatPercent(x, x - 1 < 0.1 ? 2 : 1);
let desc = <>Effects:</>;
let desc = "Effects:";
// Skills
if (
mults.hacking !== 1 &&
mults.hacking == mults.strength &&
mults.hacking == mults.defense &&
mults.hacking == mults.dexterity &&
mults.hacking == mults.agility &&
mults.hacking == mults.charisma
mults.hacking === mults.strength &&
mults.hacking === mults.defense &&
mults.hacking === mults.dexterity &&
mults.hacking === mults.agility &&
mults.hacking === mults.charisma
) {
desc = (
<>
{desc}
<br />+{f(mults.hacking - 1)} all skills
</>
);
desc += `\n+${f(mults.hacking - 1)} all skills`;
} else {
if (mults.hacking !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacking - 1)} hacking skill
</>
);
// Not allskills
if (mults.hacking !== 1) desc += `\n+${f(mults.hacking - 1)} hacking skill`;
if (
mults.strength !== 1 &&
mults.strength == mults.defense &&
mults.strength == mults.dexterity &&
mults.strength == mults.agility
mults.strength === mults.defense &&
mults.strength === mults.dexterity &&
mults.strength === mults.agility
) {
desc = (
<>
{desc}
<br />+{f(mults.strength - 1)} combat skills
</>
);
desc += `\n+${f(mults.strength - 1)} combat skills`;
} else {
if (mults.strength !== 1)
desc = (
<>
{desc}
<br />+{f(mults.strength - 1)} strength 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
</>
);
// Not all combat
if (mults.strength !== 1) desc += `\n+${f(mults.strength - 1)} strength skill`;
if (mults.defense !== 1) desc += `\n+${f(mults.defense - 1)} defense skill`;
if (mults.dexterity !== 1) desc += `\n+${f(mults.dexterity - 1)} dexterity skill`;
if (mults.agility !== 1) desc += `\n+${f(mults.agility - 1)} agility skill`;
}
if (mults.charisma !== 1)
desc = (
<>
{desc}
<br />+{f(mults.charisma - 1)} charisma skill
</>
);
if (mults.charisma !== 1) desc += `\n+${f(mults.charisma - 1)} charisma skill`;
}
// Skill XP
if (
mults.hacking_exp !== 1 &&
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.charisma_exp
) {
desc = (
<>
{desc}
<br />+{f(mults.hacking_exp - 1)} exp for all skills
</>
);
desc += `\n+${f(mults.hacking_exp - 1)} exp for all skills`;
} else {
if (mults.hacking_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacking_exp - 1)} hacking exp
</>
);
// Not allskillxp
if (mults.hacking_exp !== 1) desc += `\n+${f(mults.hacking_exp - 1)} hacking exp`;
if (
mults.strength_exp !== 1 &&
mults.strength_exp === mults.defense_exp &&
mults.strength_exp === mults.dexterity_exp &&
mults.strength_exp === mults.agility_exp
) {
desc = (
<>
{desc}
<br />+{f(mults.strength_exp - 1)} combat exp
</>
);
desc += `\n+${f(mults.strength_exp - 1)} combat exp`;
} else {
if (mults.strength_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.strength_exp - 1)} strength 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
</>
);
// Not all combat
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`;
if (mults.dexterity_exp !== 1) desc += `\n+${f(mults.dexterity_exp - 1)} dexterity exp`;
if (mults.agility_exp !== 1) desc += `\n+${f(mults.agility_exp - 1)} agility exp`;
}
if (mults.charisma_exp !== 1)
desc = (
<>
{desc}
<br />+{f(mults.charisma_exp - 1)} charisma exp
</>
);
if (mults.charisma_exp !== 1) desc += `\n+${f(mults.charisma_exp - 1)} charisma exp`;
}
if (mults.hacking_speed !== 1)
desc = (
<>
{desc}
<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.hacking_speed !== 1) desc += `\n+${f(mults.hacking_speed - 1)} faster hack(), grow(), and weaken()`;
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`;
if (mults.hacking_grow !== 1) desc += `\n+${f(mults.hacking_grow - 1)} grow() power`;
if (mults.faction_rep !== 1 && mults.faction_rep === mults.company_rep) {
desc = (
<>
{desc}
<br />+{f(mults.faction_rep - 1)} reputation from factions and 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
</>
);
// Reputation
if (mults.faction_rep !== 1 && mults.faction_rep === mults.company_rep)
desc += `\n+${f(mults.faction_rep - 1)} reputation from factions and companies`;
else {
// 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`;
}
if (mults.crime_money !== 1)
desc = (
<>
{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.crime_money !== 1) desc += `\n+${f(mults.crime_money - 1)} crime money`;
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`;
if (mults.hacknet_node_money !== 1)
desc = (
<>
{desc}
<br />+{f(mults.hacknet_node_money - 1)} hacknet production
</>
);
if (mults.hacknet_node_purchase_cost !== 1)
desc = (
<>
{desc}
<br />-{f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost
</>
);
if (mults.hacknet_node_level_cost !== 1)
desc = (
<>
{desc}
<br />-{f(-(mults.hacknet_node_level_cost - 1))} hacknet nodes upgrade cost
</>
);
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.
</>
);
// Hacknet: costs are negative
if (mults.hacknet_node_money !== 1) desc += `\n+${f(mults.hacknet_node_money - 1)} hacknet production`;
if (mults.hacknet_node_purchase_cost !== 1) {
desc += `\n-${f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost`;
}
if (mults.hacknet_node_level_cost !== 1) {
desc += `\n-${f(-(mults.hacknet_node_level_cost - 1))} hacknet nodes upgrade cost`;
}
// Bladeburner
if (mults.bladeburner_max_stamina !== 1) desc += `\n+${f(mults.bladeburner_max_stamina - 1)} Bladeburner Max Stamina`;
if (mults.bladeburner_stamina_gain !== 1) {
desc += `\n+${f(mults.bladeburner_stamina_gain - 1)} Bladeburner Stamina gain`;
}
if (mults.bladeburner_analysis !== 1) {
desc += `\n+${f(mults.bladeburner_analysis - 1)} Bladeburner Field Analysis effectiveness`;
}
if (mults.bladeburner_success_chance !== 1) {
desc += `\n+${f(mults.bladeburner_success_chance - 1)} Bladeburner Contracts and Operations success chance`;
}
if (startingMoney) desc += `\nStart with ${startingMoney} after installing Augmentations.`;
if (programs) desc += `\nStart with ${programs.join(" and ")} after installing Augmentations.`;
return desc;
}
@ -372,36 +170,28 @@ export class Augmentation {
baseRepRequirement = 0;
// 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.
stats: JSX.Element | null;
stats: string;
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial = false;
// Name of Augmentation
name = "";
name: AugmentationName;
// Array of names of all prerequisites
prereqs: string[] = [];
prereqs: AugmentationName[] = [];
// Multipliers given by this Augmentation. Must match the property name in
// The Player/Person classes
mults: Multipliers = defaultMultipliers();
// Factions that offer this aug.
factions: string[] = [];
factions: FactionName[] = [];
constructor(
params: IConstructorParams = {
info: "",
moneyCost: 0,
name: "",
repCost: 0,
factions: [],
},
) {
constructor(params: AugmentationCtorParams) {
this.name = params.name;
this.info = params.info;
this.prereqs = params.prereqs ? params.prereqs : [];
@ -417,95 +207,9 @@ export class Augmentation {
}
// Set multipliers
if (params.hacking) {
this.mults.hacking = params.hacking;
}
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;
for (const multName of getRecordKeys(this.mults)) {
const mult = params[multName];
if (mult) this.mults[multName] = mult;
}
if (params.stats === undefined)
@ -513,89 +217,14 @@ export class Augmentation {
else this.stats = params.stats;
}
// Adds this Augmentation to the specified Factions
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 };
}
/** Get the current level of an augmentation before buying. Currently only relevant for NFG. */
getLevel(): number {
// Get current Neuroflux level based on Player's augmentations
if (this.name === AugmentationName.NeuroFluxGovernor) {
let currLevel = 0;
for (let i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationName.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
// Only NFG currently has levels, all others will be level 0 before purchase
if (this.name !== AugmentationName.NeuroFluxGovernor) return 0;
// Owned NFG has the level baked in
const ownedNFGLevel = Player.augmentations.find((aug) => aug.name === this.name)?.level ?? 0;
// Queued NFG is queued multiple times for each level purchased
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 { StaticAugmentations } from "./StaticAugmentations";
import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums";
import { AugmentationName } from "@enums";
import { CONSTANTS } from "../Constants";
import { Factions, factionExists } from "../Faction/Factions";
import { Player } from "@player";
import { prestigeAugmentation } from "../Prestige";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import {
initBladeburnerAugmentations,
initChurchOfTheMachineGodAugmentations,
initGeneralAugmentations,
initSoAAugmentations,
initNeuroFluxGovernor,
initUnstableCircadianModulator,
} from "./data/AugmentationCreator";
import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { mergeMultipliers } from "../PersonObjects/Multipliers";
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();
}
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
export function getBaseAugmentationPriceMultiplier(): number {
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);
}
//Resets an Augmentation during (re-initialization)
function resetAugmentation(aug: Augmentation): void {
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];
export function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
const staticAugmentation = Augmentations[aug.name];
// Apply multipliers
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) {
dialogBoxCreate("You have not purchased any Augmentations to install!");
return false;
@ -108,7 +62,7 @@ function installAugmentations(force?: boolean): boolean {
}
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
const ownedAug = Player.queuedAugmentations[i];
const aug = StaticAugmentations[ownedAug.name];
const aug = Augmentations[ownedAug.name];
if (aug == null) {
console.error(`Invalid augmentation: ${ownedAug.name}`);
continue;
@ -137,13 +91,59 @@ function installAugmentations(force?: boolean): boolean {
return true;
}
function augmentationExists(name: string): boolean {
return Object.hasOwn(StaticAugmentations, name);
}
export function isRepeatableAug(aug: Augmentation | string): boolean {
const augName = typeof aug === "string" ? aug : aug.name;
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 {
level = 1;
name = "";
name: AugmentationName;
constructor(name = "") {
constructor(name: AugmentationName) {
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 { Player } from "@player";
import { AugmentationName } from "@enums";
import { StaticAugmentations } from "../StaticAugmentations";
import { Augmentations } from "../Augmentations";
import { CONSTANTS } from "../../Constants";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { Info } from "@mui/icons-material";
@ -49,9 +49,7 @@ const NeuroFluxDisplay = (): React.ReactElement => {
<Typography variant="h5" color={Settings.theme.info}>
NeuroFlux Governor - Level {level}
</Typography>
<Typography color={Settings.theme.info}>
{StaticAugmentations[AugmentationName.NeuroFluxGovernor].stats}
</Typography>
<Typography color={Settings.theme.info}>{Augmentations[AugmentationName.NeuroFluxGovernor].stats}</Typography>
<Typography color={Settings.theme.info}>
The power of {AugmentationName.NeuroFluxGovernor} increases with blood donations from players in real life.
Learn more <Link onClick={openBloodDonation}>here</Link>

@ -13,7 +13,7 @@ import React, { useState } from "react";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { Player } from "@player";
import { StaticAugmentations } from "../StaticAugmentations";
import { Augmentations } from "../Augmentations";
import { AugmentationName } from "@enums";
import { useRerender } from "../../ui/React/hooks";
@ -73,7 +73,7 @@ export function InstalledAugmentations(): React.ReactElement {
</Typography>
<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 tooltip = (

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

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

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

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

@ -1,7 +1,7 @@
import React from "react";
import { Player } from "@player";
import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNodeMultipliers";
import { CityName, FactionName } from "@enums";
import { BitNodeMultipliers, replaceCurrentNodeMults } from "./BitNodeMultipliers";
class BitNode {
// A short description, or tagline, about the BitNode
@ -447,82 +447,16 @@ export function initBitNodes() {
);
}
export const defaultMultipliers: IBitNodeMultipliers = {
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,
};
export const defaultMultipliers = new BitNodeMultipliers();
Object.freeze(defaultMultipliers);
export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultipliers {
const mults = Object.assign({}, defaultMultipliers);
export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultipliers {
switch (n) {
case 1: {
return mults;
return new BitNodeMultipliers();
}
case 2: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.8,
ServerGrowthRate: 0.8,
@ -546,7 +480,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 3: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.8,
ServerGrowthRate: 0.2,
@ -578,7 +512,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 4: {
return Object.assign(mults, {
return new BitNodeMultipliers({
ServerMaxMoney: 0.1125,
ServerStartingMoney: 0.75,
@ -606,7 +540,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 5: {
return Object.assign(mults, {
return new BitNodeMultipliers({
ServerStartingSecurity: 2,
ServerStartingMoney: 0.5,
@ -635,7 +569,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 6: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35,
ServerMaxMoney: 0.2,
@ -669,7 +603,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 7: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35,
ServerMaxMoney: 0.2,
@ -711,7 +645,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 8: {
return Object.assign(mults, {
return new BitNodeMultipliers({
PurchasedServerSoftcap: 4,
CompanyWorkMoney: 0,
@ -739,7 +673,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 9: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.5,
StrengthLevelMultiplier: 0.45,
DefenseLevelMultiplier: 0.45,
@ -780,7 +714,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 10: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.35,
StrengthLevelMultiplier: 0.4,
DefenseLevelMultiplier: 0.4,
@ -823,7 +757,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 11: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.6,
ServerGrowthRate: 0.2,
@ -861,8 +795,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
const inc = Math.pow(1.02, lvl);
const dec = 1 / inc;
return Object.assign(mults, {
DaedalusAugsRequirement: Math.floor(Math.min(mults.DaedalusAugsRequirement + inc, 40)),
return new BitNodeMultipliers({
DaedalusAugsRequirement: Math.floor(Math.min(defaultMultipliers.DaedalusAugsRequirement + inc, 40)),
HackingLevelMultiplier: dec,
StrengthLevelMultiplier: dec,
@ -929,7 +863,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
});
}
case 13: {
return Object.assign(mults, {
return new BitNodeMultipliers({
HackingLevelMultiplier: 0.25,
StrengthLevelMultiplier: 0.7,
DefenseLevelMultiplier: 0.7,
@ -982,5 +916,5 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}
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.
* 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...
*/
export interface IBitNodeMultipliers {
export class BitNodeMultipliers {
/** Influences how quickly the player's agility level (not exp) scales */
AgilityLevelMultiplier: number;
AgilityLevelMultiplier = 1;
/** 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. */
AugmentationRepCost: number;
AugmentationRepCost = 1;
/** Influences how quickly the player can gain rank within Bladeburner. */
BladeburnerRank: number;
BladeburnerRank = 1;
/** Influences the cost of skill levels from Bladeburner. */
BladeburnerSkillCost: number;
BladeburnerSkillCost = 1;
/** 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. */
ClassGymExpGain: number;
ClassGymExpGain = 1;
/**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. */
CompanyWorkExpGain: number;
CompanyWorkExpGain = 1;
/** 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. */
CorporationValuation: number;
CorporationValuation = 1;
/** 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. */
CrimeMoney: number;
CrimeMoney = 1;
/** 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 */
DefenseLevelMultiplier: number;
DefenseLevelMultiplier = 1;
/** 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. */
FactionPassiveRepGain: number;
FactionPassiveRepGain = 1;
/** 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. */
FactionWorkRepGain: number;
FactionWorkRepGain = 1;
/** 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) */
FourSigmaMarketDataCost: number;
FourSigmaMarketDataCost = 1;
/** Reduces gangs earning. */
GangSoftcap: number;
GangSoftcap = 1;
/** Percentage of unique augs that the gang has. */
GangUniqueAugs: number;
GangUniqueAugs = 1;
/** Influences the experienced gained when hacking a server. */
HackExpGain: number;
HackExpGain = 1;
/** 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 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 */
HomeComputerRamCost: number;
HomeComputerRamCost = 1;
/** 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 */
InfiltrationRep: number;
InfiltrationRep = 1;
/**
* Influences how much money can be stolen from a server when the player performs a hack against it through
* the Terminal.
*/
ManualHackMoney: number;
ManualHackMoney = 1;
/** Influence how much it costs to purchase a server */
PurchasedServerCost: number;
PurchasedServerCost = 1;
/** Influence how much it costs to purchase a server */
PurchasedServerSoftcap: number;
PurchasedServerSoftcap = 1;
/** Influences the maximum number of purchased servers you can have */
PurchasedServerLimit: number;
PurchasedServerLimit = 1;
/** 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. */
RepToDonateToFaction: number;
RepToDonateToFaction = 1;
/** 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
* different than the above because you can reduce the amount of money but
* not gain that same amount.
*/
ScriptHackMoneyGain: number;
ScriptHackMoneyGain = 1;
/** Influences the growth percentage per cycle against a server. */
ServerGrowthRate: number;
ServerGrowthRate = 1;
/** Influences the maximum money that a server can grow to. */
ServerMaxMoney: number;
ServerMaxMoney = 1;
/** Influences the initial money that a server starts with. */
ServerStartingMoney: number;
ServerStartingMoney = 1;
/** Influences the initial security level (hackDifficulty) of a server. */
ServerStartingSecurity: number;
ServerStartingSecurity = 1;
/** Influences the weaken amount per invocation against a server. */
ServerWeakenRate: number;
ServerWeakenRate = 1;
/** Influences how quickly the player's strength level (not exp) scales */
StrengthLevelMultiplier: number;
StrengthLevelMultiplier = 1;
/** Influences the power of the gift. */
StaneksGiftPowerMultiplier: number;
StaneksGiftPowerMultiplier = 1;
/** Influences the size of the gift. */
StaneksGiftExtraSize: number;
StaneksGiftExtraSize = 0;
/** Influences the hacking skill required to backdoor the world daemon. */
WorldDaemonDifficulty: number;
WorldDaemonDifficulty = 1;
/** Influences profits from corporation dividends and selling shares. */
CorporationSoftcap: number;
CorporationSoftcap = 1;
/** Influences number of divisions a corporation can have. */
CorporationDivisions: number;
CorporationDivisions = 1;
// Index signature
[key: string]: number;
constructor(a: PartialRecord<keyof BitNodeMultipliers, number> = {}) {
for (const [key, value] of getRecordEntries(a)) this[key] = value;
}
}
/** The multipliers that are influenced by current Bitnode progression. */
export const BitNodeMultipliers = Object.assign({}, defaultMultipliers);
/** The multipliers currently in effect */
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 ExpandMore from "@mui/icons-material/ExpandMore";
import { Box, Collapse, ListItemButton, ListItemText, Paper, Table, TableBody, Typography } from "@mui/material";
import { uniqueId } from "lodash";
import React from "react";
import { Player } from "@player";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings";
import { Player } from "@player";
import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers";
import { BitNodeMultipliers } from "../BitNodeMultipliers";
import { PartialRecord, getRecordEntries } from "../../Types/Record";
interface IProps {
n: number;
@ -60,8 +62,8 @@ export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElem
);
};
type IBNMultRows = Record<
string,
type IBNMultRows = PartialRecord<
keyof BitNodeMultipliers,
{
name: string;
content?: string;
@ -72,11 +74,11 @@ type IBNMultRows = Record<
interface IBNMultTableProps {
sectionName: string;
rowData: IBNMultRows;
mults: IBitNodeMultipliers;
mults: BitNodeMultipliers;
}
const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
const rowsArray = Object.entries(props.rowData)
const rowsArray = getRecordEntries(props.rowData)
.filter(([key]) => props.mults[key] !== defaultMultipliers[key])
.map(([key, value]) => (
<StatsRow
@ -101,7 +103,7 @@ const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
interface IMultsProps {
n: number;
mults: IBitNodeMultipliers;
mults: BitNodeMultipliers;
}
function GeneralMults({ mults }: IMultsProps): React.ReactElement {

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

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
interface ISkillParams {
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,
const recursiveMode = (currentLevel: number, count: number): number => {
if (count <= 1) {
return Math.floor((this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost);
return Math.floor((this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost);
} else {
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;
}
@ -161,7 +161,7 @@ export class Skill {
//(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost
//being repeated for increasing currentLevel
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);
}
}

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

@ -9,9 +9,9 @@ import { CalculateEffect } from "./formulas/effect";
import { StaneksGiftEvents } from "./StaneksGiftEvents";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { StanekConstants } from "./data/Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { defaultMultipliers, mergeMultipliers, Multipliers, scaleMultipliers } from "../PersonObjects/Multipliers";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { Augmentations } from "../Augmentation/Augmentations";
import { getKeyList } from "../utils/helpers/getKeyList";
export class StaneksGift extends BaseGift {
@ -23,7 +23,7 @@ export class StaneksGift extends BaseGift {
}
baseSize(): number {
return StanekConstants.BaseSize + BitNodeMultipliers.StaneksGiftExtraSize + Player.sourceFileLvl(13);
return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.sourceFileLvl(13);
}
width(): number {
@ -226,7 +226,7 @@ export class StaneksGift extends BaseGift {
sleeve.resetMultipliers();
//reapplying augmentation's multiplier
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);
}
//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 {
return (
@ -7,6 +7,6 @@ export function CalculateEffect(highestCharge: number, numCharge: number, power:
Math.pow((numCharge + 1) / 5, 0.07) *
power *
boost *
BitNodeMultipliers.StaneksGiftPowerMultiplier
currentNodeMults.StaneksGiftPowerMultiplier
);
}

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

@ -1,6 +1,8 @@
import type { AugmentationName } from "@enums";
import { FactionInfo, FactionInfos } from "./FactionInfo";
import { favorToRep, repToFavor } from "./formulas/favor";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getEnumHelper } from "../utils/EnumHelper";
export class Faction {
/**
@ -10,7 +12,7 @@ export class Faction {
alreadyInvited = false;
/** Holds names of all augmentations that this Faction offers */
augmentations: string[] = [];
augmentations: AugmentationName[] = [];
/** Amount of favor the player has with this faction. */
favor = 0;
@ -67,7 +69,11 @@ export class Faction {
/** Initializes a Faction object from a JSON save state. */
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 { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
@ -18,6 +18,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal";
import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork";
import { getAugCost } from "../Augmentation/AugmentationHelpers";
export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name);
@ -55,7 +56,7 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const hasPrereqs = hasAugmentationPrereqs(aug);
const augCosts = aug.getCost();
const augCosts = getAugCost(aug);
if (!hasPrereqs) {
const txt = `You must first purchase or install ${aug.prereqs
.filter((req) => !Player.hasAugmentation(req))
@ -127,21 +128,21 @@ export function processPassiveFactionRepGain(numCycles: number): void {
const fRep = getFactionFieldWorkRepGain(Player, faction.favor);
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.hasGangWith(faction.name)) {
let augs = Object.values(StaticAugmentations);
let augs = Object.values(Augmentations);
// Remove special augs
augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationName.CongruityImplant);
if (Player.bitNodeN === 2) {
// 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)}`);
@ -156,7 +157,7 @@ export const getFactionAugmentationsFiltered = (faction: Faction): string[] => {
return true;
}
return rng() >= 1 - BitNodeMultipliers.GangUniqueAugs;
return rng() >= 1 - currentNodeMults.GangUniqueAugs;
};
augs = augs.filter(uniqueFilter);

@ -6,6 +6,8 @@ import { Faction } from "./Faction";
import { FactionInfos } from "./FactionInfo";
import { Reviver } from "../utils/JSONReviver";
import { getRecordValues } from "../Types/Record";
import { Augmentations, initCircadianModulator } from "../Augmentation/Augmentations";
export let Factions: Record<string, Faction> = {};
@ -47,4 +49,16 @@ function resetFaction(newFactionObject: Faction): void {
delete Factions[factionName];
}
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 { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Person as IPerson } from "@nsdefs";
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 React from "react";
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { Augmentations } from "../../Augmentation/Augmentations";
import { getAugCost, getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { AugmentationName, FactionName } from "@enums";
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
@ -25,11 +25,11 @@ interface IProps {
export function AugmentationsPage(props: IProps): React.ReactElement {
const rerender = useRerender();
function getAugs(): string[] {
function getAugs(): AugmentationName[] {
return getFactionAugmentationsFiltered(props.faction);
}
function getAugsSorted(): string[] {
function getAugsSorted(): AugmentationName[] {
switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: {
return getAugsSortedByCost();
@ -45,26 +45,26 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
}
}
function getAugsSortedByCost(): string[] {
function getAugsSortedByCost(): AugmentationName[] {
const augs = getAugs();
augs.sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1],
aug2 = StaticAugmentations[augName2];
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.getCost().moneyCost - aug2.getCost().moneyCost;
return getAugCost(aug1).moneyCost - getAugCost(aug2).moneyCost;
});
return augs;
}
function getAugsSortedByPurchasable(): string[] {
function getAugsSortedByPurchasable(): AugmentationName[] {
const augs = getAugs();
function canBuy(augName: string): boolean {
const aug = StaticAugmentations[augName];
const augCosts = aug.getCost();
function canBuy(augName: AugmentationName): boolean {
const aug = Augmentations[augName];
const augCosts = getAugCost(aug);
const repCost = augCosts.repCost;
const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug);
@ -72,43 +72,43 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
return hasCost && hasReq && hasRep;
}
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1],
aug2 = StaticAugmentations[augName2];
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.getCost().moneyCost - aug2.getCost().moneyCost;
return getAugCost(aug1).moneyCost - getAugCost(aug2).moneyCost;
});
const cantBuy = augs
.filter((aug) => !canBuy(aug))
.sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1],
aug2 = StaticAugmentations[augName2];
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.getCost().repCost - aug2.getCost().repCost;
return getAugCost(aug1).repCost - getAugCost(aug2).repCost;
});
return buy.concat(cantBuy);
}
function getAugsSortedByReputation(): string[] {
function getAugsSortedByReputation(): AugmentationName[] {
const augs = getAugs();
augs.sort((augName1, augName2) => {
const aug1 = StaticAugmentations[augName1],
aug2 = StaticAugmentations[augName2];
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.getCost().repCost - aug2.getCost().repCost;
return getAugCost(aug1).repCost - getAugCost(aug2).repCost;
});
return augs;
}
function getAugsSortedByDefault(): string[] {
function getAugsSortedByDefault(): AugmentationName[] {
return getAugs();
}
@ -123,7 +123,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
aug === AugmentationName.NeuroFluxGovernor ||
(!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 =
props.faction.name !== FactionName.ShadowsOfAnarchy ? (
@ -213,7 +213,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
augNames={purchasable}
ownedAugNames={owned}
canPurchase={(aug) => {
const costs = aug.getCost();
const costs = getAugCost(aug);
return (
hasAugmentationPrereqs(aug) &&
props.faction.playerReputation >= costs.repCost &&

@ -12,7 +12,7 @@ import { Option } from "./Option";
import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../Faction";
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.)
// 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 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 { GangMemberTask } from "../GangMemberTask";
@ -24,7 +24,7 @@ export function calculateRespectGain(gang: FormulaGang, member: GangMember, task
statWeight -= 4 * task.difficulty;
if (statWeight <= 0) return 0;
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;
const respectMult = calculateWantedPenalty(gang);
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);
if (isNaN(territoryMult) || territoryMult <= 0) return 0;
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);
}

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
import { Person as IPerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { Server as IServer } from "@nsdefs";
@ -32,7 +32,7 @@ export function calculateHackingExpGain(server: IServer, person: IPerson): numbe
const diffFactor = 0.3;
let expGain = baseExpGain;
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 skillMult = (person.skills.hacking - (requiredHackingSkill - 1)) / person.skills.hacking;
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));
}

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { HacknetNodeConstants } from "../data/Constants";
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 ramMult = Math.pow(1.035, ram - 1);
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 {

@ -1,4 +1,4 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { HacknetServerConstants } from "../data/Constants";
export function calculateHashGainRate(
@ -13,7 +13,7 @@ export function calculateHashGainRate(
const coreMultiplier = 1 + (cores - 1) / 5;
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 {

@ -1,5 +1,5 @@
import { Player } from "@player";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { LocationsMetadata } from "../../Locations/data/LocationsMetadata";
import { AugmentationName } from "@enums";
import { Faction } from "../../Faction/Faction";
@ -13,7 +13,7 @@ export function calculateSellInformationCashReward(reward: number, maxLevel: num
3e3 *
levelBonus *
(Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.5 : 1) *
BitNodeMultipliers.InfiltrationMoney
currentNodeMults.InfiltrationMoney
);
}
@ -26,7 +26,7 @@ export function calculateTradeInformationRepReward(reward: number, maxLevel: num
30 *
levelBonus *
(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 { MathJax } from "better-react-mathjax";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
interface IProps {
rerender: () => void;
@ -30,7 +30,7 @@ export function RamButton(props: IProps): React.ReactElement {
props.rerender();
}
const bnMult = BitNodeMultipliers.HomeComputerRamCost === 1 ? "" : `\\cdot ${BitNodeMultipliers.HomeComputerRamCost}`;
const bnMult = currentNodeMults.HomeComputerRamCost === 1 ? "" : `\\cdot ${currentNodeMults.HomeComputerRamCost}`;
return (
<Tooltip

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

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

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

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

@ -56,7 +56,7 @@ import { IndustriesData, IndustryResearchTrees } from "../Corporation/data/Indus
import * as corpConstants from "../Corporation/data/Constants";
import { ResearchMap } from "../Corporation/ResearchMap";
import { Factions } from "../Faction/Factions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { getEnumHelper } from "../utils/EnumHelper";
@ -70,7 +70,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
if (!player.canAccessCorporation() || player.corporation) return false;
if (!corporationName) return false;
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}`);
if (selfFund) {

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

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

@ -1,9 +1,9 @@
import { Player } from "@player";
import type { Augmentation } from "../Augmentation/Augmentation";
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 { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper";
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 { helpers } from "../Netscript/NetscriptHelpers";
import { cloneDeep } from "lodash";
import { getAugCost } from "../Augmentation/AugmentationHelpers";
export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
@ -198,7 +199,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
},
purchaseSleeveAug: (ctx) => (_sleeveNumber, _augName) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const augName = helpers.string(ctx, "augName", _augName);
const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
@ -206,7 +207,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`);
}
const aug = StaticAugmentations[augName];
const aug = Augmentations[augName];
if (!aug) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
}
@ -215,15 +216,15 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
},
getSleeveAugmentationPrice: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName];
const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug: Augmentation = Augmentations[augName];
return aug.baseCost;
},
getSleeveAugmentationRepReq: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName];
return aug.getCost().repCost;
const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const aug: Augmentation = Augmentations[augName];
return getAugCost(aug).repCost;
},
setToBladeburnerAction: (ctx) => (_sleeveNumber, _action, _contract?) => {
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 { AugmentationName, FactionName } from "@enums";
import { Augmentations } from "../../Augmentation/Augmentations";
import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { GraftableAugmentation } from "./GraftableAugmentation";
import { getRecordEntries } from "../../Types/Record";
export const getGraftingAvailableAugs = (): string[] => {
const augs: string[] = [];
export const getGraftingAvailableAugs = (): AugmentationName[] => {
const augs: AugmentationName[] = [];
for (const [augName, aug] of Object.entries(StaticAugmentations)) {
if (Player.factions.includes("Bladeburners")) {
if (aug.isSpecial && !aug.factions.includes("Bladeburners")) continue;
for (const [augName, aug] of getRecordEntries(Augmentations)) {
if (Player.factions.includes(FactionName.Bladeburners)) {
if (aug.isSpecial && !aug.factions.includes(FactionName.Bladeburners)) continue;
} else {
if (aug.isSpecial) continue;
}

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

@ -1,6 +1,6 @@
import { Person } from "./Person";
import { calculateSkill } from "./formulas/skill";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Player } from "@player";
import { WorkStats } from "@nsdefs";
@ -14,10 +14,7 @@ export function gainHackingExp(this: Person, exp: number): void {
this.exp.hacking = 0;
}
this.skills.hacking = calculateSkill(
this.exp.hacking,
this.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier,
);
this.skills.hacking = calculateSkill(this.exp.hacking, this.mults.hacking * currentNodeMults.HackingLevelMultiplier);
}
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.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.skills.defense = calculateSkill(
this.exp.defense,
this.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier,
);
this.skills.defense = calculateSkill(this.exp.defense, this.mults.defense * currentNodeMults.DefenseLevelMultiplier);
const ratio = this.hp.current / this.hp.max;
this.hp.max = Math.floor(10 + this.skills.defense / 10);
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.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.skills.agility = calculateSkill(
this.exp.agility,
this.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier,
);
this.skills.agility = calculateSkill(this.exp.agility, this.mults.agility * currentNodeMults.AgilityLevelMultiplier);
}
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.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 {
this.skills.hacking = Math.max(
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(
1,
Math.floor(
this.calculateSkill(this.exp.strength, this.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier),
),
Math.floor(this.calculateSkill(this.exp.strength, this.mults.strength * currentNodeMults.StrengthLevelMultiplier)),
);
this.skills.defense = Math.max(
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(
1,
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(
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(
1,
Math.floor(
this.calculateSkill(this.exp.charisma, this.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier),
),
Math.floor(this.calculateSkill(this.exp.charisma, this.mults.charisma * currentNodeMults.CharismaLevelMultiplier)),
);
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 { CONSTANTS } from "../../Constants";
import { Person } from "../Person";
import { getEnumHelper } from "../../utils/EnumHelper";
export class PlayerObject extends Person implements IPlayer {
// Player-specific properties
@ -167,9 +168,19 @@ export class PlayerObject extends Person implements IPlayer {
/** Initializes a PlayerObject object from a JSON save state. */
static fromJSON(value: IReviverValue): PlayerObject {
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.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();
// 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)) {
// Expect pre-2.3 sourcefile format here.
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 { ProgramFilePath } from "../../Paths/ProgramFilePath";
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, CityName, CompletedProgramName, FactionName, LocationName, ToastVariant } from "@enums";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { CodingContractRewardType, ICodingContractReward } from "../../CodingContracts";
import { Company } from "../../Company/Company";
import { Companies } from "../../Company/Companies";
@ -548,22 +549,16 @@ export function reapplyAllAugmentations(this: PlayerObject, resetMultipliers = t
this.resetMultipliers();
}
for (let i = 0; i < this.augmentations.length; ++i) {
//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];
for (const playerAug of this.augmentations) {
const augName = playerAug.name;
if (augName == AugmentationName.NeuroFluxGovernor) {
for (let j = 0; j < playerAug.level; ++j) {
applyAugmentation(this.augmentations[i], true);
for (let i = 0; i < playerAug.level; ++i) {
applyAugmentation(playerAug, true);
}
continue;
}
applyAugmentation(this.augmentations[i], true);
applyAugmentation(playerAug, true);
}
this.updateSkillLevels();
@ -644,7 +639,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] {
!daedalusFac.isBanned &&
!daedalusFac.isMember &&
!daedalusFac.alreadyInvited &&
numAugmentations >= BitNodeMultipliers.DaedalusAugsRequirement &&
numAugmentations >= currentNodeMults.DaedalusAugsRequirement &&
this.money >= 100000000000 &&
(this.skills.hacking >= 2500 ||
(this.skills.strength >= 1500 &&
@ -1080,7 +1075,7 @@ export function setBitNodeNumber(this: PlayerObject, n: number): void {
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) {
if (aug.name == name) {
console.warn(`tried to queue ${name} twice, this may be a bug`);
@ -1152,7 +1147,7 @@ export function gainCodingContractReward(
}
case CodingContractRewardType.Money:
default: {
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney;
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney;
this.gainMoney(moneyGain, "codingcontract");
return `Gained ${formatMoney(moneyGain)}`;
}

@ -1,7 +1,7 @@
// Server and HacknetServer-related methods for the Player class (PlayerObject)
import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Server } from "../../Server/Server";
import { BaseServer } from "../../Server/BaseServer";
import { HacknetServer } from "../../Hacknet/HacknetServer";
@ -35,7 +35,7 @@ export function getUpgradeHomeRamCost(this: PlayerObject): number {
//Calculate cost
//Have cost increase by some percentage each time RAM has been upgraded
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;
}

@ -7,14 +7,16 @@
* 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 { Person } from "../Person";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions";
import { Contracts } from "../../Bladeburner/data/Contracts";
import { CONSTANTS } from "../../Constants";
@ -24,7 +26,6 @@ import { Factions } from "../../Faction/Factions";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
import { formatPercent } from "../../ui/formatNumber";
import { SleeveWork } from "./Work/Work";
import { SleeveClassWork } from "./Work/SleeveClassWork";
import { SleeveSynchroWork } from "./Work/SleeveSynchroWork";
import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork";
@ -35,8 +36,8 @@ import { SleeveSupportWork } from "./Work/SleeveSupportWork";
import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork";
import { SleeveCrimeWork } from "./Work/SleeveCrimeWork";
import * as sleeveMethods from "./SleeveMethods";
import { SleevePerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { getEnumHelper } from "../../utils/EnumHelper";
export class Sleeve extends Person implements SleevePerson {
currentWork: SleeveWork | null = null;
@ -475,8 +476,17 @@ export class Sleeve extends Person implements SleevePerson {
/** Initializes a Sleeve object from a JSON save state. */
static fromJSON(value: IReviverValue): Sleeve {
if (!value.data.hp?.current || !value.data.hp?.max) value.data.hp = { current: 10, max: 10 };
return Generic_fromJSON(Sleeve, value.data);
const sleeve = 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 { Sleeve } from "./Sleeve";
import { Augmentation } from "../../Augmentation/Augmentation";
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
import { Augmentations } from "../../Augmentation/Augmentations";
import { Factions } from "../../Faction/Factions";
import { mergeMultipliers, Multipliers } from "../Multipliers";
import { getFactionAugmentationsFiltered } from "../../Faction/FactionHelpers";
import { getAugCost } from "../../Augmentation/AugmentationHelpers";
/** Updates this object's multipliers for the given augmentation */
export function applyAugmentation(this: Sleeve, aug: Augmentation): void {
@ -61,10 +62,10 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
const gangAugs = getFactionAugmentationsFiltered(fac);
for (const augName of gangAugs) {
const aug = StaticAugmentations[augName];
const aug = Augmentations[augName];
if (!isAvailableForSleeve(aug)) continue;
if (fac.playerReputation > aug.getCost().repCost) {
if (fac.playerReputation > getAugCost(aug).repCost) {
availableAugs.push(aug);
}
}
@ -77,10 +78,10 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
if (!fac) continue;
for (const augName of fac.augmentations) {
const aug = StaticAugmentations[augName];
const aug = Augmentations[augName];
if (!isAvailableForSleeve(aug)) continue;
if (fac.playerReputation > aug.getCost().repCost) {
if (fac.playerReputation > getAugCost(aug).repCost) {
availableAugs.push(aug);
}
}
@ -88,7 +89,7 @@ export function findPurchasableAugs(this: Sleeve): Augmentation[] {
// Add the stanek sleeve aug
if (!ownedAugNames.includes(AugmentationName.ZOE) && Player.factions.includes(FactionName.ChurchOfTheMachineGod)) {
const aug = StaticAugmentations[AugmentationName.ZOE];
const aug = Augmentations[AugmentationName.ZOE];
availableAugs.push(aug);
}

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

@ -1,6 +1,4 @@
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 { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData";
@ -51,15 +49,15 @@ export function prestigeAugmentation(): void {
AddToAllServers(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.relaySmtp);
}
if (augmentationExists(AugmentationName.CashRoot) && Player.hasAugmentation(AugmentationName.CashRoot, true)) {
if (Player.hasAugmentation(AugmentationName.CashRoot, true)) {
Player.setMoney(1e6);
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.autoLink);
}
@ -86,7 +84,7 @@ export function prestigeAugmentation(): void {
initFactions(); // Factions must be initialized before augmentations
Player.factionInvitations = Player.factionInvitations.concat(maintainMembership);
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
Player.hp.current = Player.hp.max;
initCompanies();
@ -136,7 +134,7 @@ export function prestigeAugmentation(): void {
}
// Red Pill
if (augmentationExists(AugmentationName.TheRedPill) && Player.hasAugmentation(AugmentationName.TheRedPill, true)) {
if (Player.hasAugmentation(AugmentationName.TheRedPill, true)) {
const WorldDaemon = GetServer(SpecialServers.WorldDaemon);
const DaedalusServer = GetServer(SpecialServers.DaedalusServer);
if (WorldDaemon && DaedalusServer) {
@ -145,10 +143,7 @@ export function prestigeAugmentation(): void {
}
}
if (
augmentationExists(AugmentationName.StaneksGift1) &&
Player.hasAugmentation(AugmentationName.StaneksGift1, true)
) {
if (Player.hasAugmentation(AugmentationName.StaneksGift1, true)) {
joinFaction(Factions[FactionName.ChurchOfTheMachineGod]);
}
@ -205,11 +200,6 @@ export function prestigeSourceFile(isFlume: boolean): void {
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
if (Player.sourceFileLvl(12) > 0) {
Player.augmentations.push({
@ -220,7 +210,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
// Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
initCompanies();

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

@ -28,7 +28,6 @@ import { save } from "./db";
import { AwardNFG, v1APIBreak } from "./utils/v1APIBreak";
import { AugmentationName, FactionName, LocationName, ToastVariant } from "@enums";
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { pushGameSaved } from "./Electron";
import { defaultMonacoTheme } from "./ScriptEditor/ui/themes";
import { Faction } from "./Faction/Faction";
@ -364,7 +363,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
if (typeof ver !== "number") return;
if (ver < 2) {
AwardNFG(10);
initAugmentations();
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
}
if (ver < 3) {
@ -447,7 +446,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
];
v22PlayerBreak();
initAugmentations();
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
}
@ -679,17 +678,6 @@ function evaluateVersionCompatibility(ver: string | number): void {
}
//2.3 hotfix changes and 2.3.1 changes
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
let anyExportsFailed = false;
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.",
);
}
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 {

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

@ -1,7 +1,7 @@
// Class representing a single hackable Server
import { BaseServer } from "./BaseServer";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { createRandomString } from "../utils/helpers/createRandomString";
import { createRandomIp } from "../utils/IPAddress";
@ -72,12 +72,12 @@ export class Server extends BaseServer {
/* Hacking information (only valid for "foreign" aka non-purchased servers) */
this.requiredHackingSkill = params.requiredHackingSkill != null ? params.requiredHackingSkill : 1;
const baseMoney = params.moneyAvailable ?? 0;
this.moneyAvailable = baseMoney * BitNodeMultipliers.ServerStartingMoney;
this.moneyMax = 25 * baseMoney * BitNodeMultipliers.ServerMaxMoney;
this.moneyAvailable = baseMoney * currentNodeMults.ServerStartingMoney;
this.moneyMax = 25 * baseMoney * currentNodeMults.ServerMaxMoney;
//Hack Difficulty is synonymous with server security. Base Difficulty = Starting difficulty
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.baseDifficulty = this.hackDifficulty;
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) */
weaken(amt: number): void {
this.hackDifficulty -= amt * BitNodeMultipliers.ServerWeakenRate;
this.hackDifficulty -= amt * currentNodeMults.ServerWeakenRate;
this.capDifficulty();
}

@ -3,7 +3,7 @@ import { Server, IConstructorParams } from "./Server";
import { BaseServer } from "./BaseServer";
import { calculateServerGrowth } from "./formulas/grow";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Player } from "@player";
import { CompletedProgramName, LiteratureName } from "@enums";
@ -66,7 +66,7 @@ export function numCycleForGrowth(server: IServer, growth: number, cores = 1): n
(Math.log(ajdGrowthRate) *
Player.mults.hacking_grow *
serverGrowthPercentage *
BitNodeMultipliers.ServerGrowthRate *
currentNodeMults.ServerGrowthRate *
coreBonus);
return cycles;
@ -105,7 +105,7 @@ export function numCycleForGrowthCorrected(
const serverGrowthPercentage = server.serverGrowth / 100.0;
const coreMultiplier = 1 + (cores - 1) / 16;
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.
* First of, the names will be shortened for ease of manipulation:

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

@ -1,5 +1,5 @@
import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { Person as IPerson, Server as IServer } from "@nsdefs";
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
const serverGrowthPercentage = server.serverGrowth / 100;
const numServerGrowthCyclesAdjusted =
numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
numServerGrowthCycles * serverGrowthPercentage * currentNodeMults.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles
const coreBonus = 1 + (cores - 1) / 16;

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

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

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

@ -9,17 +9,17 @@ import { applyAugmentation } from "../Augmentation/AugmentationHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
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;
interface GraftingWorkParams {
augmentation: string;
augmentation: AugmentationName;
singularity: boolean;
}
export class GraftingWork extends Work {
augmentation: string;
augmentation: AugmentationName;
unitCompleted: number;
constructor(params?: GraftingWorkParams) {
@ -31,7 +31,7 @@ export class GraftingWork extends Work {
}
unitNeeded(): number {
return new GraftableAugmentation(StaticAugmentations[this.augmentation]).time;
return new GraftableAugmentation(Augmentations[this.augmentation]).time;
}
process(cycles: number): boolean {

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

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

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

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

@ -28,13 +28,17 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
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(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(
ctx,
`Argument ${argName} should be a ${
this.name
} enum member.\nProvided value: "${toValidate}".\nAllowable values: ${this.valueArray
.map((val) => `"${val}"`)
.join(", ")}`,
`Argument ${argName} should be a ${this.name} enum member.\nProvided value: "${toValidate}".\n${allowableValues}`,
);
}
/** Provides case insensitivty and ignores spaces and dashes, and can always match the input */

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