// Class definition for a single Augmentation object import { Player } from "@player"; 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 AugmentationCtorParams { info: string; stats?: string; isSpecial?: boolean; moneyCost: number; name: AugmentationName; prereqs?: AugmentationName[]; repCost: number; factions: FactionName[]; hacking?: number; strength?: number; defense?: number; dexterity?: number; agility?: number; charisma?: number; hacking_exp?: number; strength_exp?: number; defense_exp?: number; dexterity_exp?: number; agility_exp?: number; charisma_exp?: number; hacking_chance?: number; hacking_speed?: number; hacking_money?: number; hacking_grow?: number; company_rep?: number; faction_rep?: number; crime_money?: number; crime_success?: number; work_money?: number; hacknet_node_money?: number; hacknet_node_purchase_cost?: number; hacknet_node_ram_cost?: number; hacknet_node_core_cost?: number; hacknet_node_level_cost?: number; bladeburner_max_stamina?: number; bladeburner_stamina_gain?: number; bladeburner_analysis?: number; bladeburner_success_chance?: number; startingMoney?: number; programs?: CompletedProgramName[]; } 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:"; // 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 ) { desc += `\n+${f(mults.hacking - 1)} all skills`; } else { // 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 ) { desc += `\n+${f(mults.strength - 1)} combat skills`; } else { // 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 += `\n+${f(mults.charisma - 1)} charisma skill`; } // Skill XP if ( mults.hacking_exp !== 1 && mults.hacking_exp === mults.strength_exp && mults.hacking_exp === mults.defense_exp && mults.hacking_exp === mults.dexterity_exp && mults.hacking_exp === mults.agility_exp && mults.hacking_exp === mults.charisma_exp ) { desc += `\n+${f(mults.hacking_exp - 1)} exp for all skills`; } else { // 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 += `\n+${f(mults.strength_exp - 1)} combat exp`; } else { // 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 += `\n+${f(mults.charisma_exp - 1)} charisma exp`; } 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`; // 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 += `\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`; // 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 purchase cost`; } if (mults.hacknet_node_level_cost !== 1) { desc += `\n-${f(-(mults.hacknet_node_level_cost - 1))} hacknet level upgrade cost`; } if (mults.hacknet_node_ram_cost !== 1) { desc += `\n-${f(-(mults.hacknet_node_ram_cost - 1))} hacknet RAM upgrade cost`; } if (mults.hacknet_node_core_cost !== 1) { desc += `\n-${f(-(mults.hacknet_node_core_cost - 1))} hacknet core 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; } export class Augmentation { // How much money this costs to buy before multipliers baseCost = 0; // How much faction reputation is required to unlock this before multipliers baseRepRequirement = 0; // Description of what this Aug is and what it does info: string; // Description of the stats, often autogenerated, sometimes manually written. stats: string; // Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs) isSpecial = false; // Name of Augmentation name: AugmentationName; // Array of names of all prerequisites 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: FactionName[] = []; constructor(params: AugmentationCtorParams) { this.name = params.name; this.info = params.info; this.prereqs = params.prereqs ? params.prereqs : []; this.baseRepRequirement = params.repCost; Object.freeze(this.baseRepRequirement); this.baseCost = params.moneyCost; Object.freeze(this.baseCost); this.factions = params.factions; if (params.isSpecial) { this.isSpecial = true; } // Set multipliers for (const multName of getRecordKeys(this.mults)) { const mult = params[multName]; if (mult) this.mults[multName] = mult; } if (params.stats === undefined) this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney); else this.stats = params.stats; } /** Get the current level of an augmentation before buying. Currently only relevant for NFG. */ getLevel(): number { // 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; } /** Get the next level of an augmentation to buy. Currently only relevant for NFG. */ getNextLevel(): number { return this.getLevel() + 1; } }