diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 70a6c2b0f..af50c8c5c 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -232,6 +232,8 @@ export const RamCosts: IMap = { connect: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), manualHack: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), installBackdoor: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), + getDarkwebProgramCost: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4), + getDarkwebPrograms: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4), getStats: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4), getCharacterInformation: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4), hospitalize: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4), diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index 92c89d12e..af2e4a001 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -370,7 +370,8 @@ export function NetscriptSingularity( if (player.city != CityName.Aevum) { workerScript.log( "gymWorkout", - () => `You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`, + () => + `You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`, ); return false; } @@ -382,7 +383,8 @@ export function NetscriptSingularity( if (player.city != CityName.Aevum) { workerScript.log( "gymWorkout", - () => `You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`, + () => + `You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`, ); return false; } @@ -394,7 +396,8 @@ export function NetscriptSingularity( if (player.city != CityName.Sector12) { workerScript.log( "gymWorkout", - () => `You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`, + () => + `You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`, ); return false; } @@ -406,7 +409,8 @@ export function NetscriptSingularity( if (player.city != CityName.Sector12) { workerScript.log( "gymWorkout", - () => `You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`, + () => + `You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`, ); return false; } @@ -418,7 +422,8 @@ export function NetscriptSingularity( if (player.city != CityName.Volhaven) { workerScript.log( "gymWorkout", - () => `You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`, + () => + `You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`, ); return false; } @@ -476,7 +481,7 @@ export function NetscriptSingularity( case CityName.Volhaven: if (player.money < CONSTANTS.TravelCost) { workerScript.log("travelToCity", () => "Not enough money to travel."); - return false + return false; } player.loseMoney(CONSTANTS.TravelCost, "other"); player.city = cityname; @@ -1325,5 +1330,49 @@ export function NetscriptSingularity( return Object.assign({}, crime); }, + getDarkwebPrograms: function (): string[] { + helper.updateDynamicRam("getDarkwebPrograms", getRamCost(player, "getDarkwebPrograms")); + helper.checkSingularityAccess("getDarkwebPrograms"); + + // If we don't have Tor, log it and return [] (empty list) + if (!player.hasTorRouter()) { + workerScript.log("getDarkwebPrograms", () => "You do not have the TOR router."); + return []; + } + return Object.values(DarkWebItems).map((p) => p.program); + }, + getDarkwebProgramCost: function (programName: any): any { + helper.updateDynamicRam("getDarkwebProgramCost", getRamCost(player, "getDarkwebProgramCost")); + helper.checkSingularityAccess("getDarkwebProgramCost"); + + // If we don't have Tor, log it and return -1 + if (!player.hasTorRouter()) { + workerScript.log("getDarkwebProgramCost", () => "You do not have the TOR router."); + // returning -1 rather than throwing an error to be consistent with purchaseProgram + // which returns false if tor has + return -1; + } + + programName = programName.toLowerCase(); + const item = Object.values(DarkWebItems).find((i) => i.program.toLowerCase() === programName); + + // If the program doesn't exist, throw an error. The reasoning here is that the 99% case is that + // the player will be using this in automation scripts, and if they're asking for a program that + // doesn't exist, it's the first time they've run the script. So throw an error to let them know + // that they need to fix it. + if (item == null) { + throw helper.makeRuntimeErrorMsg( + "getDarkwebProgramCost", + `No such exploit ('${programName}') found on the darkweb! ` + + `\nThis function is not case-sensitive. Did you perhaps forget .exe at the end?`, + ); + } + + if (player.hasProgram(item.program)) { + workerScript.log("getDarkwebProgramCost", () => `You already have the '${item.program}' program`); + return 0; + } + return item.price; + }, }; } diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index dec9cdf33..a062ddd29 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -2289,6 +2289,67 @@ export interface Singularity { * @returns True if the focus was changed. */ setFocus(focus: boolean): boolean; + + /** + * Get a list of programs offered on the dark web. + * @remarks + * RAM cost: 1 GB * 16/4/1 + * + * + * This function allows the player to get a list of programs available for purchase + * on the dark web. Players MUST have purchased Tor to get the list of programs + * available. If Tor has not been purchased yet, this function will return an + * empty list. + * + * @example + * ```ts + * // NS1 + * getDarkwebProgramsAvailable(); + * // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc] + * ``` + * @example + * ```ts + * // NS2 + * ns.getDarkwebProgramsAvailable(); + * // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc] + * ``` + * @returns - a list of programs available for purchase on the dark web, or [] if Tor has not + * been purchased + */ + getDarkwebPrograms(): string[]; + + /** + * Check the price of an exploit on the dark web + * @remarks + * RAM cost: 0.5 GB * 16/4/1 + * + * + * This function allows you to check the price of a darkweb exploit/program. + * You MUST have a TOR router in order to use this function. The price returned + * by this function is the same price you would see with buy -l from the terminal. + * Returns the cost of the program if it has not been purchased yet, 0 if it + * has already been purchased, or -1 if Tor has not been purchased (and thus + * the program/exploit is not available for purchase). + * + * If the program does not exist, an error is thrown. + * + * + * @example + * ```ts + * // NS1 + * getDarkwebProgramCost("brutessh.exe"); + * ``` + * @example + * ```ts + * // NS2 + * ns.getDarkwebProgramCost("brutessh.exe"); + * ``` + * @param programName - Name of program to check the price of + * @returns Price of the specified darkweb program + * (if not yet purchased), 0 if it has already been purchased, or -1 if Tor has not been + * purchased. Throws an error if the specified program/exploit does not exist + */ + getDarkwebProgramCost(programName: string): number; } /** @@ -6674,8 +6735,7 @@ export interface Corporation extends WarehouseAPI, OfficeAPI { * * @returns Bonus time for the Corporation mechanic in milliseconds. */ - getBonusTime(): number; - + getBonusTime(): number; } /** diff --git a/test/jest/Netscript/DynamicRamCalculation.test.js b/test/jest/Netscript/DynamicRamCalculation.test.js index b5bd4db8d..4170f9ebd 100644 --- a/test/jest/Netscript/DynamicRamCalculation.test.js +++ b/test/jest/Netscript/DynamicRamCalculation.test.js @@ -703,6 +703,16 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function () { await testNonzeroDynamicRamCost(f); }); + it("getDarkwebProgramCost()", async function () { + const f = ["getDarkwebProgramCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getDarkwebPrograms()", async function () { + const f = ["getDarkwebPrograms"]; + await testNonzeroDynamicRamCost(f); + }); + it("getCharacterInformation()", async function () { const f = ["getCharacterInformation"]; await testNonzeroDynamicRamCost(f); diff --git a/test/jest/Netscript/StaticRamCalculation.test.js b/test/jest/Netscript/StaticRamCalculation.test.js index dd323709b..55e56823f 100644 --- a/test/jest/Netscript/StaticRamCalculation.test.js +++ b/test/jest/Netscript/StaticRamCalculation.test.js @@ -656,6 +656,16 @@ describe("Netscript Static RAM Calculation/Generation Tests", function () { await expectNonZeroRamCost(f); }); + it("getDarkwebPrograms()", async function () { + const f = ["getDarkwebPrograms"]; + await expectNonZeroRamCost(f); + }); + + it("getDarkwebProgramCost()", async function () { + const f = ["getDarkwebProgramCost"]; + await expectNonZeroRamCost(f); + }); + it("upgradeHomeRam()", async function () { const f = ["upgradeHomeRam"]; await expectNonZeroRamCost(f);