diff --git a/src/PersonObjects/Person.ts b/src/PersonObjects/Person.ts index 276410fd4..37852bd6a 100644 --- a/src/PersonObjects/Person.ts +++ b/src/PersonObjects/Person.ts @@ -1,13 +1,16 @@ import type { Skills } from "./Skills"; import type { HP } from "./HP"; -import type { Person as IPerson } from "@nsdefs"; +import type { Person as IPerson, WorkStats } from "@nsdefs"; -import * as personMethods from "./PersonMethods"; import { CityName } from "@enums"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { calculateSkill } from "./formulas/skill"; import { defaultMultipliers } from "./Multipliers"; import { IReviverValue } from "../utils/JSONReviver"; +import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; +import { Player } from "../Player"; +import { CONSTANTS } from "../Constants"; +import { PlayerObject } from "./Player/PlayerObject"; // Base class representing a person-like object export abstract class Person implements IPerson { @@ -40,18 +43,195 @@ export abstract class Person implements IPerson { /** City that the person is in */ city: CityName = CityName.Sector12; - gainHackingExp = personMethods.gainHackingExp; - gainStrengthExp = personMethods.gainStrengthExp; - gainDefenseExp = personMethods.gainDefenseExp; - gainDexterityExp = personMethods.gainDexterityExp; - gainAgilityExp = personMethods.gainAgilityExp; - gainCharismaExp = personMethods.gainCharismaExp; - gainIntelligenceExp = personMethods.gainIntelligenceExp; - gainStats = personMethods.gainStats; - regenerateHp = personMethods.regenerateHp; - updateSkillLevels = personMethods.updateSkillLevels; - hasAugmentation = personMethods.hasAugmentation; - travel = personMethods.travel; + gainHackingExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into Player.gainHackingExp()"); + return; + } + this.exp.hacking += exp; + if (this.exp.hacking < 0) { + this.exp.hacking = 0; + } + + this.skills.hacking = calculateSkill( + this.exp.hacking, + this.mults.hacking * currentNodeMults.HackingLevelMultiplier, + ); + } + + gainStrengthExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into Player.gainStrengthExp()"); + return; + } + this.exp.strength += exp; + if (this.exp.strength < 0) { + this.exp.strength = 0; + } + + this.skills.strength = calculateSkill( + this.exp.strength, + this.mults.strength * currentNodeMults.StrengthLevelMultiplier, + ); + } + + gainDefenseExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into player.gainDefenseExp()"); + return; + } + this.exp.defense += exp; + if (this.exp.defense < 0) { + this.exp.defense = 0; + } + + 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); + } + + gainDexterityExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into Player.gainDexterityExp()"); + return; + } + this.exp.dexterity += exp; + if (this.exp.dexterity < 0) { + this.exp.dexterity = 0; + } + + this.skills.dexterity = calculateSkill( + this.exp.dexterity, + this.mults.dexterity * currentNodeMults.DexterityLevelMultiplier, + ); + } + + gainAgilityExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into Player.gainAgilityExp()"); + return; + } + this.exp.agility += exp; + if (this.exp.agility < 0) { + this.exp.agility = 0; + } + + this.skills.agility = calculateSkill( + this.exp.agility, + this.mults.agility * currentNodeMults.AgilityLevelMultiplier, + ); + } + + gainCharismaExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERR: NaN passed into Player.gainCharismaExp()"); + return; + } + this.exp.charisma += exp; + if (this.exp.charisma < 0) { + this.exp.charisma = 0; + } + + this.skills.charisma = calculateSkill( + this.exp.charisma, + this.mults.charisma * currentNodeMults.CharismaLevelMultiplier, + ); + } + + gainIntelligenceExp(exp: number): void { + if (isNaN(exp)) { + console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); + return; + } + if (Player.sourceFileLvl(5) > 0 || this.skills.intelligence > 0 || Player.bitNodeN === 5) { + this.exp.intelligence += exp; + this.skills.intelligence = Math.floor(this.calculateSkill(this.exp.intelligence, 1)); + } + } + + gainStats(retValue: WorkStats): void { + this.gainHackingExp(retValue.hackExp * this.mults.hacking_exp); + this.gainStrengthExp(retValue.strExp * this.mults.strength_exp); + this.gainDefenseExp(retValue.defExp * this.mults.defense_exp); + this.gainDexterityExp(retValue.dexExp * this.mults.dexterity_exp); + this.gainAgilityExp(retValue.agiExp * this.mults.agility_exp); + this.gainCharismaExp(retValue.chaExp * this.mults.charisma_exp); + this.gainIntelligenceExp(retValue.intExp); + } + + regenerateHp(amt: number): void { + if (typeof amt !== "number") { + console.warn(`Player.regenerateHp() called without a numeric argument: ${amt}`); + return; + } + this.hp.current += amt; + if (this.hp.current > this.hp.max) { + this.hp.current = this.hp.max; + } + } + + updateSkillLevels(this: Person): void { + this.skills.hacking = Math.max( + 1, + 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 * currentNodeMults.StrengthLevelMultiplier), + ), + ); + this.skills.defense = Math.max( + 1, + 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 * currentNodeMults.DexterityLevelMultiplier), + ), + ); + this.skills.agility = Math.max( + 1, + 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 * currentNodeMults.CharismaLevelMultiplier), + ), + ); + + const ratio: number = Math.min(this.hp.current / this.hp.max, 1); + this.hp.max = Math.floor(10 + this.skills.defense / 10); + this.hp.current = Math.round(this.hp.max * ratio); + } + + hasAugmentation(augName: string, ignoreQueued = false) { + if (this.augmentations.some((a) => a.name === augName)) { + return true; + } + if (!ignoreQueued && this.queuedAugmentations.some((a) => a.name === augName)) { + return true; + } + return false; + } + + travel(cityName: CityName): boolean { + if (!Player.canAfford(CONSTANTS.TravelCost)) { + return false; + } + + Player.loseMoney(CONSTANTS.TravelCost, this instanceof PlayerObject ? "other" : "sleeves"); + this.city = cityName; + + return true; + } + calculateSkill = calculateSkill; //Class version is equal to imported version /** Reset all multipliers to 1 */ diff --git a/src/PersonObjects/PersonMethods.ts b/src/PersonObjects/PersonMethods.ts deleted file mode 100644 index 9106ddb50..000000000 --- a/src/PersonObjects/PersonMethods.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { CityName } from "@enums"; -import { Person } from "./Person"; -import { calculateSkill } from "./formulas/skill"; -import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; -import { Player } from "@player"; -import { WorkStats } from "@nsdefs"; -import { CONSTANTS } from "../Constants"; -import { PlayerObject } from "./Player/PlayerObject"; - -export function gainHackingExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into Player.gainHackingExp()"); - return; - } - this.exp.hacking += exp; - if (this.exp.hacking < 0) { - this.exp.hacking = 0; - } - - this.skills.hacking = calculateSkill(this.exp.hacking, this.mults.hacking * currentNodeMults.HackingLevelMultiplier); -} - -export function gainStrengthExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into Player.gainStrengthExp()"); - return; - } - this.exp.strength += exp; - if (this.exp.strength < 0) { - this.exp.strength = 0; - } - - this.skills.strength = calculateSkill( - this.exp.strength, - this.mults.strength * currentNodeMults.StrengthLevelMultiplier, - ); -} - -export function gainDefenseExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into player.gainDefenseExp()"); - return; - } - this.exp.defense += exp; - if (this.exp.defense < 0) { - this.exp.defense = 0; - } - - 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); -} - -export function gainDexterityExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into Player.gainDexterityExp()"); - return; - } - this.exp.dexterity += exp; - if (this.exp.dexterity < 0) { - this.exp.dexterity = 0; - } - - this.skills.dexterity = calculateSkill( - this.exp.dexterity, - this.mults.dexterity * currentNodeMults.DexterityLevelMultiplier, - ); -} - -export function gainAgilityExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into Player.gainAgilityExp()"); - return; - } - this.exp.agility += exp; - if (this.exp.agility < 0) { - this.exp.agility = 0; - } - - this.skills.agility = calculateSkill(this.exp.agility, this.mults.agility * currentNodeMults.AgilityLevelMultiplier); -} - -export function gainCharismaExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERR: NaN passed into Player.gainCharismaExp()"); - return; - } - this.exp.charisma += exp; - if (this.exp.charisma < 0) { - this.exp.charisma = 0; - } - - this.skills.charisma = calculateSkill( - this.exp.charisma, - this.mults.charisma * currentNodeMults.CharismaLevelMultiplier, - ); -} - -export function gainIntelligenceExp(this: Person, exp: number): void { - if (isNaN(exp)) { - console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); - return; - } - if (Player.sourceFileLvl(5) > 0 || this.skills.intelligence > 0 || Player.bitNodeN === 5) { - this.exp.intelligence += exp; - this.skills.intelligence = Math.floor(this.calculateSkill(this.exp.intelligence, 1)); - } -} -export function gainStats(this: Person, retValue: WorkStats): void { - this.gainHackingExp(retValue.hackExp * this.mults.hacking_exp); - this.gainStrengthExp(retValue.strExp * this.mults.strength_exp); - this.gainDefenseExp(retValue.defExp * this.mults.defense_exp); - this.gainDexterityExp(retValue.dexExp * this.mults.dexterity_exp); - this.gainAgilityExp(retValue.agiExp * this.mults.agility_exp); - this.gainCharismaExp(retValue.chaExp * this.mults.charisma_exp); - this.gainIntelligenceExp(retValue.intExp); -} - -export function regenerateHp(this: Person, amt: number): void { - if (typeof amt !== "number") { - console.warn(`Player.regenerateHp() called without a numeric argument: ${amt}`); - return; - } - this.hp.current += amt; - if (this.hp.current > this.hp.max) { - this.hp.current = this.hp.max; - } -} - -export function updateSkillLevels(this: Person): void { - this.skills.hacking = Math.max( - 1, - 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 * currentNodeMults.StrengthLevelMultiplier)), - ); - this.skills.defense = Math.max( - 1, - 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 * currentNodeMults.DexterityLevelMultiplier), - ), - ); - this.skills.agility = Math.max( - 1, - 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 * currentNodeMults.CharismaLevelMultiplier)), - ); - - const ratio: number = Math.min(this.hp.current / this.hp.max, 1); - this.hp.max = Math.floor(10 + this.skills.defense / 10); - this.hp.current = Math.round(this.hp.max * ratio); -} - -export function hasAugmentation(this: Person, augName: string, ignoreQueued = false) { - if (this.augmentations.some((a) => a.name === augName)) { - return true; - } - if (!ignoreQueued && this.queuedAugmentations.some((a) => a.name === augName)) { - return true; - } - return false; -} - -/** Travel to another City. Costs money from player regardless of which person is traveling */ -export function travel(this: Person, cityName: CityName): boolean { - if (!Player.canAfford(CONSTANTS.TravelCost)) { - return false; - } - - Player.loseMoney(CONSTANTS.TravelCost, this instanceof PlayerObject ? "other" : "sleeves"); - this.city = cityName; - - return true; -} diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 2a2cdfb68..bc4d5e07a 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -27,6 +27,7 @@ import { FactionName, BladeActionType, BladeGeneralActionName, + AugmentationName, } from "@enums"; import { Factions } from "../../Faction/Factions"; @@ -41,9 +42,12 @@ import { SleeveInfiltrateWork } from "./Work/SleeveInfiltrateWork"; import { SleeveSupportWork } from "./Work/SleeveSupportWork"; import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork"; import { SleeveCrimeWork } from "./Work/SleeveCrimeWork"; -import * as sleeveMethods from "./SleeveMethods"; import { calculateIntelligenceBonus } from "../formulas/intelligence"; import { getEnumHelper } from "../../utils/EnumHelper"; +import { Multipliers, mergeMultipliers } from "../Multipliers"; +import { getFactionAugmentationsFiltered } from "../../Faction/FactionHelpers"; +import { Augmentations } from "../../Augmentation/Augmentations"; +import { getAugCost } from "../../Augmentation/AugmentationHelpers"; export class Sleeve extends Person implements SleevePerson { currentWork: SleeveWork | null = null; @@ -75,8 +79,93 @@ export class Sleeve extends Person implements SleevePerson { this.shockRecovery(); } - applyAugmentation = sleeveMethods.applyAugmentation; - findPurchasableAugs = sleeveMethods.findPurchasableAugs; + /** Updates this object's multipliers for the given augmentation */ + applyAugmentation(aug: Augmentation): void { + this.mults = mergeMultipliers(this.mults, aug.mults); + } + + findPurchasableAugs(): Augmentation[] { + // You can only purchase Augmentations that are actually available from + // your factions. I.e. you must be in a faction that has the Augmentation + // and you must also have enough rep in that faction in order to purchase it. + + const ownedAugNames = this.augmentations.map((e) => e.name); + const availableAugs: Augmentation[] = []; + + // Helper function that helps filter out augs that are already owned + // and augs that aren't allowed for sleeves + function isAvailableForSleeve(aug: Augmentation): boolean { + if (ownedAugNames.includes(aug.name)) return false; + if (availableAugs.includes(aug)) return false; + if (aug.isSpecial) return false; + + type MultKey = keyof Multipliers; + const validMults: MultKey[] = [ + "hacking", + "strength", + "defense", + "dexterity", + "agility", + "charisma", + "hacking_exp", + "strength_exp", + "defense_exp", + "dexterity_exp", + "agility_exp", + "charisma_exp", + "company_rep", + "faction_rep", + "crime_money", + "crime_success", + "work_money", + ]; + for (const mult of validMults) { + if (aug.mults[mult] !== 1) return true; + } + + return false; + } + + // If player is in a gang, then we return all augs that the player + // has enough reputation for (since that gang offers all augs) + if (Player.gang) { + const fac = Player.getGangFaction(); + const gangAugs = getFactionAugmentationsFiltered(fac); + + for (const augName of gangAugs) { + const aug = Augmentations[augName]; + if (!isAvailableForSleeve(aug)) continue; + + if (fac.playerReputation > getAugCost(aug).repCost) { + availableAugs.push(aug); + } + } + } + + for (const facName of Player.factions) { + if (facName === FactionName.Bladeburners) continue; + if (facName === FactionName.Netburners) continue; + const fac = Factions[facName]; + if (!fac) continue; + + for (const augName of fac.augmentations) { + const aug = Augmentations[augName]; + if (!isAvailableForSleeve(aug)) continue; + + if (fac.playerReputation > getAugCost(aug).repCost) { + availableAugs.push(aug); + } + } + } + + // Add the stanek sleeve aug + if (!ownedAugNames.includes(AugmentationName.ZOE) && Player.factions.includes(FactionName.ChurchOfTheMachineGod)) { + const aug = Augmentations[AugmentationName.ZOE]; + availableAugs.push(aug); + } + + return availableAugs; + } shockBonus(): number { return (100 - this.shock) / 100; diff --git a/src/PersonObjects/Sleeve/SleeveMethods.ts b/src/PersonObjects/Sleeve/SleeveMethods.ts deleted file mode 100644 index f60d7f650..000000000 --- a/src/PersonObjects/Sleeve/SleeveMethods.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Player } from "@player"; -import { AugmentationName, FactionName } from "@enums"; -import { Sleeve } from "./Sleeve"; -import { Augmentation } from "../../Augmentation/Augmentation"; -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 { - this.mults = mergeMultipliers(this.mults, aug.mults); -} - -export function findPurchasableAugs(this: Sleeve): Augmentation[] { - // You can only purchase Augmentations that are actually available from - // your factions. I.e. you must be in a faction that has the Augmentation - // and you must also have enough rep in that faction in order to purchase it. - - const ownedAugNames = this.augmentations.map((e) => e.name); - const availableAugs: Augmentation[] = []; - - // Helper function that helps filter out augs that are already owned - // and augs that aren't allowed for sleeves - function isAvailableForSleeve(aug: Augmentation): boolean { - if (ownedAugNames.includes(aug.name)) return false; - if (availableAugs.includes(aug)) return false; - if (aug.isSpecial) return false; - - type MultKey = keyof Multipliers; - const validMults: MultKey[] = [ - "hacking", - "strength", - "defense", - "dexterity", - "agility", - "charisma", - "hacking_exp", - "strength_exp", - "defense_exp", - "dexterity_exp", - "agility_exp", - "charisma_exp", - "company_rep", - "faction_rep", - "crime_money", - "crime_success", - "work_money", - ]; - for (const mult of validMults) { - if (aug.mults[mult] !== 1) return true; - } - - return false; - } - - // If player is in a gang, then we return all augs that the player - // has enough reputation for (since that gang offers all augs) - if (Player.gang) { - const fac = Player.getGangFaction(); - const gangAugs = getFactionAugmentationsFiltered(fac); - - for (const augName of gangAugs) { - const aug = Augmentations[augName]; - if (!isAvailableForSleeve(aug)) continue; - - if (fac.playerReputation > getAugCost(aug).repCost) { - availableAugs.push(aug); - } - } - } - - for (const facName of Player.factions) { - if (facName === FactionName.Bladeburners) continue; - if (facName === FactionName.Netburners) continue; - const fac = Factions[facName]; - if (!fac) continue; - - for (const augName of fac.augmentations) { - const aug = Augmentations[augName]; - if (!isAvailableForSleeve(aug)) continue; - - if (fac.playerReputation > getAugCost(aug).repCost) { - availableAugs.push(aug); - } - } - } - - // Add the stanek sleeve aug - if (!ownedAugNames.includes(AugmentationName.ZOE) && Player.factions.includes(FactionName.ChurchOfTheMachineGod)) { - const aug = Augmentations[AugmentationName.ZOE]; - availableAugs.push(aug); - } - - return availableAugs; -}