Files
bitburner-src/src/PersonObjects/Player/PlayerObject.ts
2025-11-22 23:08:15 -08:00

222 lines
9.2 KiB
TypeScript

import type { BitNodeOptions, Player as IPlayer } from "@nsdefs";
import type { PlayerAchievement } from "../../Achievements/Achievements";
import type { Bladeburner } from "../../Bladeburner/Bladeburner";
import type { Corporation } from "../../Corporation/Corporation";
import type { Infiltration } from "../../Infiltration/Infiltration";
import type { Exploit } from "../../Exploits/Exploit";
import type { Gang } from "../../Gang/Gang";
import type { HacknetNode } from "../../Hacknet/HacknetNode";
import type { Sleeve } from "../Sleeve/Sleeve";
import type { Work } from "../../Work/Work";
import * as augmentationMethods from "./PlayerObjectAugmentationMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
import * as corporationMethods from "./PlayerObjectCorporationMethods";
import * as gangMethods from "./PlayerObjectGangMethods";
import * as generalMethods from "./PlayerObjectGeneralMethods";
import * as serverMethods from "./PlayerObjectServerMethods";
import * as workMethods from "./PlayerObjectWorkMethods";
import { setPlayer } from "@player";
import { CompanyName, FactionName, JobName, LocationName } from "@enums";
import { HashManager } from "../../Hacknet/HashManager";
import { type MoneySource, MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver";
import { JSONMap, JSONSet } from "../../Types/Jsonable";
import { cyrb53 } from "../../utils/HashUtils";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
import { getKeyList } from "../../utils/helpers/getKeyList";
import { CONSTANTS } from "../../Constants";
import { Person } from "../Person";
import { isMember } from "../../utils/EnumHelper";
import { PartialRecord } from "../../Types/Record";
import { isSleeveSupportWork } from "../Sleeve/Work/SleeveSupportWork";
export class PlayerObject extends Person implements IPlayer {
// Player-specific properties
bitNodeN = 1; //current bitnode
corporation: Corporation | null = null;
gang: Gang | null = null;
bladeburner: Bladeburner | null = null;
infiltration: Infiltration | null = null;
currentServer = "";
factions: FactionName[] = [];
factionInvitations: FactionName[] = [];
factionRumors = new JSONSet<FactionName>();
hacknetNodes: (HacknetNode | string)[] = []; // HacknetNode object or hostname of Hacknet Server
has4SData = false;
has4SDataTixApi = false;
hashManager = new HashManager();
hasTixApiAccess = false;
hasWseAccount = false;
jobs: PartialRecord<CompanyName, JobName> = {};
karma = 0;
numPeopleKilled = 0;
location = LocationName.TravelAgency;
money = 1000 + CONSTANTS.Donations;
moneySourceA = new MoneySourceTracker();
moneySourceB = new MoneySourceTracker();
playtimeSinceLastAug = 0;
playtimeSinceLastBitnode = 0;
lastAugReset = -1;
lastNodeReset = -1;
purchasedServers: string[] = [];
scriptProdSinceLastAug = 0;
sleeves: Sleeve[] = [];
sleevesFromCovenant = 0;
sourceFiles = new JSONMap<number, number>();
exploits: Exploit[] = [];
achievements: PlayerAchievement[] = [];
terminalCommandHistory: string[] = [];
identifier: string;
lastUpdate = 0;
lastSave = 0;
totalPlaytime = 0;
currentWork: Work | null = null;
focus = false;
entropy = 0;
bitNodeOptions: BitNodeOptions = {
sourceFileOverrides: new JSONMap<number, number>(),
intelligenceOverride: undefined,
restrictHomePCUpgrade: false,
disableGang: false,
disableCorporation: false,
disableBladeburner: false,
disable4SData: false,
disableHacknetServer: false,
disableSleeveExpAndAugmentation: false,
};
get activeSourceFiles(): JSONMap<number, number> {
return new JSONMap([...this.sourceFiles, ...this.bitNodeOptions.sourceFileOverrides]);
}
// Player-specific methods
init = generalMethods.init;
startWork = workMethods.startWork;
processWork = workMethods.processWork;
finishWork = workMethods.finishWork;
applyForJob = generalMethods.applyForJob;
canAccessBladeburner = bladeburnerMethods.canAccessBladeburner;
canAccessCorporation = corporationMethods.canAccessCorporation;
canAccessGang = gangMethods.canAccessGang;
canAccessGrafting = generalMethods.canAccessGrafting;
canAfford = generalMethods.canAfford;
gainMoney = generalMethods.gainMoney;
getCurrentServer = serverMethods.getCurrentServer;
getGangFaction = gangMethods.getGangFaction;
getGangName = gangMethods.getGangName;
getHomeComputer = serverMethods.getHomeComputer;
getNextCompanyPosition = generalMethods.getNextCompanyPosition;
getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost;
getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost;
gotoLocation = generalMethods.gotoLocation;
hasGangWith = gangMethods.hasGangWith;
hasTorRouter = serverMethods.hasTorRouter;
hasProgram = generalMethods.hasProgram;
inGang = gangMethods.inGang;
isAwareOfGang = gangMethods.isAwareOfGang;
isQualified = generalMethods.isQualified;
loseMoney = generalMethods.loseMoney;
reapplyAllAugmentations = generalMethods.reapplyAllAugmentations;
reapplyAllSourceFiles = generalMethods.reapplyAllSourceFiles;
recordMoneySource = generalMethods.recordMoneySource;
setMoney = generalMethods.setMoney;
startBladeburner = bladeburnerMethods.startBladeburner;
startCorporation = corporationMethods.startCorporation;
startFocusing = generalMethods.startFocusing;
startGang = gangMethods.startGang;
takeDamage = generalMethods.takeDamage;
giveExploit = generalMethods.giveExploit;
giveAchievement = generalMethods.giveAchievement;
getCasinoWinnings = generalMethods.getCasinoWinnings;
quitJob = generalMethods.quitJob;
hasJob = generalMethods.hasJob;
createHacknetServer = serverMethods.createHacknetServer;
queueAugmentation = generalMethods.queueAugmentation;
receiveInvite = generalMethods.receiveInvite;
receiveRumor = generalMethods.receiveRumor;
gainCodingContractReward = generalMethods.gainCodingContractReward;
stopFocusing = generalMethods.stopFocusing;
prestigeAugmentation = generalMethods.prestigeAugmentation;
prestigeSourceFile = generalMethods.prestigeSourceFile;
calculateSkillProgress = generalMethods.calculateSkillProgress;
hospitalize = generalMethods.hospitalize;
checkForFactionInvitations = generalMethods.checkForFactionInvitations;
setBitNodeNumber = generalMethods.setBitNodeNumber;
canAccessCotMG = generalMethods.canAccessCotMG;
sourceFileLvl = generalMethods.sourceFileLvl;
activeSourceFileLvl = generalMethods.activeSourceFileLvl;
applyEntropy = augmentationMethods.applyEntropy;
focusPenalty = generalMethods.focusPenalty;
initInfiltration = generalMethods.initInfiltration;
constructor() {
super();
// Let's get a hash of some semi-random stuff so we have something unique.
this.identifier = cyrb53(
"I-" +
new Date().getTime() +
navigator.userAgent +
window.innerWidth +
window.innerHeight +
getRandomIntInclusive(100, 999),
);
this.lastAugReset = this.lastNodeReset = Date.now();
}
travelCostMoneySource(): MoneySource {
return "other";
}
whoAmI(): string {
return "Player";
}
sleevesSupportingBladeburner(): Sleeve[] {
return this.sleeves.filter((s) => isSleeveSupportWork(s.currentWork));
}
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
// For the time being, infiltration is not part of the save.
return Generic_toJSON("PlayerObject", this, getKeyList(PlayerObject, { removedKeys: ["infiltration"] }));
}
/** Initializes a PlayerObject object from a JSON save state. */
static fromJSON(value: IReviverValue): PlayerObject {
const player = Generic_fromJSON(PlayerObject, value.data);
// Any statistics that could be infinite would be serialized as null (JSON.stringify(Infinity) is "null")
player.hp = { current: player.hp?.current ?? 10, max: player.hp?.max ?? 10 };
player.money ??= 0;
// Just remove from the save file any augs that have invalid name
player.augmentations = player.augmentations.filter((ownedAug) => isMember("AugmentationName", ownedAug.name));
player.queuedAugmentations = player.queuedAugmentations.filter((ownedAug) =>
isMember("AugmentationName", ownedAug.name),
);
player.updateSkillLevels();
// Conversion code for Player.sourceFiles is here instead of normal save conversion area because it needs
// to happen earlier for use in the savegame comparison tool.
if (Array.isArray(player.sourceFiles)) {
// Expect pre-2.3 sourcefile format here.
type OldSourceFiles = { n: number; lvl: number }[];
player.sourceFiles = new JSONMap((player.sourceFiles as OldSourceFiles).map(({ n, lvl }) => [n, lvl]));
}
// Remove any invalid jobs
for (const [loadedCompanyName, loadedJobName] of Object.entries(player.jobs)) {
if (!isMember("CompanyName", loadedCompanyName) || !isMember("JobName", loadedJobName)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete player.jobs[loadedCompanyName as CompanyName];
}
}
return player;
}
}
setPlayer(new PlayerObject());
constructorsForReviver.PlayerObject = PlayerObject;