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
70 changed files with 2649 additions and 3221 deletions
+93 -464
View File
@@ -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;
}
}
// 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);
// 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;
}
}
constructorsForReviver.Augmentation = Augmentation;
+58 -58
View File
@@ -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
+119
View File
@@ -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())];
}
+4 -2
View File
@@ -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;
}
}
-3
View File
@@ -1,3 +0,0 @@
import { Augmentation } from "./Augmentation";
export const StaticAugmentations: Record<string, Augmentation> = {};
File diff suppressed because it is too large Load Diff
+2 -4
View File
@@ -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 = (
+16 -16
View File
@@ -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) {