From 47153bd31cc76cd276b84f4f1713502c4118847e Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Wed, 21 May 2025 22:38:39 +0700 Subject: [PATCH] MISC: Print logs when ns.hacknet.spendHashes fails and update param type of APIs using hash upgrade (#2145) --- .../bitburner.hacknet.gethashupgradelevel.md | 4 +- markdown/bitburner.hacknet.gethashupgrades.md | 4 +- markdown/bitburner.hacknet.hashcost.md | 4 +- markdown/bitburner.hacknet.spendhashes.md | 4 +- .../bitburner.hacknetserverhashupgrade.md | 22 ++ ....hacknetserversformulas.hashupgradecost.md | 4 +- markdown/bitburner.md | 1 + src/Enums.ts | 1 + src/Hacknet/Enums.ts | 13 + src/Hacknet/HacknetHelpers.tsx | 263 +++++++++--------- src/Hacknet/HashManager.ts | 23 +- src/Hacknet/HashUpgrade.ts | 5 +- src/Hacknet/HashUpgrades.ts | 11 +- src/Hacknet/data/HashUpgradesMetadata.tsx | 23 +- src/Hacknet/ui/HacknetUpgradeElem.tsx | 24 +- src/Hacknet/ui/HashUpgradeModal.tsx | 3 +- src/NetscriptFunctions/Formulas.ts | 2 +- src/NetscriptFunctions/Hacknet.ts | 11 +- src/ScriptEditor/NetscriptDefinitions.d.ts | 27 +- 19 files changed, 252 insertions(+), 197 deletions(-) create mode 100644 markdown/bitburner.hacknetserverhashupgrade.md create mode 100644 src/Hacknet/Enums.ts diff --git a/markdown/bitburner.hacknet.gethashupgradelevel.md b/markdown/bitburner.hacknet.gethashupgradelevel.md index 8bf459d69..9ce756f93 100644 --- a/markdown/bitburner.hacknet.gethashupgradelevel.md +++ b/markdown/bitburner.hacknet.gethashupgradelevel.md @@ -9,14 +9,14 @@ Get the level of a hash upgrade. **Signature:** ```typescript -getHashUpgradeLevel(upgName: string): number; +getHashUpgradeLevel(upgName: HacknetServerHashUpgrade): number; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| upgName | string | | +| upgName | [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) | | **Returns:** diff --git a/markdown/bitburner.hacknet.gethashupgrades.md b/markdown/bitburner.hacknet.gethashupgrades.md index d309531fa..94125267a 100644 --- a/markdown/bitburner.hacknet.gethashupgrades.md +++ b/markdown/bitburner.hacknet.gethashupgrades.md @@ -9,11 +9,11 @@ Get the list of hash upgrades **Signature:** ```typescript -getHashUpgrades(): string[]; +getHashUpgrades(): HacknetServerHashUpgrade[]; ``` **Returns:** -string\[\] +[HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md)\[\] An array containing the available upgrades diff --git a/markdown/bitburner.hacknet.hashcost.md b/markdown/bitburner.hacknet.hashcost.md index 569768727..118c28221 100644 --- a/markdown/bitburner.hacknet.hashcost.md +++ b/markdown/bitburner.hacknet.hashcost.md @@ -9,14 +9,14 @@ Get the cost of a hash upgrade. **Signature:** ```typescript -hashCost(upgName: string, count?: number): number; +hashCost(upgName: HacknetServerHashUpgrade, count?: number): number; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| upgName | string | Name of the upgrade of Hacknet Node. | +| upgName | [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) | Name of the upgrade using hash of Hacknet Server. | | count | number | _(Optional)_ Number of upgrades to buy at once. Defaults to 1 if not specified. | **Returns:** diff --git a/markdown/bitburner.hacknet.spendhashes.md b/markdown/bitburner.hacknet.spendhashes.md index 855e1c31f..029b9aff6 100644 --- a/markdown/bitburner.hacknet.spendhashes.md +++ b/markdown/bitburner.hacknet.spendhashes.md @@ -9,14 +9,14 @@ Purchase a hash upgrade. **Signature:** ```typescript -spendHashes(upgName: string, upgTarget?: string, count?: number): boolean; +spendHashes(upgName: HacknetServerHashUpgrade, upgTarget?: string, count?: number): boolean; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| upgName | string | Name of the upgrade of Hacknet Node. | +| upgName | [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) | Name of the upgrade using hash of Hacknet Server. | | upgTarget | string | _(Optional)_ Object to which upgrade applies. Required for certain upgrades. | | count | number | _(Optional)_ Number of upgrades to buy at once. Must be a non-negative integer. Defaults to 1 if not specified. For compatibility reasons, upgTarget must be specified, even if it is not used, in order to specify count. | diff --git a/markdown/bitburner.hacknetserverhashupgrade.md b/markdown/bitburner.hacknetserverhashupgrade.md new file mode 100644 index 000000000..c02b9c09e --- /dev/null +++ b/markdown/bitburner.hacknetserverhashupgrade.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [bitburner](./bitburner.md) > [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) + +## HacknetServerHashUpgrade type + +**Signature:** + +```typescript +type HacknetServerHashUpgrade = + | "Sell for Money" + | "Sell for Corporation Funds" + | "Reduce Minimum Security" + | "Increase Maximum Money" + | "Improve Studying" + | "Improve Gym Training" + | "Exchange for Corporation Research" + | "Exchange for Bladeburner Rank" + | "Exchange for Bladeburner SP" + | "Generate Coding Contract" + | "Company Favor"; +``` diff --git a/markdown/bitburner.hacknetserversformulas.hashupgradecost.md b/markdown/bitburner.hacknetserversformulas.hashupgradecost.md index 8c4da240b..cf8d1b098 100644 --- a/markdown/bitburner.hacknetserversformulas.hashupgradecost.md +++ b/markdown/bitburner.hacknetserversformulas.hashupgradecost.md @@ -9,14 +9,14 @@ Calculate hash cost of an upgrade. **Signature:** ```typescript -hashUpgradeCost(upgName: string, level: number): number; +hashUpgradeCost(upgName: HacknetServerHashUpgrade, level: number): number; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| upgName | string | name of the upgrade | +| upgName | [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) | name of the upgrade | | level | number | level of the upgrade | **Returns:** diff --git a/markdown/bitburner.md b/markdown/bitburner.md index aeeaacf1d..afdf4ba57 100644 --- a/markdown/bitburner.md +++ b/markdown/bitburner.md @@ -182,6 +182,7 @@ | [GymLocationName](./bitburner.gymlocationname.md) | | | [GymLocationNameEnumType](./bitburner.gymlocationnameenumtype.md) | Locations of gym | | [GymType](./bitburner.gymtype.md) | | +| [HacknetServerHashUpgrade](./bitburner.hacknetserverhashupgrade.md) | | | [JobField](./bitburner.jobfield.md) | | | [JobFieldEnumType](./bitburner.jobfieldenumtype.md) | | | [JobName](./bitburner.jobname.md) | | diff --git a/src/Enums.ts b/src/Enums.ts index ead1e0770..46415f81e 100644 --- a/src/Enums.ts +++ b/src/Enums.ts @@ -15,3 +15,4 @@ export * from "./StockMarket/Enums"; export * from "./ui/Enums"; export * from "./Work/Enums"; export * from "./CodingContract/Enums"; +export * from "./Hacknet/Enums"; diff --git a/src/Hacknet/Enums.ts b/src/Hacknet/Enums.ts new file mode 100644 index 000000000..98bd88583 --- /dev/null +++ b/src/Hacknet/Enums.ts @@ -0,0 +1,13 @@ +export enum HashUpgradeEnum { + SellForMoney = "Sell for Money", + SellForCorporationFunds = "Sell for Corporation Funds", + ReduceMinimumSecurity = "Reduce Minimum Security", + IncreaseMaximumMoney = "Increase Maximum Money", + ImproveStudying = "Improve Studying", + ImproveGymTraining = "Improve Gym Training", + ExchangeForCorporationResearch = "Exchange for Corporation Research", + ExchangeForBladeburnerRank = "Exchange for Bladeburner Rank", + ExchangeForBladeburnerSP = "Exchange for Bladeburner SP", + GenerateCodingContract = "Generate Coding Contract", + CompanyFavor = "Company Favor", +} diff --git a/src/Hacknet/HacknetHelpers.tsx b/src/Hacknet/HacknetHelpers.tsx index 92b3d5912..d60e4a6ab 100644 --- a/src/Hacknet/HacknetHelpers.tsx +++ b/src/Hacknet/HacknetHelpers.tsx @@ -25,6 +25,9 @@ import { Companies } from "../Company/Companies"; import { isMember } from "../utils/EnumHelper"; import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils"; import { checkServerOwnership, ServerOwnershipType } from "../Server/ServerHelpers"; +import { Result } from "../types"; +import { exceptionAlert } from "../utils/helpers/exceptionAlert"; +import { HashUpgradeEnum } from "./Enums"; // Returns a boolean indicating whether the player has Hacknet Servers // (the upgraded form of Hacknet Nodes) @@ -415,7 +418,7 @@ function processAllHacknetServerEarnings(numCycles: number): number { const wastedHashes = Player.hashManager.storeHashes(hashes); if (wastedHashes > 0) { - const upgrade = HashUpgrades["Sell for Money"]; + const upgrade = HashUpgrades[HashUpgradeEnum.SellForMoney]; if (upgrade === null) throw new Error("Could not get the hash upgrade"); if (!upgrade.cost) throw new Error("Upgrade is not properly configured"); @@ -460,139 +463,137 @@ export function updateHashManagerCapacity(): void { Player.hashManager.updateCapacity(total); } -export function purchaseHashUpgrade(upgName: string, upgTarget: string, count = 1): boolean { - if (!(Player.hashManager instanceof HashManager)) { - console.error(`Player does not have a HashManager`); - return false; - } +function applyEffectOfHashUpgrade(upgName: HashUpgradeEnum, upgTarget: string, count = 1): Result { + const upg = HashUpgrades[upgName]; - // HashManager handles the transaction. This just needs to actually implement - // the effects of the upgrade - if (Player.hashManager.upgrade(upgName, count)) { - const upg = HashUpgrades[upgName]; - - switch (upgName) { - case "Sell for Money": { - Player.gainMoney(upg.value * count, "hacknet"); - break; - } - case "Sell for Corporation Funds": { - const corp = Player.corporation; - if (corp === null) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - corp.gainFunds(upg.value * count, "hacknet"); - break; - } - case "Reduce Minimum Security": { - try { - const target = GetServer(upgTarget); - if (target == null) { - console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); - throw new Error(`'${upgTarget}' is not a server.`); - } - if (!(target instanceof Server)) { - throw new Error(`'${upgTarget}' is not a normal server.`); - } - if (!checkServerOwnership(target, ServerOwnershipType.Foreign)) { - throw new Error( - `'${upgTarget}' is not a valid target. You can only perform this action on servers that you do not own.`, - ); - } - - target.changeMinimumSecurity(upg.value ** count, true); - } catch (e) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - break; - } - case "Increase Maximum Money": { - try { - const target = GetServer(upgTarget); - if (target == null) { - console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); - throw new Error(`'${upgTarget}' is not a server.`); - } - if (!(target instanceof Server)) { - throw new Error(`'${upgTarget}' is not a normal server.`); - } - if (!checkServerOwnership(target, ServerOwnershipType.Foreign)) { - throw new Error( - `'${upgTarget}' is not a valid target. You can only perform this action on servers that you do not own.`, - ); - } - - //Manually loop the change so as to properly handle the softcap - for (let i = 0; i < count; i++) { - target.changeMaximumMoney(upg.value); - } - } catch (e) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - break; - } - case "Improve Studying": { - // Multiplier is handled by HashManager - break; - } - case "Improve Gym Training": { - // Multiplier is handled by HashManager - break; - } - case "Exchange for Corporation Research": { - const corp = Player.corporation; - if (corp === null) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - for (const division of corp.divisions.values()) { - division.researchPoints += upg.value * count; - } - break; - } - case "Exchange for Bladeburner Rank": { - const bladeburner = Player.bladeburner; - if (bladeburner === null) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - bladeburner.changeRank(Player, upg.value * count); - break; - } - case "Exchange for Bladeburner SP": { - const bladeburner = Player.bladeburner; - if (bladeburner === null) { - Player.hashManager.refundUpgrade(upgName, count); - return false; - } - - bladeburner.skillPoints += upg.value * count; - break; - } - case "Generate Coding Contract": { - for (let i = 0; i < count; i++) { - generateRandomContract(); - } - break; - } - case "Company Favor": { - if (!isMember("CompanyName", upgTarget)) { - console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); - throw new Error(`'${upgTarget}' is not a company.`); - } - Companies[upgTarget].setFavor(Companies[upgTarget].favor + 5 * count); - break; - } - default: - console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`); - return false; + switch (upgName) { + case HashUpgradeEnum.SellForMoney: { + Player.gainMoney(upg.value * count, "hacknet"); + break; } + case HashUpgradeEnum.SellForCorporationFunds: { + const corp = Player.corporation; + if (corp === null) { + return { success: false, message: "You have not created a corporation." }; + } + corp.gainFunds(upg.value * count, "hacknet"); + break; + } + case HashUpgradeEnum.ReduceMinimumSecurity: { + const target = GetServer(upgTarget); + if (target == null) { + return { success: false, message: `'${upgTarget}' is not a server.` }; + } + if (!(target instanceof Server)) { + return { success: false, message: `'${upgTarget}' is not a normal server.` }; + } + if (!checkServerOwnership(target, ServerOwnershipType.Foreign)) { + return { + success: false, + message: `'${upgTarget}' is not a valid target. You can only perform this action on servers that you do not own.`, + }; + } - return true; + target.changeMinimumSecurity(upg.value ** count, true); + break; + } + case HashUpgradeEnum.IncreaseMaximumMoney: { + const target = GetServer(upgTarget); + if (target == null) { + return { success: false, message: `'${upgTarget}' is not a server.` }; + } + if (!(target instanceof Server)) { + return { success: false, message: `'${upgTarget}' is not a normal server.` }; + } + if (!checkServerOwnership(target, ServerOwnershipType.Foreign)) { + return { + success: false, + message: `'${upgTarget}' is not a valid target. You can only perform this action on servers that you do not own.`, + }; + } + + //Manually loop the change so as to properly handle the softcap + for (let i = 0; i < count; i++) { + target.changeMaximumMoney(upg.value); + } + break; + } + case HashUpgradeEnum.ImproveStudying: { + // Multiplier is handled by HashManager + break; + } + case HashUpgradeEnum.ImproveGymTraining: { + // Multiplier is handled by HashManager + break; + } + case HashUpgradeEnum.ExchangeForCorporationResearch: { + const corp = Player.corporation; + if (corp === null) { + return { success: false, message: "You have not created a corporation." }; + } + for (const division of corp.divisions.values()) { + division.researchPoints += upg.value * count; + } + break; + } + case HashUpgradeEnum.ExchangeForBladeburnerRank: { + const bladeburner = Player.bladeburner; + if (bladeburner === null) { + return { success: false, message: "You have not joined Bladeburner." }; + } + bladeburner.changeRank(Player, upg.value * count); + break; + } + case HashUpgradeEnum.ExchangeForBladeburnerSP: { + const bladeburner = Player.bladeburner; + if (bladeburner === null) { + return { success: false, message: "You have not joined Bladeburner." }; + } + + bladeburner.skillPoints += upg.value * count; + break; + } + case HashUpgradeEnum.GenerateCodingContract: { + for (let i = 0; i < count; i++) { + generateRandomContract(); + } + break; + } + case HashUpgradeEnum.CompanyFavor: { + if (!isMember("CompanyName", upgTarget)) { + return { success: false, message: `'${upgTarget}' is not a company.` }; + } + Companies[upgTarget].setFavor(Companies[upgTarget].favor + 5 * count); + break; + } + default: { + // Verify that the switch statement is exhaustive. + const __a: never = upgName; + } } - return false; + return { success: true }; +} + +export function purchaseHashUpgrade(upgName: HashUpgradeEnum, upgTarget: string, count = 1): Result { + if (!(Player.hashManager instanceof HashManager)) { + exceptionAlert(new Error("Player does not have a HashManager")); + return { success: false, message: "Player does not have a HashManager" }; + } + + /** + * Spend hashes to buy the upgrade. The hashManager validates and handles the transaction (e.g., checks the upgrade + * name, deducts the hash amount, increases the count of the upgrade). + */ + const upgradeResult = Player.hashManager.upgrade(upgName, count); + if (!upgradeResult.success) { + return upgradeResult; + } + + // Apply the effect. If we cannot apply it, the hashManager will roll back the transaction. + const result = applyEffectOfHashUpgrade(upgName, upgTarget, count); + if (!result.success) { + Player.hashManager.refundUpgrade(upgName, count); + } + return result; } diff --git a/src/Hacknet/HashManager.ts b/src/Hacknet/HashManager.ts index bc9015769..0a0493f40 100644 --- a/src/Hacknet/HashManager.ts +++ b/src/Hacknet/HashManager.ts @@ -10,6 +10,8 @@ import { HashUpgrades } from "./HashUpgrades"; import { HashUpgrade } from "./HashUpgrade"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; +import { Result } from "../types"; +import { HashUpgradeEnum } from "./Enums"; export class HashManager { // Max number of hashes this can hold. Equal to the sum of capacities of @@ -29,7 +31,7 @@ export class HashManager { } /** Generic helper function for getting a multiplier from a HashUpgrade */ - getMult(upgName: string): number { + getMult(upgName: HashUpgradeEnum): number { const upg = HashUpgrades[upgName]; const currLevel = this.upgrades[upgName]; if (upg == null || currLevel == null) { @@ -42,19 +44,19 @@ export class HashManager { /** One of the Hash upgrades improves studying. This returns that multiplier */ getStudyMult(): number { - const upgName = "Improve Studying"; + const upgName = HashUpgradeEnum.ImproveStudying; return this.getMult(upgName); } /** One of the Hash upgrades improves gym training. This returns that multiplier */ getTrainingMult(): number { - const upgName = "Improve Gym Training"; + const upgName = HashUpgradeEnum.ImproveGymTraining; return this.getMult(upgName); } - getUpgrade(upgName: string): HashUpgrade | null { + getUpgrade(upgName: HashUpgradeEnum): HashUpgrade | null { const upg = HashUpgrades[upgName]; if (!upg) { console.error(`Invalid Upgrade Name given to HashManager.getUpgrade(): ${upgName}`); @@ -64,7 +66,7 @@ export class HashManager { } /** Get the cost (in hashes) of an upgrade */ - getUpgradeCost(upgName: string, count = 1): number { + getUpgradeCost(upgName: HashUpgradeEnum, count = 1): number { const upg = this.getUpgrade(upgName); const currLevel = this.upgrades[upgName]; if (upg == null || currLevel == null) { @@ -86,7 +88,7 @@ export class HashManager { } /** Reverts an upgrade and refunds the hashes used to buy it */ - refundUpgrade(upgName: string, count = 1): void { + refundUpgrade(upgName: HashUpgradeEnum, count = 1): void { const upg = HashUpgrades[upgName]; // Reduce the level first, so we get the right cost @@ -126,23 +128,22 @@ export class HashManager { * Returns boolean indicating whether or not the upgrade was successfully purchased. * Note that this function does NOT actually implement the effect. */ - upgrade(upgName: string, count = 1): boolean { + upgrade(upgName: HashUpgradeEnum, count = 1): Result { const upg = HashUpgrades[upgName]; if (upg == null) { - console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`); - return false; + return { success: false, message: `Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}` }; } const cost = this.getUpgradeCost(upgName, count); if (this.hashes < cost) { - return false; + return { success: false, message: "Not enough hashes" }; } this.hashes -= cost; this.upgrades[upgName] += count; - return true; + return { success: true }; } //Serialize the current object to a JSON save state. diff --git a/src/Hacknet/HashUpgrade.ts b/src/Hacknet/HashUpgrade.ts index 1cf6ffec6..457f2145c 100644 --- a/src/Hacknet/HashUpgrade.ts +++ b/src/Hacknet/HashUpgrade.ts @@ -1,4 +1,5 @@ import type { ReactNode } from "react"; +import type { HashUpgradeEnum } from "./Enums"; /** Object representing an upgrade that can be purchased with hashes */ export interface HashUpgradeParams { @@ -7,7 +8,7 @@ export interface HashUpgradeParams { desc: ReactNode; hasTargetServer?: boolean; hasTargetCompany?: boolean; - name: string; + name: HashUpgradeEnum; value: number; effectText?: (level: number) => JSX.Element | null; } @@ -43,7 +44,7 @@ export class HashUpgrade { hasTargetCompany = false; /** Name of upgrade */ - name = ""; + name: HashUpgradeEnum; // Generic value used to indicate the potency/amount of this upgrade's effect // The meaning varies between different upgrades diff --git a/src/Hacknet/HashUpgrades.ts b/src/Hacknet/HashUpgrades.ts index 37a418d6d..73e2be303 100644 --- a/src/Hacknet/HashUpgrades.ts +++ b/src/Hacknet/HashUpgrades.ts @@ -2,15 +2,12 @@ * Map of all Hash Upgrades * Key = Hash name, Value = HashUpgrade object */ -import { HashUpgrade, HashUpgradeParams } from "./HashUpgrade"; +import { HashUpgradeEnum } from "./Enums"; +import { HashUpgrade } from "./HashUpgrade"; import { HashUpgradesMetadata } from "./data/HashUpgradesMetadata"; -export const HashUpgrades: Record = {}; - -function createHashUpgrade(p: HashUpgradeParams): void { - HashUpgrades[p.name] = new HashUpgrade(p); -} +export const HashUpgrades = {} as Record; for (const metadata of HashUpgradesMetadata) { - createHashUpgrade(metadata); + HashUpgrades[metadata.name] = new HashUpgrade(metadata); } diff --git a/src/Hacknet/data/HashUpgradesMetadata.tsx b/src/Hacknet/data/HashUpgradesMetadata.tsx index b83cfbe05..a57db679a 100644 --- a/src/Hacknet/data/HashUpgradesMetadata.tsx +++ b/src/Hacknet/data/HashUpgradesMetadata.tsx @@ -3,6 +3,7 @@ import React from "react"; import { HashUpgradeParams } from "../HashUpgrade"; import { formatInt } from "../../ui/formatNumber"; import { Money } from "../../ui/React/Money"; +import { HashUpgradeEnum } from "../Enums"; export const HashUpgradesMetadata: HashUpgradeParams[] = [ { @@ -13,7 +14,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ Sell hashes for ), - name: "Sell for Money", + name: HashUpgradeEnum.SellForMoney, effectText: (level: number): JSX.Element | null => ( <> Sold for @@ -28,7 +29,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ Sell hashes for in Corporation funds ), - name: "Sell for Corporation Funds", + name: HashUpgradeEnum.SellForCorporationFunds, effectText: (level: number): JSX.Element | null => ( <> Sold for Corporation funds. @@ -43,7 +44,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ "Note that a server's minimum security cannot go below 1. This effect persists " + "until you install augmentations (since servers are reset at that time).", hasTargetServer: true, - name: "Reduce Minimum Security", + name: HashUpgradeEnum.ReduceMinimumSecurity, value: 0.98, }, { @@ -56,7 +57,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ ), hasTargetServer: true, - name: "Increase Maximum Money", + name: HashUpgradeEnum.IncreaseMaximumMoney, value: 1.02, }, { @@ -64,7 +65,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ desc: "Use hashes to improve the experience earned when studying at a university by 20%. " + "This effect persists until you install augmentations.", - name: "Improve Studying", + name: HashUpgradeEnum.ImproveStudying, effectText: (level: number): JSX.Element | null => <>Improves studying by {level * 20}%, value: 20, // Improves studying by value% }, @@ -73,14 +74,14 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ desc: "Use hashes to improve the experience earned when training at the gym by 20%. This effect " + "persists until you install augmentations.", - name: "Improve Gym Training", + name: HashUpgradeEnum.ImproveGymTraining, effectText: (level: number): JSX.Element | null => <>Improves training by {level * 20}%, value: 20, // Improves training by value% }, { costPerLevel: 200, desc: "Exchange hashes for 1k Scientific Research in all of your corporation's divisions", - name: "Exchange for Corporation Research", + name: HashUpgradeEnum.ExchangeForCorporationResearch, effectText: (level: number): JSX.Element | null => ( <>Acquired a total of {formatInt(level * 1000)} Scientific Research in your divisions. ), @@ -89,7 +90,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ { costPerLevel: 250, desc: "Exchange hashes for 100 Bladeburner Rank", - name: "Exchange for Bladeburner Rank", + name: HashUpgradeEnum.ExchangeForBladeburnerRank, effectText: (level: number): JSX.Element | null => ( <>Acquired a total of {formatInt(100 * level)} Bladeburner rank ), @@ -98,7 +99,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ { costPerLevel: 250, desc: "Exchanges hashes for 10 Bladeburner Skill Points", - name: "Exchange for Bladeburner SP", + name: HashUpgradeEnum.ExchangeForBladeburnerSP, effectText: (level: number): JSX.Element | null => ( <>Acquired a total of {formatInt(10 * level)} Bladeburner Skill Points ), @@ -107,7 +108,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ { costPerLevel: 200, desc: "Generate a random Coding Contract somewhere on the network", - name: "Generate Coding Contract", + name: HashUpgradeEnum.GenerateCodingContract, effectText: (level: number): JSX.Element | null => <>Generated {level} contracts., value: 1, }, @@ -115,7 +116,7 @@ export const HashUpgradesMetadata: HashUpgradeParams[] = [ costPerLevel: 200, desc: "Use hashes to increase the favor with a company by 5. This effect persists until you enter a new BitNode.", hasTargetCompany: true, - name: "Company Favor", + name: HashUpgradeEnum.CompanyFavor, value: 5, }, ]; diff --git a/src/Hacknet/ui/HacknetUpgradeElem.tsx b/src/Hacknet/ui/HacknetUpgradeElem.tsx index 05be64471..427135dd9 100644 --- a/src/Hacknet/ui/HacknetUpgradeElem.tsx +++ b/src/Hacknet/ui/HacknetUpgradeElem.tsx @@ -15,7 +15,7 @@ import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; import Button from "@mui/material/Button"; import { SelectChangeEvent } from "@mui/material/Select"; -import { CompanyName, FactionName } from "@enums"; +import { CompanyName, FactionName, HashUpgradeEnum } from "@enums"; import { PartialRecord } from "../../Types/Record"; import { isMember } from "../../utils/EnumHelper"; import { ServerOwnershipType } from "../../Server/ServerHelpers"; @@ -46,19 +46,17 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement { } function purchase(): void { const canPurchase = props.hashManager.hashes >= props.hashManager.getUpgradeCost(props.upg.name); - if (canPurchase) { - const res = purchaseHashUpgrade( - props.upg.name, - props.upg.name === "Company Favor" ? selectedCompany : selectedServer, - ); - if (!res) { - dialogBoxCreate( - "Failed to purchase upgrade. This may be because you do not have enough hashes, " + - "or because you do not have access to the feature upgrade affects.", - ); - } - props.rerender(); + if (!canPurchase) { + return; } + const result = purchaseHashUpgrade( + props.upg.name, + props.upg.name === HashUpgradeEnum.CompanyFavor ? selectedCompany : selectedServer, + ); + if (!result.success) { + dialogBoxCreate(`Failed to purchase upgrade. Reason: ${result.message} `); + } + props.rerender(); } const hashManager = props.hashManager; diff --git a/src/Hacknet/ui/HashUpgradeModal.tsx b/src/Hacknet/ui/HashUpgradeModal.tsx index 3d1ea5521..aca58fa44 100644 --- a/src/Hacknet/ui/HashUpgradeModal.tsx +++ b/src/Hacknet/ui/HashUpgradeModal.tsx @@ -8,6 +8,7 @@ import { Modal } from "../../ui/React/Modal"; import { Player } from "@player"; import Typography from "@mui/material/Typography"; import { useCycleRerender } from "../../ui/React/hooks"; +import { getRecordKeys } from "../../Types/Record"; interface IProps { open: boolean; @@ -30,7 +31,7 @@ export function HashUpgradeModal(props: IProps): React.ReactElement { Hashes: - {Object.keys(HashUpgrades).map((upgName) => { + {getRecordKeys(HashUpgrades).map((upgName) => { const upg = HashUpgrades[upgName]; return ; })} diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts index 7354fd6ec..2b1d25588 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -330,7 +330,7 @@ export function NetscriptFormulas(): InternalAPI { return HScalculateCacheUpgradeCost(startingCache, extraCache); }, hashUpgradeCost: (ctx) => (_upgName, _level) => { - const upgName = helpers.string(ctx, "upgName", _upgName); + const upgName = getEnumHelper("HashUpgradeEnum").nsGetMember(ctx, _upgName); const level = helpers.number(ctx, "level", _level); checkFormulasAccess(ctx); const upg = Player.hashManager.getUpgrade(upgName); diff --git a/src/NetscriptFunctions/Hacknet.ts b/src/NetscriptFunctions/Hacknet.ts index b23bb189c..17216cec6 100644 --- a/src/NetscriptFunctions/Hacknet.ts +++ b/src/NetscriptFunctions/Hacknet.ts @@ -21,6 +21,7 @@ import { GetServer } from "../Server/AllServers"; import { Hacknet as IHacknet, NodeStats } from "@nsdefs"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; +import { getEnumHelper } from "../utils/EnumHelper"; export function NetscriptHacknet(): InternalAPI { // Utility function to get Hacknet Node object @@ -188,7 +189,7 @@ export function NetscriptHacknet(): InternalAPI { hashCost: (ctx) => (_upgName, _count = 1) => { - const upgName = helpers.string(ctx, "upgName", _upgName); + const upgName = getEnumHelper("HashUpgradeEnum").nsGetMember(ctx, _upgName); const count = helpers.number(ctx, "count", _count); if (!hasHacknetServers()) { return Infinity; @@ -199,7 +200,7 @@ export function NetscriptHacknet(): InternalAPI { spendHashes: (ctx) => (_upgName, _upgTarget = "", _count = 1) => { - const upgName = helpers.string(ctx, "upgName", _upgName); + const upgName = getEnumHelper("HashUpgradeEnum").nsGetMember(ctx, _upgName); const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget); const count = helpers.integer(ctx, "count", _count); if (count < 0) { @@ -208,7 +209,11 @@ export function NetscriptHacknet(): InternalAPI { if (!hasHacknetServers()) { return false; } - return purchaseHashUpgrade(upgName, upgTarget, count); + const result = purchaseHashUpgrade(upgName, upgTarget, count); + if (!result.success) { + helpers.log(ctx, () => result.message); + } + return result.success; }, getHashUpgrades: () => () => { if (!hasHacknetServers()) { diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index ccdfcd1e9..e78a683fc 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -2821,6 +2821,19 @@ export interface CompanyPositionInfo { requiredSkills: Skills; } +type HacknetServerHashUpgrade = + | "Sell for Money" + | "Sell for Corporation Funds" + | "Reduce Minimum Security" + | "Increase Maximum Money" + | "Improve Studying" + | "Improve Gym Training" + | "Exchange for Corporation Research" + | "Exchange for Bladeburner Rank" + | "Exchange for Bladeburner SP" + | "Generate Coding Contract" + | "Company Favor"; + /** * Hacknet API * @remarks @@ -3075,11 +3088,11 @@ export interface Hacknet { * ns.hacknet.spendHashes(upgradeName); * } * ``` - * @param upgName - Name of the upgrade of Hacknet Node. + * @param upgName - Name of the upgrade using hash of Hacknet Server. * @param count - Number of upgrades to buy at once. Defaults to 1 if not specified. * @returns Number of hashes required for the specified upgrade. */ - hashCost(upgName: string, count?: number): number; + hashCost(upgName: HacknetServerHashUpgrade, count?: number): number; /** * Purchase a hash upgrade. @@ -3102,13 +3115,13 @@ export interface Hacknet { * // For upgrades requiring a target * ns.hacknet.spendHashes("Increase Maximum Money", "foodnstuff"); * ``` - * @param upgName - Name of the upgrade of Hacknet Node. + * @param upgName - Name of the upgrade using hash of Hacknet Server. * @param upgTarget - Object to which upgrade applies. Required for certain upgrades. * @param count - Number of upgrades to buy at once. Must be a non-negative integer. Defaults to 1 if not specified. * For compatibility reasons, upgTarget must be specified, even if it is not used, in order to specify count. * @returns True if the upgrade is successfully purchased, and false otherwise. */ - spendHashes(upgName: string, upgTarget?: string, count?: number): boolean; + spendHashes(upgName: HacknetServerHashUpgrade, upgTarget?: string, count?: number): boolean; /** * Get the list of hash upgrades @@ -3124,7 +3137,7 @@ export interface Hacknet { * ``` * @returns An array containing the available upgrades */ - getHashUpgrades(): string[]; + getHashUpgrades(): HacknetServerHashUpgrade[]; /** * Get the level of a hash upgrade. @@ -3135,7 +3148,7 @@ export interface Hacknet { * * @returns Level of the upgrade. */ - getHashUpgradeLevel(upgName: string): number; + getHashUpgradeLevel(upgName: HacknetServerHashUpgrade): number; /** * Get the multiplier to study. @@ -5579,7 +5592,7 @@ interface HacknetServersFormulas { * @param level - level of the upgrade * @returns The calculated hash cost. */ - hashUpgradeCost(upgName: string, level: number): number; + hashUpgradeCost(upgName: HacknetServerHashUpgrade, level: number): number; /** * Calculate the cost of a hacknet server. * @param n - number of the hacknet server