mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-07 16:17:49 +02:00
266 lines
8.1 KiB
TypeScript
266 lines
8.1 KiB
TypeScript
import type { Person as IPerson, WorkStats } from "@nsdefs";
|
|
import type { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
|
import type { IReviverValue } from "../utils/JSONReviver";
|
|
import type { MoneySource } from "../utils/MoneySourceTracker";
|
|
import type { HP } from "./HP";
|
|
import type { Skills } from "./Skills";
|
|
|
|
import { CityName } from "@enums";
|
|
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
|
import { CONSTANTS } from "../Constants";
|
|
import { Player } from "../Player";
|
|
import { defaultMultipliers } from "./Multipliers";
|
|
import { calculateExp, calculateSkill } from "./formulas/skill";
|
|
|
|
// Base class representing a person-like object
|
|
export abstract class Person implements IPerson {
|
|
hp: HP = { current: 10, max: 10 };
|
|
skills: Skills = {
|
|
hacking: 1,
|
|
strength: 1,
|
|
defense: 1,
|
|
dexterity: 1,
|
|
agility: 1,
|
|
charisma: 1,
|
|
intelligence: 0,
|
|
};
|
|
exp: Skills = {
|
|
hacking: 0,
|
|
strength: 0,
|
|
defense: 0,
|
|
dexterity: 0,
|
|
agility: 0,
|
|
charisma: 0,
|
|
intelligence: 0,
|
|
};
|
|
|
|
persistentIntelligenceData = {
|
|
exp: 0,
|
|
};
|
|
|
|
mults = defaultMultipliers();
|
|
|
|
/** Augmentations */
|
|
augmentations: PlayerOwnedAugmentation[] = [];
|
|
queuedAugmentations: PlayerOwnedAugmentation[] = [];
|
|
|
|
/** City that the person is in */
|
|
city: CityName = CityName.Sector12;
|
|
|
|
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,
|
|
);
|
|
}
|
|
|
|
overrideIntelligence(): void {
|
|
// Reset intelligence data if the player has not unlocked Intelligence.
|
|
// Note that this check cannot reset intelligence data in some edge cases (e.g., bitflume from non-BN5 to BN5). This
|
|
// is an accepted limitation.
|
|
// For more information, please check https://github.com/bitburner-official/bitburner-src/pull/2666
|
|
if (Player.sourceFileLvl(5) === 0 && Player.bitNodeN !== 5) {
|
|
this.skills.intelligence = 0;
|
|
this.exp.intelligence = 0;
|
|
this.persistentIntelligenceData.exp = 0;
|
|
return;
|
|
}
|
|
const persistentIntelligenceSkill = this.calculateSkill(this.persistentIntelligenceData.exp, 1);
|
|
// Reset exp and skill to the persistent values if there is no limit (intelligenceOverride) or the limit is greater
|
|
// than or equal to the persistent skill.
|
|
if (
|
|
Player.bitNodeOptions.intelligenceOverride === undefined ||
|
|
Player.bitNodeOptions.intelligenceOverride >= persistentIntelligenceSkill
|
|
) {
|
|
this.exp.intelligence = this.persistentIntelligenceData.exp;
|
|
this.skills.intelligence = persistentIntelligenceSkill;
|
|
return;
|
|
}
|
|
// Limit exp and skill based on intelligenceOverride only if it's smaller than the persistent skill.
|
|
this.exp.intelligence = calculateExp(Player.bitNodeOptions.intelligenceOverride, 1);
|
|
this.skills.intelligence = Player.bitNodeOptions.intelligenceOverride;
|
|
}
|
|
|
|
gainIntelligenceExp(exp: number): void {
|
|
if (isNaN(exp)) {
|
|
console.error("ERROR: NaN passed into Player.gainIntelligenceExp()");
|
|
return;
|
|
}
|
|
/**
|
|
* Don't change sourceFileLvl to activeSourceFileLvl. When the player has int level, the ability to gain more int is
|
|
* a permanent benefit.
|
|
*/
|
|
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
|
this.exp.intelligence += exp;
|
|
this.skills.intelligence = Math.floor(this.calculateSkill(this.exp.intelligence, 1));
|
|
this.persistentIntelligenceData.exp += exp;
|
|
}
|
|
}
|
|
|
|
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 {
|
|
for (const [skill, bnMult] of [
|
|
["hacking", "HackingLevelMultiplier"],
|
|
["strength", "StrengthLevelMultiplier"],
|
|
["defense", "DefenseLevelMultiplier"],
|
|
["dexterity", "DexterityLevelMultiplier"],
|
|
["agility", "AgilityLevelMultiplier"],
|
|
["charisma", "CharismaLevelMultiplier"],
|
|
] as const) {
|
|
this.skills[skill] = Math.max(
|
|
1,
|
|
Math.floor(this.calculateSkill(this.exp[skill], this.mults[skill] * currentNodeMults[bnMult])),
|
|
);
|
|
}
|
|
|
|
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.travelCostMoneySource());
|
|
this.city = cityName;
|
|
|
|
return true;
|
|
}
|
|
|
|
calculateSkill = calculateSkill; //Class version is equal to imported version
|
|
|
|
/** Reset all multipliers to 1 */
|
|
resetMultipliers() {
|
|
this.mults = defaultMultipliers();
|
|
}
|
|
|
|
abstract travelCostMoneySource(): MoneySource;
|
|
abstract takeDamage(amt: number): boolean;
|
|
abstract whoAmI(): string;
|
|
abstract toJSON(): IReviverValue;
|
|
}
|