From 8976d545320aee5de10e1486ba416c20f7cf7f08 Mon Sep 17 00:00:00 2001
From: catloversg <152669316+catloversg@users.noreply.github.com>
Date: Thu, 31 Jul 2025 04:44:06 +0700
Subject: [PATCH] API: Add ns.singularity.getHackingLevelRequirementOfProgram
(#2271)
---
.../bitburner.singularity.createprogram.md | 2 +-
...ity.gethackinglevelrequirementofprogram.md | 32 +++++++++++++++++++
markdown/bitburner.singularity.md | 1 +
src/Netscript/RamCostGenerator.ts | 1 +
src/NetscriptFunctions/Singularity.ts | 28 ++++++++++++++--
src/Programs/Programs.ts | 7 +++-
src/ScriptEditor/NetscriptDefinitions.d.ts | 27 +++++++++-------
7 files changed, 82 insertions(+), 16 deletions(-)
create mode 100644 markdown/bitburner.singularity.gethackinglevelrequirementofprogram.md
diff --git a/markdown/bitburner.singularity.createprogram.md b/markdown/bitburner.singularity.createprogram.md
index b53488d74..82283aa33 100644
--- a/markdown/bitburner.singularity.createprogram.md
+++ b/markdown/bitburner.singularity.createprogram.md
@@ -33,7 +33,7 @@ This function will automatically set you to start working on creating the specif
This function returns true if you successfully start working on the specified program, and false otherwise.
-Note that creating a program using this function has the same hacking level requirements as it normally would. These level requirements are:
- BruteSSH.exe: 50
- FTPCrack.exe: 100
- relaySMTP.exe: 250
- HTTPWorm.exe: 500
- SQLInject.exe: 750
- DeepscanV1.exe: 75
- DeepscanV2.exe: 400
- ServerProfiler.exe: 75
- AutoLink.exe: 25
+Note that creating a program using this function has the same hacking level requirements as it normally would. You can call [getHackingLevelRequirementOfProgram](./bitburner.singularity.gethackinglevelrequirementofprogram.md) to get that value.
## Example
diff --git a/markdown/bitburner.singularity.gethackinglevelrequirementofprogram.md b/markdown/bitburner.singularity.gethackinglevelrequirementofprogram.md
new file mode 100644
index 000000000..d37f56e9d
--- /dev/null
+++ b/markdown/bitburner.singularity.gethackinglevelrequirementofprogram.md
@@ -0,0 +1,32 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [Singularity](./bitburner.singularity.md) > [getHackingLevelRequirementOfProgram](./bitburner.singularity.gethackinglevelrequirementofprogram.md)
+
+## Singularity.getHackingLevelRequirementOfProgram() method
+
+Get the hacking level requirement of a program.
+
+**Signature:**
+
+```typescript
+getHackingLevelRequirementOfProgram(program: string): number;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| program | string | Name of program to create. |
+
+**Returns:**
+
+number
+
+Hacking level requirement. Return Infinity if the specified program cannot be created.
+
+## Remarks
+
+RAM cost: 5 GB \* 16/4/1
+
+In order to create a program via UI or [createProgram](./bitburner.singularity.createprogram.md), your hacking level must meet the requirement of that program. This API returns that value.
+
diff --git a/markdown/bitburner.singularity.md b/markdown/bitburner.singularity.md
index 301809b6f..bd386e303 100644
--- a/markdown/bitburner.singularity.md
+++ b/markdown/bitburner.singularity.md
@@ -55,6 +55,7 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
| [getFactionInviteRequirements(faction)](./bitburner.singularity.getfactioninviterequirements.md) | List conditions for being invited to a faction. |
| [getFactionRep(faction)](./bitburner.singularity.getfactionrep.md) | Get faction reputation. |
| [getFactionWorkTypes(faction)](./bitburner.singularity.getfactionworktypes.md) | Get the work types of a faction. |
+| [getHackingLevelRequirementOfProgram(program)](./bitburner.singularity.gethackinglevelrequirementofprogram.md) | Get the hacking level requirement of a program. |
| [getOwnedAugmentations(purchased)](./bitburner.singularity.getownedaugmentations.md) | Get a list of owned augmentation. |
| [getOwnedSourceFiles()](./bitburner.singularity.getownedsourcefiles.md) | Get a list of acquired Source-Files. |
| [getSaveData()](./bitburner.singularity.getsavedata.md) | This function returns the save data. |
diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts
index 5b1c27db6..36fe4487e 100644
--- a/src/Netscript/RamCostGenerator.ts
+++ b/src/Netscript/RamCostGenerator.ts
@@ -199,6 +199,7 @@ const singularity = {
getFactionFavorGain: SF4Cost(RamCostConstants.SingularityFn2 / 4),
donateToFaction: SF4Cost(RamCostConstants.SingularityFn3),
createProgram: SF4Cost(RamCostConstants.SingularityFn3),
+ getHackingLevelRequirementOfProgram: SF4Cost(RamCostConstants.SingularityFn3),
commitCrime: SF4Cost(RamCostConstants.SingularityFn3),
getCrimeChance: SF4Cost(RamCostConstants.SingularityFn3),
getCrimeStats: SF4Cost(RamCostConstants.SingularityFn3),
diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts
index bbf017661..d66a48eb3 100644
--- a/src/NetscriptFunctions/Singularity.ts
+++ b/src/NetscriptFunctions/Singularity.ts
@@ -1,7 +1,7 @@
import type { Singularity as ISingularity } from "@nsdefs";
import { Player } from "@player";
-import { CityName, FactionWorkType, LocationName } from "@enums";
+import { CityName, CompletedProgramName, FactionWorkType, LocationName } from "@enums";
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
import { startWorkerScript } from "../NetscriptWorker";
import { Augmentations } from "../Augmentation/Augmentations";
@@ -16,7 +16,7 @@ import { Page } from "../ui/Router";
import { SpecialServers } from "../Server/data/SpecialServers";
import { Locations } from "../Locations/Locations";
import { GetServer } from "../Server/AllServers";
-import { Programs } from "../Programs/Programs";
+import { getEffectiveHackingLevelRequirement, Programs } from "../Programs/Programs";
import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Companies } from "../Company/Companies";
@@ -963,7 +963,7 @@ export function NetscriptSingularity(): InternalAPI {
const p = Object.values(Programs).find((p) => p.name.toLowerCase() === programName);
if (p == null) {
- helpers.log(ctx, () => `The specified program does not exist: '${programName}`);
+ helpers.log(ctx, () => `The specified program does not exist: '${programName}'`);
return false;
}
@@ -1002,6 +1002,28 @@ export function NetscriptSingularity(): InternalAPI {
helpers.log(ctx, () => `Began creating program: '${programName}'`);
return true;
},
+ getHackingLevelRequirementOfProgram: (ctx) => (_programName) => {
+ helpers.checkSingularityAccess(ctx);
+ const programName = helpers.string(ctx, "programName", _programName).toLowerCase();
+
+ const program = Object.values(Programs).find((p) => p.name.toLowerCase() === programName);
+ if (program == null) {
+ throw helpers.errorMessage(ctx, `The specified program does not exist: '${programName}'`);
+ }
+
+ const create = program.create;
+ // Return Infinity if this program cannot be created.
+ if (create === null) {
+ return Infinity;
+ }
+
+ // The hacking level requirement of bitFlume is exactly 1. It does not depend on Intelligence.
+ if (program.name === CompletedProgramName.bitFlume) {
+ return 1;
+ }
+
+ return getEffectiveHackingLevelRequirement(create.level);
+ },
commitCrime: (ctx) => (_crimeType, _focus) => {
helpers.checkSingularityAccess(ctx);
const crimeType = getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType);
diff --git a/src/Programs/Programs.ts b/src/Programs/Programs.ts
index 68bb35ed8..d822ea658 100644
--- a/src/Programs/Programs.ts
+++ b/src/Programs/Programs.ts
@@ -14,13 +14,18 @@ import { CompletedProgramName, FactionName } from "@enums";
import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { knowAboutBitverse } from "../BitNode/BitNodeUtils";
+import { clampNumber } from "../utils/helpers/clampNumber";
function requireHackingLevel(lvl: number) {
return function () {
- return Player.skills.hacking + Player.skills.intelligence / 2 >= lvl;
+ return Player.skills.hacking >= getEffectiveHackingLevelRequirement(lvl);
};
}
+export function getEffectiveHackingLevelRequirement(level: number): number {
+ return clampNumber(level - Player.skills.intelligence / 2, 1);
+}
+
function bitFlumeRequirements() {
return function () {
return knowAboutBitverse() && Player.skills.hacking >= 1;
diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts
index aa5de5102..fc1ab58fd 100644
--- a/src/ScriptEditor/NetscriptDefinitions.d.ts
+++ b/src/ScriptEditor/NetscriptDefinitions.d.ts
@@ -2418,17 +2418,9 @@ export interface Singularity {
*
* This function returns true if you successfully start working on the specified program, and false otherwise.
*
- * Note that creating a program using this function has the same hacking level requirements as it normally would.
- * These level requirements are:
- * - BruteSSH.exe: 50
- * - FTPCrack.exe: 100
- * - relaySMTP.exe: 250
- * - HTTPWorm.exe: 500
- * - SQLInject.exe: 750
- * - DeepscanV1.exe: 75
- * - DeepscanV2.exe: 400
- * - ServerProfiler.exe: 75
- * - AutoLink.exe: 25
+ * Note that creating a program using this function has the same hacking level requirements as it normally would. You
+ * can call {@link Singularity.getHackingLevelRequirementOfProgram | getHackingLevelRequirementOfProgram} to get that
+ * value.
*
* @example
* ```js
@@ -2442,6 +2434,19 @@ export interface Singularity {
*/
createProgram(program: string, focus?: boolean): boolean;
+ /**
+ * Get the hacking level requirement of a program.
+ * @remarks
+ * RAM cost: 5 GB * 16/4/1
+ *
+ * In order to create a program via UI or {@link Singularity.createProgram | createProgram}, your hacking level must
+ * meet the requirement of that program. This API returns that value.
+ *
+ * @param program - Name of program to create.
+ * @returns Hacking level requirement. Return Infinity if the specified program cannot be created.
+ */
+ getHackingLevelRequirementOfProgram(program: string): number;
+
/**
* Commit a crime.
* @remarks