mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-05 07:07:50 +02:00
BLADEBURNER: Store BlackOp team count in save data (#2675)
This commit is contained in:
@@ -6,6 +6,8 @@ import { ActionClass, ActionParams } from "./Action";
|
||||
import { operationSkillSuccessBonus, operationTeamSuccessBonus } from "./Operation";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
import type { TeamActionWithCasualties } from "./TeamCasualties";
|
||||
import { constructorsForReviver, Generic_fromJSON, type IReviverValue } from "../../utils/JSONReviver";
|
||||
import { clampInteger } from "../../utils/helpers/clampNumber";
|
||||
|
||||
interface BlackOpParams {
|
||||
name: BladeburnerBlackOpName;
|
||||
@@ -32,11 +34,11 @@ export class BlackOperation extends ActionClass implements TeamActionWithCasualt
|
||||
return getEnumHelper("BladeburnerBlackOpName").isMember(name);
|
||||
}
|
||||
|
||||
constructor(params: ActionParams & BlackOpParams) {
|
||||
constructor(params: (ActionParams & BlackOpParams) | null = null) {
|
||||
super(params);
|
||||
this.name = params.name;
|
||||
this.reqdRank = params.reqdRank;
|
||||
this.n = params.n;
|
||||
this.name = params?.name ?? BladeburnerBlackOpName.OperationTyphoon;
|
||||
this.reqdRank = params?.reqdRank ?? 0;
|
||||
this.n = params?.n ?? 0;
|
||||
}
|
||||
|
||||
getAvailability(bladeburner: Bladeburner): Availability {
|
||||
@@ -65,4 +67,23 @@ export class BlackOperation extends ActionClass implements TeamActionWithCasualt
|
||||
getTeamSuccessBonus = operationTeamSuccessBonus;
|
||||
|
||||
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return {
|
||||
ctor: "BlackOperation",
|
||||
data: {
|
||||
teamCount: this.teamCount,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
loadData(loadedObject: BlackOperation): void {
|
||||
this.teamCount = clampInteger(loadedObject.teamCount, 0);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): BlackOperation {
|
||||
return Generic_fromJSON(BlackOperation, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.BlackOperation = BlackOperation;
|
||||
|
||||
@@ -47,7 +47,7 @@ import { createContracts, loadContractsData } from "./data/Contracts";
|
||||
import { createOperations, loadOperationsData } from "./data/Operations";
|
||||
import { clampInteger, clampNumber } from "../utils/helpers/clampNumber";
|
||||
import { parseCommand } from "../Terminal/Parser";
|
||||
import { BlackOperations } from "./data/BlackOperations";
|
||||
import { createBlackOperations, loadBlackOperationsData } from "./data/BlackOperations";
|
||||
import { GeneralActions } from "./data/GeneralActions";
|
||||
import { PlayerObject } from "../PersonObjects/Player/PlayerObject";
|
||||
import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
|
||||
@@ -92,7 +92,7 @@ export class Bladeburner implements OperationTeam {
|
||||
}
|
||||
this._teamSize = newSize;
|
||||
// Reduce teamCount of actions if it's greater than the team size.
|
||||
for (const action of [...Object.values(this.operations), ...Object.values(BlackOperations)]) {
|
||||
for (const action of [...Object.values(this.operations), ...Object.values(this.blackOperations)]) {
|
||||
action.teamCount = Math.min(action.teamCount, this._teamSize);
|
||||
}
|
||||
}
|
||||
@@ -120,9 +120,13 @@ export class Bladeburner implements OperationTeam {
|
||||
staminaBonus = 0;
|
||||
maxStamina = 1;
|
||||
stamina = 1;
|
||||
// Contracts and operations are stored on the Bladeburner object even though they are global so that they can utilize save/load of the main bladeburner object
|
||||
// Contracts, operations and blackOps are stored on the Bladeburner object even though they are global so that they
|
||||
// can utilize save/load of the main bladeburner object
|
||||
contracts: Record<BladeburnerContractName, Contract>;
|
||||
operations: Record<BladeburnerOperationName, Operation>;
|
||||
blackOperations: Record<BladeburnerBlackOpName, BlackOperation>;
|
||||
// Array for quick lookup by BlackOp number
|
||||
blackOperationArray: BlackOperation[];
|
||||
numBlackOpsComplete = 0;
|
||||
logging = {
|
||||
general: true,
|
||||
@@ -143,6 +147,11 @@ export class Bladeburner implements OperationTeam {
|
||||
constructor() {
|
||||
this.contracts = createContracts();
|
||||
this.operations = createOperations();
|
||||
this.blackOperations = createBlackOperations();
|
||||
this.blackOperationArray = Object.values(this.blackOperations).sort((a, b) => (a.n < b.n ? -1 : 1));
|
||||
if (!this.blackOperationArray.every((blackOp, i) => blackOp.n === i)) {
|
||||
throw new Error("blackOperationArray is not initialized with correct indices");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization code that is dependent on Player is here instead of in the constructor
|
||||
@@ -1431,7 +1440,7 @@ export class Bladeburner implements OperationTeam {
|
||||
case BladeburnerActionType.Operation:
|
||||
return this.operations[actionId.name];
|
||||
case BladeburnerActionType.BlackOp:
|
||||
return BlackOperations[actionId.name];
|
||||
return this.blackOperations[actionId.name];
|
||||
case BladeburnerActionType.General:
|
||||
return GeneralActions[actionId.name];
|
||||
}
|
||||
@@ -1450,7 +1459,7 @@ export class Bladeburner implements OperationTeam {
|
||||
case BladeburnerActionType.Operation:
|
||||
return this.operations[name as BladeburnerOperationName];
|
||||
case BladeburnerActionType.BlackOp:
|
||||
return BlackOperations[name as BladeburnerBlackOpName];
|
||||
return this.blackOperations[name as BladeburnerBlackOpName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1461,9 +1470,11 @@ export class Bladeburner implements OperationTeam {
|
||||
return id ? this.getActionObject(id) : null;
|
||||
}
|
||||
|
||||
static keysToSave = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers"] });
|
||||
static keysToSave = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers", "blackOperationArray"] });
|
||||
// Don't load contracts or operations because of the special loading method they use, see fromJSON
|
||||
static keysToLoad = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers", "contracts", "operations"] });
|
||||
static keysToLoad = getKeyList(Bladeburner, {
|
||||
removedKeys: ["skillMultipliers", "contracts", "operations", "blackOperations", "blackOperationArray"],
|
||||
});
|
||||
|
||||
/** Serialize the current object to a JSON save state. */
|
||||
toJSON(): IReviverValue {
|
||||
@@ -1473,9 +1484,10 @@ export class Bladeburner implements OperationTeam {
|
||||
/** Initializes a Bladeburner object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): Bladeburner {
|
||||
assertObject(value.data);
|
||||
// operations and contracts are not loaded directly from the save, we load them in using a different method
|
||||
// Contracts, operations, and black ops are not loaded directly from the save; they are loaded via a different method.
|
||||
const contractsData = value.data.contracts;
|
||||
const operationsData = value.data.operations;
|
||||
const blackOperationsData = value.data.blackOperations;
|
||||
const bladeburner = Generic_fromJSON(Bladeburner, value.data, Bladeburner.keysToLoad);
|
||||
|
||||
/**
|
||||
@@ -1496,10 +1508,11 @@ export class Bladeburner implements OperationTeam {
|
||||
bladeburner.automateActionLow = loadActionIdentifier(bladeburner.automateActionLow);
|
||||
}
|
||||
}
|
||||
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations
|
||||
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations/blackOps
|
||||
// even from save data that is missing a lot of static info about the objects.
|
||||
loadContractsData(contractsData, bladeburner.contracts);
|
||||
loadOperationsData(operationsData, bladeburner.operations);
|
||||
loadBlackOperationsData(blackOperationsData, bladeburner.blackOperations);
|
||||
// Regenerate skill multiplier data, which is not included in savedata
|
||||
bladeburner.updateSkillMultipliers();
|
||||
// If stamina or maxStamina is invalid, we set both of them to 1 and recalculate them.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -113,7 +113,9 @@ export function createContracts(): Record<BladeburnerContractName, Contract> {
|
||||
export function loadContractsData(data: unknown, contracts: Record<BladeburnerContractName, Contract>) {
|
||||
// loading data as "unknown" and typechecking it down is probably not necessary
|
||||
// but this will prevent crashes even with malformed savedata
|
||||
if (!data || typeof data !== "object") return;
|
||||
if (data == null || typeof data !== "object" || Array.isArray(data)) {
|
||||
return;
|
||||
}
|
||||
assertLoadingType<Record<BladeburnerContractName, unknown>>(data);
|
||||
for (const contractName of Object.values(BladeburnerContractName)) {
|
||||
const loadedContract = data[contractName];
|
||||
|
||||
@@ -230,7 +230,9 @@ export function createOperations(): Record<BladeburnerOperationName, Operation>
|
||||
export function loadOperationsData(data: unknown, operations: Record<BladeburnerOperationName, Operation>) {
|
||||
// loading data as "unknown" and typechecking it down is probably not necessary
|
||||
// but this will prevent crashes even with malformed savedata
|
||||
if (!data || typeof data !== "object") return;
|
||||
if (data == null || typeof data !== "object" || Array.isArray(data)) {
|
||||
return;
|
||||
}
|
||||
assertLoadingType<Record<BladeburnerOperationName, unknown>>(data);
|
||||
for (const operationName of Object.values(BladeburnerOperationName)) {
|
||||
const loadedOperation = data[operationName];
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlackOpElem } from "./BlackOpElem";
|
||||
import { Router } from "../../ui/GameRoot";
|
||||
import { Page } from "../../ui/Router";
|
||||
import { CorruptibleText } from "../../ui/React/CorruptibleText";
|
||||
import { blackOpsArray } from "../data/BlackOperations";
|
||||
import { numberOfBlackOperations } from "../data/BlackOperations";
|
||||
import { finishBitNode } from "../../BitNode/BitNodeUtils";
|
||||
import { Player } from "@player";
|
||||
|
||||
@@ -16,7 +16,7 @@ interface BlackOpPageProps {
|
||||
}
|
||||
|
||||
export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactElement {
|
||||
const blackOperations = blackOpsArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
|
||||
const blackOperations = bladeburner.blackOperationArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -36,11 +36,11 @@ export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactEleme
|
||||
Unaffected by Charisma.
|
||||
</Typography>
|
||||
|
||||
{bladeburner.numBlackOpsComplete >= blackOpsArray.length && (
|
||||
{bladeburner.numBlackOpsComplete >= numberOfBlackOperations && (
|
||||
<Button
|
||||
sx={{ my: 1, p: 1 }}
|
||||
onClick={() => {
|
||||
if (!Player.bladeburner || Player.bladeburner.numBlackOpsComplete < blackOpsArray.length) {
|
||||
if (!Player.bladeburner || Player.bladeburner.numBlackOpsComplete < numberOfBlackOperations) {
|
||||
return;
|
||||
}
|
||||
finishBitNode();
|
||||
|
||||
@@ -16,7 +16,7 @@ import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import { getEnumHelper } from "../utils/EnumHelper";
|
||||
import { Skills } from "../Bladeburner/data/Skills";
|
||||
import { assertStringWithNSContext } from "../Netscript/TypeAssertion";
|
||||
import { BlackOperations, blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
import { numberOfBlackOperations } from "../Bladeburner/data/BlackOperations";
|
||||
import { checkSleeveAPIAccess, checkSleeveNumber } from "../NetscriptFunctions/Sleeve";
|
||||
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
|
||||
import {
|
||||
@@ -81,20 +81,21 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
||||
return Object.values(BladeburnerOperationName);
|
||||
},
|
||||
getBlackOpNames: (ctx) => () => {
|
||||
getBladeburner(ctx);
|
||||
const bladeburner = getBladeburner(ctx);
|
||||
// Ensures they are sent in the correct order
|
||||
return blackOpsArray.map((blackOp) => blackOp.name);
|
||||
return bladeburner.blackOperationArray.map((blackOp) => blackOp.name);
|
||||
},
|
||||
getNextBlackOp: (ctx) => () => {
|
||||
const bladeburner = getBladeburner(ctx);
|
||||
if (bladeburner.numBlackOpsComplete >= blackOpsArray.length) return null;
|
||||
const blackOp = blackOpsArray[bladeburner.numBlackOpsComplete];
|
||||
if (bladeburner.numBlackOpsComplete >= numberOfBlackOperations) return null;
|
||||
const blackOp = bladeburner.blackOperationArray[bladeburner.numBlackOpsComplete];
|
||||
return { name: blackOp.name, rank: blackOp.reqdRank };
|
||||
},
|
||||
getBlackOpRank: (ctx) => (_blackOpName) => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const blackOpName = getEnumHelper("BladeburnerBlackOpName").nsGetMember(ctx, _blackOpName);
|
||||
return BlackOperations[blackOpName].reqdRank;
|
||||
const bladeburner = getBladeburner(ctx);
|
||||
return bladeburner.blackOperations[blackOpName].reqdRank;
|
||||
},
|
||||
getGeneralActionNames: (ctx) => () => {
|
||||
getBladeburner(ctx);
|
||||
|
||||
@@ -45,7 +45,7 @@ import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
|
||||
import { getRecordEntries } from "../Types/Record";
|
||||
import { JobTracks } from "../Company/data/JobTracks";
|
||||
import { ServerConstants } from "../Server/data/Constants";
|
||||
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
import { numberOfBlackOperations } from "../Bladeburner/data/BlackOperations";
|
||||
import { calculateEffectiveRequiredReputation } from "../Company/utils";
|
||||
import { addRepToFavor } from "../Faction/formulas/favor";
|
||||
import { validBitNodes } from "../BitNode/Constants";
|
||||
@@ -1176,7 +1176,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
if (!Player.bladeburner) {
|
||||
return false;
|
||||
}
|
||||
return Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
|
||||
return Player.bladeburner.numBlackOpsComplete >= numberOfBlackOperations;
|
||||
};
|
||||
|
||||
if (!hackingRequirements() && !bladeburnerRequirements()) {
|
||||
|
||||
Reference in New Issue
Block a user