From 596a04515dfddb8611ca7a6c0959f9090113c1e7 Mon Sep 17 00:00:00 2001 From: Noah Kantrowitz Date: Sat, 10 Sep 2022 17:39:40 -0700 Subject: [PATCH 01/11] =?UTF-8?q?=E2=9C=A8=20Allow=20using=20the=20regener?= =?UTF-8?q?ation=20chamber=20with=20sleeves=20to=20heal=20them.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also allows using sleeves to generate stamina faster even if at full HP. --- src/PersonObjects/Sleeve/Sleeve.ts | 3 +++ src/PersonObjects/Sleeve/ui/TaskSelector.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index ef9031473..2db710a0b 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -440,6 +440,9 @@ export class Sleeve extends Person { case "Diplomacy": this.startWork(p, new SleeveBladeburnerWork({ type: "General", name: "Diplomacy" })); return true; + case "Hyperbolic Regeneration Chamber": + this.startWork(p, new SleeveBladeburnerWork({ type: "General", name: "Hyperbolic Regeneration Chamber" })); + return true; case "Infiltrate synthoids": this.startWork(p, new SleeveInfiltrateWork()); return true; diff --git a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx index d94f6888e..d7cb0be3f 100644 --- a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx +++ b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx @@ -35,6 +35,7 @@ const bladeburnerSelectorOptions: string[] = [ "Field analysis", "Recruitment", "Diplomacy", + "Hyperbolic Regeneration Chamber", "Infiltrate synthoids", "Support main sleeve", "Take on contracts", @@ -285,6 +286,8 @@ function getABC(sleeve: Sleeve): [string, string, string] { return ["Perform Bladeburner Actions", "Diplomacy", "------"]; case "Recruitment": return ["Perform Bladeburner Actions", "Recruitment", "------"]; + case: "Hyperbolic Regeneration Chamber": + return ["Perform Bladeburner Actions", "Hyperbolic Regeneration Chamber", "------"]; } } From 232dcab5cd1a0238c7633d6cf0eab11d3f8a4baf Mon Sep 17 00:00:00 2001 From: Noah Kantrowitz Date: Sat, 10 Sep 2022 17:44:50 -0700 Subject: [PATCH 02/11] =?UTF-8?q?=E2=9C=A8=20Reset=20sleeves=20to=20max=20?= =?UTF-8?q?HP=20when=20starting=20a=20new=20node.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/PersonObjects/Sleeve/Sleeve.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 2db710a0b..3218576e6 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -159,6 +159,7 @@ export class Sleeve extends Person { this.exp.agility = 0; this.exp.charisma = 0; this.updateStatLevels(); + this.hp.current = this.hp.max; // Reset task-related stuff this.stopWork(p); From e4961f452e80eeb6e6330837fcd1271cccae1d35 Mon Sep 17 00:00:00 2001 From: Noah Kantrowitz Date: Sat, 10 Sep 2022 17:52:06 -0700 Subject: [PATCH 03/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Copy=20paste=20typo.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/PersonObjects/Sleeve/ui/TaskSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx index d7cb0be3f..8df94de47 100644 --- a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx +++ b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx @@ -286,7 +286,7 @@ function getABC(sleeve: Sleeve): [string, string, string] { return ["Perform Bladeburner Actions", "Diplomacy", "------"]; case "Recruitment": return ["Perform Bladeburner Actions", "Recruitment", "------"]; - case: "Hyperbolic Regeneration Chamber": + case "Hyperbolic Regeneration Chamber": return ["Perform Bladeburner Actions", "Hyperbolic Regeneration Chamber", "------"]; } } From d16576f3a710cae800426f2c7fe3e7bf60e2ea7d Mon Sep 17 00:00:00 2001 From: Mughur Date: Wed, 14 Sep 2022 15:49:50 +0300 Subject: [PATCH 04/11] TRP message is sent only after installing --- src/Message/MessageHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Message/MessageHelpers.ts b/src/Message/MessageHelpers.ts index 82d0d6353..b24a25991 100644 --- a/src/Message/MessageHelpers.ts +++ b/src/Message/MessageHelpers.ts @@ -69,7 +69,7 @@ function checkForMessagesToSend(): void { const truthGazer = Messages[MessageFilenames.TruthGazer]; const redpill = Messages[MessageFilenames.RedPill]; - if (Player.hasAugmentation(AugmentationNames.TheRedPill)) { + if (Player.hasAugmentation(AugmentationNames.TheRedPill,true)) { //Get the world daemon required hacking level const worldDaemon = GetServer(SpecialServers.WorldDaemon); if (!(worldDaemon instanceof Server)) { From 4b859131b4b5c4326b1b84c9f9c1c525762bfa08 Mon Sep 17 00:00:00 2001 From: Mughur Date: Wed, 14 Sep 2022 15:49:50 +0300 Subject: [PATCH 05/11] TRP message is sent only after installing --- src/Message/MessageHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Message/MessageHelpers.ts b/src/Message/MessageHelpers.ts index 82d0d6353..26764b72c 100644 --- a/src/Message/MessageHelpers.ts +++ b/src/Message/MessageHelpers.ts @@ -69,7 +69,7 @@ function checkForMessagesToSend(): void { const truthGazer = Messages[MessageFilenames.TruthGazer]; const redpill = Messages[MessageFilenames.RedPill]; - if (Player.hasAugmentation(AugmentationNames.TheRedPill)) { + if (Player.hasAugmentation(AugmentationNames.TheRedPill, true)) { //Get the world daemon required hacking level const worldDaemon = GetServer(SpecialServers.WorldDaemon); if (!(worldDaemon instanceof Server)) { From fcdc7ecee1b36800b21f79919163fd31421aa463 Mon Sep 17 00:00:00 2001 From: Glenn Eggleton Date: Wed, 14 Sep 2022 11:20:56 -0400 Subject: [PATCH 06/11] fix import statement, add jetbrains gitignore --- .gitignore | 2 ++ src/RemoteFileAPI/MessageHandlers.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 84671db69..7ffbc4168 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Netburner.txt # editor files .vscode + +.idea/ diff --git a/src/RemoteFileAPI/MessageHandlers.ts b/src/RemoteFileAPI/MessageHandlers.ts index a9e3dc3fc..091ba340c 100644 --- a/src/RemoteFileAPI/MessageHandlers.ts +++ b/src/RemoteFileAPI/MessageHandlers.ts @@ -13,7 +13,7 @@ import { isFileData, } from "./MessageDefinitions"; -import libSource from "!!raw-loader!../ScriptEditor/NetscriptDefinitions.d.ts"; +import libSource from "../ScriptEditor/NetscriptDefinitions.d.ts"; function error(errorMsg: string, { id }: RFAMessage): RFAMessage { return new RFAMessage({ error: errorMsg, id: id }); From 19ad9be8aad9aa2b164f01ae5ced07fed33fcbd3 Mon Sep 17 00:00:00 2001 From: Glenn Eggleton Date: Wed, 14 Sep 2022 13:05:55 -0400 Subject: [PATCH 07/11] add raw-loader to jest config --- jest.config.js | 1 + src/RemoteFileAPI/MessageHandlers.ts | 2 +- test/__mocks__/rawLoader.js | 0 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/__mocks__/rawLoader.js diff --git a/jest.config.js b/jest.config.js index 22f371dcd..25c9858c2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,5 +9,6 @@ module.exports = { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", "\\.(css|less)$": "/test/__mocks__/styleMock.js", + "\\!!raw-loader!.*$": "/test/__mocks__/rawLoader.js", }, }; diff --git a/src/RemoteFileAPI/MessageHandlers.ts b/src/RemoteFileAPI/MessageHandlers.ts index 091ba340c..a9e3dc3fc 100644 --- a/src/RemoteFileAPI/MessageHandlers.ts +++ b/src/RemoteFileAPI/MessageHandlers.ts @@ -13,7 +13,7 @@ import { isFileData, } from "./MessageDefinitions"; -import libSource from "../ScriptEditor/NetscriptDefinitions.d.ts"; +import libSource from "!!raw-loader!../ScriptEditor/NetscriptDefinitions.d.ts"; function error(errorMsg: string, { id }: RFAMessage): RFAMessage { return new RFAMessage({ error: errorMsg, id: id }); diff --git a/test/__mocks__/rawLoader.js b/test/__mocks__/rawLoader.js new file mode 100644 index 000000000..e69de29bb From 4c5adc8dfbc60c3861b4bf3d94323f4eeadfe717 Mon Sep 17 00:00:00 2001 From: Mughur Date: Wed, 14 Sep 2022 21:02:12 +0300 Subject: [PATCH 08/11] More hasAugmentation fixes --- src/NetscriptFunctions/Stanek.ts | 2 +- src/Prestige.ts | 13 ++++++++----- src/Work/FactionWork.tsx | 4 ++-- src/Work/GraftingWork.tsx | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/NetscriptFunctions/Stanek.ts b/src/NetscriptFunctions/Stanek.ts index c2e936f1b..f0753b379 100644 --- a/src/NetscriptFunctions/Stanek.ts +++ b/src/NetscriptFunctions/Stanek.ts @@ -142,7 +142,7 @@ export function NetscriptStanek(): InternalAPI { //Return true iff the player is in CotMG and has the first Stanek aug installed return ( Factions[FactionNames.ChurchOfTheMachineGod].isMember && - player.hasAugmentation(AugmentationNames.StaneksGift1) + player.hasAugmentation(AugmentationNames.StaneksGift1, true) ); }, }; diff --git a/src/Prestige.ts b/src/Prestige.ts index aed8096e7..b526946fe 100755 --- a/src/Prestige.ts +++ b/src/Prestige.ts @@ -55,15 +55,15 @@ export function prestigeAugmentation(): void { AddToAllServers(homeComp); prestigeHomeComputer(Player, homeComp); - if (augmentationExists(AugmentationNames.Neurolink) && Player.hasAugmentation(AugmentationNames.Neurolink)) { + if (augmentationExists(AugmentationNames.Neurolink) && Player.hasAugmentation(AugmentationNames.Neurolink, true)) { homeComp.programs.push(Programs.FTPCrackProgram.name); homeComp.programs.push(Programs.RelaySMTPProgram.name); } - if (augmentationExists(AugmentationNames.CashRoot) && Player.hasAugmentation(AugmentationNames.CashRoot)) { + if (augmentationExists(AugmentationNames.CashRoot) && Player.hasAugmentation(AugmentationNames.CashRoot, true)) { Player.setMoney(1e6); homeComp.programs.push(Programs.BruteSSHProgram.name); } - if (augmentationExists(AugmentationNames.PCMatrix) && Player.hasAugmentation(AugmentationNames.PCMatrix)) { + if (augmentationExists(AugmentationNames.PCMatrix) && Player.hasAugmentation(AugmentationNames.PCMatrix, true)) { homeComp.programs.push(Programs.DeepscanV1.name); homeComp.programs.push(Programs.AutoLink.name); } @@ -151,7 +151,7 @@ export function prestigeAugmentation(): void { } // Red Pill - if (augmentationExists(AugmentationNames.TheRedPill) && Player.hasAugmentation(AugmentationNames.TheRedPill)) { + if (augmentationExists(AugmentationNames.TheRedPill) && Player.hasAugmentation(AugmentationNames.TheRedPill, true)) { const WorldDaemon = GetServer(SpecialServers.WorldDaemon); const DaedalusServer = GetServer(SpecialServers.DaedalusServer); if (WorldDaemon && DaedalusServer) { @@ -160,7 +160,10 @@ export function prestigeAugmentation(): void { } } - if (augmentationExists(AugmentationNames.StaneksGift1) && Player.hasAugmentation(AugmentationNames.StaneksGift1)) { + if ( + augmentationExists(AugmentationNames.StaneksGift1) && + Player.hasAugmentation(AugmentationNames.StaneksGift1, true) + ) { joinFaction(Factions[FactionNames.ChurchOfTheMachineGod]); } diff --git a/src/Work/FactionWork.tsx b/src/Work/FactionWork.tsx index 8ec1e0f23..d1ad4e988 100644 --- a/src/Work/FactionWork.tsx +++ b/src/Work/FactionWork.tsx @@ -39,7 +39,7 @@ export class FactionWork extends Work { getReputationRate(player: IPlayer): number { let focusBonus = 1; - if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager,true)) { + if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager, true)) { focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus; } return calculateFactionRep(player, this.factionWorkType, this.getFaction().favor) * focusBonus; @@ -47,7 +47,7 @@ export class FactionWork extends Work { getExpRates(player: IPlayer): WorkStats { let focusBonus = 1; - if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager,true)) { + if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager, true)) { focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus; } const rate = calculateFactionExp(player, this.factionWorkType); diff --git a/src/Work/GraftingWork.tsx b/src/Work/GraftingWork.tsx index 3eacb278d..c0279235d 100644 --- a/src/Work/GraftingWork.tsx +++ b/src/Work/GraftingWork.tsx @@ -37,7 +37,7 @@ export class GraftingWork extends Work { process(player: IPlayer, cycles: number): boolean { let focusBonus = 1; - if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager,true)) { + if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager, true)) { focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus; } @@ -52,7 +52,7 @@ export class GraftingWork extends Work { if (!cancelled) { applyAugmentation({ name: augName, level: 1 }); - if (!player.hasAugmentation(AugmentationNames.CongruityImplant)) { + if (!player.hasAugmentation(AugmentationNames.CongruityImplant, true)) { player.entropy += 1; player.applyEntropy(player.entropy); } @@ -62,7 +62,7 @@ export class GraftingWork extends Work { <> You've finished grafting {augName}.
The augmentation has been applied to your body{" "} - {player.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."} + {player.hasAugmentation(AugmentationNames.CongruityImplant, true) ? "." : ", but you feel a bit off."} , ); } From 26ee7d9e36cdf3c56e9e7a1bd5c5e026353d4d05 Mon Sep 17 00:00:00 2001 From: Lagicrus Date: Mon, 19 Sep 2022 15:55:53 +0100 Subject: [PATCH 09/11] Adds Shadows of Anarchy --- doc/source/basicgameplay/factions.rst | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/doc/source/basicgameplay/factions.rst b/doc/source/basicgameplay/factions.rst index 430cab103..bab97651e 100644 --- a/doc/source/basicgameplay/factions.rst +++ b/doc/source/basicgameplay/factions.rst @@ -39,21 +39,23 @@ List of Factions and their Requirements .. _gameplay_factions:: -+---------------------+----------------+-----------------------------------------+-------------------------------+ -| Early Game | Faction Name | Requirements | Joining this Faction prevents | -| Factions | | | you from joining: | -+ +----------------+-----------------------------------------+-------------------------------+ -| | CyberSec | * Install a backdoor on the CSEC server | | -+ +----------------+-----------------------------------------+-------------------------------+ -| | Tian Di Hui | * $1m | | -| | | * Hacking Level 50 | | -| | | * Be in Chongqing, New Tokyo, or Ishima | | -+ +----------------+-----------------------------------------+-------------------------------+ -| | Netburners | * Hacking Level 80 | | -| | | * Total Hacknet Levels of 100 | | -| | | * Total Hacknet RAM of 8 | | -| | | * Total Hacknet Cores of 4 | | -+---------------------+----------------+-----------------------------------------+-------------------------------+ ++---------------------+--------------------+-----------------------------------------+-------------------------------+ +| Early Game | Faction Name | Requirements | Joining this Faction prevents | +| Factions | | | you from joining: | ++ +--------------------+-----------------------------------------+-------------------------------+ +| | CyberSec | * Install a backdoor on the CSEC server | | ++ +--------------------+-----------------------------------------+-------------------------------+ +| | Tian Di Hui | * $1m | | +| | | * Hacking Level 50 | | +| | | * Be in Chongqing, New Tokyo, or Ishima | | ++ +--------------------+-----------------------------------------+-------------------------------+ +| | Netburners | * Hacking Level 80 | | +| | | * Total Hacknet Levels of 100 | | +| | | * Total Hacknet RAM of 8 | | +| | | * Total Hacknet Cores of 4 | | ++ +--------------------+-----------------------------------------+-------------------------------+ +| | Shadows of Anarchy | * Successfully infiltrate a company | | ++---------------------+--------------------+-----------------------------------------+-------------------------------+ .. raw:: html From 10a2f180cef5157cdcbbc822cbaba0f9e357e4a3 Mon Sep 17 00:00:00 2001 From: SilverNexus Date: Mon, 19 Sep 2022 20:23:26 -0400 Subject: [PATCH 10/11] Fix #4106 -- Crime EXP is exactly One Fifth? --- src/ui/WorkInProgressRoot.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 8f5ba77ce..f99730478 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -119,6 +119,7 @@ function ExpRows(rate: WorkStats): React.ReactElement[] { ]; } +/* Because crime exp is given all at once at the end, we don't care about the cycles per second. */ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { return [ rate.hackExp > 0 ? ( @@ -126,7 +127,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Hacking Exp" color={Settings.theme.hack} data={{ - content: `${numeralWrapper.formatExp(rate.hackExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.hackExp)}`, }} /> ) : ( @@ -137,7 +138,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Strength Exp" color={Settings.theme.combat} data={{ - content: `${numeralWrapper.formatExp(rate.strExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.strExp)}`, }} /> ) : ( @@ -148,7 +149,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Defense Exp" color={Settings.theme.combat} data={{ - content: `${numeralWrapper.formatExp(rate.defExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.defExp)}`, }} /> ) : ( @@ -159,7 +160,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Dexterity Exp" color={Settings.theme.combat} data={{ - content: `${numeralWrapper.formatExp(rate.dexExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.dexExp)}`, }} /> ) : ( @@ -170,7 +171,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Agility Exp" color={Settings.theme.combat} data={{ - content: `${numeralWrapper.formatExp(rate.agiExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.agiExp)}`, }} /> ) : ( @@ -181,7 +182,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] { name="Charisma Exp" color={Settings.theme.cha} data={{ - content: `${numeralWrapper.formatExp(rate.chaExp * CYCLES_PER_SEC)}`, + content: `${numeralWrapper.formatExp(rate.chaExp)}`, }} /> ) : ( From 4175960fc68669491a8d0b0bc639d7c26a06008c Mon Sep 17 00:00:00 2001 From: phyzical <5182053+phyzical@users.noreply.github.com> Date: Tue, 20 Sep 2022 17:56:05 +0800 Subject: [PATCH 11/11] use bitnode multiplers in the output of crime stats --- src/NetscriptFunctions/Singularity.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index 82ef6e3b2..d6e13d86c 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -53,6 +53,7 @@ import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWor import { FactionWork } from "../Work/FactionWork"; import { FactionWorkType } from "../Work/data/FactionWorkType"; import { CompanyWork } from "../Work/CompanyWork"; +import { calculateCrimeWorkStats } from "../Work/formulas/Crime"; export function NetscriptSingularity(): InternalAPI { const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation { @@ -1208,7 +1209,19 @@ export function NetscriptSingularity(): InternalAPI { throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: ${crimeRoughName}`); } - return Object.assign({}, crime); + const crimeStatsWithMultipliers = calculateCrimeWorkStats(crime); + + return Object.assign({}, crime, { + money: crimeStatsWithMultipliers.money, + reputation: crimeStatsWithMultipliers.reputation, + hacking_exp: crimeStatsWithMultipliers.hackExp, + strength_exp: crimeStatsWithMultipliers.strExp, + defense_exp: crimeStatsWithMultipliers.defExp, + dexterity_exp: crimeStatsWithMultipliers.dexExp, + agility_exp: crimeStatsWithMultipliers.agiExp, + charisma_exp: crimeStatsWithMultipliers.chaExp, + intelligence_exp: crimeStatsWithMultipliers.intExp, + }); }, getDarkwebPrograms: (ctx: NetscriptContext) => function (): string[] {