extracted some of the Netscript functions into their own file.

This commit is contained in:
Olivier Gagnon 2021-10-04 22:25:21 -04:00
parent 48b839d68c
commit bb0bdb776b
6 changed files with 615 additions and 431 deletions

@ -57,7 +57,6 @@ import { calculateServerGrowth } from "./Server/formulas/grow";
import { AllGangs } from "./Gang/AllGangs";
import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, purchaseAugmentation } from "./Faction/FactionHelpers";
import { FactionWorkType } from "./Faction/FactionWorkTypeEnum";
import { netscriptCanGrow, netscriptCanHack, netscriptCanWeaken } from "./Hacking/netscriptCanHack";
import {
@ -133,9 +132,6 @@ import { workerScripts } from "./Netscript/WorkerScripts";
import { WorkerScript } from "./Netscript/WorkerScript";
import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads } from "./NetscriptEvaluator";
import { Interpreter } from "./ThirdParty/JSInterpreter";
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/SleeveHelpers";
import { Exploit } from "./Exploits/Exploit";
import { Router } from "./ui/GameRoot";
import { numeralWrapper } from "./ui/numeralFormat";
@ -161,6 +157,9 @@ import { CodingContract } from "./CodingContracts";
import { Stock } from "./StockMarket/Stock";
import { BaseServer } from "./Server/BaseServer";
import { INetscriptGang, NetscriptGang } from "./NetscriptFunctions/Gang";
import { INetscriptSleeve, NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
import { INetscriptExtra, NetscriptExtra } from "./NetscriptFunctions/Extra";
import { INetscriptHacknet, NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
const defaultInterpreter = new Interpreter("", () => undefined);
@ -197,9 +196,11 @@ function toNative(pseudoObj: any): any {
return nativeObj;
}
interface NS {
interface NS extends INetscriptExtra {
[key: string]: any;
hacknet: INetscriptHacknet;
gang: INetscriptGang;
sleeve: INetscriptSleeve;
}
function NetscriptFunctions(workerScript: WorkerScript): NS {
@ -366,35 +367,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
};
// Utility function to get Hacknet Node object
const getHacknetNode = function (i: any, callingFn = ""): HacknetNode | HacknetServer {
if (isNaN(i)) {
throw makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
}
if (i < 0 || i >= Player.hacknetNodes.length) {
throw makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i);
}
if (hasHacknetServers(Player)) {
const hi = Player.hacknetNodes[i];
if (typeof hi !== "string") throw new Error("hacknet node was not a string");
const hserver = AllServers[hi];
if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server");
if (hserver == null) {
throw makeRuntimeErrorMsg(
callingFn,
`Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`,
);
}
return hserver;
} else {
const node = Player.hacknetNodes[i];
if (!(node instanceof HacknetNode)) throw new Error("hacknet node was not node.");
return node;
}
};
const makeRuntimeErrorMsg = function (caller: string, msg: string): string {
const errstack = new Error().stack;
if (errstack === undefined) throw new Error("how did we not throw an error?");
@ -510,23 +482,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
};
const checkSleeveAPIAccess = function (func: any): void {
if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) {
throw makeRuntimeErrorMsg(
`sleeve.${func}`,
"You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10",
);
}
};
const checkSleeveNumber = function (func: any, sleeveNumber: any): void {
if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
const msg = `Invalid sleeve number: ${sleeveNumber}`;
workerScript.log(func, msg);
throw makeRuntimeErrorMsg(`sleeve.${func}`, msg);
}
};
const getCodingContract = function (func: any, ip: any, fn: any): CodingContract {
const server = safeGetServer(ip, func);
const contract = server.getContract(fn);
@ -758,146 +713,28 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
const helper = {
updateDynamicRam: updateDynamicRam,
makeRuntimeErrorMsg: makeRuntimeErrorMsg,
string: (funcName: string, argName: string, v: any): string => {
if (typeof v === "string") return v;
if (typeof v === "number") return v + ""; // cast to string;
throw makeRuntimeErrorMsg(funcName, `${argName} should be a string`);
},
number: (funcName: string, argName: string, v: any): number => {
if (typeof v === "number") return v;
if (!isNaN(v) && !isNaN(parseFloat(v))) return parseFloat(v);
throw makeRuntimeErrorMsg(funcName, `${argName} should be a number`);
},
boolean: (v: any): boolean => {
return !!v; // Just convert it to boolean.
},
};
const gang = NetscriptGang(Player, workerScript, helper);
const sleeve = NetscriptSleeve(Player, workerScript, helper);
const extra = NetscriptExtra(Player, workerScript, helper);
const hacknet = NetscriptHacknet(Player, workerScript, helper);
const functions = {
hacknet: {
numNodes: function (): any {
return Player.hacknetNodes.length;
},
maxNumNodes: function (): any {
if (hasHacknetServers(Player)) {
return HacknetServerConstants.MaxServers;
}
return Infinity;
},
purchaseNode: function (): any {
return purchaseHacknet(Player);
},
getPurchaseNodeCost: function (): any {
if (hasHacknetServers(Player)) {
return getCostOfNextHacknetServer(Player);
} else {
return getCostOfNextHacknetNode(Player);
}
},
getNodeStats: function (i: any): any {
const node = getHacknetNode(i, "getNodeStats");
const hasUpgraded = hasHacknetServers(Player);
const res: any = {
name: node instanceof HacknetServer ? node.hostname : node.name,
level: node.level,
ram: node instanceof HacknetServer ? node.maxRam : node.ram,
cores: node.cores,
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
timeOnline: node.onlineTimeSeconds,
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
};
if (hasUpgraded && node instanceof HacknetServer) {
res.cache = node.cache;
res.hashCapacity = node.hashCapacity;
}
return res;
},
upgradeLevel: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeLevel");
return purchaseLevelUpgrade(Player, node, n);
},
upgradeRam: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeRam");
return purchaseRamUpgrade(Player, node, n);
},
upgradeCore: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeCore");
return purchaseCoreUpgrade(Player, node, n);
},
upgradeCache: function (i: any, n: any): any {
if (!hasHacknetServers(Player)) {
return false;
}
const node = getHacknetNode(i, "upgradeCache");
if (!(node instanceof HacknetServer)) {
workerScript.log("upgradeCache", "Can only be called on hacknet servers");
return false;
}
const res = purchaseCacheUpgrade(Player, node, n);
if (res) {
updateHashManagerCapacity(Player);
}
return res;
},
getLevelUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeLevel");
return node.calculateLevelUpgradeCost(n, Player.hacknet_node_level_cost_mult);
},
getRamUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeRam");
return node.calculateRamUpgradeCost(n, Player.hacknet_node_ram_cost_mult);
},
getCoreUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeCore");
return node.calculateCoreUpgradeCost(n, Player.hacknet_node_core_cost_mult);
},
getCacheUpgradeCost: function (i: any, n: any): any {
if (!hasHacknetServers(Player)) {
return Infinity;
}
const node = getHacknetNode(i, "upgradeCache");
if (!(node instanceof HacknetServer)) {
workerScript.log("getCacheUpgradeCost", "Can only be called on hacknet servers");
return -1;
}
return node.calculateCacheUpgradeCost(n);
},
numHashes: function (): any {
if (!hasHacknetServers(Player)) {
return 0;
}
return Player.hashManager.hashes;
},
hashCapacity: function (): any {
if (!hasHacknetServers(Player)) {
return 0;
}
return Player.hashManager.capacity;
},
hashCost: function (upgName: any): any {
if (!hasHacknetServers(Player)) {
return Infinity;
}
return Player.hashManager.getUpgradeCost(upgName);
},
spendHashes: function (upgName: any, upgTarget: any): any {
if (!hasHacknetServers(Player)) {
return false;
}
return purchaseHashUpgrade(Player, upgName, upgTarget);
},
getHashUpgradeLevel: function (upgName: any): any {
const level = Player.hashManager.upgrades[upgName];
if (level === undefined) {
throw makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`);
}
return level;
},
getStudyMult: function (): any {
if (!hasHacknetServers(Player)) {
return false;
}
return Player.hashManager.getStudyMult();
},
getTrainingMult: function (): any {
if (!hasHacknetServers(Player)) {
return false;
}
return Player.hashManager.getTrainingMult();
},
},
hacknet: hacknet,
sprintf: sprintf,
vsprintf: vsprintf,
scan: function (ip: any = workerScript.serverIp, hostnames: any = true): any {
@ -4653,227 +4490,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
},
}, // End coding contracts
// Duplicate Sleeve API
sleeve: {
getNumSleeves: function (): any {
updateDynamicRam("getNumSleeves", getRamCost("sleeve", "getNumSleeves"));
checkSleeveAPIAccess("getNumSleeves");
return Player.sleeves.length;
},
setToShockRecovery: function (sleeveNumber: any = 0): any {
updateDynamicRam("setToShockRecovery", getRamCost("sleeve", "setToShockRecovery"));
checkSleeveAPIAccess("setToShockRecovery");
checkSleeveNumber("setToShockRecovery", sleeveNumber);
return Player.sleeves[sleeveNumber].shockRecovery(Player);
},
setToSynchronize: function (sleeveNumber: any = 0): any {
updateDynamicRam("setToSynchronize", getRamCost("sleeve", "setToSynchronize"));
checkSleeveAPIAccess("setToSynchronize");
checkSleeveNumber("setToSynchronize", sleeveNumber);
return Player.sleeves[sleeveNumber].synchronize(Player);
},
setToCommitCrime: function (sleeveNumber: any = 0, crimeName: any = ""): any {
updateDynamicRam("setToCommitCrime", getRamCost("sleeve", "setToCommitCrime"));
checkSleeveAPIAccess("setToCommitCrime");
checkSleeveNumber("setToCommitCrime", sleeveNumber);
return Player.sleeves[sleeveNumber].commitCrime(Player, crimeName);
},
setToUniversityCourse: function (sleeveNumber: any = 0, universityName: any = "", className: any = ""): any {
updateDynamicRam("setToUniversityCourse", getRamCost("sleeve", "setToUniversityCourse"));
checkSleeveAPIAccess("setToUniversityCourse");
checkSleeveNumber("setToUniversityCourse", sleeveNumber);
return Player.sleeves[sleeveNumber].takeUniversityCourse(Player, universityName, className);
},
travel: function (sleeveNumber: any = 0, cityName: any = ""): any {
updateDynamicRam("travel", getRamCost("sleeve", "travel"));
checkSleeveAPIAccess("travel");
checkSleeveNumber("travel", sleeveNumber);
return Player.sleeves[sleeveNumber].travel(Player, cityName);
},
setToCompanyWork: function (sleeveNumber: any = 0, companyName: any = ""): any {
updateDynamicRam("setToCompanyWork", getRamCost("sleeve", "setToCompanyWork"));
checkSleeveAPIAccess("setToCompanyWork");
checkSleeveNumber("setToCompanyWork", sleeveNumber);
// Cannot work at the same company that another sleeve is working at
for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = Player.sleeves[i];
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
throw makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
);
}
}
return Player.sleeves[sleeveNumber].workForCompany(Player, companyName);
},
setToFactionWork: function (sleeveNumber: any = 0, factionName: any = "", workType: any = ""): any {
updateDynamicRam("setToFactionWork", getRamCost("sleeve", "setToFactionWork"));
checkSleeveAPIAccess("setToFactionWork");
checkSleeveNumber("setToFactionWork", sleeveNumber);
// Cannot work at the same faction that another sleeve is working at
for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = Player.sleeves[i];
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
throw makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
);
}
}
return Player.sleeves[sleeveNumber].workForFaction(Player, factionName, workType);
},
setToGymWorkout: function (sleeveNumber: any = 0, gymName: any = "", stat: any = ""): any {
updateDynamicRam("setToGymWorkout", getRamCost("sleeve", "setToGymWorkout"));
checkSleeveAPIAccess("setToGymWorkout");
checkSleeveNumber("setToGymWorkout", sleeveNumber);
return Player.sleeves[sleeveNumber].workoutAtGym(Player, gymName, stat);
},
getSleeveStats: function (sleeveNumber: any = 0): any {
updateDynamicRam("getSleeveStats", getRamCost("sleeve", "getSleeveStats"));
checkSleeveAPIAccess("getSleeveStats");
checkSleeveNumber("getSleeveStats", sleeveNumber);
const sl = Player.sleeves[sleeveNumber];
return {
shock: 100 - sl.shock,
sync: sl.sync,
hacking_skill: sl.hacking_skill,
strength: sl.strength,
defense: sl.defense,
dexterity: sl.dexterity,
agility: sl.agility,
charisma: sl.charisma,
};
},
getTask: function (sleeveNumber: any = 0): any {
updateDynamicRam("getTask", getRamCost("sleeve", "getTask"));
checkSleeveAPIAccess("getTask");
checkSleeveNumber("getTask", sleeveNumber);
const sl = Player.sleeves[sleeveNumber];
return {
task: SleeveTaskType[sl.currentTask],
crime: sl.crimeType,
location: sl.currentTaskLocation,
gymStatType: sl.gymStatType,
factionWorkType: FactionWorkType[sl.factionWorkType],
};
},
getInformation: function (sleeveNumber: any = 0): any {
updateDynamicRam("getInformation", getRamCost("sleeve", "getInformation"));
checkSleeveAPIAccess("getInformation");
checkSleeveNumber("getInformation", sleeveNumber);
const sl = Player.sleeves[sleeveNumber];
return {
city: sl.city,
hp: sl.hp,
jobs: Object.keys(Player.jobs), // technically sleeves have the same jobs as the player.
jobTitle: Object.values(Player.jobs),
maxHp: sl.max_hp,
tor: SpecialServerIps.hasOwnProperty("Darkweb Server"), // There's no reason not to give that infomation here as well. Worst case scenario it isn't used.
mult: {
agility: sl.agility_mult,
agilityExp: sl.agility_exp_mult,
companyRep: sl.company_rep_mult,
crimeMoney: sl.crime_money_mult,
crimeSuccess: sl.crime_success_mult,
defense: sl.defense_mult,
defenseExp: sl.defense_exp_mult,
dexterity: sl.dexterity_mult,
dexterityExp: sl.dexterity_exp_mult,
factionRep: sl.faction_rep_mult,
hacking: sl.hacking_mult,
hackingExp: sl.hacking_exp_mult,
strength: sl.strength_mult,
strengthExp: sl.strength_exp_mult,
workMoney: sl.work_money_mult,
},
timeWorked: sl.currentTaskTime,
earningsForSleeves: {
workHackExpGain: sl.earningsForSleeves.hack,
workStrExpGain: sl.earningsForSleeves.str,
workDefExpGain: sl.earningsForSleeves.def,
workDexExpGain: sl.earningsForSleeves.dex,
workAgiExpGain: sl.earningsForSleeves.agi,
workChaExpGain: sl.earningsForSleeves.cha,
workMoneyGain: sl.earningsForSleeves.money,
},
earningsForPlayer: {
workHackExpGain: sl.earningsForPlayer.hack,
workStrExpGain: sl.earningsForPlayer.str,
workDefExpGain: sl.earningsForPlayer.def,
workDexExpGain: sl.earningsForPlayer.dex,
workAgiExpGain: sl.earningsForPlayer.agi,
workChaExpGain: sl.earningsForPlayer.cha,
workMoneyGain: sl.earningsForPlayer.money,
},
earningsForTask: {
workHackExpGain: sl.earningsForTask.hack,
workStrExpGain: sl.earningsForTask.str,
workDefExpGain: sl.earningsForTask.def,
workDexExpGain: sl.earningsForTask.dex,
workAgiExpGain: sl.earningsForTask.agi,
workChaExpGain: sl.earningsForTask.cha,
workMoneyGain: sl.earningsForTask.money,
},
workRepGain: sl.getRepGain(Player),
};
},
getSleeveAugmentations: function (sleeveNumber: any = 0): any {
updateDynamicRam("getSleeveAugmentations", getRamCost("sleeve", "getSleeveAugmentations"));
checkSleeveAPIAccess("getSleeveAugmentations");
checkSleeveNumber("getSleeveAugmentations", sleeveNumber);
const augs = [];
for (let i = 0; i < Player.sleeves[sleeveNumber].augmentations.length; i++) {
augs.push(Player.sleeves[sleeveNumber].augmentations[i].name);
}
return augs;
},
getSleevePurchasableAugs: function (sleeveNumber: any = 0): any {
updateDynamicRam("getSleevePurchasableAugs", getRamCost("sleeve", "getSleevePurchasableAugs"));
checkSleeveAPIAccess("getSleevePurchasableAugs");
checkSleeveNumber("getSleevePurchasableAugs", sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(Player.sleeves[sleeveNumber], Player);
const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i];
augs.push({
name: aug.name,
cost: aug.startingCost,
});
}
return augs;
},
purchaseSleeveAug: function (sleeveNumber: any = 0, augName: any = ""): any {
updateDynamicRam("purchaseSleeveAug", getRamCost("sleeve", "purchaseSleeveAug"));
checkSleeveAPIAccess("purchaseSleeveAug");
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
const aug = Augmentations[augName];
if (!aug) {
throw makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
}
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
},
}, // End sleeve
sleeve: sleeve,
formulas: {
basic: {
calculateSkill: function (exp: any, mult: any = 1): any {
@ -4981,28 +4598,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
},
},
}, // end formulas
heart: {
// Easter egg function
break: function (): number {
return Player.karma;
},
},
exploit: function (): any {
Player.giveExploit(Exploit.UndocumentedFunctionCall);
},
bypass: function (doc: any): any {
// reset both fields first
doc.completely_unused_field = undefined;
const real_document: any = document;
real_document.completely_unused_field = undefined;
// set one to true and check that it affected the other.
real_document.completely_unused_field = true;
if (doc.completely_unused_field && workerScript.ramUsage === 1.6) {
Player.giveExploit(Exploit.Bypass);
}
doc.completely_unused_field = undefined;
real_document.completely_unused_field = undefined;
},
flags: function (data: any): any {
data = toNative(data);
// We always want the help flag.
@ -5035,6 +4630,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
return ret;
},
...extra,
};
function getFunctionNames(obj: NS): string[] {

@ -0,0 +1,39 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Exploit } from "../Exploits/Exploit";
export interface INetscriptExtra {
heart: {
break(): number;
};
exploit(): void;
bypass(doc: Document): void;
}
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): INetscriptExtra {
return {
heart: {
// Easter egg function
break: function (): number {
return player.karma;
},
},
exploit: function (): void {
player.giveExploit(Exploit.UndocumentedFunctionCall);
},
bypass: function (doc: any): void {
// reset both fields first
doc.completely_unused_field = undefined;
const real_document: any = document;
real_document.completely_unused_field = undefined;
// set one to true and check that it affected the other.
real_document.completely_unused_field = true;
if (doc.completely_unused_field && workerScript.ramUsage === 1.6) {
player.giveExploit(Exploit.Bypass);
}
doc.completely_unused_field = undefined;
real_document.completely_unused_field = undefined;
},
};
}

@ -10,7 +10,7 @@ import { GangMember } from "../Gang/GangMember";
import { GangMemberTask } from "../Gang/GangMemberTask";
export interface INetscriptGang {
createGang(faction: any): boolean;
createGang(faction: string): boolean;
inGang(): boolean;
getMemberNames(): string[];
getGangInformation(): any;
@ -59,7 +59,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
};
return {
createGang: function (faction: string): any {
createGang: function (faction: string): boolean {
helper.updateDynamicRam("createGang", getRamCost("gang", "createGang"));
// this list is copied from Faction/ui/Root.tsx
const GangNames = [

@ -0,0 +1,213 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer";
import { WorkerScript } from "../Netscript/WorkerScript";
import { HacknetServerConstants } from "../Hacknet/data/Constants";
import {
getCostOfNextHacknetNode,
getCostOfNextHacknetServer,
hasHacknetServers,
purchaseHacknet,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
purchaseCacheUpgrade,
purchaseHashUpgrade,
updateHashManagerCapacity,
} from "../Hacknet/HacknetHelpers";
import { HacknetServer } from "../Hacknet/HacknetServer";
import { HacknetNode } from "../Hacknet/HacknetNode";
import { AllServers } from "../Server/AllServers";
export interface INetscriptHacknet {
numNodes(): number;
maxNumNodes(): number;
purchaseNode(): any;
getPurchaseNodeCost(): number;
getNodeStats(i: number): any;
upgradeLevel(i: number, n: number): boolean;
upgradeRam(i: number, n: number): boolean;
upgradeCore(i: number, n: number): boolean;
upgradeCache(i: number, n: number): boolean;
getLevelUpgradeCost(i: number, n: number): number;
getRamUpgradeCost(i: number, n: number): number;
getCoreUpgradeCost(i: number, n: number): number;
getCacheUpgradeCost(i: number, n: number): number;
numHashes(): number;
hashCapacity(): number;
hashCost(upgName: string): number;
spendHashes(upgName: string, upgTarget: string): any;
getHashUpgradeLevel(upgName: string): number;
getStudyMult(): number;
getTrainingMult(): number;
}
export function NetscriptHacknet(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptHacknet {
// Utility function to get Hacknet Node object
const getHacknetNode = function (i: any, callingFn = ""): HacknetNode | HacknetServer {
if (isNaN(i)) {
throw helper.makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
}
if (i < 0 || i >= player.hacknetNodes.length) {
throw helper.makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i);
}
if (hasHacknetServers(player)) {
const hi = player.hacknetNodes[i];
if (typeof hi !== "string") throw new Error("hacknet node was not a string");
const hserver = AllServers[hi];
if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server");
if (hserver == null) {
throw helper.makeRuntimeErrorMsg(
callingFn,
`Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`,
);
}
return hserver;
} else {
const node = player.hacknetNodes[i];
if (!(node instanceof HacknetNode)) throw new Error("hacknet node was not node.");
return node;
}
};
return {
numNodes: function (): any {
return player.hacknetNodes.length;
},
maxNumNodes: function (): any {
if (hasHacknetServers(player)) {
return HacknetServerConstants.MaxServers;
}
return Infinity;
},
purchaseNode: function (): any {
return purchaseHacknet(player);
},
getPurchaseNodeCost: function (): any {
if (hasHacknetServers(player)) {
return getCostOfNextHacknetServer(player);
} else {
return getCostOfNextHacknetNode(player);
}
},
getNodeStats: function (i: any): any {
const node = getHacknetNode(i, "getNodeStats");
const hasUpgraded = hasHacknetServers(player);
const res: any = {
name: node instanceof HacknetServer ? node.hostname : node.name,
level: node.level,
ram: node instanceof HacknetServer ? node.maxRam : node.ram,
cores: node.cores,
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
timeOnline: node.onlineTimeSeconds,
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
};
if (hasUpgraded && node instanceof HacknetServer) {
res.cache = node.cache;
res.hashCapacity = node.hashCapacity;
}
return res;
},
upgradeLevel: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeLevel");
return purchaseLevelUpgrade(player, node, n);
},
upgradeRam: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeRam");
return purchaseRamUpgrade(player, node, n);
},
upgradeCore: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeCore");
return purchaseCoreUpgrade(player, node, n);
},
upgradeCache: function (i: any, n: any): any {
if (!hasHacknetServers(player)) {
return false;
}
const node = getHacknetNode(i, "upgradeCache");
if (!(node instanceof HacknetServer)) {
workerScript.log("upgradeCache", "Can only be called on hacknet servers");
return false;
}
const res = purchaseCacheUpgrade(player, node, n);
if (res) {
updateHashManagerCapacity(player);
}
return res;
},
getLevelUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeLevel");
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
},
getRamUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeRam");
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
},
getCoreUpgradeCost: function (i: any, n: any): any {
const node = getHacknetNode(i, "upgradeCore");
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
},
getCacheUpgradeCost: function (i: any, n: any): any {
if (!hasHacknetServers(player)) {
return Infinity;
}
const node = getHacknetNode(i, "upgradeCache");
if (!(node instanceof HacknetServer)) {
workerScript.log("getCacheUpgradeCost", "Can only be called on hacknet servers");
return -1;
}
return node.calculateCacheUpgradeCost(n);
},
numHashes: function (): any {
if (!hasHacknetServers(player)) {
return 0;
}
return player.hashManager.hashes;
},
hashCapacity: function (): any {
if (!hasHacknetServers(player)) {
return 0;
}
return player.hashManager.capacity;
},
hashCost: function (upgName: any): any {
if (!hasHacknetServers(player)) {
return Infinity;
}
return player.hashManager.getUpgradeCost(upgName);
},
spendHashes: function (upgName: any, upgTarget: any): any {
if (!hasHacknetServers(player)) {
return false;
}
return purchaseHashUpgrade(player, upgName, upgTarget);
},
getHashUpgradeLevel: function (upgName: any): any {
const level = player.hashManager.upgrades[upgName];
if (level === undefined) {
throw helper.makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`);
}
return level;
},
getStudyMult: function (): any {
if (!hasHacknetServers(player)) {
return false;
}
return player.hashManager.getStudyMult();
},
getTrainingMult: function (): any {
if (!hasHacknetServers(player)) {
return false;
}
return player.hashManager.getTrainingMult();
},
};
}

