Wrap most of the API

This commit is contained in:
Olivier Gagnon 2022-05-07 19:08:07 -04:00
parent b46718d188
commit c6806caca2
10 changed files with 2179 additions and 2146 deletions

@ -82,6 +82,7 @@ import {
Gang as IGang, Gang as IGang,
Bladeburner as IBladeburner, Bladeburner as IBladeburner,
Stanek as IStanek, Stanek as IStanek,
Sleeve as ISleeve,
Infiltration as IInfiltration, Infiltration as IInfiltration,
RunningScript as IRunningScript, RunningScript as IRunningScript,
RecentScript as IRecentScript, RecentScript as IRecentScript,
@ -93,6 +94,12 @@ import {
BitNodeMultipliers as IBNMults, BitNodeMultipliers as IBNMults,
Server as IServerDef, Server as IServerDef,
RunningScript as IRunningScriptDef, RunningScript as IRunningScriptDef,
Grafting as IGrafting,
UserInterface as IUserInterface,
TIX as ITIX,
Corporation as ICorporation,
CodingContract as ICodingContract,
Hacknet as IHacknet,
// ToastVariant, // ToastVariant,
} from "./ScriptEditor/NetscriptDefinitions"; } from "./ScriptEditor/NetscriptDefinitions";
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity"; import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
@ -360,7 +367,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
} }
}; };
const hack = function ( const hack = async function (
hostname: string, hostname: string,
manual: boolean, manual: boolean,
{ threads: requestedThreads, stock }: any = {}, { threads: requestedThreads, stock }: any = {},
@ -524,23 +531,35 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
}, },
}; };
const gang = NetscriptGang(Player, workerScript, helper);
const sleeve = NetscriptSleeve(Player, workerScript, helper);
const extra = NetscriptExtra(Player, workerScript, helper); const extra = NetscriptExtra(Player, workerScript, helper);
const hacknet = NetscriptHacknet(Player, workerScript, helper); const formulas = NetscriptFormulas(Player, workerScript, helper);
const gang = wrapAPI(helper, {}, workerScript, NetscriptGang(Player, workerScript), "gang").gang as unknown as IGang;
const sleeve = wrapAPI(helper, {}, workerScript, NetscriptSleeve(Player), "sleeve").sleeve as unknown as ISleeve;
const hacknet = wrapAPI(helper, {}, workerScript, NetscriptHacknet(Player, workerScript), "hacknet")
.hacknet as unknown as IHacknet;
const bladeburner = wrapAPI(helper, {}, workerScript, NetscriptBladeburner(Player, workerScript), "bladeburner")
.bladeburner as unknown as IBladeburner;
const codingcontract = wrapAPI(
helper,
{},
workerScript,
NetscriptCodingContract(Player, workerScript),
"codingcontract",
).codingcontract as unknown as ICodingContract;
const infiltration = wrapAPI(helper, {}, workerScript, NetscriptInfiltration(Player), "infiltration") const infiltration = wrapAPI(helper, {}, workerScript, NetscriptInfiltration(Player), "infiltration")
.infiltration as unknown as IInfiltration; .infiltration as unknown as IInfiltration;
const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek") const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek")
.stanek as unknown as IStanek; .stanek as unknown as IStanek;
const bladeburner = NetscriptBladeburner(Player, workerScript, helper); const corporation = wrapAPI(helper, {}, workerScript, NetscriptCorporation(Player, workerScript), "corporation")
const codingcontract = NetscriptCodingContract(Player, workerScript, helper); .corporation as unknown as ICorporation;
const corporation = NetscriptCorporation(Player, workerScript, helper);
const formulas = NetscriptFormulas(Player, workerScript, helper);
const singularity = wrapAPI(helper, {}, workerScript, NetscriptSingularity(Player, workerScript), "singularity") const singularity = wrapAPI(helper, {}, workerScript, NetscriptSingularity(Player, workerScript), "singularity")
.singularity as unknown as ISingularity; .singularity as unknown as ISingularity;
const stockmarket = NetscriptStockMarket(Player, workerScript, helper); const stockmarket = wrapAPI(helper, {}, workerScript, NetscriptStockMarket(Player, workerScript), "stock")
const ui = NetscriptUserInterface(Player, workerScript, helper); .stock as unknown as ITIX;
const grafting = NetscriptGrafting(Player, workerScript, helper); const ui = wrapAPI(helper, {}, workerScript, NetscriptUserInterface(), "ui").ui as unknown as IUserInterface;
const grafting = wrapAPI(helper, {}, workerScript, NetscriptGrafting(Player), "grafting")
.grafting as unknown as IGrafting;
const base: INS = { const base: INS = {
...singularity, ...singularity,

@ -1,18 +1,13 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner/Bladeburner"; import { Bladeburner } from "../Bladeburner/Bladeburner";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions"; import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions";
import { IAction } from "src/Bladeburner/IAction"; import { IAction } from "src/Bladeburner/IAction";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
export function NetscriptBladeburner( export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript): InternalAPI<INetscriptBladeburner> {
player: IPlayer, const checkBladeburnerAccess = function (ctx: NetscriptContext, skipjoined = false): void {
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptBladeburner {
const checkBladeburnerAccess = function (func: string, skipjoined = false): void {
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (bladeburner === null) throw new Error("Must have joined bladeburner");
const apiAccess = const apiAccess =
@ -22,146 +17,141 @@ export function NetscriptBladeburner(
}); });
if (!apiAccess) { if (!apiAccess) {
const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`; const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`;
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied); throw ctx.makeRuntimeErrorMsg(apiDenied);
} }
if (!skipjoined) { if (!skipjoined) {
const bladeburnerAccess = bladeburner instanceof Bladeburner; const bladeburnerAccess = bladeburner instanceof Bladeburner;
if (!bladeburnerAccess) { if (!bladeburnerAccess) {
const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`; const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`;
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied); throw ctx.makeRuntimeErrorMsg(bladeburnerDenied);
} }
} }
}; };
const checkBladeburnerCity = function (func: string, city: string): void { const checkBladeburnerCity = function (ctx: NetscriptContext, city: string): void {
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (bladeburner === null) throw new Error("Must have joined bladeburner");
if (!bladeburner.cities.hasOwnProperty(city)) { if (!bladeburner.cities.hasOwnProperty(city)) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid city: ${city}`); throw ctx.makeRuntimeErrorMsg(`Invalid city: ${city}`);
} }
}; };
const getBladeburnerActionObject = function (func: string, type: string, name: string): IAction { const getBladeburnerActionObject = function (ctx: NetscriptContext, type: string, name: string): IAction {
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (bladeburner === null) throw new Error("Must have joined bladeburner");
const actionId = bladeburner.getActionIdFromTypeAndName(type, name); const actionId = bladeburner.getActionIdFromTypeAndName(type, name);
if (!actionId) { if (!actionId) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`);
} }
const actionObj = bladeburner.getActionObject(actionId); const actionObj = bladeburner.getActionObject(actionId);
if (!actionObj) { if (!actionObj) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`);
} }
return actionObj; return actionObj;
}; };
const updateRam = (funcName: string): void =>
helper.updateDynamicRam(funcName, getRamCost(player, "bladeburner", funcName));
return { return {
getContractNames: function (): string[] { getContractNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getContractNames"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getContractNames");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getContractNamesNetscriptFn(); return bladeburner.getContractNamesNetscriptFn();
}, },
getOperationNames: function (): string[] { getOperationNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getOperationNames"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getOperationNames");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getOperationNamesNetscriptFn(); return bladeburner.getOperationNamesNetscriptFn();
}, },
getBlackOpNames: function (): string[] { getBlackOpNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getBlackOpNames"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getBlackOpNames");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getBlackOpNamesNetscriptFn(); return bladeburner.getBlackOpNamesNetscriptFn();
}, },
getBlackOpRank: function (_blackOpName: unknown): number { getBlackOpRank:
updateRam("getBlackOpRank"); (ctx: NetscriptContext) =>
const blackOpName = helper.string("getBlackOpRank", "blackOpName", _blackOpName); (_blackOpName: unknown): number => {
checkBladeburnerAccess("getBlackOpRank"); const blackOpName = ctx.helper.string("blackOpName", _blackOpName);
const action: any = getBladeburnerActionObject("getBlackOpRank", "blackops", blackOpName); checkBladeburnerAccess(ctx);
const action: any = getBladeburnerActionObject(ctx, "blackops", blackOpName);
return action.reqdRank; return action.reqdRank;
}, },
getGeneralActionNames: function (): string[] { getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getGeneralActionNames"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getGeneralActionNames");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getGeneralActionNamesNetscriptFn(); return bladeburner.getGeneralActionNamesNetscriptFn();
}, },
getSkillNames: function (): string[] { getSkillNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getSkillNames"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getSkillNames");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getSkillNamesNetscriptFn(); return bladeburner.getSkillNamesNetscriptFn();
}, },
startAction: function (_type: unknown, _name: unknown): boolean { startAction:
updateRam("startAction"); (ctx: NetscriptContext) =>
const type = helper.string("startAction", "type", _type); (_type: unknown, _name: unknown): boolean => {
const name = helper.string("startAction", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("startAction"); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.startActionNetscriptFn(player, type, name, workerScript); return bladeburner.startActionNetscriptFn(player, type, name, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.startAction", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
stopBladeburnerAction: function (): void { stopBladeburnerAction: (ctx: NetscriptContext) => (): void => {
updateRam("stopBladeburnerAction"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("stopBladeburnerAction");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.resetAction(); return bladeburner.resetAction();
}, },
getCurrentAction: function (): BladeburnerCurAction { getCurrentAction: (ctx: NetscriptContext) => (): BladeburnerCurAction => {
updateRam("getCurrentAction"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getCurrentAction");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getTypeAndNameFromActionId(bladeburner.action); return bladeburner.getTypeAndNameFromActionId(bladeburner.action);
}, },
getActionTime: function (_type: unknown, _name: unknown): number { getActionTime:
updateRam("getActionTime"); (ctx: NetscriptContext) =>
const type = helper.string("getActionTime", "type", _type); (_type: unknown, _name: unknown): number => {
const name = helper.string("getActionTime", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionTime"); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript); return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionTime", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getActionEstimatedSuccessChance: function (_type: unknown, _name: unknown): [number, number] { getActionEstimatedSuccessChance:
updateRam("getActionEstimatedSuccessChance"); (ctx: NetscriptContext) =>
const type = helper.string("getActionEstimatedSuccessChance", "type", _type); (_type: unknown, _name: unknown): [number, number] => {
const name = helper.string("getActionEstimatedSuccessChance", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionEstimatedSuccessChance"); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript); return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getActionRepGain: function (_type: unknown, _name: unknown, _level: unknown): number { getActionRepGain:
updateRam("getActionRepGain"); (ctx: NetscriptContext) =>
const type = helper.string("getActionRepGain", "type", _type); (_type: unknown, _name: unknown, _level: unknown): number => {
const name = helper.string("getActionRepGain", "name", _name); const type = ctx.helper.string("type", _type);
const level = helper.number("getActionRepGain", "level", _level); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess("getActionRepGain"); const level = ctx.helper.number("level", _level);
const action = getBladeburnerActionObject("getActionRepGain", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
let rewardMultiplier; let rewardMultiplier;
if (level == null || isNaN(level)) { if (level == null || isNaN(level)) {
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1); rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
@ -171,204 +161,210 @@ export function NetscriptBladeburner(
return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank; return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank;
}, },
getActionCountRemaining: function (_type: unknown, _name: unknown): number { getActionCountRemaining:
updateRam("getActionCountRemaining"); (ctx: NetscriptContext) =>
const type = helper.string("getActionCountRemaining", "type", _type); (_type: unknown, _name: unknown): number => {
const name = helper.string("getActionCountRemaining", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionCountRemaining"); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getActionMaxLevel: function (_type: unknown, _name: unknown): number { getActionMaxLevel:
updateRam("getActionMaxLevel"); (ctx: NetscriptContext) =>
const type = helper.string("getActionMaxLevel", "type", _type); (_type: unknown, _name: unknown): number => {
const name = helper.string("getActionMaxLevel", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionMaxLevel"); const name = ctx.helper.string("name", _name);
const action = getBladeburnerActionObject("getActionMaxLevel", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
return action.maxLevel; return action.maxLevel;
}, },
getActionCurrentLevel: function (_type: unknown, _name: unknown): number { getActionCurrentLevel:
updateRam("getActionCurrentLevel"); (ctx: NetscriptContext) =>
const type = helper.string("getActionCurrentLevel", "type", _type); (_type: unknown, _name: unknown): number => {
const name = helper.string("getActionCurrentLevel", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionCurrentLevel"); const name = ctx.helper.string("name", _name);
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
return action.level; return action.level;
}, },
getActionAutolevel: function (_type: unknown, _name: unknown): boolean { getActionAutolevel:
updateRam("getActionAutolevel"); (ctx: NetscriptContext) =>
const type = helper.string("getActionAutolevel", "type", _type); (_type: unknown, _name: unknown): boolean => {
const name = helper.string("getActionAutolevel", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getActionAutolevel"); const name = ctx.helper.string("name", _name);
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
return action.autoLevel; return action.autoLevel;
}, },
setActionAutolevel: function (_type: unknown, _name: unknown, _autoLevel: unknown = true): void { setActionAutolevel:
updateRam("setActionAutolevel"); (ctx: NetscriptContext) =>
const type = helper.string("setActionAutolevel", "type", _type); (_type: unknown, _name: unknown, _autoLevel: unknown = true): void => {
const name = helper.string("setActionAutolevel", "name", _name); const type = ctx.helper.string("type", _type);
const autoLevel = helper.boolean(_autoLevel); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess("setActionAutolevel"); const autoLevel = ctx.helper.boolean(_autoLevel);
const action = getBladeburnerActionObject("setActionAutolevel", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
action.autoLevel = autoLevel; action.autoLevel = autoLevel;
}, },
setActionLevel: function (_type: unknown, _name: unknown, _level: unknown = 1): void { setActionLevel:
updateRam("setActionLevel"); (ctx: NetscriptContext) =>
const type = helper.string("setActionLevel", "type", _type); (_type: unknown, _name: unknown, _level: unknown = 1): void => {
const name = helper.string("setActionLevel", "name", _name); const type = ctx.helper.string("type", _type);
const level = helper.number("setActionLevel", "level", _level); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess("setActionLevel"); const level = ctx.helper.number("level", _level);
const action = getBladeburnerActionObject("setActionLevel", type, name); checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
if (level < 1 || level > action.maxLevel) { if (level < 1 || level > action.maxLevel) {
throw helper.makeRuntimeErrorMsg( ctx.helper.makeRuntimeErrorMsg(`Level must be between 1 and ${action.maxLevel}, is ${level}`);
"bladeburner.setActionLevel",
`Level must be between 1 and ${action.maxLevel}, is ${level}`,
);
} }
action.level = level; action.level = level;
}, },
getRank: function (): number { getRank: (ctx: NetscriptContext) => (): number => {
updateRam("getRank"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getRank");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.rank; return bladeburner.rank;
}, },
getSkillPoints: function (): number { getSkillPoints: (ctx: NetscriptContext) => (): number => {
updateRam("getSkillPoints"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getSkillPoints");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.skillPoints; return bladeburner.skillPoints;
}, },
getSkillLevel: function (_skillName: unknown): number { getSkillLevel:
updateRam("getSkillLevel"); (ctx: NetscriptContext) =>
const skillName = helper.string("getSkillLevel", "skillName", _skillName); (_skillName: unknown): number => {
checkBladeburnerAccess("getSkillLevel"); const skillName = ctx.helper.string("skillName", _skillName);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillLevel", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getSkillUpgradeCost: function (_skillName: unknown): number { getSkillUpgradeCost:
updateRam("getSkillUpgradeCost"); (ctx: NetscriptContext) =>
const skillName = helper.string("getSkillUpgradeCost", "skillName", _skillName); (_skillName: unknown): number => {
checkBladeburnerAccess("getSkillUpgradeCost"); const skillName = ctx.helper.string("skillName", _skillName);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
upgradeSkill: function (_skillName: unknown): boolean { upgradeSkill:
updateRam("upgradeSkill"); (ctx: NetscriptContext) =>
const skillName = helper.string("upgradeSkill", "skillName", _skillName); (_skillName: unknown): boolean => {
checkBladeburnerAccess("upgradeSkill"); const skillName = ctx.helper.string("skillName", _skillName);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.upgradeSkill", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getTeamSize: function (_type: unknown, _name: unknown): number { getTeamSize:
updateRam("getTeamSize"); (ctx: NetscriptContext) =>
const type = helper.string("getTeamSize", "type", _type); (_type: unknown, _name: unknown): number => {
const name = helper.string("getTeamSize", "name", _name); const type = ctx.helper.string("type", _type);
checkBladeburnerAccess("getTeamSize"); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getTeamSize", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
setTeamSize: function (_type: unknown, _name: unknown, _size: unknown): number { setTeamSize:
updateRam("setTeamSize"); (ctx: NetscriptContext) =>
const type = helper.string("setTeamSize", "type", _type); (_type: unknown, _name: unknown, _size: unknown): number => {
const name = helper.string("setTeamSize", "name", _name); const type = ctx.helper.string("type", _type);
const size = helper.number("setTeamSize", "size", _size); const name = ctx.helper.string("name", _name);
checkBladeburnerAccess("setTeamSize"); const size = ctx.helper.number("size", _size);
checkBladeburnerAccess(ctx);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
} catch (e: any) { } catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.setTeamSize", e); throw ctx.makeRuntimeErrorMsg(e);
} }
}, },
getCityEstimatedPopulation: function (_cityName: unknown): number { getCityEstimatedPopulation:
updateRam("getCityEstimatedPopulation"); (ctx: NetscriptContext) =>
const cityName = helper.string("getCityEstimatedPopulation", "cityName", _cityName); (_cityName: unknown): number => {
checkBladeburnerAccess("getCityEstimatedPopulation"); const cityName = ctx.helper.string("cityName", _cityName);
checkBladeburnerCity("getCityEstimatedPopulation", cityName); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].popEst; return bladeburner.cities[cityName].popEst;
}, },
getCityCommunities: function (_cityName: unknown): number { getCityCommunities:
updateRam("getCityCommunities"); (ctx: NetscriptContext) =>
const cityName = helper.string("getCityCommunities", "cityName", _cityName); (_cityName: unknown): number => {
checkBladeburnerAccess("getCityCommunities"); const cityName = ctx.helper.string("cityName", _cityName);
checkBladeburnerCity("getCityCommunities", cityName); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].comms; return bladeburner.cities[cityName].comms;
}, },
getCityChaos: function (_cityName: unknown): number { getCityChaos:
updateRam("getCityChaos"); (ctx: NetscriptContext) =>
const cityName = helper.string("getCityChaos", "cityName", _cityName); (_cityName: unknown): number => {
checkBladeburnerAccess("getCityChaos"); const cityName = ctx.helper.string("cityName", _cityName);
checkBladeburnerCity("getCityChaos", cityName); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].chaos; return bladeburner.cities[cityName].chaos;
}, },
getCity: function (): string { getCity: (ctx: NetscriptContext) => (): string => {
updateRam("getCity"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getCityChaos");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.city; return bladeburner.city;
}, },
switchCity: function (_cityName: unknown): boolean { switchCity:
updateRam("switchCity"); (ctx: NetscriptContext) =>
const cityName = helper.string("switchCity", "cityName", _cityName); (_cityName: unknown): boolean => {
checkBladeburnerAccess("switchCity"); const cityName = ctx.helper.string("cityName", _cityName);
checkBladeburnerCity("switchCity", cityName); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
bladeburner.city = cityName; bladeburner.city = cityName;
return true; return true;
}, },
getStamina: function (): [number, number] { getStamina: (ctx: NetscriptContext) => (): [number, number] => {
updateRam("getStamina"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getStamina");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return [bladeburner.stamina, bladeburner.maxStamina]; return [bladeburner.stamina, bladeburner.maxStamina];
}, },
joinBladeburnerFaction: function (): boolean { joinBladeburnerFaction: (ctx: NetscriptContext) => (): boolean => {
updateRam("joinBladeburnerFaction"); checkBladeburnerAccess(ctx, true);
checkBladeburnerAccess("joinBladeburnerFaction", true);
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript); return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript);
}, },
joinBladeburnerDivision: function (): boolean { joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => {
updateRam("joinBladeburnerDivision");
if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) { if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) {
if (player.bitNodeN === 8) { if (player.bitNodeN === 8) {
return false; return false;
@ -382,22 +378,18 @@ export function NetscriptBladeburner(
player.agility >= 100 player.agility >= 100
) { ) {
player.bladeburner = new Bladeburner(player); player.bladeburner = new Bladeburner(player);
workerScript.log("joinBladeburnerDivision", () => "You have been accepted into the Bladeburner division"); ctx.log(() => "You have been accepted into the Bladeburner division");
return true; return true;
} else { } else {
workerScript.log( ctx.log(() => "You do not meet the requirements for joining the Bladeburner division");
"joinBladeburnerDivision",
() => "You do not meet the requirements for joining the Bladeburner division",
);
return false; return false;
} }
} }
return false; return false;
}, },
getBonusTime: function (): number { getBonusTime: (ctx: NetscriptContext) => (): number => {
updateRam("getBonusTime"); checkBladeburnerAccess(ctx);
checkBladeburnerAccess("getBonusTime");
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return Math.round(bladeburner.storedCycles / 5) * 1000; return Math.round(bladeburner.storedCycles / 5) * 1000;

@ -1,43 +1,38 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { is2DArray } from "../utils/helpers/is2DArray"; import { is2DArray } from "../utils/helpers/is2DArray";
import { CodingContract } from "../CodingContracts"; import { CodingContract } from "../CodingContracts";
import { CodingAttemptOptions, CodingContract as ICodingContract } from "../ScriptEditor/NetscriptDefinitions"; import { CodingAttemptOptions, CodingContract as ICodingContract } from "../ScriptEditor/NetscriptDefinitions";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
export function NetscriptCodingContract( export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScript): InternalAPI<ICodingContract> {
player: IPlayer, const getCodingContract = function (
workerScript: WorkerScript, ctx: NetscriptContext,
helper: INetscriptHelper, func: string,
): ICodingContract { hostname: string,
const getCodingContract = function (func: string, hostname: string, filename: string): CodingContract { filename: string,
const server = helper.getServer(hostname, func); ): CodingContract {
const server = ctx.helper.getServer(hostname);
const contract = server.getContract(filename); const contract = server.getContract(filename);
if (contract == null) { if (contract == null) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(`Cannot find contract '${filename}' on server '${hostname}'`);
`codingcontract.${func}`,
`Cannot find contract '${filename}' on server '${hostname}'`,
);
} }
return contract; return contract;
}; };
const updateRam = (funcName: string): void =>
helper.updateDynamicRam(funcName, getRamCost(player, "codingcontract", funcName));
return { return {
attempt: function ( attempt:
(ctx: NetscriptContext) =>
(
answer: any, answer: any,
_filename: unknown, _filename: unknown,
_hostname: unknown = workerScript.hostname, _hostname: unknown = workerScript.hostname,
{ returnReward }: CodingAttemptOptions = { returnReward: false }, { returnReward }: CodingAttemptOptions = { returnReward: false },
): boolean | string { ): boolean | string => {
updateRam("attempt"); const filename = ctx.helper.string("filename", _filename);
const filename = helper.string("attempt", "filename", _filename); const hostname = ctx.helper.string("hostname", _hostname);
const hostname = helper.string("attempt", "hostname", _hostname); const contract = getCodingContract(ctx, "attempt", hostname, filename);
const contract = getCodingContract("attempt", hostname, filename);
// Convert answer to string. If the answer is a 2D array, then we have to // Convert answer to string. If the answer is a 2D array, then we have to
// manually add brackets for the inner arrays // manually add brackets for the inner arrays
@ -55,26 +50,19 @@ export function NetscriptCodingContract(
const creward = contract.reward; const creward = contract.reward;
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward"); if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
const serv = helper.getServer(hostname, "codingcontract.attempt"); const serv = ctx.helper.getServer(hostname);
if (contract.isSolution(answer)) { if (contract.isSolution(answer)) {
const reward = player.gainCodingContractReward(creward, contract.getDifficulty()); const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
workerScript.log( ctx.log(() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
"codingcontract.attempt",
() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`,
);
serv.removeContract(filename); serv.removeContract(filename);
return returnReward ? reward : true; return returnReward ? reward : true;
} else { } else {
++contract.tries; ++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) { if (contract.tries >= contract.getMaxNumTries()) {
workerScript.log( ctx.log(() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`);
"codingcontract.attempt",
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
);
serv.removeContract(filename); serv.removeContract(filename);
} else { } else {
workerScript.log( ctx.log(
"codingcontract.attempt",
() => () =>
`Coding Contract attempt '${filename}' failed. ${ `Coding Contract attempt '${filename}' failed. ${
contract.getMaxNumTries() - contract.tries contract.getMaxNumTries() - contract.tries
@ -85,18 +73,20 @@ export function NetscriptCodingContract(
return returnReward ? "" : false; return returnReward ? "" : false;
} }
}, },
getContractType: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string { getContractType:
updateRam("getContractType"); (ctx: NetscriptContext) =>
const filename = helper.string("getContractType", "filename", _filename); (_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
const hostname = helper.string("getContractType", "hostname", _hostname); const filename = ctx.helper.string("filename", _filename);
const contract = getCodingContract("getContractType", hostname, filename); const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getContractType", hostname, filename);
return contract.getType(); return contract.getType();
}, },
getData: function (_filename: unknown, _hostname: unknown = workerScript.hostname): any { getData:
updateRam("getData"); (ctx: NetscriptContext) =>
const filename = helper.string("getContractType", "filename", _filename); (_filename: unknown, _hostname: unknown = workerScript.hostname): any => {
const hostname = helper.string("getContractType", "hostname", _hostname); const filename = ctx.helper.string("filename", _filename);
const contract = getCodingContract("getData", hostname, filename); const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getData", hostname, filename);
const data = contract.getData(); const data = contract.getData();
if (data.constructor === Array) { if (data.constructor === Array) {
// For two dimensional arrays, we have to copy the internal arrays using // For two dimensional arrays, we have to copy the internal arrays using
@ -114,18 +104,20 @@ export function NetscriptCodingContract(
return data; return data;
} }
}, },
getDescription: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string { getDescription:
updateRam("getDescription"); (ctx: NetscriptContext) =>
const filename = helper.string("getDescription", "filename", _filename); (_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
const hostname = helper.string("getDescription", "hostname", _hostname); const filename = ctx.helper.string("filename", _filename);
const contract = getCodingContract("getDescription", hostname, filename); const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getDescription", hostname, filename);
return contract.getDescription(); return contract.getDescription();
}, },
getNumTriesRemaining: function (_filename: unknown, _hostname: unknown = workerScript.hostname): number { getNumTriesRemaining:
updateRam("getNumTriesRemaining"); (ctx: NetscriptContext) =>
const filename = helper.string("getNumTriesRemaining", "filename", _filename); (_filename: unknown, _hostname: unknown = workerScript.hostname): number => {
const hostname = helper.string("getNumTriesRemaining", "hostname", _hostname); const filename = ctx.helper.string("filename", _filename);
const contract = getCodingContract("getNumTriesRemaining", hostname, filename); const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getNumTriesRemaining", hostname, filename);
return contract.getMaxNumTries() - contract.tries; return contract.getMaxNumTries() - contract.tries;
}, },
}; };

File diff suppressed because it is too large Load Diff

@ -1,8 +1,6 @@
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { GangConstants } from "../Gang/data/Constants"; import { GangConstants } from "../Gang/data/Constants";
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { Gang } from "../Gang/Gang"; import { Gang } from "../Gang/Gang";
import { AllGangs } from "../Gang/AllGangs"; import { AllGangs } from "../Gang/AllGangs";
import { GangMemberTasks } from "../Gang/GangMemberTasks"; import { GangMemberTasks } from "../Gang/GangMemberTasks";
@ -20,39 +18,39 @@ import {
EquipmentStats, EquipmentStats,
GangTaskStats, GangTaskStats,
} from "../ScriptEditor/NetscriptDefinitions"; } from "../ScriptEditor/NetscriptDefinitions";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGang { export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): InternalAPI<IGang> {
const checkGangApiAccess = function (func: string): void { const checkGangApiAccess = function (ctx: NetscriptContext): void {
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Must have joined gang"); if (gang === null) throw new Error("Must have joined gang");
const hasAccess = gang instanceof Gang; const hasAccess = gang instanceof Gang;
if (!hasAccess) { if (!hasAccess) {
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `You do not currently have a Gang`); throw ctx.makeRuntimeErrorMsg(`You do not currently have a Gang`);
} }
}; };
const getGangMember = function (func: string, name: string): GangMember { const getGangMember = function (ctx: NetscriptContext, name: string): GangMember {
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Must have joined gang"); if (gang === null) throw new Error("Must have joined gang");
for (const member of gang.members) if (member.name === name) return member; for (const member of gang.members) if (member.name === name) return member;
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `Invalid gang member: '${name}'`); throw ctx.makeRuntimeErrorMsg(`Invalid gang member: '${name}'`);
}; };
const getGangTask = function (func: string, name: string): GangMemberTask { const getGangTask = function (ctx: NetscriptContext, name: string): GangMemberTask {
const task = GangMemberTasks[name]; const task = GangMemberTasks[name];
if (!task) { if (!task) {
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `Invalid task: '${name}'`); throw ctx.makeRuntimeErrorMsg(`Invalid task: '${name}'`);
} }
return task; return task;
}; };
const updateRam = (funcName: string): void => helper.updateDynamicRam(funcName, getRamCost(player, "gang", funcName));
return { return {
createGang: function (_faction: unknown): boolean { createGang:
updateRam("createGang"); (ctx: NetscriptContext) =>
const faction = helper.string("createGang", "faction", _faction); (_faction: unknown): boolean => {
const faction = ctx.helper.string("faction", _faction);
// this list is copied from Faction/ui/Root.tsx // this list is copied from Faction/ui/Root.tsx
if (!player.canAccessGang() || !GangConstants.Names.includes(faction)) return false; if (!player.canAccessGang() || !GangConstants.Names.includes(faction)) return false;
@ -63,20 +61,17 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
player.startGang(faction, isHacking); player.startGang(faction, isHacking);
return true; return true;
}, },
inGang: function (): boolean { inGang: () => (): boolean => {
updateRam("inGang");
return player.inGang(); return player.inGang();
}, },
getMemberNames: function (): string[] { getMemberNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getMemberNames"); checkGangApiAccess(ctx);
checkGangApiAccess("getMemberNames");
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
return gang.members.map((member) => member.name); return gang.members.map((member) => member.name);
}, },
getGangInformation: function (): GangGenInfo { getGangInformation: (ctx: NetscriptContext) => (): GangGenInfo => {
updateRam("getGangInformation"); checkGangApiAccess(ctx);
checkGangApiAccess("getGangInformation");
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
return { return {
@ -94,9 +89,8 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
wantedPenalty: gang.getWantedPenalty(), wantedPenalty: gang.getWantedPenalty(),
}; };
}, },
getOtherGangInformation: function (): GangOtherInfo { getOtherGangInformation: (ctx: NetscriptContext) => (): GangOtherInfo => {
updateRam("getOtherGangInformation"); checkGangApiAccess(ctx);
checkGangApiAccess("getOtherGangInformation");
const cpy: any = {}; const cpy: any = {};
for (const gang of Object.keys(AllGangs)) { for (const gang of Object.keys(AllGangs)) {
cpy[gang] = Object.assign({}, AllGangs[gang]); cpy[gang] = Object.assign({}, AllGangs[gang]);
@ -104,13 +98,14 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
return cpy; return cpy;
}, },
getMemberInformation: function (_memberName: unknown): GangMemberInfo { getMemberInformation:
updateRam("getMemberInformation"); (ctx: NetscriptContext) =>
const memberName = helper.string("getMemberInformation", "memberName", _memberName); (_memberName: unknown): GangMemberInfo => {
checkGangApiAccess("getMemberInformation"); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const member = getGangMember("getMemberInformation", memberName); const member = getGangMember(ctx, memberName);
return { return {
name: member.name, name: member.name,
task: member.task, task: member.task,
@ -158,17 +153,17 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
moneyGain: member.calculateMoneyGain(gang), moneyGain: member.calculateMoneyGain(gang),
}; };
}, },
canRecruitMember: function (): boolean { canRecruitMember: (ctx: NetscriptContext) => (): boolean => {
updateRam("canRecruitMember"); checkGangApiAccess(ctx);
checkGangApiAccess("canRecruitMember");
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
return gang.canRecruitMember(); return gang.canRecruitMember();
}, },
recruitMember: function (_memberName: unknown): boolean { recruitMember:
updateRam("recruitMember"); (ctx: NetscriptContext) =>
const memberName = helper.string("recruitMember", "memberName", _memberName); (_memberName: unknown): boolean => {
checkGangApiAccess("recruitMember"); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const recruited = gang.recruitMember(memberName); const recruited = gang.recruitMember(memberName);
@ -180,21 +175,21 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
return recruited; return recruited;
}, },
getTaskNames: function (): string[] { getTaskNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getTaskNames"); checkGangApiAccess(ctx);
checkGangApiAccess("getTaskNames");
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const tasks = gang.getAllTaskNames(); const tasks = gang.getAllTaskNames();
tasks.unshift("Unassigned"); tasks.unshift("Unassigned");
return tasks; return tasks;
}, },
setMemberTask: function (_memberName: unknown, _taskName: unknown): boolean { setMemberTask:
updateRam("setMemberTask"); (ctx: NetscriptContext) =>
const memberName = helper.string("setMemberTask", "memberName", _memberName); (_memberName: unknown, _taskName: unknown): boolean => {
const taskName = helper.string("setMemberTask", "taskName", _taskName); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess("setMemberTask"); const taskName = ctx.helper.string("taskName", _taskName);
const member = getGangMember("setMemberTask", memberName); checkGangApiAccess(ctx);
const member = getGangMember(ctx, memberName);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
if (!gang.getAllTaskNames().includes(taskName)) { if (!gang.getAllTaskNames().includes(taskName)) {
@ -214,63 +209,68 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
} else { } else {
workerScript.log( workerScript.log(
"gang.setMemberTask", "gang.setMemberTask",
() => `Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`, () =>
`Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`,
); );
} }
return success; return success;
}, },
getTaskStats: function (_taskName: unknown): GangTaskStats { getTaskStats:
updateRam("getTaskStats"); (ctx: NetscriptContext) =>
const taskName = helper.string("getTaskStats", "taskName", _taskName); (_taskName: unknown): GangTaskStats => {
checkGangApiAccess("getTaskStats"); const taskName = ctx.helper.string("taskName", _taskName);
const task = getGangTask("getTaskStats", taskName); checkGangApiAccess(ctx);
const task = getGangTask(ctx, taskName);
const copy = Object.assign({}, task); const copy = Object.assign({}, task);
copy.territory = Object.assign({}, task.territory); copy.territory = Object.assign({}, task.territory);
return copy; return copy;
}, },
getEquipmentNames: function (): string[] { getEquipmentNames: (ctx: NetscriptContext) => (): string[] => {
updateRam("getEquipmentNames"); checkGangApiAccess(ctx);
checkGangApiAccess("getEquipmentNames");
return Object.keys(GangMemberUpgrades); return Object.keys(GangMemberUpgrades);
}, },
getEquipmentCost: function (_equipName: any): number { getEquipmentCost:
updateRam("getEquipmentCost"); (ctx: NetscriptContext) =>
const equipName = helper.string("getEquipmentCost", "equipName", _equipName); (_equipName: any): number => {
checkGangApiAccess("getEquipmentCost"); const equipName = ctx.helper.string("equipName", _equipName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const upg = GangMemberUpgrades[equipName]; const upg = GangMemberUpgrades[equipName];
if (upg === null) return Infinity; if (upg === null) return Infinity;
return gang.getUpgradeCost(upg); return gang.getUpgradeCost(upg);
}, },
getEquipmentType: function (_equipName: unknown): string { getEquipmentType:
updateRam("getEquipmentType"); (ctx: NetscriptContext) =>
const equipName = helper.string("getEquipmentType", "equipName", _equipName); (_equipName: unknown): string => {
checkGangApiAccess("getEquipmentType"); const equipName = ctx.helper.string("equipName", _equipName);
checkGangApiAccess(ctx);
const upg = GangMemberUpgrades[equipName]; const upg = GangMemberUpgrades[equipName];
if (upg == null) return ""; if (upg == null) return "";
return upg.getType(); return upg.getType();
}, },
getEquipmentStats: function (_equipName: unknown): EquipmentStats { getEquipmentStats:
updateRam("getEquipmentStats"); (ctx: NetscriptContext) =>
const equipName = helper.string("getEquipmentStats", "equipName", _equipName); (_equipName: unknown): EquipmentStats => {
checkGangApiAccess("getEquipmentStats"); const equipName = ctx.helper.string("equipName", _equipName);
checkGangApiAccess(ctx);
const equipment = GangMemberUpgrades[equipName]; const equipment = GangMemberUpgrades[equipName];
if (!equipment) { if (!equipment) {
throw helper.makeRuntimeErrorMsg("getEquipmentStats", `Invalid equipment: ${equipName}`); throw ctx.makeRuntimeErrorMsg(`Invalid equipment: ${equipName}`);
} }
const typecheck: EquipmentStats = equipment.mults; const typecheck: EquipmentStats = equipment.mults;
return Object.assign({}, typecheck) as any; return Object.assign({}, typecheck) as any;
}, },
purchaseEquipment: function (_memberName: unknown, _equipName: unknown): boolean { purchaseEquipment:
updateRam("purchaseEquipment"); (ctx: NetscriptContext) =>
const memberName = helper.string("purchaseEquipment", "memberName", _memberName); (_memberName: unknown, _equipName: unknown): boolean => {
const equipName = helper.string("purchaseEquipment", "equipName", _equipName); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess("purchaseEquipment"); const equipName = ctx.helper.string("equipName", _equipName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const member = getGangMember("purchaseEquipment", memberName); const member = getGangMember(ctx, memberName);
const equipment = GangMemberUpgrades[equipName]; const equipment = GangMemberUpgrades[equipName];
if (!equipment) return false; if (!equipment) return false;
const res = member.buyUpgrade(equipment, player, gang); const res = member.buyUpgrade(equipment, player, gang);
@ -285,33 +285,36 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
return res; return res;
}, },
ascendMember: function (_memberName: unknown): GangMemberAscension | undefined { ascendMember:
updateRam("ascendMember"); (ctx: NetscriptContext) =>
const memberName = helper.string("ascendMember", "memberName", _memberName); (_memberName: unknown): GangMemberAscension | undefined => {
checkGangApiAccess("ascendMember"); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const member = getGangMember("ascendMember", memberName); const member = getGangMember(ctx, memberName);
if (!member.canAscend()) return; if (!member.canAscend()) return;
return gang.ascendMember(member, workerScript); return gang.ascendMember(member, workerScript);
}, },
getAscensionResult: function (_memberName: unknown): GangMemberAscension | undefined { getAscensionResult:
updateRam("getAscensionResult"); (ctx: NetscriptContext) =>
const memberName = helper.string("getAscensionResult", "memberName", _memberName); (_memberName: unknown): GangMemberAscension | undefined => {
checkGangApiAccess("getAscensionResult"); const memberName = ctx.helper.string("memberName", _memberName);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
const member = getGangMember("getAscensionResult", memberName); const member = getGangMember(ctx, memberName);
if (!member.canAscend()) return; if (!member.canAscend()) return;
return { return {
respect: member.earnedRespect, respect: member.earnedRespect,
...member.getAscensionResults(), ...member.getAscensionResults(),
}; };
}, },
setTerritoryWarfare: function (_engage: unknown): void { setTerritoryWarfare:
updateRam("setTerritoryWarfare"); (ctx: NetscriptContext) =>
const engage = helper.boolean(_engage); (_engage: unknown): void => {
checkGangApiAccess("setTerritoryWarfare"); const engage = ctx.helper.boolean(_engage);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
if (engage) { if (engage) {
@ -322,14 +325,15 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare"); workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare");
} }
}, },
getChanceToWinClash: function (_otherGang: unknown): number { getChanceToWinClash:
updateRam("getChanceToWinClash"); (ctx: NetscriptContext) =>
const otherGang = helper.string("getChanceToWinClash", "otherGang", _otherGang); (_otherGang: unknown): number => {
checkGangApiAccess("getChanceToWinClash"); const otherGang = ctx.helper.string("otherGang", _otherGang);
checkGangApiAccess(ctx);
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
if (AllGangs[otherGang] == null) { if (AllGangs[otherGang] == null) {
throw helper.makeRuntimeErrorMsg(`gang.getChanceToWinClash`, `Invalid gang: ${otherGang}`); throw ctx.makeRuntimeErrorMsg(`Invalid gang: ${otherGang}`);
} }
const playerPower = AllGangs[gang.facName].power; const playerPower = AllGangs[gang.facName].power;
@ -337,9 +341,8 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
return playerPower / (otherPower + playerPower); return playerPower / (otherPower + playerPower);
}, },
getBonusTime: function (): number { getBonusTime: (ctx: NetscriptContext) => (): number => {
updateRam("getBonusTime"); checkGangApiAccess(ctx);
checkGangApiAccess("getBonusTime");
const gang = player.gang; const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang"); if (gang === null) throw new Error("Should not be called without Gang");
return Math.round(gang.storedCycles / 5) * 1000; return Math.round(gang.storedCycles / 5) * 1000;

@ -1,88 +1,81 @@
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers"; import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { WorkerScript } from "../Netscript/WorkerScript";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation"; import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers"; import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { INetscriptHelper } from "./INetscriptHelper";
export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGrafting { export function NetscriptGrafting(player: IPlayer): InternalAPI<IGrafting> {
const checkGraftingAPIAccess = (func: string): void => { const checkGraftingAPIAccess = (ctx: NetscriptContext): void => {
if (!player.canAccessGrafting()) { if (!player.canAccessGrafting()) {
throw helper.makeRuntimeErrorMsg( throw ctx.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", "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",
); );
} }
}; };
const updateRam = (funcName: string): void =>
helper.updateDynamicRam(funcName, getRamCost(player, "grafting", funcName));
return { return {
getAugmentationGraftPrice: (_augName: unknown): number => { getAugmentationGraftPrice:
updateRam("getAugmentationGraftPrice"); (ctx: NetscriptContext) =>
const augName = helper.string("getAugmentationGraftPrice", "augName", _augName); (_augName: unknown): number => {
checkGraftingAPIAccess("getAugmentationGraftPrice"); const augName = ctx.helper.string("augName", _augName);
checkGraftingAPIAccess(ctx);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftPrice", `Invalid aug: ${augName}`); throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
return graftableAug.cost; return graftableAug.cost;
}, },
getAugmentationGraftTime: (_augName: string): number => { getAugmentationGraftTime:
updateRam("getAugmentationGraftTime"); (ctx: NetscriptContext) =>
const augName = helper.string("getAugmentationGraftTime", "augName", _augName); (_augName: string): number => {
checkGraftingAPIAccess("getAugmentationGraftTime"); const augName = ctx.helper.string("augName", _augName);
checkGraftingAPIAccess(ctx);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftTime", `Invalid aug: ${augName}`); throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
return calculateGraftingTimeWithBonus(player, graftableAug); return calculateGraftingTimeWithBonus(player, graftableAug);
}, },
getGraftableAugmentations: (): string[] => { getGraftableAugmentations: (ctx: NetscriptContext) => (): string[] => {
updateRam("getGraftableAugmentations"); checkGraftingAPIAccess(ctx);
checkGraftingAPIAccess("getGraftableAugmentations");
const graftableAugs = getGraftingAvailableAugs(player); const graftableAugs = getGraftingAvailableAugs(player);
return graftableAugs; return graftableAugs;
}, },
graftAugmentation: (_augName: string, _focus: unknown = true): boolean => { graftAugmentation:
updateRam("graftAugmentation"); (ctx: NetscriptContext) =>
const augName = helper.string("graftAugmentation", "augName", _augName); (_augName: string, _focus: unknown = true): boolean => {
const focus = helper.boolean(_focus); const augName = ctx.helper.string("augName", _augName);
checkGraftingAPIAccess("graftAugmentation"); const focus = ctx.helper.boolean(_focus);
checkGraftingAPIAccess(ctx);
if (player.city !== CityName.NewTokyo) { if (player.city !== CityName.NewTokyo) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must be in New Tokyo to begin grafting an Augmentation.");
"grafting.graftAugmentation",
"You must be in New Tokyo to begin grafting an Augmentation.",
);
} }
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
workerScript.log("grafting.graftAugmentation", () => `Invalid aug: ${augName}`); ctx.log(() => `Invalid aug: ${augName}`);
return false; return false;
} }
const wasFocusing = player.focus; const wasFocusing = player.focus;
if (player.isWorking) { if (player.isWorking) {
const txt = player.singularityStopWork(); const txt = player.singularityStopWork();
workerScript.log("graftAugmentation", () => txt); ctx.log(() => txt);
} }
const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
if (player.money < craftableAug.cost) { if (player.money < craftableAug.cost) {
workerScript.log("grafting.graftAugmentation", () => `You don't have enough money to craft ${augName}`); ctx.log(() => `You don't have enough money to craft ${augName}`);
return false; return false;
} }
if (!hasAugmentationPrereqs(craftableAug.augmentation)) { if (!hasAugmentationPrereqs(craftableAug.augmentation)) {
workerScript.log("grafting.graftAugmentation", () => `You don't have the pre-requisites for ${augName}`); ctx.log(() => `You don't have the pre-requisites for ${augName}`);
return false; return false;
} }
@ -97,7 +90,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
Router.toTerminal(); Router.toTerminal();
} }
workerScript.log("grafting.graftAugmentation", () => `Began grafting Augmentation ${augName}.`); ctx.log(() => `Began grafting Augmentation ${augName}.`);
return true; return true;
}, },
}; };

@ -1,4 +1,3 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { HacknetServerConstants } from "../Hacknet/data/Constants";
@ -21,12 +20,13 @@ import { HashUpgrade } from "../Hacknet/HashUpgrade";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinitions"; import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinitions";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IHacknet { export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): InternalAPI<IHacknet> {
// Utility function to get Hacknet Node object // Utility function to get Hacknet Node object
const getHacknetNode = function (i: number, callingFn = ""): HacknetNode | HacknetServer { const getHacknetNode = function (ctx: NetscriptContext, i: number): HacknetNode | HacknetServer {
if (i < 0 || i >= player.hacknetNodes.length) { if (i < 0 || i >= player.hacknetNodes.length) {
throw helper.makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i); throw ctx.makeRuntimeErrorMsg("Index specified for Hacknet Node is out-of-bounds: " + i);
} }
if (hasHacknetServers(player)) { if (hasHacknetServers(player)) {
@ -35,8 +35,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
const hserver = GetServer(hi); const hserver = GetServer(hi);
if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server"); if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server");
if (hserver == null) { if (hserver == null) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(
callingFn,
`Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`, `Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`,
); );
} }
@ -50,28 +49,30 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
}; };
return { return {
numNodes: function (): number { numNodes: () => (): number => {
return player.hacknetNodes.length; return player.hacknetNodes.length;
}, },
maxNumNodes: function (): number { maxNumNodes: () => (): number => {
if (hasHacknetServers(player)) { if (hasHacknetServers(player)) {
return HacknetServerConstants.MaxServers; return HacknetServerConstants.MaxServers;
} }
return Infinity; return Infinity;
}, },
purchaseNode: function (): number { purchaseNode: () => (): number => {
return purchaseHacknet(player); return purchaseHacknet(player);
}, },
getPurchaseNodeCost: function (): number { getPurchaseNodeCost: () => (): number => {
if (hasHacknetServers(player)) { if (hasHacknetServers(player)) {
return getCostOfNextHacknetServer(player); return getCostOfNextHacknetServer(player);
} else { } else {
return getCostOfNextHacknetNode(player); return getCostOfNextHacknetNode(player);
} }
}, },
getNodeStats: function (_i: unknown): NodeStats { getNodeStats:
const i = helper.number("getNodeStats", "i", _i); (ctx: NetscriptContext) =>
const node = getHacknetNode(i, "getNodeStats"); (_i: unknown): NodeStats => {
const i = ctx.helper.number("i", _i);
const node = getHacknetNode(ctx, i);
const hasUpgraded = hasHacknetServers(player); const hasUpgraded = hasHacknetServers(player);
const res: any = { const res: any = {
name: node instanceof HacknetServer ? node.hostname : node.name, name: node instanceof HacknetServer ? node.hostname : node.name,
@ -91,31 +92,39 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
return res; return res;
}, },
upgradeLevel: function (_i: unknown, _n: unknown = 1): boolean { upgradeLevel:
const i = helper.number("upgradeLevel", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("upgradeLevel", "n", _n); (_i: unknown, _n: unknown = 1): boolean => {
const node = getHacknetNode(i, "upgradeLevel"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return purchaseLevelUpgrade(player, node, n); return purchaseLevelUpgrade(player, node, n);
}, },
upgradeRam: function (_i: unknown, _n: unknown = 1): boolean { upgradeRam:
const i = helper.number("upgradeRam", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("upgradeRam", "n", _n); (_i: unknown, _n: unknown = 1): boolean => {
const node = getHacknetNode(i, "upgradeRam"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return purchaseRamUpgrade(player, node, n); return purchaseRamUpgrade(player, node, n);
}, },
upgradeCore: function (_i: unknown, _n: unknown = 1): boolean { upgradeCore:
const i = helper.number("upgradeCore", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("upgradeCore", "n", _n); (_i: unknown, _n: unknown = 1): boolean => {
const node = getHacknetNode(i, "upgradeCore"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return purchaseCoreUpgrade(player, node, n); return purchaseCoreUpgrade(player, node, n);
}, },
upgradeCache: function (_i: unknown, _n: unknown = 1): boolean { upgradeCache:
const i = helper.number("upgradeCache", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("upgradeCache", "n", _n); (_i: unknown, _n: unknown = 1): boolean => {
const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return false; return false;
} }
const node = getHacknetNode(i, "upgradeCache"); const node = getHacknetNode(ctx, i);
if (!(node instanceof HacknetServer)) { if (!(node instanceof HacknetServer)) {
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers"); workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
return false; return false;
@ -126,86 +135,100 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
} }
return res; return res;
}, },
getLevelUpgradeCost: function (_i: unknown, _n: unknown = 1): number { getLevelUpgradeCost:
const i = helper.number("getLevelUpgradeCost", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("getLevelUpgradeCost", "n", _n); (_i: unknown, _n: unknown = 1): number => {
const node = getHacknetNode(i, "upgradeLevel"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult); return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
}, },
getRamUpgradeCost: function (_i: unknown, _n: unknown = 1): number { getRamUpgradeCost:
const i = helper.number("getRamUpgradeCost", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("getRamUpgradeCost", "n", _n); (_i: unknown, _n: unknown = 1): number => {
const node = getHacknetNode(i, "upgradeRam"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult); return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
}, },
getCoreUpgradeCost: function (_i: unknown, _n: unknown = 1): number { getCoreUpgradeCost:
const i = helper.number("getCoreUpgradeCost", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("getCoreUpgradeCost", "n", _n); (_i: unknown, _n: unknown = 1): number => {
const node = getHacknetNode(i, "upgradeCore"); const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
const node = getHacknetNode(ctx, i);
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult); return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
}, },
getCacheUpgradeCost: function (_i: unknown, _n: unknown = 1): number { getCacheUpgradeCost:
const i = helper.number("getCacheUpgradeCost", "i", _i); (ctx: NetscriptContext) =>
const n = helper.number("getCacheUpgradeCost", "n", _n); (_i: unknown, _n: unknown = 1): number => {
const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return Infinity; return Infinity;
} }
const node = getHacknetNode(i, "upgradeCache"); const node = getHacknetNode(ctx, i);
if (!(node instanceof HacknetServer)) { if (!(node instanceof HacknetServer)) {
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers"); workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
return -1; return -1;
} }
return node.calculateCacheUpgradeCost(n); return node.calculateCacheUpgradeCost(n);
}, },
numHashes: function (): number { numHashes: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return 0; return 0;
} }
return player.hashManager.hashes; return player.hashManager.hashes;
}, },
hashCapacity: function (): number { hashCapacity: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return 0; return 0;
} }
return player.hashManager.capacity; return player.hashManager.capacity;
}, },
hashCost: function (_upgName: unknown): number { hashCost:
const upgName = helper.string("hashCost", "upgName", _upgName); (ctx: NetscriptContext) =>
(_upgName: unknown): number => {
const upgName = ctx.helper.string("upgName", _upgName);
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return Infinity; return Infinity;
} }
return player.hashManager.getUpgradeCost(upgName); return player.hashManager.getUpgradeCost(upgName);
}, },
spendHashes: function (_upgName: unknown, _upgTarget: unknown = ""): boolean { spendHashes:
const upgName = helper.string("spendHashes", "upgName", _upgName); (ctx: NetscriptContext) =>
const upgTarget = helper.string("spendHashes", "upgTarget", _upgTarget); (_upgName: unknown, _upgTarget: unknown = ""): boolean => {
const upgName = ctx.helper.string("upgName", _upgName);
const upgTarget = ctx.helper.string("upgTarget", _upgTarget);
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return false; return false;
} }
return purchaseHashUpgrade(player, upgName, upgTarget); return purchaseHashUpgrade(player, upgName, upgTarget);
}, },
getHashUpgrades: function (): string[] { getHashUpgrades: () => (): string[] => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return []; return [];
} }
return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name); return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name);
}, },
getHashUpgradeLevel: function (_upgName: unknown): number { getHashUpgradeLevel:
const upgName = helper.string("getHashUpgradeLevel", "upgName", _upgName); (ctx: NetscriptContext) =>
(_upgName: unknown): number => {
const upgName = ctx.helper.string("upgName", _upgName);
const level = player.hashManager.upgrades[upgName]; const level = player.hashManager.upgrades[upgName];
if (level === undefined) { if (level === undefined) {
throw helper.makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`); throw ctx.makeRuntimeErrorMsg(`Invalid Hash Upgrade: ${upgName}`);
} }
return level; return level;
}, },
getStudyMult: function (): number { getStudyMult: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return 1; return 1;
} }
return player.hashManager.getStudyMult(); return player.hashManager.getStudyMult();
}, },
getTrainingMult: function (): number { getTrainingMult: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers(player)) {
return 1; return 1;
} }

