MISC: Refactor Person.ts and Sleeve.ts (#1532)

This commit is contained in:
catloversg 2024-08-02 12:59:41 +07:00 committed by GitHub
parent d4b73531f5
commit 06677a1306
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 286 additions and 298 deletions

@ -1,13 +1,16 @@
import type { Skills } from "./Skills"; import type { Skills } from "./Skills";
import type { HP } from "./HP"; 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 { CityName } from "@enums";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { calculateSkill } from "./formulas/skill"; import { calculateSkill } from "./formulas/skill";
import { defaultMultipliers } from "./Multipliers"; import { defaultMultipliers } from "./Multipliers";
import { IReviverValue } from "../utils/JSONReviver"; 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 // Base class representing a person-like object
export abstract class Person implements IPerson { export abstract class Person implements IPerson {
@ -40,18 +43,195 @@ export abstract class Person implements IPerson {
/** City that the person is in */ /** City that the person is in */
city: CityName = CityName.Sector12; city: CityName = CityName.Sector12;
gainHackingExp = personMethods.gainHackingExp; gainHackingExp(exp: number): void {
gainStrengthExp = personMethods.gainStrengthExp; if (isNaN(exp)) {
gainDefenseExp = personMethods.gainDefenseExp; console.error("ERR: NaN passed into Player.gainHackingExp()");
gainDexterityExp = personMethods.gainDexterityExp; return;
gainAgilityExp = personMethods.gainAgilityExp; }
gainCharismaExp = personMethods.gainCharismaExp; this.exp.hacking += exp;
gainIntelligenceExp = personMethods.gainIntelligenceExp; if (this.exp.hacking < 0) {
gainStats = personMethods.gainStats; this.exp.hacking = 0;
regenerateHp = personMethods.regenerateHp; }
updateSkillLevels = personMethods.updateSkillLevels;
hasAugmentation = personMethods.hasAugmentation; this.skills.hacking = calculateSkill(
travel = personMethods.travel; 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 calculateSkill = calculateSkill; //Class version is equal to imported version
/** Reset all multipliers to 1 */ /** Reset all multipliers to 1 */

@ -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;
}

@ -27,6 +27,7 @@ import {
FactionName, FactionName,
BladeActionType, BladeActionType,
BladeGeneralActionName, BladeGeneralActionName,
AugmentationName,
} from "@enums"; } from "@enums";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
@ -41,9 +42,12 @@ import { SleeveInfiltrateWork } from "./Work/SleeveInfiltrateWork";
import { SleeveSupportWork } from "./Work/SleeveSupportWork"; import { SleeveSupportWork } from "./Work/SleeveSupportWork";
import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork"; import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork";
import { SleeveCrimeWork } from "./Work/SleeveCrimeWork"; import { SleeveCrimeWork } from "./Work/SleeveCrimeWork";
import * as sleeveMethods from "./SleeveMethods";
import { calculateIntelligenceBonus } from "../formulas/intelligence"; import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { getEnumHelper } from "../../utils/EnumHelper"; 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 { export class Sleeve extends Person implements SleevePerson {
currentWork: SleeveWork | null = null; currentWork: SleeveWork | null = null;
@ -75,8 +79,93 @@ export class Sleeve extends Person implements SleevePerson {
this.shockRecovery(); this.shockRecovery();
} }
applyAugmentation = sleeveMethods.applyAugmentation; /** Updates this object's multipliers for the given augmentation */
findPurchasableAugs = sleeveMethods.findPurchasableAugs; 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 { shockBonus(): number {
return (100 - this.shock) / 100; return (100 - this.shock) / 100;

@ -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;
}