API: Standardize "nextCompletion" promise in tasks (#2687)

This commit is contained in:
catloversg
2026-04-28 13:49:53 +07:00
committed by GitHub
parent ee3014b029
commit 36e1adf2d2
72 changed files with 1750 additions and 364 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ 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 type { PlayerBaseWork } from "../../Work/Work";
import * as augmentationMethods from "./PlayerObjectAugmentationMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
@@ -73,7 +73,7 @@ export class PlayerObject extends Person implements IPlayer {
lastSave = 0;
totalPlaytime = 0;
currentWork: Work | null = null;
currentWork: PlayerBaseWork | null = null;
focus = false;
entropy = 0;
@@ -1,8 +1,8 @@
import { Work } from "../../Work/Work";
import type { PlayerBaseWork } from "../../Work/Work";
import type { PlayerObject } from "./PlayerObject";
export function startWork(this: PlayerObject, w: Work): void {
export function startWork(this: PlayerObject, w: PlayerBaseWork): void {
if (this.currentWork !== null) {
this.currentWork.finish(true);
}
@@ -1,13 +1,11 @@
import type { Sleeve } from "../Sleeve";
import type { ActionIdentifier } from "../../../Bladeburner/Types";
import type { PromisePair } from "../../../Types/Promises";
import { Player } from "@player";
import { BladeburnerActionType, BladeburnerGeneralActionName } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
import { scaleWorkStats } from "../../../Work/WorkStats";
import { getKeyList } from "../../../utils/helpers/getKeyList";
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
import { invalidWork } from "../../../Work/InvalidWork";
import { assertObject } from "../../../utils/TypeAssertion";
@@ -16,15 +14,14 @@ interface SleeveBladeburnerWorkParams {
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
}
export const isSleeveBladeburnerWork = (w: SleeveWorkClass | null): w is SleeveBladeburnerWork =>
export const isSleeveBladeburnerWork = (w: SleeveBaseWork | null): w is SleeveBladeburnerWork =>
w?.type === SleeveWorkType.BLADEBURNER;
export class SleeveBladeburnerWork extends SleeveWorkClass {
export class SleeveBladeburnerWork extends SleeveBaseWork {
type: SleeveWorkType.BLADEBURNER = SleeveWorkType.BLADEBURNER;
tasksCompleted = 0;
cyclesWorked = 0;
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
nextCompletionPair: PromisePair<void> = { promise: null, resolve: null };
constructor(params?: SleeveBladeburnerWorkParams) {
super();
@@ -41,14 +38,6 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
return timeInMs / CONSTANTS.MilliPerCycle;
}
finish() {
if (this.nextCompletionPair.resolve) {
this.nextCompletionPair.resolve();
this.nextCompletionPair.resolve = null;
this.nextCompletionPair.promise = null;
}
}
process(sleeve: Sleeve, cycles: number) {
if (!Player.bladeburner) return sleeve.stopWork();
this.cyclesWorked += cycles;
@@ -67,17 +56,10 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
this.tasksCompleted++;
this.cyclesWorked -= this.cyclesNeeded(sleeve);
// Resolve and reset nextCompletion promise
this.finish();
this.resolveNextCompletion();
}
}
get nextCompletion(): Promise<void> {
if (!this.nextCompletionPair.promise)
this.nextCompletionPair.promise = new Promise((r) => (this.nextCompletionPair.resolve = r));
return this.nextCompletionPair.promise;
}
APICopy(sleeve: Sleeve) {
return {
type: SleeveWorkType.BLADEBURNER as const,
@@ -90,11 +72,9 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
};
}
static savedKeys = getKeyList(SleeveBladeburnerWork, { removedKeys: ["nextCompletionPair"] });
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
return Generic_toJSON("SleeveBladeburnerWork", this, SleeveBladeburnerWork.savedKeys);
return Generic_toJSON("SleeveBladeburnerWork", this);
}
/** Initializes a BladeburnerWork object from a JSON save state. */
@@ -115,7 +95,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
}
}
value.data.actionId = actionId;
return Generic_fromJSON(SleeveBladeburnerWork, value.data, SleeveBladeburnerWork.savedKeys);
return Generic_fromJSON(SleeveBladeburnerWork, value.data);
}
}
@@ -1,6 +1,6 @@
import { ClassType, LocationName, UniversityClassType } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { Classes } from "../../../Work/ClassWork";
import { calculateClassEarnings } from "../../../Work/Formulas";
import { Sleeve } from "../Sleeve";
@@ -9,7 +9,7 @@ import { Locations } from "../../../Locations/Locations";
import { isMember } from "../../../utils/EnumHelper";
import { assertObject } from "../../../utils/TypeAssertion";
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
export const isSleeveClassWork = (w: SleeveBaseWork | null): w is SleeveClassWork =>
w !== null && w.type === SleeveWorkType.CLASS;
interface ClassWorkParams {
@@ -17,7 +17,7 @@ interface ClassWorkParams {
location: LocationName;
}
export class SleeveClassWork extends SleeveWorkClass {
export class SleeveClassWork extends SleeveBaseWork {
type: SleeveWorkType.CLASS = SleeveWorkType.CLASS;
classType: ClassType;
location: LocationName;
@@ -46,6 +46,7 @@ export class SleeveClassWork extends SleeveWorkClass {
type: SleeveWorkType.CLASS as const,
classType: this.classType,
location: this.location,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -2,7 +2,7 @@ import { Player } from "@player";
import { CompanyName, JobName } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { Companies } from "../../../Company/Companies";
import { Company } from "../../../Company/Company";
import { calculateCompanyWorkStats } from "../../../Work/Formulas";
@@ -12,10 +12,10 @@ import { CompanyPositions } from "../../../Company/CompanyPositions";
import { isMember } from "../../../utils/EnumHelper";
import { invalidWork } from "../../../Work/InvalidWork";
export const isSleeveCompanyWork = (w: SleeveWorkClass | null): w is SleeveCompanyWork =>
export const isSleeveCompanyWork = (w: SleeveBaseWork | null): w is SleeveCompanyWork =>
w !== null && w.type === SleeveWorkType.COMPANY;
export class SleeveCompanyWork extends SleeveWorkClass {
export class SleeveCompanyWork extends SleeveBaseWork {
type: SleeveWorkType.COMPANY = SleeveWorkType.COMPANY;
companyName: CompanyName;
@@ -51,6 +51,7 @@ export class SleeveCompanyWork extends SleeveWorkClass {
return {
type: SleeveWorkType.COMPANY as const,
companyName: this.companyName,
nextCompletion: this.nextCompletion,
};
}
@@ -1,7 +1,7 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { CrimeType } from "@enums";
import { Crimes } from "../../../Crime/Crimes";
import { Crime } from "../../../Crime/Crime";
@@ -9,10 +9,10 @@ import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants";
import { calculateCrimeWorkStats } from "../../../Work/Formulas";
export const isSleeveCrimeWork = (w: SleeveWorkClass | null): w is SleeveCrimeWork =>
export const isSleeveCrimeWork = (w: SleeveBaseWork | null): w is SleeveCrimeWork =>
w !== null && w.type === SleeveWorkType.CRIME;
export class SleeveCrimeWork extends SleeveWorkClass {
export class SleeveCrimeWork extends SleeveBaseWork {
type: SleeveWorkType.CRIME = SleeveWorkType.CRIME;
crimeType: CrimeType;
tasksCompleted = 0;
@@ -49,6 +49,7 @@ export class SleeveCrimeWork extends SleeveWorkClass {
applySleeveGains(sleeve, gains, success ? 1 : 0.25);
this.tasksCompleted++;
this.cyclesWorked -= this.cyclesNeeded();
this.resolveNextCompletion();
}
}
@@ -59,6 +60,7 @@ export class SleeveCrimeWork extends SleeveWorkClass {
tasksCompleted: this.tasksCompleted,
cyclesWorked: this.cyclesWorked,
cyclesNeeded: this.cyclesNeeded(),
nextCompletion: this.nextCompletion,
};
}
@@ -1,7 +1,7 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { FactionName, FactionWorkType } from "@enums";
import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
@@ -14,10 +14,10 @@ interface SleeveFactionWorkParams {
factionName: FactionName;
}
export const isSleeveFactionWork = (w: SleeveWorkClass | null): w is SleeveFactionWork =>
export const isSleeveFactionWork = (w: SleeveBaseWork | null): w is SleeveFactionWork =>
w !== null && w.type === SleeveWorkType.FACTION;
export class SleeveFactionWork extends SleeveWorkClass {
export class SleeveFactionWork extends SleeveBaseWork {
type: SleeveWorkType.FACTION = SleeveWorkType.FACTION;
factionWorkType: FactionWorkType;
factionName: FactionName;
@@ -56,6 +56,7 @@ export class SleeveFactionWork extends SleeveWorkClass {
type: SleeveWorkType.FACTION as const,
factionWorkType: this.factionWorkType,
factionName: this.factionName,
nextCompletion: this.nextCompletion,
};
}
@@ -1,20 +1,17 @@
import type { PromisePair } from "../../../Types/Promises";
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
import { getKeyList } from "../../../utils/helpers/getKeyList";
const infiltrateCycles = 60000 / CONSTANTS.MilliPerCycle;
export const isSleeveInfiltrateWork = (w: SleeveWorkClass | null): w is SleeveInfiltrateWork =>
export const isSleeveInfiltrateWork = (w: SleeveBaseWork | null): w is SleeveInfiltrateWork =>
w !== null && w.type === SleeveWorkType.INFILTRATE;
export class SleeveInfiltrateWork extends SleeveWorkClass {
export class SleeveInfiltrateWork extends SleeveBaseWork {
type: SleeveWorkType.INFILTRATE = SleeveWorkType.INFILTRATE;
cyclesWorked = 0;
nextCompletionPair: PromisePair<void> = { promise: null, resolve: null };
cyclesNeeded(): number {
return infiltrateCycles;
@@ -26,19 +23,7 @@ export class SleeveInfiltrateWork extends SleeveWorkClass {
if (this.cyclesWorked > this.cyclesNeeded()) {
this.cyclesWorked -= this.cyclesNeeded();
Player.bladeburner.infiltrateSynthoidCommunities();
this.finish();
}
}
get nextCompletion(): Promise<void> {
if (!this.nextCompletionPair.promise)
this.nextCompletionPair.promise = new Promise((r) => (this.nextCompletionPair.resolve = r));
return this.nextCompletionPair.promise;
}
finish() {
if (this.nextCompletionPair.resolve) {
this.nextCompletionPair.resolve();
this.nextCompletionPair.resolve = null;
this.nextCompletionPair.promise = null;
this.resolveNextCompletion();
}
}
@@ -51,16 +36,14 @@ export class SleeveInfiltrateWork extends SleeveWorkClass {
};
}
static savedKeys = getKeyList(SleeveInfiltrateWork, { removedKeys: ["nextCompletionPair"] });
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
return Generic_toJSON("SleeveInfiltrateWork", this, SleeveInfiltrateWork.savedKeys);
return Generic_toJSON("SleeveInfiltrateWork", this);
}
/** Initializes a BladeburnerWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveInfiltrateWork {
return Generic_fromJSON(SleeveInfiltrateWork, value.data, SleeveInfiltrateWork.savedKeys);
return Generic_fromJSON(SleeveInfiltrateWork, value.data);
}
}
@@ -1,12 +1,12 @@
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { calculateIntelligenceBonus } from "../../formulas/intelligence";
export const isSleeveRecoveryWork = (w: SleeveWorkClass | null): w is SleeveRecoveryWork =>
export const isSleeveRecoveryWork = (w: SleeveBaseWork | null): w is SleeveRecoveryWork =>
w !== null && w.type === SleeveWorkType.RECOVERY;
export class SleeveRecoveryWork extends SleeveWorkClass {
export class SleeveRecoveryWork extends SleeveBaseWork {
type: SleeveWorkType.RECOVERY = SleeveWorkType.RECOVERY;
process(sleeve: Sleeve, cycles: number) {
@@ -18,7 +18,10 @@ export class SleeveRecoveryWork extends SleeveWorkClass {
}
APICopy() {
return { type: SleeveWorkType.RECOVERY as const };
return {
type: SleeveWorkType.RECOVERY as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -1,11 +1,11 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
export const isSleeveSupportWork = (w: SleeveWorkClass | null): w is SleeveSupportWork =>
export const isSleeveSupportWork = (w: SleeveBaseWork | null): w is SleeveSupportWork =>
w !== null && w.type === SleeveWorkType.SUPPORT;
export class SleeveSupportWork extends SleeveWorkClass {
export class SleeveSupportWork extends SleeveBaseWork {
type: SleeveWorkType.SUPPORT = SleeveWorkType.SUPPORT;
constructor() {
super();
@@ -16,10 +16,14 @@ export class SleeveSupportWork extends SleeveWorkClass {
finish(): void {
Player.bladeburner?.sleeveSupport(false);
super.resolveNextCompletion();
}
APICopy() {
return { type: SleeveWorkType.SUPPORT as const };
return {
type: SleeveWorkType.SUPPORT as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -1,13 +1,13 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { calculateIntelligenceBonus } from "../../formulas/intelligence";
export const isSleeveSynchroWork = (w: SleeveWorkClass | null): w is SleeveSynchroWork =>
export const isSleeveSynchroWork = (w: SleeveBaseWork | null): w is SleeveSynchroWork =>
w !== null && w.type === SleeveWorkType.SYNCHRO;
export class SleeveSynchroWork extends SleeveWorkClass {
export class SleeveSynchroWork extends SleeveBaseWork {
type: SleeveWorkType.SYNCHRO = SleeveWorkType.SYNCHRO;
process(sleeve: Sleeve, cycles: number) {
@@ -19,7 +19,10 @@ export class SleeveSynchroWork extends SleeveWorkClass {
}
APICopy() {
return { type: SleeveWorkType.SYNCHRO as const };
return {
type: SleeveWorkType.SYNCHRO as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
+7 -2
View File
@@ -12,6 +12,7 @@ import { SleeveSynchroWork } from "./SleeveSynchroWork";
import { SleeveBladeburnerWork } from "./SleeveBladeburnerWork";
import { SleeveInfiltrateWork } from "./SleeveInfiltrateWork";
import { SleeveSupportWork } from "./SleeveSupportWork";
import { BaseWork } from "../../../Work/Work";
export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult = 1): void => {
applyWorkStatsExp(sleeve, shockedStats, mult);
@@ -23,13 +24,17 @@ export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult =
Player.sleeves.forEach((s) => s !== sleeve && applyWorkStatsExp(s, shockedStats, mult * sync * s.shockBonus()));
};
export abstract class SleeveWorkClass {
export abstract class SleeveBaseWork extends BaseWork {
abstract type: SleeveWorkType;
abstract process(sleeve: Sleeve, cycles: number): void;
abstract APICopy(sleeve: Sleeve): SleeveTask;
abstract toJSON(): IReviverValue;
/**
* Child classes that override this function must call `this.resolveNextCompletion()` when appropriate to ensure the
* completion promise is resolved.
*/
finish(): void {
/* left for children to implement */
this.resolveNextCompletion();
}
}