@ -1,4 +1,7 @@
export interface INetscriptHelper {
updateDynamicRam(functionName: string, ram: number): void;
makeRuntimeErrorMsg(functionName: string, message: string): void;
string(funcName: string, argName: string, v: any): string;
number(funcName: string, argName: string, v: any): number;
boolean(v: any): boolean;
}

@ -0,0 +1,333 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { SpecialServerIps } from "../Server/SpecialServerIps";
import { WorkerScript } from "../Netscript/WorkerScript";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
import { Augmentations } from "../Augmentation/Augmentations";
import { CityName } from "../Locations/data/CityNames";
export interface INetscriptSleeve {
getNumSleeves(): number;
setToShockRecovery(sleeveNumber?: number): boolean;
setToSynchronize(sleeveNumber?: number): boolean;
setToCommitCrime(sleeveNumber?: number, crimeName?: string): boolean;
setToUniversityCourse(sleeveNumber?: number, universityName?: string, className?: string): boolean;
travel(sleeveNumber?: number, cityName?: string): boolean;
setToCompanyWork(sleeveNumber?: number, companyName?: string): boolean;
setToFactionWork(sleeveNumber?: number, factionName?: string, workType?: string): boolean;
setToGymWorkout(sleeveNumber?: number, gymName?: string, stat?: string): boolean;
getSleeveStats(sleeveNumber?: number): {
shock: number;
sync: number;
hacking_skill: number;
strength: number;
defense: number;
dexterity: number;
agility: number;
charisma: number;
};
getTask(sleeveNumber?: number): {
task: string;
crime: string;
location: string;
gymStatType: string;
factionWorkType: string;
};
getInformation(sleeveNumber?: number): any;
getSleeveAugmentations(sleeveNumber?: number): string[];
getSleevePurchasableAugs(sleeveNumber?: number): {
name: string;
cost: number;
}[];
purchaseSleeveAug(sleeveNumber?: number, augName?: string): boolean;
}
export function NetscriptSleeve(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptSleeve {
const checkSleeveAPIAccess = function (func: any): void {
if (player.bitNodeN !== 10 && !SourceFileFlags[10]) {
throw helper.makeRuntimeErrorMsg(
`sleeve.${func}`,
"You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10",
);
}
};
const checkSleeveNumber = function (func: any, sleeveNumber: any): void {
if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) {
const msg = `Invalid sleeve number: ${sleeveNumber}`;
workerScript.log(func, msg);
throw helper.makeRuntimeErrorMsg(`sleeve.${func}`, msg);
}
};
return {
getNumSleeves: function (): number {
helper.updateDynamicRam("getNumSleeves", getRamCost("sleeve", "getNumSleeves"));
checkSleeveAPIAccess("getNumSleeves");
return player.sleeves.length;
},
setToShockRecovery: function (asleeveNumber: any = 0): boolean {
const sleeveNumber = helper.number("setToShockRecovery", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("setToShockRecovery", getRamCost("sleeve", "setToShockRecovery"));
checkSleeveAPIAccess("setToShockRecovery");
checkSleeveNumber("setToShockRecovery", sleeveNumber);
return player.sleeves[sleeveNumber].shockRecovery(player);
},
setToSynchronize: function (asleeveNumber: any = 0): boolean {
const sleeveNumber = helper.number("setToSynchronize", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("setToSynchronize", getRamCost("sleeve", "setToSynchronize"));
checkSleeveAPIAccess("setToSynchronize");
checkSleeveNumber("setToSynchronize", sleeveNumber);
return player.sleeves[sleeveNumber].synchronize(player);
},
setToCommitCrime: function (asleeveNumber: any = 0, acrimeName: any = ""): boolean {
const sleeveNumber = helper.number("setToCommitCrime", "sleeveNumber", asleeveNumber);
const crimeName = helper.string("setToUniversityCourse", "crimeName", acrimeName);
helper.updateDynamicRam("setToCommitCrime", getRamCost("sleeve", "setToCommitCrime"));
checkSleeveAPIAccess("setToCommitCrime");
checkSleeveNumber("setToCommitCrime", sleeveNumber);
return player.sleeves[sleeveNumber].commitCrime(player, crimeName);
},
setToUniversityCourse: function (asleeveNumber: any = 0, auniversityName: any = "", aclassName: any = ""): boolean {
const sleeveNumber = helper.number("setToUniversityCourse", "sleeveNumber", asleeveNumber);
const universityName = helper.string("setToUniversityCourse", "universityName", auniversityName);
const className = helper.string("setToUniversityCourse", "className", aclassName);
helper.updateDynamicRam("setToUniversityCourse", getRamCost("sleeve", "setToUniversityCourse"));
checkSleeveAPIAccess("setToUniversityCourse");
checkSleeveNumber("setToUniversityCourse", sleeveNumber);
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
},
travel: function (asleeveNumber: any = 0, acityName: any = ""): boolean {
const sleeveNumber = helper.number("travel", "sleeveNumber", asleeveNumber);
const cityName = helper.string("setToUniversityCourse", "cityName", acityName);
helper.updateDynamicRam("travel", getRamCost("sleeve", "travel"));
checkSleeveAPIAccess("travel");
checkSleeveNumber("travel", sleeveNumber);
return player.sleeves[sleeveNumber].travel(player, cityName as CityName);
},
setToCompanyWork: function (asleeveNumber: any = 0, acompanyName: any = ""): boolean {
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", asleeveNumber);
const companyName = helper.string("setToUniversityCourse", "companyName", acompanyName);
helper.updateDynamicRam("setToCompanyWork", getRamCost("sleeve", "setToCompanyWork"));
checkSleeveAPIAccess("setToCompanyWork");
checkSleeveNumber("setToCompanyWork", sleeveNumber);
// Cannot work at the same company that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
throw helper.makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
);
}
}
return player.sleeves[sleeveNumber].workForCompany(player, companyName);
},
setToFactionWork: function (asleeveNumber: any = 0, afactionName: any = "", aworkType: any = ""): boolean {
const sleeveNumber = helper.number("setToFactionWork", "sleeveNumber", asleeveNumber);
const factionName = helper.string("setToUniversityCourse", "factionName", afactionName);
const workType = helper.string("setToUniversityCourse", "workType", aworkType);
helper.updateDynamicRam("setToFactionWork", getRamCost("sleeve", "setToFactionWork"));
checkSleeveAPIAccess("setToFactionWork");
checkSleeveNumber("setToFactionWork", sleeveNumber);
// Cannot work at the same faction that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
throw helper.makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
);
}
}
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType);
},
setToGymWorkout: function (asleeveNumber: any = 0, agymName: any = "", astat: any = ""): boolean {
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", asleeveNumber);
const gymName = helper.string("setToUniversityCourse", "gymName", agymName);
const stat = helper.string("setToUniversityCourse", "stat", astat);
helper.updateDynamicRam("setToGymWorkout", getRamCost("sleeve", "setToGymWorkout"));
checkSleeveAPIAccess("setToGymWorkout");
checkSleeveNumber("setToGymWorkout", sleeveNumber);
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat);
},
getSleeveStats: function (asleeveNumber: any = 0): {
shock: number;
sync: number;
hacking_skill: number;
strength: number;
defense: number;
dexterity: number;
agility: number;
charisma: number;
} {
const sleeveNumber = helper.number("getSleeveStats", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("getSleeveStats", getRamCost("sleeve", "getSleeveStats"));
checkSleeveAPIAccess("getSleeveStats");
checkSleeveNumber("getSleeveStats", sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
shock: 100 - sl.shock,
sync: sl.sync,
hacking_skill: sl.hacking_skill,
strength: sl.strength,
defense: sl.defense,
dexterity: sl.dexterity,
agility: sl.agility,
charisma: sl.charisma,
};
},
getTask: function (asleeveNumber: any = 0): {
task: string;
crime: string;
location: string;
gymStatType: string;
factionWorkType: string;
} {
const sleeveNumber = helper.number("getTask", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("getTask", getRamCost("sleeve", "getTask"));
checkSleeveAPIAccess("getTask");
checkSleeveNumber("getTask", sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
task: SleeveTaskType[sl.currentTask],
crime: sl.crimeType,
location: sl.currentTaskLocation,
gymStatType: sl.gymStatType,
factionWorkType: FactionWorkType[sl.factionWorkType],
};
},
getInformation: function (asleeveNumber: any = 0): any {
const sleeveNumber = helper.number("getInformation", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("getInformation", getRamCost("sleeve", "getInformation"));
checkSleeveAPIAccess("getInformation");
checkSleeveNumber("getInformation", sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
city: sl.city,
hp: sl.hp,
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player.
jobTitle: Object.values(player.jobs),
maxHp: sl.max_hp,
tor: SpecialServerIps.hasOwnProperty("Darkweb Server"), // There's no reason not to give that infomation here as well. Worst case scenario it isn't used.
mult: {
agility: sl.agility_mult,
agilityExp: sl.agility_exp_mult,
companyRep: sl.company_rep_mult,
crimeMoney: sl.crime_money_mult,
crimeSuccess: sl.crime_success_mult,
defense: sl.defense_mult,
defenseExp: sl.defense_exp_mult,
dexterity: sl.dexterity_mult,
dexterityExp: sl.dexterity_exp_mult,
factionRep: sl.faction_rep_mult,
hacking: sl.hacking_mult,
hackingExp: sl.hacking_exp_mult,
strength: sl.strength_mult,
strengthExp: sl.strength_exp_mult,
workMoney: sl.work_money_mult,
},
timeWorked: sl.currentTaskTime,
earningsForSleeves: {
workHackExpGain: sl.earningsForSleeves.hack,
workStrExpGain: sl.earningsForSleeves.str,
workDefExpGain: sl.earningsForSleeves.def,
workDexExpGain: sl.earningsForSleeves.dex,
workAgiExpGain: sl.earningsForSleeves.agi,
workChaExpGain: sl.earningsForSleeves.cha,
workMoneyGain: sl.earningsForSleeves.money,
},
earningsForPlayer: {
workHackExpGain: sl.earningsForPlayer.hack,
workStrExpGain: sl.earningsForPlayer.str,
workDefExpGain: sl.earningsForPlayer.def,
workDexExpGain: sl.earningsForPlayer.dex,
workAgiExpGain: sl.earningsForPlayer.agi,
workChaExpGain: sl.earningsForPlayer.cha,
workMoneyGain: sl.earningsForPlayer.money,
},
earningsForTask: {
workHackExpGain: sl.earningsForTask.hack,
workStrExpGain: sl.earningsForTask.str,
workDefExpGain: sl.earningsForTask.def,
workDexExpGain: sl.earningsForTask.dex,
workAgiExpGain: sl.earningsForTask.agi,
workChaExpGain: sl.earningsForTask.cha,
workMoneyGain: sl.earningsForTask.money,
},
workRepGain: sl.getRepGain(player),
};
},
getSleeveAugmentations: function (asleeveNumber: any = 0): string[] {
const sleeveNumber = helper.number("getSleeveAugmentations", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("getSleeveAugmentations", getRamCost("sleeve", "getSleeveAugmentations"));
checkSleeveAPIAccess("getSleeveAugmentations");
checkSleeveNumber("getSleeveAugmentations", sleeveNumber);
const augs = [];
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
augs.push(player.sleeves[sleeveNumber].augmentations[i].name);
}
return augs;
},
getSleevePurchasableAugs: function (asleeveNumber: any = 0): {
name: string;
cost: number;
}[] {
const sleeveNumber = helper.number("getSleevePurchasableAugs", "sleeveNumber", asleeveNumber);
helper.updateDynamicRam("getSleevePurchasableAugs", getRamCost("sleeve", "getSleevePurchasableAugs"));
checkSleeveAPIAccess("getSleevePurchasableAugs");
checkSleeveNumber("getSleevePurchasableAugs", sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i];
augs.push({
name: aug.name,
cost: aug.startingCost,
});
}
return augs;
},
purchaseSleeveAug: function (asleeveNumber: any = 0, aaugName: any = ""): boolean {
const sleeveNumber = helper.number("purchaseSleeveAug", "sleeveNumber", asleeveNumber);
const augName = helper.string("setToUniversityCourse", "augName", aaugName);
helper.updateDynamicRam("purchaseSleeveAug", getRamCost("sleeve", "purchaseSleeveAug"));
checkSleeveAPIAccess("purchaseSleeveAug");
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
const aug = Augmentations[augName];
if (!aug) {
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
}
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);
},
};
}