mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-08 08:43:53 +01:00
MISC: Refactor BLADEBURNER Identifier Lookup (#1646)
This commit is contained in:
parent
f39402e8be
commit
cde5e3f6ae
@ -1,9 +1,10 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { Availability, ActionIdentifier } from "../Types";
|
||||
import type { ActionIdFor, Availability } from "../Types";
|
||||
|
||||
import { BladeburnerActionType, BladeburnerBlackOpName } from "@enums";
|
||||
import { ActionClass, ActionParams } from "./Action";
|
||||
import { operationSkillSuccessBonus, operationTeamSuccessBonus } from "./Operation";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
|
||||
interface BlackOpParams {
|
||||
name: BladeburnerBlackOpName;
|
||||
@ -12,13 +13,22 @@ interface BlackOpParams {
|
||||
}
|
||||
|
||||
export class BlackOperation extends ActionClass {
|
||||
type: BladeburnerActionType.BlackOp = BladeburnerActionType.BlackOp;
|
||||
name: BladeburnerBlackOpName;
|
||||
readonly type: BladeburnerActionType.BlackOp = BladeburnerActionType.BlackOp;
|
||||
readonly name: BladeburnerBlackOpName;
|
||||
n: number;
|
||||
reqdRank: number;
|
||||
teamCount = 0;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
|
||||
get id() {
|
||||
return BlackOperation.createId(this.name);
|
||||
}
|
||||
|
||||
static createId(name: BladeburnerBlackOpName): ActionIdFor<BlackOperation> {
|
||||
return { type: BladeburnerActionType.BlackOp, name };
|
||||
}
|
||||
|
||||
static IsAcceptedName(name: unknown): name is BladeburnerBlackOpName {
|
||||
return getEnumHelper("BladeburnerBlackOpName").isMember(name);
|
||||
}
|
||||
|
||||
constructor(params: ActionParams & BlackOpParams) {
|
||||
|
@ -1,20 +1,30 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { ActionIdentifier } from "../Types";
|
||||
import type { ActionIdFor } from "../Types";
|
||||
|
||||
import { Generic_fromJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
|
||||
import { BladeburnerActionType, BladeburnerContractName, BladeburnerMultName } from "../Enums";
|
||||
import { LevelableActionClass, LevelableActionParams } from "./LevelableAction";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
|
||||
export class Contract extends LevelableActionClass {
|
||||
type: BladeburnerActionType.Contract = BladeburnerActionType.Contract;
|
||||
name: BladeburnerContractName = BladeburnerContractName.Tracking;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
readonly type: BladeburnerActionType.Contract = BladeburnerActionType.Contract;
|
||||
readonly name: BladeburnerContractName;
|
||||
|
||||
get id() {
|
||||
return Contract.createId(this.name);
|
||||
}
|
||||
|
||||
static IsAcceptedName(name: unknown): name is BladeburnerContractName {
|
||||
return getEnumHelper("BladeburnerContractName").isMember(name);
|
||||
}
|
||||
|
||||
static createId(name: BladeburnerContractName): ActionIdFor<Contract> {
|
||||
return { type: BladeburnerActionType.Contract, name };
|
||||
}
|
||||
|
||||
constructor(params: (LevelableActionParams & { name: BladeburnerContractName }) | null = null) {
|
||||
super(params);
|
||||
if (params) this.name = params.name;
|
||||
this.name = params?.name ?? BladeburnerContractName.Tracking;
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import type { Person } from "../../PersonObjects/Person";
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { ActionIdentifier } from "../Types";
|
||||
import type { ActionIdFor } from "../Types";
|
||||
|
||||
import { BladeburnerActionType, BladeburnerGeneralActionName } from "@enums";
|
||||
import { ActionClass, ActionParams } from "./Action";
|
||||
import { clampNumber } from "../../utils/helpers/clampNumber";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
|
||||
type GeneralActionParams = ActionParams & {
|
||||
name: BladeburnerGeneralActionName;
|
||||
@ -13,10 +14,19 @@ type GeneralActionParams = ActionParams & {
|
||||
};
|
||||
|
||||
export class GeneralAction extends ActionClass {
|
||||
type: BladeburnerActionType.General = BladeburnerActionType.General;
|
||||
name: BladeburnerGeneralActionName;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
readonly type: BladeburnerActionType.General = BladeburnerActionType.General;
|
||||
readonly name: BladeburnerGeneralActionName;
|
||||
|
||||
get id() {
|
||||
return GeneralAction.createId(this.name);
|
||||
}
|
||||
|
||||
static IsAcceptedName(name: unknown): name is BladeburnerGeneralActionName {
|
||||
return getEnumHelper("BladeburnerGeneralActionName").isMember(name);
|
||||
}
|
||||
|
||||
static createId(name: BladeburnerGeneralActionName): ActionIdFor<GeneralAction> {
|
||||
return { type: BladeburnerActionType.General, name };
|
||||
}
|
||||
|
||||
constructor(params: GeneralActionParams) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { Person } from "../../PersonObjects/Person";
|
||||
import type { BlackOperation } from "./BlackOperation";
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { Availability, ActionIdentifier, SuccessChanceParams } from "../Types";
|
||||
import type { ActionIdFor, Availability, SuccessChanceParams } from "../Types";
|
||||
|
||||
import { BladeburnerActionType, BladeburnerMultName, BladeburnerOperationName } from "@enums";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
@ -9,6 +9,7 @@ import { ActionClass } from "./Action";
|
||||
import { Generic_fromJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
|
||||
import { LevelableActionClass, LevelableActionParams } from "./LevelableAction";
|
||||
import { clampInteger } from "../../utils/helpers/clampNumber";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
|
||||
export interface OperationParams extends LevelableActionParams {
|
||||
name: BladeburnerOperationName;
|
||||
@ -16,18 +17,26 @@ export interface OperationParams extends LevelableActionParams {
|
||||
}
|
||||
|
||||
export class Operation extends LevelableActionClass {
|
||||
type: BladeburnerActionType.Operation = BladeburnerActionType.Operation;
|
||||
name = BladeburnerOperationName.Investigation;
|
||||
readonly type: BladeburnerActionType.Operation = BladeburnerActionType.Operation;
|
||||
readonly name: BladeburnerOperationName;
|
||||
teamCount = 0;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
|
||||
get id() {
|
||||
return Operation.createId(this.name);
|
||||
}
|
||||
|
||||
static IsAcceptedName(name: unknown): name is BladeburnerOperationName {
|
||||
return getEnumHelper("BladeburnerOperationName").isMember(name);
|
||||
}
|
||||
|
||||
static createId(name: BladeburnerOperationName): ActionIdFor<Operation> {
|
||||
return { type: BladeburnerActionType.Operation, name };
|
||||
}
|
||||
|
||||
constructor(params: OperationParams | null = null) {
|
||||
super(params);
|
||||
if (!params) return;
|
||||
this.name = params.name;
|
||||
if (params.getAvailability) this.getAvailability = params.getAvailability;
|
||||
this.name = params?.name ?? BladeburnerOperationName.Investigation;
|
||||
if (params && params.getAvailability) this.getAvailability = params.getAvailability;
|
||||
}
|
||||
|
||||
// These functions are shared between operations and blackops, so they are defined outside of Operation
|
||||
@ -45,6 +54,7 @@ export class Operation extends LevelableActionClass {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
getSuccessChance(inst: Bladeburner, person: Person, params: SuccessChanceParams) {
|
||||
if (this.name === BladeburnerOperationName.Raid && inst.getCurrentCity().comms <= 0) {
|
||||
return 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { PromisePair } from "../Types/Promises";
|
||||
import type { BlackOperation, Contract, GeneralAction, Operation } from "./Actions";
|
||||
import type { ActionIdentifier, Action, Attempt } from "./Types";
|
||||
import type { Action, ActionIdFor, ActionIdentifier, Attempt } from "./Types";
|
||||
import type { Person } from "../PersonObjects/Person";
|
||||
import type { Skills as PersonSkills } from "../PersonObjects/Skills";
|
||||
|
||||
@ -49,6 +49,7 @@ import { BlackOperations } from "./data/BlackOperations";
|
||||
import { GeneralActions } from "./data/GeneralActions";
|
||||
import { PlayerObject } from "../PersonObjects/Player/PlayerObject";
|
||||
import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
|
||||
import { autoCompleteTypeShorthand } from "./utils/terminalShorthands";
|
||||
|
||||
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
|
||||
|
||||
@ -433,61 +434,53 @@ export class Bladeburner {
|
||||
highLow = true;
|
||||
}
|
||||
|
||||
let actionId: ActionIdentifier;
|
||||
switch (type) {
|
||||
case "stamina":
|
||||
// For stamina, the "name" variable is actually the stamina threshold
|
||||
if (isNaN(parseFloat(name))) {
|
||||
this.postToConsole("Invalid value specified for stamina threshold (must be numeric): " + name);
|
||||
if (type === "stamina") {
|
||||
// For stamina, the "name" variable is actually the stamina threshold
|
||||
if (isNaN(parseFloat(name))) {
|
||||
this.postToConsole("Invalid value specified for stamina threshold (must be numeric): " + name);
|
||||
} else {
|
||||
if (highLow) {
|
||||
this.automateThreshHigh = Number(name);
|
||||
} else {
|
||||
if (highLow) {
|
||||
this.automateThreshHigh = Number(name);
|
||||
} else {
|
||||
this.automateThreshLow = Number(name);
|
||||
}
|
||||
this.log("Automate (" + (highLow ? "HIGH" : "LOW") + ") stamina threshold set to " + name);
|
||||
this.automateThreshLow = Number(name);
|
||||
}
|
||||
return;
|
||||
case "general":
|
||||
case "gen": {
|
||||
if (!getEnumHelper("BladeburnerGeneralActionName").isMember(name)) {
|
||||
this.log("Automate (" + (highLow ? "HIGH" : "LOW") + ") stamina threshold set to " + name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const actionId = autoCompleteTypeShorthand(type, name);
|
||||
|
||||
if (actionId === null) {
|
||||
switch (type) {
|
||||
case "general":
|
||||
case "gen": {
|
||||
this.postToConsole("Invalid General Action name specified: " + name);
|
||||
return;
|
||||
}
|
||||
actionId = { type: BladeburnerActionType.General, name };
|
||||
break;
|
||||
}
|
||||
case "contract":
|
||||
case "contracts": {
|
||||
if (!getEnumHelper("BladeburnerContractName").isMember(name)) {
|
||||
case "contract":
|
||||
case "contracts": {
|
||||
this.postToConsole("Invalid Contract name specified: " + name);
|
||||
return;
|
||||
}
|
||||
actionId = { type: BladeburnerActionType.Contract, name };
|
||||
break;
|
||||
}
|
||||
case "ops":
|
||||
case "op":
|
||||
case "operations":
|
||||
case "operation":
|
||||
if (!getEnumHelper("BladeburnerOperationName").isMember(name)) {
|
||||
case "ops":
|
||||
case "op":
|
||||
case "operations":
|
||||
case "operation":
|
||||
this.postToConsole("Invalid Operation name specified: " + name);
|
||||
return;
|
||||
}
|
||||
actionId = { type: BladeburnerActionType.Operation, name };
|
||||
break;
|
||||
default:
|
||||
this.postToConsole("Invalid use of automate command.");
|
||||
return;
|
||||
default:
|
||||
this.postToConsole("Invalid use of automate command.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (highLow) {
|
||||
this.automateActionHigh = actionId;
|
||||
} else {
|
||||
this.automateActionLow = actionId;
|
||||
}
|
||||
this.log("Automate (" + (highLow ? "HIGH" : "LOW") + ") action set to " + name);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1406,10 +1399,10 @@ export class Bladeburner {
|
||||
}
|
||||
|
||||
/** Return the action based on an ActionIdentifier, discriminating types when possible */
|
||||
getActionObject(actionId: ActionIdentifier & { type: BladeburnerActionType.BlackOp }): BlackOperation;
|
||||
getActionObject(actionId: ActionIdentifier & { type: BladeburnerActionType.Operation }): Operation;
|
||||
getActionObject(actionId: ActionIdentifier & { type: BladeburnerActionType.Contract }): Contract;
|
||||
getActionObject(actionId: ActionIdentifier & { type: BladeburnerActionType.General }): GeneralAction;
|
||||
getActionObject(actionId: ActionIdFor<BlackOperation>): BlackOperation;
|
||||
getActionObject(actionId: ActionIdFor<Operation>): Operation;
|
||||
getActionObject(actionId: ActionIdFor<Contract>): Contract;
|
||||
getActionObject(actionId: ActionIdFor<GeneralAction>): GeneralAction;
|
||||
getActionObject(actionId: ActionIdentifier): Action;
|
||||
getActionObject(actionId: ActionIdentifier): Action {
|
||||
switch (actionId.type) {
|
||||
@ -1427,36 +1420,8 @@ export class Bladeburner {
|
||||
/** Fuzzy matching for action identifiers. Should be removed in 3.0 */
|
||||
getActionFromTypeAndName(type: string, name: string): Action | null {
|
||||
if (!type || !name) return null;
|
||||
const convertedType = type.toLowerCase().trim();
|
||||
switch (convertedType) {
|
||||
case "contract":
|
||||
case "contracts":
|
||||
case "contr":
|
||||
if (!getEnumHelper("BladeburnerContractName").isMember(name)) return null;
|
||||
return this.contracts[name];
|
||||
case "operation":
|
||||
case "operations":
|
||||
case "op":
|
||||
case "ops":
|
||||
if (!getEnumHelper("BladeburnerOperationName").isMember(name)) return null;
|
||||
return this.operations[name];
|
||||
case "blackoperation":
|
||||
case "black operation":
|
||||
case "black operations":
|
||||
case "black op":
|
||||
case "black ops":
|
||||
case "blackop":
|
||||
case "blackops":
|
||||
if (!getEnumHelper("BladeburnerBlackOpName").isMember(name)) return null;
|
||||
return BlackOperations[name];
|
||||
case "general":
|
||||
case "general action":
|
||||
case "gen": {
|
||||
if (!getEnumHelper("BladeburnerGeneralActionName").isMember(name)) return null;
|
||||
return GeneralActions[name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
const id = autoCompleteTypeShorthand(type, name);
|
||||
return id ? this.getActionObject(id) : null;
|
||||
}
|
||||
|
||||
static keysToSave = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers"] });
|
||||
|
@ -1,11 +1,4 @@
|
||||
import type { BlackOperation, Contract, GeneralAction, Operation } from "./Actions";
|
||||
import type {
|
||||
BladeburnerActionType,
|
||||
BladeburnerBlackOpName,
|
||||
BladeburnerContractName,
|
||||
BladeburnerOperationName,
|
||||
BladeburnerGeneralActionName,
|
||||
} from "@enums";
|
||||
|
||||
export interface SuccessChanceParams {
|
||||
/** Whether the success chance should be based on estimated statistics */
|
||||
@ -21,11 +14,12 @@ type AttemptFailure = { success?: undefined; message: string };
|
||||
export type Attempt<T extends object = object> = AttemptSuccess<T> | AttemptFailure;
|
||||
|
||||
export type Action = Contract | Operation | BlackOperation | GeneralAction;
|
||||
export type ActionIdFor<ActionType extends Action> = Pick<ActionType, "type" | "name">;
|
||||
|
||||
export type ActionIdentifier =
|
||||
| { type: BladeburnerActionType.BlackOp; name: BladeburnerBlackOpName }
|
||||
| { type: BladeburnerActionType.Contract; name: BladeburnerContractName }
|
||||
| { type: BladeburnerActionType.Operation; name: BladeburnerOperationName }
|
||||
| { type: BladeburnerActionType.General; name: BladeburnerGeneralActionName };
|
||||
| ActionIdFor<Contract>
|
||||
| ActionIdFor<Operation>
|
||||
| ActionIdFor<BlackOperation>
|
||||
| ActionIdFor<GeneralAction>;
|
||||
|
||||
export type LevelableAction = Contract | Operation;
|
||||
|
39
src/Bladeburner/utils/terminalShorthands.ts
Normal file
39
src/Bladeburner/utils/terminalShorthands.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { ActionIdentifier } from "../Types";
|
||||
import { BladeburnerActionType } from "@enums";
|
||||
import { BlackOperation, Contract, GeneralAction, Operation } from "../Actions";
|
||||
|
||||
const resolveActionIdentifierFromName = (name: unknown): ActionIdentifier | null => {
|
||||
if (Contract.IsAcceptedName(name)) return Contract.createId(name);
|
||||
if (BlackOperation.IsAcceptedName(name)) return BlackOperation.createId(name);
|
||||
if (GeneralAction.IsAcceptedName(name)) return GeneralAction.createId(name);
|
||||
if (Operation.IsAcceptedName(name)) return Operation.createId(name);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/** Resolve identifier by auto completing from a fuzzy type match, e.g. "blackops" */
|
||||
export function autoCompleteTypeShorthand(typeShorthand: string, name: string): ActionIdentifier | null {
|
||||
let id = resolveActionIdentifierFromName(name);
|
||||
|
||||
if (id && !TerminalShorthands[id.type].includes(typeShorthand.toLowerCase().trim())) {
|
||||
id = null;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/** These shorthands match those documented in the BB Terminal Help */
|
||||
export const TerminalShorthands = {
|
||||
[BladeburnerActionType.Contract]: <string[]>["contract", "contracts", "contr"],
|
||||
[BladeburnerActionType.Operation]: <string[]>["operation", "operations", "op", "ops"],
|
||||
[BladeburnerActionType.BlackOp]: <string[]>[
|
||||
"blackoperation",
|
||||
"black operation",
|
||||
"black operations",
|
||||
"black op",
|
||||
"black ops",
|
||||
"blackop",
|
||||
"blackops",
|
||||
],
|
||||
[BladeburnerActionType.General]: <string[]>["general", "general action", "gen"],
|
||||
} as const;
|
@ -70,6 +70,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
|
||||
get nextCompletion(): Promise<void> {
|
||||
if (!this.nextCompletionPair.promise)
|
||||
this.nextCompletionPair.promise = new Promise((r) => (this.nextCompletionPair.resolve = r));
|
||||
|
@ -12,6 +12,7 @@ export interface IReviverValue<T = any> {
|
||||
ctor: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
function isReviverValue(value: unknown): value is IReviverValue {
|
||||
return (
|
||||
typeof value === "object" && value !== null && "ctor" in value && typeof value.ctor === "string" && "data" in value
|
||||
|
38
test/jest/Netscript/Bladeburner/TerminalMatching.test.ts
Normal file
38
test/jest/Netscript/Bladeburner/TerminalMatching.test.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { autoCompleteTypeShorthand, TerminalShorthands } from "../../../../src/Bladeburner/utils/terminalShorthands";
|
||||
import {
|
||||
BladeburnerActionType,
|
||||
BladeburnerBlackOpName,
|
||||
BladeburnerContractName,
|
||||
BladeburnerGeneralActionName,
|
||||
BladeburnerOperationName,
|
||||
} from "@enums";
|
||||
|
||||
const ShorthandCases = (type: keyof typeof TerminalShorthands) => <string[][]>TerminalShorthands[type].map(Array);
|
||||
|
||||
describe("Bladeburner Actions", () => {
|
||||
const EXAMPLES = [
|
||||
[BladeburnerActionType.General, BladeburnerGeneralActionName.Diplomacy],
|
||||
[BladeburnerActionType.BlackOp, BladeburnerBlackOpName.OperationTyphoon],
|
||||
[BladeburnerActionType.Contract, BladeburnerContractName.BountyHunter],
|
||||
[BladeburnerActionType.Operation, BladeburnerOperationName.Assassination],
|
||||
] as const;
|
||||
|
||||
describe("May be described with shorthands", () => {
|
||||
describe.each(EXAMPLES)("Type: %s", (type, name) => {
|
||||
it.each(ShorthandCases(type))("%s", (shorthand) => {
|
||||
const action = autoCompleteTypeShorthand(shorthand, name);
|
||||
expect(action).toMatchObject({ type, name });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Does not match for existing action where type differs", () => {
|
||||
const action = autoCompleteTypeShorthand(BladeburnerActionType.Contract, BladeburnerOperationName.Assassination);
|
||||
expect(action).toBeNull();
|
||||
});
|
||||
|
||||
it("Does not match for undocumented shorthands", () => {
|
||||
const action = autoCompleteTypeShorthand("blackoperations", BladeburnerOperationName.Assassination);
|
||||
expect(action).toBeNull();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user