@ -1,9 +1,6 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { FactionWorkType } from "../Faction/FactionWorkTypeEnum"; import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum"; import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { WorkerScript } from "../Netscript/WorkerScript";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers"; import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
@ -17,22 +14,22 @@ import {
SleeveTask, SleeveTask,
} from "../ScriptEditor/NetscriptDefinitions"; } from "../ScriptEditor/NetscriptDefinitions";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): ISleeve { export function NetscriptSleeve(player: IPlayer): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (func: string): void { const checkSleeveAPIAccess = function (ctx: NetscriptContext): void {
if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) { if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) {
throw helper.makeRuntimeErrorMsg( throw ctx.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", "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: string, sleeveNumber: number): void { const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void {
if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) { if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) {
const msg = `Invalid sleeve number: ${sleeveNumber}`; const msg = `Invalid sleeve number: ${sleeveNumber}`;
workerScript.log(func, () => msg); ctx.log(() => msg);
throw helper.makeRuntimeErrorMsg(`sleeve.${func}`, msg); throw ctx.makeRuntimeErrorMsg(msg);
} }
}; };
@ -50,68 +47,70 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
}; };
}; };
const updateRam = (funcName: string): void =>
helper.updateDynamicRam(funcName, getRamCost(player, "sleeve", funcName));
return { return {
getNumSleeves: function (): number { getNumSleeves: (ctx: NetscriptContext) => (): number => {
updateRam("getNumSleeves"); checkSleeveAPIAccess(ctx);
checkSleeveAPIAccess("getNumSleeves");
return player.sleeves.length; return player.sleeves.length;
}, },
setToShockRecovery: function (_sleeveNumber: unknown): boolean { setToShockRecovery:
updateRam("setToShockRecovery"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToShockRecovery", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): boolean => {
checkSleeveAPIAccess("setToShockRecovery"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("setToShockRecovery", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].shockRecovery(player); return player.sleeves[sleeveNumber].shockRecovery(player);
}, },
setToSynchronize: function (_sleeveNumber: unknown): boolean { setToSynchronize:
updateRam("setToSynchronize"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToSynchronize", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): boolean => {
checkSleeveAPIAccess("setToSynchronize"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("setToSynchronize", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].synchronize(player); return player.sleeves[sleeveNumber].synchronize(player);
}, },
setToCommitCrime: function (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean { setToCommitCrime:
updateRam("setToCommitCrime"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToCommitCrime", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean => {
const crimeRoughName = helper.string("setToCommitCrime", "crimeName", _crimeRoughName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("setToCommitCrime"); const crimeRoughName = ctx.helper.string("crimeName", _crimeRoughName);
checkSleeveNumber("setToCommitCrime", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const crime = findCrime(crimeRoughName); const crime = findCrime(crimeRoughName);
if (crime === null) { if (crime === null) {
return false; return false;
} }
return player.sleeves[sleeveNumber].commitCrime(player, crime.name); return player.sleeves[sleeveNumber].commitCrime(player, crime.name);
}, },
setToUniversityCourse: function (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean { setToUniversityCourse:
updateRam("setToUniversityCourse"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToUniversityCourse", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean => {
const universityName = helper.string("setToUniversityCourse", "universityName", _universityName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const className = helper.string("setToUniversityCourse", "className", _className); const universityName = ctx.helper.string("universityName", _universityName);
checkSleeveAPIAccess("setToUniversityCourse"); const className = ctx.helper.string("className", _className);
checkSleeveNumber("setToUniversityCourse", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className); return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
}, },
travel: function (_sleeveNumber: unknown, _cityName: unknown): boolean { travel:
updateRam("travel"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("travel", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, _cityName: unknown): boolean => {
const cityName = helper.string("travel", "cityName", _cityName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("travel"); const cityName = ctx.helper.string("cityName", _cityName);
checkSleeveNumber("travel", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
if (checkEnum(CityName, cityName)) { if (checkEnum(CityName, cityName)) {
return player.sleeves[sleeveNumber].travel(player, cityName); return player.sleeves[sleeveNumber].travel(player, cityName);
} else { } else {
throw helper.makeRuntimeErrorMsg("sleeve.setToCompanyWork", `Invalid city name: '${cityName}'.`); throw ctx.makeRuntimeErrorMsg(`Invalid city name: '${cityName}'.`);
} }
}, },
setToCompanyWork: function (_sleeveNumber: unknown, acompanyName: unknown): boolean { setToCompanyWork:
updateRam("setToCompanyWork"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, acompanyName: unknown): boolean => {
const companyName = helper.string("setToCompanyWork", "companyName", acompanyName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("setToCompanyWork"); const companyName = ctx.helper.string("companyName", acompanyName);
checkSleeveNumber("setToCompanyWork", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same company that another sleeve is working at // Cannot work at the same company that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) { for (let i = 0; i < player.sleeves.length; ++i) {
@ -120,8 +119,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
} }
const other = player.sleeves[i]; const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) { if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(
"sleeve.setToCompanyWork",
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`, `Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
); );
} }
@ -129,17 +127,14 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
return player.sleeves[sleeveNumber].workForCompany(player, companyName); return player.sleeves[sleeveNumber].workForCompany(player, companyName);
}, },
setToFactionWork: function ( setToFactionWork:
_sleeveNumber: unknown, (ctx: NetscriptContext) =>
_factionName: unknown, (_sleeveNumber: unknown, _factionName: unknown, _workType: unknown): boolean | undefined => {
_workType: unknown, const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
): boolean | undefined { const factionName = ctx.helper.string("factionName", _factionName);
updateRam("setToFactionWork"); const workType = ctx.helper.string("workType", _workType);
const sleeveNumber = helper.number("setToFactionWork", "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx);
const factionName = helper.string("setToFactionWork", "factionName", _factionName); checkSleeveNumber(ctx, sleeveNumber);
const workType = helper.string("setToFactionWork", "workType", _workType);
checkSleeveAPIAccess("setToFactionWork");
checkSleeveNumber("setToFactionWork", sleeveNumber);
// Cannot work at the same faction that another sleeve is working at // Cannot work at the same faction that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) { for (let i = 0; i < player.sleeves.length; ++i) {
@ -148,44 +143,45 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
} }
const other = player.sleeves[i]; const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) { if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`, `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
); );
} }
} }
if (player.gang && player.gang.facName == factionName) { if (player.gang && player.gang.facName == factionName) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(
"sleeve.setToFactionWork",
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`, `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
); );
} }
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType); return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType);
}, },
setToGymWorkout: function (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean { setToGymWorkout:
updateRam("setToGymWorkout"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean => {
const gymName = helper.string("setToGymWorkout", "gymName", _gymName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const stat = helper.string("setToGymWorkout", "stat", _stat); const gymName = ctx.helper.string("gymName", _gymName);
checkSleeveAPIAccess("setToGymWorkout"); const stat = ctx.helper.string("stat", _stat);
checkSleeveNumber("setToGymWorkout", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat); return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat);
}, },
getSleeveStats: function (_sleeveNumber: unknown): SleeveSkills { getSleeveStats:
updateRam("getSleeveStats"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("getSleeveStats", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): SleeveSkills => {
checkSleeveAPIAccess("getSleeveStats"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("getSleeveStats", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return getSleeveStats(sleeveNumber); return getSleeveStats(sleeveNumber);
}, },
getTask: function (_sleeveNumber: unknown): SleeveTask { getTask:
updateRam("getTask"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("getTask", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): SleeveTask => {
checkSleeveAPIAccess("getTask"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("getTask", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber]; const sl = player.sleeves[sleeveNumber];
return { return {
@ -196,11 +192,12 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
factionWorkType: FactionWorkType[sl.factionWorkType], factionWorkType: FactionWorkType[sl.factionWorkType],
}; };
}, },
getInformation: function (_sleeveNumber: unknown): SleeveInformation { getInformation:
updateRam("getInformation"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("getInformation", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): SleeveInformation => {
checkSleeveAPIAccess("getInformation"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("getInformation", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber]; const sl = player.sleeves[sleeveNumber];
return { return {
@ -262,11 +259,12 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
workRepGain: sl.getRepGain(player), workRepGain: sl.getRepGain(player),
}; };
}, },
getSleeveAugmentations: function (_sleeveNumber: unknown): string[] { getSleeveAugmentations:
updateRam("getSleeveAugmentations"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("getSleeveAugmentations", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): string[] => {
checkSleeveAPIAccess("getSleeveAugmentations"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("getSleeveAugmentations", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const augs = []; const augs = [];
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) { for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
@ -274,11 +272,12 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
} }
return augs; return augs;
}, },
getSleevePurchasableAugs: function (_sleeveNumber: unknown): AugmentPair[] { getSleevePurchasableAugs:
updateRam("getSleevePurchasableAugs"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("getSleevePurchasableAugs", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown): AugmentPair[] => {
checkSleeveAPIAccess("getSleevePurchasableAugs"); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveNumber("getSleevePurchasableAugs", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player); const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
const augs = []; const augs = [];
@ -292,20 +291,21 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
return augs; return augs;
}, },
purchaseSleeveAug: function (_sleeveNumber: unknown, _augName: unknown): boolean { purchaseSleeveAug:
updateRam("purchaseSleeveAug"); (ctx: NetscriptContext) =>
const sleeveNumber = helper.number("purchaseSleeveAug", "sleeveNumber", _sleeveNumber); (_sleeveNumber: unknown, _augName: unknown): boolean => {
const augName = helper.string("purchaseSleeveAug", "augName", _augName); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("purchaseSleeveAug"); const augName = ctx.helper.string("augName", _augName);
checkSleeveNumber("purchaseSleeveAug", sleeveNumber); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
if (getSleeveStats(sleeveNumber).shock > 0) { if (getSleeveStats(sleeveNumber).shock > 0) {
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`); throw ctx.makeRuntimeErrorMsg(`Sleeve shock too high: Sleeve ${sleeveNumber}`);
} }
const aug = StaticAugmentations[augName]; const aug = StaticAugmentations[augName];
if (!aug) { if (!aug) {
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`); throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
} }
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug); return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);

@ -1,7 +1,5 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling"; import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket"; import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket";
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers"; import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
@ -16,87 +14,90 @@ import {
} from "../StockMarket/StockMarketCosts"; } from "../StockMarket/StockMarketCosts";
import { Stock } from "../StockMarket/Stock"; import { Stock } from "../StockMarket/Stock";
import { TIX } from "../ScriptEditor/NetscriptDefinitions"; import { TIX } from "../ScriptEditor/NetscriptDefinitions";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): TIX { export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript): InternalAPI<TIX> {
/** /**
* Checks if the player has TIX API access. Throws an error if the player does not * Checks if the player has TIX API access. Throws an error if the player does not
*/ */
const checkTixApiAccess = function (callingFn: string): void { const checkTixApiAccess = function (ctx: NetscriptContext): void {
if (!player.hasWseAccount) { if (!player.hasWseAccount) {
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have WSE Access! Cannot use ${callingFn}()`); throw ctx.makeRuntimeErrorMsg(`You don't have WSE Access! Cannot use ${ctx.function}()`);
} }
if (!player.hasTixApiAccess) { if (!player.hasTixApiAccess) {
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have TIX API Access! Cannot use ${callingFn}()`); throw ctx.makeRuntimeErrorMsg(`You don't have TIX API Access! Cannot use ${ctx.function}()`);
} }
}; };
const getStockFromSymbol = function (symbol: string, callingFn: string): Stock { const getStockFromSymbol = function (ctx: NetscriptContext, symbol: string): Stock {
const stock = SymbolToStockMap[symbol]; const stock = SymbolToStockMap[symbol];
if (stock == null) { if (stock == null) {
throw helper.makeRuntimeErrorMsg(callingFn, `Invalid stock symbol: '${symbol}'`); throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: '${symbol}'`);
} }
return stock; return stock;
}; };
const updateRam = (funcName: string): void =>
helper.updateDynamicRam(funcName, getRamCost(player, "stock", funcName));
return { return {
getSymbols: function (): string[] { getSymbols: (ctx: NetscriptContext) => (): string[] => {
updateRam("getSymbols"); checkTixApiAccess(ctx);
checkTixApiAccess("getSymbols");
return Object.values(StockSymbols); return Object.values(StockSymbols);
}, },
getPrice: function (_symbol: unknown): number { getPrice:
updateRam("getPrice"); (ctx: NetscriptContext) =>
const symbol = helper.string("getPrice", "symbol", _symbol); (_symbol: unknown): number => {
checkTixApiAccess("getPrice"); const symbol = ctx.helper.string("symbol", _symbol);
const stock = getStockFromSymbol(symbol, "getPrice"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.price; return stock.price;
}, },
getAskPrice: function (_symbol: unknown): number { getAskPrice:
updateRam("getAskPrice"); (ctx: NetscriptContext) =>
const symbol = helper.string("getAskPrice", "symbol", _symbol); (_symbol: unknown): number => {
checkTixApiAccess("getAskPrice"); const symbol = ctx.helper.string("symbol", _symbol);
const stock = getStockFromSymbol(symbol, "getAskPrice"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getAskPrice(); return stock.getAskPrice();
}, },
getBidPrice: function (_symbol: unknown): number { getBidPrice:
updateRam("getBidPrice"); (ctx: NetscriptContext) =>
const symbol = helper.string("getBidPrice", "symbol", _symbol); (_symbol: unknown): number => {
checkTixApiAccess("getBidPrice"); const symbol = ctx.helper.string("symbol", _symbol);
const stock = getStockFromSymbol(symbol, "getBidPrice"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getBidPrice(); return stock.getBidPrice();
}, },
getPosition: function (_symbol: unknown): [number, number, number, number] { getPosition:
updateRam("getPosition"); (ctx: NetscriptContext) =>
const symbol = helper.string("getPosition", "symbol", _symbol); (_symbol: unknown): [number, number, number, number] => {
checkTixApiAccess("getPosition"); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess(ctx);
const stock = SymbolToStockMap[symbol]; const stock = SymbolToStockMap[symbol];
if (stock == null) { if (stock == null) {
throw helper.makeRuntimeErrorMsg("getPosition", `Invalid stock symbol: ${symbol}`); throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: ${symbol}`);
} }
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
}, },
getMaxShares: function (_symbol: unknown): number { getMaxShares:
updateRam("getMaxShares"); (ctx: NetscriptContext) =>
const symbol = helper.string("getMaxShares", "symbol", _symbol); (_symbol: unknown): number => {
checkTixApiAccess("getMaxShares"); const symbol = ctx.helper.string("symbol", _symbol);
const stock = getStockFromSymbol(symbol, "getMaxShares"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.maxShares; return stock.maxShares;
}, },
getPurchaseCost: function (_symbol: unknown, _shares: unknown, _posType: unknown): number { getPurchaseCost:
updateRam("getPurchaseCost"); (ctx: NetscriptContext) =>
const symbol = helper.string("getPurchaseCost", "symbol", _symbol); (_symbol: unknown, _shares: unknown, _posType: unknown): number => {
let shares = helper.number("getPurchaseCost", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
const posType = helper.string("getPurchaseCost", "posType", _posType); let shares = ctx.helper.number("shares", _shares);
checkTixApiAccess("getPurchaseCost"); const posType = ctx.helper.string("posType", _posType);
const stock = getStockFromSymbol(symbol, "getPurchaseCost"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares); shares = Math.round(shares);
let pos; let pos;
@ -116,13 +117,14 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res; return res;
}, },
getSaleGain: function (_symbol: unknown, _shares: unknown, _posType: unknown): number { getSaleGain:
updateRam("getSaleGain"); (ctx: NetscriptContext) =>
const symbol = helper.string("getSaleGain", "symbol", _symbol); (_symbol: unknown, _shares: unknown, _posType: unknown): number => {
let shares = helper.number("getSaleGain", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
const posType = helper.string("getSaleGain", "posType", _posType); let shares = ctx.helper.number("shares", _shares);
checkTixApiAccess("getSaleGain"); const posType = ctx.helper.string("posType", _posType);
const stock = getStockFromSymbol(symbol, "getSaleGain"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares); shares = Math.round(shares);
let pos; let pos;
@ -142,78 +144,74 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res; return res;
}, },
buy: function (_symbol: unknown, _shares: unknown): number { buy:
updateRam("buy"); (ctx: NetscriptContext) =>
const symbol = helper.string("buy", "symbol", _symbol); (_symbol: unknown, _shares: unknown): number => {
const shares = helper.number("buy", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess("buy"); const shares = ctx.helper.number("shares", _shares);
const stock = getStockFromSymbol(symbol, "buy"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
const res = buyStock(stock, shares, workerScript, {}); const res = buyStock(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0; return res ? stock.getAskPrice() : 0;
}, },
sell: function (_symbol: unknown, _shares: unknown): number { sell:
updateRam("sell"); (ctx: NetscriptContext) =>
const symbol = helper.string("sell", "symbol", _symbol); (_symbol: unknown, _shares: unknown): number => {
const shares = helper.number("sell", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess("sell"); const shares = ctx.helper.number("shares", _shares);
const stock = getStockFromSymbol(symbol, "sell"); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
const res = sellStock(stock, shares, workerScript, {}); const res = sellStock(stock, shares, workerScript, {});
return res ? stock.getBidPrice() : 0; return res ? stock.getBidPrice() : 0;
}, },
short: function (_symbol: unknown, _shares: unknown): number { short:
updateRam("short"); (ctx: NetscriptContext) =>
const symbol = helper.string("short", "symbol", _symbol); (_symbol: unknown, _shares: unknown): number => {
const shares = helper.number("short", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess("short"); const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) { if (player.sourceFileLvl(8) <= 1) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
"short",
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
} }
} }
const stock = getStockFromSymbol(symbol, "short"); const stock = getStockFromSymbol(ctx, symbol);
const res = shortStock(stock, shares, workerScript, {}); const res = shortStock(stock, shares, workerScript, {});
return res ? stock.getBidPrice() : 0; return res ? stock.getBidPrice() : 0;
}, },
sellShort: function (_symbol: unknown, _shares: unknown): number { sellShort:
updateRam("sellShort"); (ctx: NetscriptContext) =>
const symbol = helper.string("sellShort", "symbol", _symbol); (_symbol: unknown, _shares: unknown): number => {
const shares = helper.number("sellShort", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess("sellShort"); const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) { if (player.sourceFileLvl(8) <= 1) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
"sellShort",
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
} }
} }
const stock = getStockFromSymbol(symbol, "sellShort"); const stock = getStockFromSymbol(ctx, symbol);
const res = sellShort(stock, shares, workerScript, {}); const res = sellShort(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0; return res ? stock.getAskPrice() : 0;
}, },
placeOrder: function (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean { placeOrder:
updateRam("placeOrder"); (ctx: NetscriptContext) =>
const symbol = helper.string("placeOrder", "symbol", _symbol); (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
const shares = helper.number("placeOrder", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
const price = helper.number("placeOrder", "price", _price); const shares = ctx.helper.number("shares", _shares);
const type = helper.string("placeOrder", "type", _type); const price = ctx.helper.number("price", _price);
const pos = helper.string("placeOrder", "pos", _pos); const type = ctx.helper.string("type", _type);
checkTixApiAccess("placeOrder"); const pos = ctx.helper.string("pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) { if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
"placeOrder",
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
} }
} }
const stock = getStockFromSymbol(symbol, "placeOrder"); const stock = getStockFromSymbol(ctx, symbol);
let orderType; let orderType;
let orderPos; let orderPos;
@ -227,7 +225,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
} else if (ltype.includes("stop") && ltype.includes("sell")) { } else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell; orderType = OrderTypes.StopSell;
} else { } else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`); throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
} }
const lpos = pos.toLowerCase(); const lpos = pos.toLowerCase();
@ -236,39 +234,28 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
} else if (lpos.includes("s")) { } else if (lpos.includes("s")) {
orderPos = PositionTypes.Short; orderPos = PositionTypes.Short;
} else { } else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`); throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
} }
return placeOrder(stock, shares, price, orderType, orderPos, workerScript); return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
}, },
cancelOrder: function ( cancelOrder:
_symbol: unknown, (ctx: NetscriptContext) =>
_shares: unknown, (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
_price: unknown, const symbol = ctx.helper.string("symbol", _symbol);
_type: unknown, const shares = ctx.helper.number("shares", _shares);
_pos: unknown, const price = ctx.helper.number("price", _price);
): boolean { const type = ctx.helper.string("type", _type);
updateRam("cancelOrder"); const pos = ctx.helper.string("pos", _pos);
const symbol = helper.string("cancelOrder", "symbol", _symbol); checkTixApiAccess(ctx);
const shares = helper.number("cancelOrder", "shares", _shares);
const price = helper.number("cancelOrder", "price", _price);
const type = helper.string("cancelOrder", "type", _type);
const pos = helper.string("cancelOrder", "pos", _pos);
checkTixApiAccess("cancelOrder");
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) { if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
"cancelOrder",
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
} }
} }
const stock = getStockFromSymbol(symbol, "cancelOrder"); const stock = getStockFromSymbol(ctx, symbol);
if (isNaN(shares) || isNaN(price)) { if (isNaN(shares) || isNaN(price)) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg(`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`);
"cancelOrder",
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
);
} }
let orderType; let orderType;
let orderPos; let orderPos;
@ -282,7 +269,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
} else if (ltype.includes("stop") && ltype.includes("sell")) { } else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell; orderType = OrderTypes.StopSell;
} else { } else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`); throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
} }
const lpos = pos.toLowerCase(); const lpos = pos.toLowerCase();
@ -291,7 +278,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
} else if (lpos.includes("s")) { } else if (lpos.includes("s")) {
orderPos = PositionTypes.Short; orderPos = PositionTypes.Short;
} else { } else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`); throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
} }
const params = { const params = {
stock: stock, stock: stock,
@ -302,15 +289,11 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
}; };
return cancelOrder(params, workerScript); return cancelOrder(params, workerScript);
}, },
getOrders: function (): any { getOrders: (ctx: NetscriptContext) => (): any => {
updateRam("getOrders"); checkTixApiAccess(ctx);
checkTixApiAccess("getOrders");
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) { if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg( throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or have Source-File 8 Level 3.");
"getOrders",
"You must either be in BitNode-8 or have Source-File 8 Level 3.",
);
} }
} }
@ -334,103 +317,95 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return orders; return orders;
}, },
getVolatility: function (_symbol: unknown): number { getVolatility:
updateRam("getVolatility"); (ctx: NetscriptContext) =>
const symbol = helper.string("getVolatility", "symbol", _symbol); (_symbol: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
if (!player.has4SDataTixApi) { if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getVolatility", "You don't have 4S Market Data TIX API Access!"); throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!");
} }
const stock = getStockFromSymbol(symbol, "getVolatility"); const stock = getStockFromSymbol(ctx, symbol);
return stock.mv / 100; // Convert from percentage to decimal return stock.mv / 100; // Convert from percentage to decimal
}, },
getForecast: function (_symbol: unknown): number { getForecast:
updateRam("getForecast"); (ctx: NetscriptContext) =>
const symbol = helper.string("getForecast", "symbol", _symbol); (_symbol: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
if (!player.has4SDataTixApi) { if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getForecast", "You don't have 4S Market Data TIX API Access!"); throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!");
} }
const stock = getStockFromSymbol(symbol, "getForecast"); const stock = getStockFromSymbol(ctx, symbol);
let forecast = 50; let forecast = 50;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag); stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal return forecast / 100; // Convert from percentage to decimal
}, },
purchase4SMarketData: function (): boolean { purchase4SMarketData: (ctx: NetscriptContext) => (): boolean => {
updateRam("purchase4SMarketData");
if (player.has4SData) { if (player.has4SData) {
workerScript.log("stock.purchase4SMarketData", () => "Already purchased 4S Market Data."); ctx.log(() => "Already purchased 4S Market Data.");
return true; return true;
} }
if (player.money < getStockMarket4SDataCost()) { if (player.money < getStockMarket4SDataCost()) {
workerScript.log("stock.purchase4SMarketData", () => "Not enough money to purchase 4S Market Data."); ctx.log(() => "Not enough money to purchase 4S Market Data.");
return false; return false;
} }
player.has4SData = true; player.has4SData = true;
player.loseMoney(getStockMarket4SDataCost(), "stock"); player.loseMoney(getStockMarket4SDataCost(), "stock");
workerScript.log("stock.purchase4SMarketData", () => "Purchased 4S Market Data"); ctx.log(() => "Purchased 4S Market Data");
return true; return true;
}, },
purchase4SMarketDataTixApi: function (): boolean { purchase4SMarketDataTixApi: (ctx: NetscriptContext) => (): boolean => {
updateRam("purchase4SMarketDataTixApi"); checkTixApiAccess(ctx);
checkTixApiAccess("purchase4SMarketDataTixApi");
if (player.has4SDataTixApi) { if (player.has4SDataTixApi) {
workerScript.log("stock.purchase4SMarketDataTixApi", () => "Already purchased 4S Market Data TIX API"); ctx.log(() => "Already purchased 4S Market Data TIX API");
return true; return true;
} }
if (player.money < getStockMarket4STixApiCost()) { if (player.money < getStockMarket4STixApiCost()) {
workerScript.log( ctx.log(() => "Not enough money to purchase 4S Market Data TIX API");
"stock.purchase4SMarketDataTixApi",
() => "Not enough money to purchase 4S Market Data TIX API",
);
return false; return false;
} }
player.has4SDataTixApi = true; player.has4SDataTixApi = true;
player.loseMoney(getStockMarket4STixApiCost(), "stock"); player.loseMoney(getStockMarket4STixApiCost(), "stock");
workerScript.log("stock.purchase4SMarketDataTixApi", () => "Purchased 4S Market Data TIX API"); ctx.log(() => "Purchased 4S Market Data TIX API");
return true; return true;
}, },
purchaseWseAccount: function (): boolean { purchaseWseAccount: (ctx: NetscriptContext) => (): boolean => {
updateRam("PurchaseWseAccount");
if (player.hasWseAccount) { if (player.hasWseAccount) {
workerScript.log("stock.purchaseWseAccount", () => "Already purchased WSE Account"); ctx.log(() => "Already purchased WSE Account");
return true; return true;
} }
if (player.money < getStockMarketWseCost()) { if (player.money < getStockMarketWseCost()) {
workerScript.log("stock.purchaseWseAccount", () => "Not enough money to purchase WSE Account Access"); ctx.log(() => "Not enough money to purchase WSE Account Access");
return false; return false;
} }
player.hasWseAccount = true; player.hasWseAccount = true;
initStockMarketFn(); initStockMarketFn();
player.loseMoney(getStockMarketWseCost(), "stock"); player.loseMoney(getStockMarketWseCost(), "stock");
workerScript.log("stock.purchaseWseAccount", () => "Purchased WSE Account Access"); ctx.log(() => "Purchased WSE Account Access");
return true; return true;
}, },
purchaseTixApi: function (): boolean { purchaseTixApi: (ctx: NetscriptContext) => (): boolean => {
updateRam("purchaseTixApi");
if (player.hasTixApiAccess) { if (player.hasTixApiAccess) {
workerScript.log("stock.purchaseTixApi", () => "Already purchased TIX API"); ctx.log(() => "Already purchased TIX API");
return true; return true;
} }
if (player.money < getStockMarketTixApiCost()) { if (player.money < getStockMarketTixApiCost()) {
workerScript.log("stock.purchaseTixApi", () => "Not enough money to purchase TIX API Access"); ctx.log(() => "Not enough money to purchase TIX API Access");
return false; return false;
} }
player.hasTixApiAccess = true; player.hasTixApiAccess = true;
player.loseMoney(getStockMarketTixApiCost(), "stock"); player.loseMoney(getStockMarketTixApiCost(), "stock");
workerScript.log("stock.purchaseTixApi", () => "Purchased TIX API"); ctx.log(() => "Purchased TIX API");
return true; return true;
}, },
}; };

