mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-04 22:59:42 +02:00
BLADEBURNER: Typesafety / refactoring (#1154)
This commit is contained in:
@@ -49,7 +49,6 @@ export abstract class Person implements IPerson {
|
||||
gainIntelligenceExp = personMethods.gainIntelligenceExp;
|
||||
gainStats = personMethods.gainStats;
|
||||
regenerateHp = personMethods.regenerateHp;
|
||||
queryStatFromString = personMethods.queryStatFromString;
|
||||
updateSkillLevels = personMethods.updateSkillLevels;
|
||||
hasAugmentation = personMethods.hasAugmentation;
|
||||
calculateSkill = calculateSkill; //Class version is equal to imported version
|
||||
|
||||
@@ -114,33 +114,6 @@ export function gainStats(this: Person, retValue: WorkStats): void {
|
||||
this.gainIntelligenceExp(retValue.intExp);
|
||||
}
|
||||
|
||||
//Given a string expression like "str" or "strength", returns the given stat
|
||||
export function queryStatFromString(this: Person, str: string): number {
|
||||
const tempStr = str.toLowerCase();
|
||||
if (tempStr.includes("hack")) {
|
||||
return this.skills.hacking;
|
||||
}
|
||||
if (tempStr.includes("str")) {
|
||||
return this.skills.strength;
|
||||
}
|
||||
if (tempStr.includes("def")) {
|
||||
return this.skills.defense;
|
||||
}
|
||||
if (tempStr.includes("dex")) {
|
||||
return this.skills.dexterity;
|
||||
}
|
||||
if (tempStr.includes("agi")) {
|
||||
return this.skills.agility;
|
||||
}
|
||||
if (tempStr.includes("cha")) {
|
||||
return this.skills.charisma;
|
||||
}
|
||||
if (tempStr.includes("int")) {
|
||||
return this.skills.intelligence;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function regenerateHp(this: Person, amt: number): void {
|
||||
if (typeof amt !== "number") {
|
||||
console.warn(`Player.regenerateHp() called without a numeric argument: ${amt}`);
|
||||
|
||||
@@ -8,4 +8,5 @@ export function canAccessBladeburner(this: PlayerObject): boolean {
|
||||
|
||||
export function startBladeburner(this: PlayerObject): void {
|
||||
this.bladeburner = new Bladeburner();
|
||||
this.bladeburner.init();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import type { SleeveWork } from "./Work/Work";
|
||||
import { Player } from "@player";
|
||||
import { Person } from "../Person";
|
||||
|
||||
import { Contracts } from "../../Bladeburner/data/Contracts";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import {
|
||||
ClassType,
|
||||
@@ -26,12 +25,13 @@ import {
|
||||
UniversityClassType,
|
||||
CompanyName,
|
||||
FactionName,
|
||||
BladeActionType,
|
||||
BladeGeneralActionName,
|
||||
} from "@enums";
|
||||
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
|
||||
import { formatPercent } from "../../ui/formatNumber";
|
||||
import { SleeveClassWork } from "./Work/SleeveClassWork";
|
||||
import { SleeveSynchroWork } from "./Work/SleeveSynchroWork";
|
||||
import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork";
|
||||
@@ -391,24 +391,44 @@ export class Sleeve extends Person implements SleevePerson {
|
||||
}
|
||||
|
||||
/** Begin a bladeburner task */
|
||||
bladeburner(action: string, contract: string): boolean {
|
||||
bladeburner(action: string, contract?: string): boolean {
|
||||
if (!Player.bladeburner) return false;
|
||||
switch (action) {
|
||||
case "Training":
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Training" }));
|
||||
this.startWork(
|
||||
new SleeveBladeburnerWork({
|
||||
actionId: { type: BladeActionType.general, name: BladeGeneralActionName.training },
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
case "Field analysis":
|
||||
case "Field Analysis":
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Field Analysis" }));
|
||||
this.startWork(
|
||||
new SleeveBladeburnerWork({
|
||||
actionId: { type: BladeActionType.general, name: BladeGeneralActionName.fieldAnalysis },
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
case "Recruitment":
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Recruitment" }));
|
||||
this.startWork(
|
||||
new SleeveBladeburnerWork({
|
||||
actionId: { type: BladeActionType.general, name: BladeGeneralActionName.recruitment },
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
case "Diplomacy":
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Diplomacy" }));
|
||||
this.startWork(
|
||||
new SleeveBladeburnerWork({
|
||||
actionId: { type: BladeActionType.general, name: BladeGeneralActionName.diplomacy },
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
case "Hyperbolic Regeneration Chamber":
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Hyperbolic Regeneration Chamber" }));
|
||||
this.startWork(
|
||||
new SleeveBladeburnerWork({
|
||||
actionId: { type: BladeActionType.general, name: BladeGeneralActionName.hyperbolicRegen },
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
case "Infiltrate synthoids":
|
||||
case "Infiltrate Synthoids":
|
||||
@@ -418,36 +438,13 @@ export class Sleeve extends Person implements SleevePerson {
|
||||
this.startWork(new SleeveSupportWork());
|
||||
return true;
|
||||
case "Take on contracts":
|
||||
if (!Contracts[contract]) return false;
|
||||
this.startWork(new SleeveBladeburnerWork({ type: "Contracts", name: contract }));
|
||||
if (!getEnumHelper("BladeContractName").isMember(contract)) return false;
|
||||
this.startWork(new SleeveBladeburnerWork({ actionId: { type: BladeActionType.contract, name: contract } }));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
recruitmentSuccessChance(): number {
|
||||
return Math.max(0, Math.min(1, Player.bladeburner?.getRecruitmentSuccessChance(this) ?? 0));
|
||||
}
|
||||
|
||||
contractSuccessChance(type: string, name: string): string {
|
||||
const bb = Player.bladeburner;
|
||||
if (bb === null) {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.contractSuccessChance; Message: '${errorLogText}'`);
|
||||
return "0%";
|
||||
}
|
||||
const chances = bb.getActionEstimatedSuccessChanceNetscriptFn(this, type, name);
|
||||
if (typeof chances === "string") {
|
||||
console.error(`Function: sleeves.contractSuccessChance; Message: '${chances}'`);
|
||||
return "0%";
|
||||
}
|
||||
if (chances[0] >= 1) {
|
||||
return "100%";
|
||||
} else {
|
||||
return `${formatPercent(chances[0])} - ${formatPercent(chances[1])}`;
|
||||
}
|
||||
}
|
||||
|
||||
takeDamage(amt: number): boolean {
|
||||
if (typeof amt !== "number") {
|
||||
console.warn(`Player.takeDamage() called without a numeric argument: ${amt}`);
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
import type { Sleeve } from "../Sleeve";
|
||||
import type { ActionIdentifier } from "../../../Bladeburner/Types";
|
||||
import type { PromisePair } from "../../../Types/Promises";
|
||||
import { Player } from "@player";
|
||||
import { BladeActionType, BladeGeneralActionName } from "@enums";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { GeneralActions } from "../../../Bladeburner/data/GeneralActions";
|
||||
import { scaleWorkStats } from "../../../Work/WorkStats";
|
||||
import { getKeyList } from "../../../utils/helpers/getKeyList";
|
||||
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
|
||||
import { invalidWork } from "../../../Work/InvalidWork";
|
||||
|
||||
interface SleeveBladeburnerWorkParams {
|
||||
type: "General" | "Contracts";
|
||||
name: string;
|
||||
actionId: ActionIdentifier & { type: BladeActionType.general | BladeActionType.contract };
|
||||
}
|
||||
|
||||
export const isSleeveBladeburnerWork = (w: SleeveWorkClass | null): w is SleeveBladeburnerWork =>
|
||||
w !== null && w.type === SleeveWorkType.BLADEBURNER;
|
||||
w?.type === SleeveWorkType.BLADEBURNER;
|
||||
|
||||
export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
type: SleeveWorkType.BLADEBURNER = SleeveWorkType.BLADEBURNER;
|
||||
tasksCompleted = 0;
|
||||
cyclesWorked = 0;
|
||||
actionType: "General" | "Contracts";
|
||||
actionName: string;
|
||||
actionId: ActionIdentifier & { type: BladeActionType.general | BladeActionType.contract };
|
||||
nextCompletionPair: PromisePair<void> = { promise: null, resolve: null };
|
||||
|
||||
constructor(params?: SleeveBladeburnerWorkParams) {
|
||||
super();
|
||||
this.actionType = params?.type ?? "General";
|
||||
this.actionName = params?.name ?? "Field Analysis";
|
||||
this.actionId = params?.actionId ?? { type: BladeActionType.general, name: BladeGeneralActionName.fieldAnalysis };
|
||||
}
|
||||
|
||||
cyclesNeeded(sleeve: Sleeve): number {
|
||||
const ret = Player.bladeburner?.getActionTimeNetscriptFn(sleeve, this.actionType, this.actionName);
|
||||
if (!ret || typeof ret === "string") throw new Error(`Error querying ${this.actionName} time`);
|
||||
return ret / CONSTANTS.MilliPerCycle;
|
||||
if (!Player.bladeburner) return Infinity;
|
||||
const action = Player.bladeburner.getActionObject(this.actionId);
|
||||
const timeInMs = action.getActionTime(Player.bladeburner, sleeve) * 1000;
|
||||
return timeInMs / CONSTANTS.MilliPerCycle;
|
||||
}
|
||||
|
||||
finish() {
|
||||
@@ -47,30 +48,19 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
if (!Player.bladeburner) return sleeve.stopWork();
|
||||
this.cyclesWorked += cycles;
|
||||
const actionIdent = Player.bladeburner.getActionIdFromTypeAndName(this.actionType, this.actionName);
|
||||
if (!actionIdent) throw new Error(`Error getting ${this.actionName} action`);
|
||||
if (this.actionType === "Contracts") {
|
||||
const action = Player.bladeburner.getActionObject(actionIdent);
|
||||
if (!action) throw new Error(`Error getting ${this.actionName} action object`);
|
||||
if (this.actionId.type === BladeActionType.contract) {
|
||||
const action = Player.bladeburner.getActionObject(this.actionId);
|
||||
if (action.count < 1) return sleeve.stopWork();
|
||||
}
|
||||
|
||||
while (this.cyclesWorked >= this.cyclesNeeded(sleeve)) {
|
||||
if (this.actionType === "Contracts") {
|
||||
const action = Player.bladeburner.getActionObject(actionIdent);
|
||||
if (!action) throw new Error(`Error getting ${this.actionName} action object`);
|
||||
if (this.actionId.type === BladeActionType.contract) {
|
||||
const action = Player.bladeburner.getActionObject(this.actionId);
|
||||
if (action.count < 1) return sleeve.stopWork();
|
||||
}
|
||||
const retValue = Player.bladeburner.completeAction(sleeve, actionIdent, false);
|
||||
if (this.actionType === "General") {
|
||||
const exp = GeneralActions[this.actionName]?.exp;
|
||||
if (!exp) throw new Error(`Somehow there was no exp for action ${this.actionType} ${this.actionName}`);
|
||||
applySleeveGains(sleeve, scaleWorkStats(exp, sleeve.shockBonus(), false));
|
||||
}
|
||||
const retValue = Player.bladeburner.completeAction(sleeve, this.actionId, false);
|
||||
applySleeveGains(sleeve, scaleWorkStats(retValue, sleeve.shockBonus(), false));
|
||||
|
||||
if (this.actionType === "Contracts") {
|
||||
applySleeveGains(sleeve, scaleWorkStats(retValue, sleeve.shockBonus(), false));
|
||||
}
|
||||
this.tasksCompleted++;
|
||||
this.cyclesWorked -= this.cyclesNeeded(sleeve);
|
||||
// Resolve and reset nextCompletion promise
|
||||
@@ -86,8 +76,8 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
APICopy(sleeve: Sleeve) {
|
||||
return {
|
||||
type: SleeveWorkType.BLADEBURNER as const,
|
||||
actionType: this.actionType,
|
||||
actionName: this.actionName,
|
||||
actionType: this.actionId.type,
|
||||
actionName: this.actionId.name,
|
||||
tasksCompleted: this.tasksCompleted,
|
||||
cyclesWorked: this.cyclesWorked,
|
||||
cyclesNeeded: this.cyclesNeeded(sleeve),
|
||||
@@ -104,6 +94,9 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
|
||||
/** Initializes a BladeburnerWork object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): SleeveBladeburnerWork {
|
||||
const actionId = loadActionIdentifier(value.data?.actionId);
|
||||
if (!actionId) return invalidWork();
|
||||
value.data.actionId = actionId;
|
||||
return Generic_fromJSON(SleeveBladeburnerWork, value.data, SleeveBladeburnerWork.savedKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ function getWorkDescription(sleeve: Sleeve, progress: number): string {
|
||||
return "This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's synchronization to increase.";
|
||||
case SleeveWorkType.BLADEBURNER:
|
||||
return (
|
||||
`This sleeve is currently attempting to perform ${work.actionName}.\n\nTasks Completed: ${formatInt(
|
||||
`This sleeve is currently attempting to perform ${work.actionId.name}.\n\nTasks Completed: ${formatInt(
|
||||
work.tasksCompleted,
|
||||
)}\n \n` + `Progress: ${formatPercent(progress)}`
|
||||
);
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import type { Sleeve } from "../Sleeve";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { MenuItem, Select, SelectChangeEvent } from "@mui/material";
|
||||
|
||||
import { Player } from "@player";
|
||||
import {
|
||||
BladeActionType,
|
||||
BladeContractName,
|
||||
CityName,
|
||||
FactionName,
|
||||
FactionWorkType,
|
||||
GymType,
|
||||
LocationName,
|
||||
} from "@enums";
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
import { CityName, FactionName, FactionWorkType, GymType, LocationName } from "@enums";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
|
||||
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
|
||||
import { isSleeveBladeburnerWork } from "../Work/SleeveBladeburnerWork";
|
||||
import { getEnumHelper } from "../../../utils/EnumHelper";
|
||||
import { SleeveWorkType } from "../Work/Work";
|
||||
|
||||
@@ -51,7 +57,7 @@ function possibleJobs(sleeve: Sleeve): string[] {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (isSleeveCompanyWork(otherSleeve.currentWork)) {
|
||||
if (otherSleeve.currentWork?.type === SleeveWorkType.COMPANY) {
|
||||
forbiddenCompanies.push(otherSleeve.currentWork.companyName);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +76,7 @@ function possibleFactions(sleeve: Sleeve): string[] {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (isSleeveFactionWork(otherSleeve.currentWork)) {
|
||||
if (otherSleeve.currentWork?.type === SleeveWorkType.FACTION) {
|
||||
forbiddenFactions.push(otherSleeve.currentWork.factionName);
|
||||
}
|
||||
}
|
||||
@@ -90,24 +96,24 @@ function possibleFactions(sleeve: Sleeve): string[] {
|
||||
});
|
||||
}
|
||||
|
||||
function possibleContracts(sleeve: Sleeve): string[] {
|
||||
function possibleContracts(sleeve: Sleeve): BladeContractName[] | ["------"] {
|
||||
const bb = Player.bladeburner;
|
||||
if (bb === null) {
|
||||
return ["------"];
|
||||
}
|
||||
let contracts = bb.getContractNamesNetscriptFn();
|
||||
let contracts = Object.values(BladeContractName);
|
||||
for (const otherSleeve of Player.sleeves) {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (isSleeveBladeburnerWork(otherSleeve.currentWork) && otherSleeve.currentWork.actionType === "Contracts") {
|
||||
if (
|
||||
otherSleeve.currentWork?.type === SleeveWorkType.BLADEBURNER &&
|
||||
otherSleeve.currentWork.actionId.type === BladeActionType.contract
|
||||
) {
|
||||
const w = otherSleeve.currentWork;
|
||||
contracts = contracts.filter((x) => x != w.actionName);
|
||||
contracts = contracts.filter((x) => x != w.actionId.name);
|
||||
}
|
||||
}
|
||||
if (contracts.length === 0) {
|
||||
return ["------"];
|
||||
}
|
||||
return contracts;
|
||||
}
|
||||
|
||||
@@ -256,10 +262,10 @@ function getABC(sleeve: Sleeve): [string, string, string] {
|
||||
return ["Work for Faction", work.factionName, workNames[work.factionWorkType] ?? ""];
|
||||
}
|
||||
case SleeveWorkType.BLADEBURNER:
|
||||
if (work.actionType === "Contracts") {
|
||||
return ["Perform Bladeburner Actions", "Take on contracts", work.actionName];
|
||||
if (work.actionId.type === BladeActionType.contract) {
|
||||
return ["Perform Bladeburner Actions", "Take on contracts", work.actionId.name];
|
||||
}
|
||||
return ["Perform Bladeburner Actions", work.actionName, "------"];
|
||||
return ["Perform Bladeburner Actions", work.actionId.name, "------"];
|
||||
case SleeveWorkType.CLASS: {
|
||||
if (!work.isGym()) return ["Take University Course", work.classType, work.location];
|
||||
const gymNames: Record<GymType, string> = {
|
||||
|
||||
Reference in New Issue
Block a user