diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 65a4b3d2f..7b292a209 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -70,6 +70,7 @@ import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract"; import { NetscriptCorporation } from "./NetscriptFunctions/Corporation"; import { NetscriptFormulas } from "./NetscriptFunctions/Formulas"; import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket"; +import { NetscriptGrafting } from "./NetscriptFunctions/Grafting"; import { IPort } from "./NetscriptPort"; import { @@ -480,6 +481,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { const singularity = NetscriptSingularity(Player, workerScript, helper); const stockmarket = NetscriptStockMarket(Player, workerScript, helper); const ui = NetscriptUserInterface(Player, workerScript, helper); + const grafting = NetscriptGrafting(Player, workerScript, helper); const base: INS = { ...singularity, @@ -493,6 +495,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { ui: ui, formulas: formulas, stock: stockmarket, + grafting: grafting, args: workerScript.args, hacknet: hacknet, sprintf: sprintf, @@ -2315,6 +2318,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { tor: Player.hasTorRouter(), inBladeburner: Player.inBladeburner(), hasCorporation: Player.hasCorporation(), + entropyStacks: Player.entropyStacks, }; Object.assign(data.jobs, Player.jobs); return data; diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts new file mode 100644 index 000000000..124ab0206 --- /dev/null +++ b/src/NetscriptFunctions/Grafting.ts @@ -0,0 +1,84 @@ +import { CityName } from "../Locations/data/CityNames"; +import { Augmentations } from "../Augmentation/Augmentations"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { CraftableAugmentation } from "../PersonObjects/Grafting/CraftableAugmentation"; +import { getAvailableAugs } from "../PersonObjects/Grafting/ui/GraftingRoot"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; +import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; +import { Router } from "../ui/GameRoot"; +import { INetscriptHelper } from "./INetscriptHelper"; + +export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGrafting { + const checkGraftingAPIAccess = (func: any): void => { + if (player.bitNodeN !== 10 && !SourceFileFlags[10]) { + throw helper.makeRuntimeErrorMsg( + `grafting.${func}`, + "You do not currently have access to the Grafting API. This is either because you are not in BitNode 10 or because you do not have Source-File 10", + ); + } + }; + + return { + getAugmentationCraftPrice: (augName: string): number => { + helper.updateDynamicRam("getAugmentationCraftPrice", getRamCost(player, "grafting", "getAugmentationCraftPrice")); + checkGraftingAPIAccess("getAugmentationCraftPrice"); + if (!Augmentations.hasOwnProperty(augName)) { + throw helper.makeRuntimeErrorMsg("grafting.getAugmentationCraftPrice", `Invalid aug: ${augName}`); + } + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + return craftableAug.cost; + }, + + getAugmentationCraftTime: (augName: string): number => { + helper.updateDynamicRam("getAugmentationCraftTime", getRamCost(player, "grafting", "getAugmentationCraftTime")); + checkGraftingAPIAccess("getAugmentationCraftTime"); + if (!Augmentations.hasOwnProperty(augName)) { + throw helper.makeRuntimeErrorMsg("grafting.getAugmentationCraftTime", `Invalid aug: ${augName}`); + } + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + return craftableAug.time; + }, + + craftAugmentation: (augName: string, focus = true): boolean => { + helper.updateDynamicRam("craftAugmentation", getRamCost(player, "grafting", "craftAugmentation")); + checkGraftingAPIAccess("craftAugmentation"); + if (player.city !== CityName.NewTokyo) { + throw helper.makeRuntimeErrorMsg( + "grafting.craftAugmentation", + "You must be in New Tokyo to begin crafting an Augmentation.", + ); + } + if (!getAvailableAugs(player).includes(augName)) { + workerScript.log("grafting.craftAugmentation", () => `Invalid aug: ${augName}`); + return false; + } + + const wasFocusing = player.focus; + if (player.isWorking) { + const txt = player.singularityStopWork(); + workerScript.log("craftAugmentation", () => txt); + } + + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + if (player.money < craftableAug.cost) { + workerScript.log("grafting.craftAugmentation", () => `You don't have enough money to craft ${augName}`); + } + + player.loseMoney(craftableAug.cost, "augmentations"); + player.startCraftAugmentationWork(augName, craftableAug.time); + + if (focus) { + player.startFocusing(); + Router.toWork(); + } else if (wasFocusing) { + player.stopFocusing(); + Router.toTerminal(); + } + + workerScript.log("grafting.craftAugmentation", () => `Began crafting Augmentation ${augName}.`); + return true; + }, + }; +} diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index eefde9c2f..838fc8f7e 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -21,7 +21,7 @@ import { CraftableAugmentation } from "../CraftableAugmentation"; const CraftableAugmentations: IMap = {}; -const getAvailableAugs = (player: IPlayer): string[] => { +export const getAvailableAugs = (player: IPlayer): string[] => { const augs: string[] = []; for (const [augName, aug] of Object.entries(Augmentations)) { diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index a062ddd29..f871c296e 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -95,6 +95,7 @@ interface Player { tor: boolean; hasCorporation: boolean; inBladeburner: boolean; + entropyStacks: number; } /** @@ -3719,6 +3720,39 @@ export interface Sleeve { purchaseSleeveAug(sleeveNumber: number, augName: string): boolean; } +export interface Grafting { + /** + * Retrieve the crafting cost of an aug. + * @remarks + * RAM cost: TODO + * + * @param augName - Name of the aug to check the price of. Must be an exact match. + * @returns The cost required to craft the named augmentation. + */ + getAugmentationCraftPrice(augName: string): number; + + /** + * Retrieves the time required to craft an aug. + * @remarks + * RAM cost: TODO + * + * @param augName - Name of the aug to check the crafting time of. Must be an exact match. + * @returns The time required, in millis, to craft the named augmentation. + */ + getAugmentationCraftTime(augName: string): number; + + /** + * Begins crafting the named aug. You must be in New Tokyo to use this. + * @remarks + * RAM cost: TODO + * + * @param augName - The name of the aug to begin crafting. Must be an exact match. + * @param focus - Acquire player focus on this Augmentation crafting. Optional. Defaults to true. + * @returns True if the aug successfully began crafting, false otherwise. + */ + craftAugmentation(augName: string, focus?: boolean): boolean; +} + /** * Skills formulas * @public @@ -4280,6 +4314,13 @@ export interface NS extends Singularity { */ readonly ui: UserInterface; + /** + * Namespace for grafting functions. + * @remarks + * RAM cost: 0 GB + */ + readonly grafting: Grafting; + /** * Arguments passed into the script. *