@ -1,7 +1,3 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { import {
GameInfo, GameInfo,
IStyleSettings, IStyleSettings,
@ -14,26 +10,21 @@ import { defaultTheme } from "../Themes/Themes";
import { defaultStyles } from "../Themes/Styles"; import { defaultStyles } from "../Themes/Styles";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { hash } from "../hash/hash"; import { hash } from "../hash/hash";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
export function NetscriptUserInterface( export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): IUserInterface {
const updateRam = (funcName: string): void => helper.updateDynamicRam(funcName, getRamCost(player, "ui", funcName));
return { return {
getTheme: function (): UserInterfaceTheme { getTheme: () => (): UserInterfaceTheme => {
updateRam("getTheme");
return { ...Settings.theme }; return { ...Settings.theme };
}, },
getStyles: function (): IStyleSettings { getStyles: () => (): IStyleSettings => {
updateRam("getStyles");
return { ...Settings.styles }; return { ...Settings.styles };
}, },
setTheme: function (newTheme: UserInterfaceTheme): void { setTheme:
updateRam("setTheme"); (ctx: NetscriptContext) =>
(newTheme: UserInterfaceTheme): void => {
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/; const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
const currentTheme = { ...Settings.theme }; const currentTheme = { ...Settings.theme };
const errors: string[] = []; const errors: string[] = [];
@ -51,15 +42,15 @@ export function NetscriptUserInterface(
if (errors.length === 0) { if (errors.length === 0) {
Object.assign(Settings.theme, currentTheme); Object.assign(Settings.theme, currentTheme);
ThemeEvents.emit(); ThemeEvents.emit();
workerScript.log("ui.setTheme", () => `Successfully set theme`); ctx.log(() => `Successfully set theme`);
} else { } else {
workerScript.log("ui.setTheme", () => `Failed to set theme. Errors: ${errors.join(", ")}`); ctx.log(() => `Failed to set theme. Errors: ${errors.join(", ")}`);
} }
}, },
setStyles: function (newStyles: IStyleSettings): void { setStyles:
updateRam("setStyles"); (ctx: NetscriptContext) =>
(newStyles: IStyleSettings): void => {
const currentStyles = { ...Settings.styles }; const currentStyles = { ...Settings.styles };
const errors: string[] = []; const errors: string[] = [];
for (const key of Object.keys(newStyles)) { for (const key of Object.keys(newStyles)) {
@ -74,28 +65,25 @@ export function NetscriptUserInterface(
if (errors.length === 0) { if (errors.length === 0) {
Object.assign(Settings.styles, currentStyles); Object.assign(Settings.styles, currentStyles);
ThemeEvents.emit(); ThemeEvents.emit();
workerScript.log("ui.setStyles", () => `Successfully set styles`); ctx.log(() => `Successfully set styles`);
} else { } else {
workerScript.log("ui.setStyles", () => `Failed to set styles. Errors: ${errors.join(", ")}`); ctx.log(() => `Failed to set styles. Errors: ${errors.join(", ")}`);
} }
}, },
resetTheme: function (): void { resetTheme: (ctx: NetscriptContext) => (): void => {
updateRam("resetTheme");
Settings.theme = { ...defaultTheme }; Settings.theme = { ...defaultTheme };
ThemeEvents.emit(); ThemeEvents.emit();
workerScript.log("ui.resetTheme", () => `Reinitialized theme to default`); ctx.log(() => `Reinitialized theme to default`);
}, },
resetStyles: function (): void { resetStyles: (ctx: NetscriptContext) => (): void => {
updateRam("resetStyles");
Settings.styles = { ...defaultStyles }; Settings.styles = { ...defaultStyles };
ThemeEvents.emit(); ThemeEvents.emit();
workerScript.log("ui.resetStyles", () => `Reinitialized styles to default`); ctx.log(() => `Reinitialized styles to default`);
}, },
getGameInfo: function (): GameInfo { getGameInfo: () => (): GameInfo => {
updateRam("getGameInfo");
const version = CONSTANTS.VersionString; const version = CONSTANTS.VersionString;
const commit = hash(); const commit = hash();
const platform = navigator.userAgent.toLowerCase().indexOf(" electron/") > -1 ? "Steam" : "Browser"; const platform = navigator.userAgent.toLowerCase().indexOf(" electron/") > -1 ? "Steam" : "Browser";