Merge pull request #3627 from danielyxie/wrap-api

MISC: Wrap most of the API in the new api wrapper
This commit is contained in:
hydroflame 2022-05-19 01:37:11 -04:00 committed by GitHub
commit a6048cdd84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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,353 +17,354 @@ 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);
return action.reqdRank; const action: any = getBladeburnerActionObject(ctx, "blackops", blackOpName);
}, return action.reqdRank;
getGeneralActionNames: function (): string[] { },
updateRam("getGeneralActionNames"); getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess("getGeneralActionNames"); 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");
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);
const bladeburner = player.bladeburner; checkBladeburnerAccess(ctx);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const bladeburner = player.bladeburner;
try { if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.startActionNetscriptFn(player, type, name, workerScript); try {
} catch (e: any) { return bladeburner.startActionNetscriptFn(player, type, name, workerScript);
throw helper.makeRuntimeErrorMsg("bladeburner.startAction", e); } catch (e: any) {
} throw ctx.makeRuntimeErrorMsg(e);
}, }
stopBladeburnerAction: function (): void { },
updateRam("stopBladeburnerAction"); stopBladeburnerAction: (ctx: NetscriptContext) => (): void => {
checkBladeburnerAccess("stopBladeburnerAction"); 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");
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);
const bladeburner = player.bladeburner; checkBladeburnerAccess(ctx);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const bladeburner = player.bladeburner;
try { if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript); try {
} catch (e: any) { return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
throw helper.makeRuntimeErrorMsg("bladeburner.getActionTime", e); } catch (e: any) {
} throw ctx.makeRuntimeErrorMsg(e);
}, }
getActionEstimatedSuccessChance: function (_type: unknown, _name: unknown): [number, number] { },
updateRam("getActionEstimatedSuccessChance"); getActionEstimatedSuccessChance:
const type = helper.string("getActionEstimatedSuccessChance", "type", _type); (ctx: NetscriptContext) =>
const name = helper.string("getActionEstimatedSuccessChance", "name", _name); (_type: unknown, _name: unknown): [number, number] => {
checkBladeburnerAccess("getActionEstimatedSuccessChance"); const type = ctx.helper.string("type", _type);
const bladeburner = player.bladeburner; const name = ctx.helper.string("name", _name);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); checkBladeburnerAccess(ctx);
try { const bladeburner = player.bladeburner;
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
} catch (e: any) { try {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e); return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
} } catch (e: any) {
}, throw ctx.makeRuntimeErrorMsg(e);
getActionRepGain: function (_type: unknown, _name: unknown, _level: unknown): number { }
updateRam("getActionRepGain"); },
const type = helper.string("getActionRepGain", "type", _type); getActionRepGain:
const name = helper.string("getActionRepGain", "name", _name); (ctx: NetscriptContext) =>
const level = helper.number("getActionRepGain", "level", _level); (_type: unknown, _name: unknown, _level: unknown): number => {
checkBladeburnerAccess("getActionRepGain"); const type = ctx.helper.string("type", _type);
const action = getBladeburnerActionObject("getActionRepGain", type, name); const name = ctx.helper.string("name", _name);
let rewardMultiplier; const level = ctx.helper.number("level", _level);
if (level == null || isNaN(level)) { checkBladeburnerAccess(ctx);
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1); const action = getBladeburnerActionObject(ctx, type, name);
} else { let rewardMultiplier;
rewardMultiplier = Math.pow(action.rewardFac, level - 1); if (level == null || isNaN(level)) {
} rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
} else {
rewardMultiplier = Math.pow(action.rewardFac, level - 1);
}
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);
const bladeburner = player.bladeburner; checkBladeburnerAccess(ctx);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const bladeburner = player.bladeburner;
try { if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); try {
} catch (e: any) { return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
throw helper.makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e); } catch (e: any) {
} throw ctx.makeRuntimeErrorMsg(e);
}, }
getActionMaxLevel: function (_type: unknown, _name: unknown): number { },
updateRam("getActionMaxLevel"); getActionMaxLevel:
const type = helper.string("getActionMaxLevel", "type", _type); (ctx: NetscriptContext) =>
const name = helper.string("getActionMaxLevel", "name", _name); (_type: unknown, _name: unknown): number => {
checkBladeburnerAccess("getActionMaxLevel"); const type = ctx.helper.string("type", _type);
const action = getBladeburnerActionObject("getActionMaxLevel", type, name); const name = ctx.helper.string("name", _name);
return action.maxLevel; checkBladeburnerAccess(ctx);
}, const action = getBladeburnerActionObject(ctx, type, name);
getActionCurrentLevel: function (_type: unknown, _name: unknown): number { return action.maxLevel;
updateRam("getActionCurrentLevel"); },
const type = helper.string("getActionCurrentLevel", "type", _type); getActionCurrentLevel:
const name = helper.string("getActionCurrentLevel", "name", _name); (ctx: NetscriptContext) =>
checkBladeburnerAccess("getActionCurrentLevel"); (_type: unknown, _name: unknown): number => {
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); const type = ctx.helper.string("type", _type);
return action.level; const name = ctx.helper.string("name", _name);
}, checkBladeburnerAccess(ctx);
getActionAutolevel: function (_type: unknown, _name: unknown): boolean { const action = getBladeburnerActionObject(ctx, type, name);
updateRam("getActionAutolevel"); return action.level;
const type = helper.string("getActionAutolevel", "type", _type); },
const name = helper.string("getActionAutolevel", "name", _name); getActionAutolevel:
checkBladeburnerAccess("getActionAutolevel"); (ctx: NetscriptContext) =>
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); (_type: unknown, _name: unknown): boolean => {
return action.autoLevel; const type = ctx.helper.string("type", _type);
}, const name = ctx.helper.string("name", _name);
setActionAutolevel: function (_type: unknown, _name: unknown, _autoLevel: unknown = true): void { checkBladeburnerAccess(ctx);
updateRam("setActionAutolevel"); const action = getBladeburnerActionObject(ctx, type, name);
const type = helper.string("setActionAutolevel", "type", _type); return action.autoLevel;
const name = helper.string("setActionAutolevel", "name", _name); },
const autoLevel = helper.boolean(_autoLevel); setActionAutolevel:
checkBladeburnerAccess("setActionAutolevel"); (ctx: NetscriptContext) =>
const action = getBladeburnerActionObject("setActionAutolevel", type, name); (_type: unknown, _name: unknown, _autoLevel: unknown = true): void => {
action.autoLevel = autoLevel; const type = ctx.helper.string("type", _type);
}, const name = ctx.helper.string("name", _name);
setActionLevel: function (_type: unknown, _name: unknown, _level: unknown = 1): void { const autoLevel = ctx.helper.boolean(_autoLevel);
updateRam("setActionLevel"); checkBladeburnerAccess(ctx);
const type = helper.string("setActionLevel", "type", _type); const action = getBladeburnerActionObject(ctx, type, name);
const name = helper.string("setActionLevel", "name", _name); action.autoLevel = autoLevel;
const level = helper.number("setActionLevel", "level", _level); },
checkBladeburnerAccess("setActionLevel"); setActionLevel:
const action = getBladeburnerActionObject("setActionLevel", type, name); (ctx: NetscriptContext) =>
if (level < 1 || level > action.maxLevel) { (_type: unknown, _name: unknown, _level: unknown = 1): void => {
throw helper.makeRuntimeErrorMsg( const type = ctx.helper.string("type", _type);
"bladeburner.setActionLevel", const name = ctx.helper.string("name", _name);
`Level must be between 1 and ${action.maxLevel}, is ${level}`, const level = ctx.helper.number("level", _level);
); checkBladeburnerAccess(ctx);
} const action = getBladeburnerActionObject(ctx, type, name);
action.level = level; if (level < 1 || level > action.maxLevel) {
}, ctx.helper.makeRuntimeErrorMsg(`Level must be between 1 and ${action.maxLevel}, is ${level}`);
getRank: function (): number { }
updateRam("getRank"); action.level = level;
checkBladeburnerAccess("getRank"); },
getRank: (ctx: NetscriptContext) => (): number => {
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");
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);
const bladeburner = player.bladeburner; checkBladeburnerAccess(ctx);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const bladeburner = player.bladeburner;
try { if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); try {
} catch (e: any) { return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillLevel", e); } catch (e: any) {
} throw ctx.makeRuntimeErrorMsg(e);
}, }
getSkillUpgradeCost: function (_skillName: unknown): number { },
updateRam("getSkillUpgradeCost"); getSkillUpgradeCost:
const skillName = helper.string("getSkillUpgradeCost", "skillName", _skillName); (ctx: NetscriptContext) =>
checkBladeburnerAccess("getSkillUpgradeCost"); (_skillName: unknown): number => {
const bladeburner = player.bladeburner; const skillName = ctx.helper.string("skillName", _skillName);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); checkBladeburnerAccess(ctx);
try { const bladeburner = player.bladeburner;
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
} catch (e: any) { try {
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e); return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
} } catch (e: any) {
}, throw ctx.makeRuntimeErrorMsg(e);
upgradeSkill: function (_skillName: unknown): boolean { }
updateRam("upgradeSkill"); },
const skillName = helper.string("upgradeSkill", "skillName", _skillName); upgradeSkill:
checkBladeburnerAccess("upgradeSkill"); (ctx: NetscriptContext) =>
const bladeburner = player.bladeburner; (_skillName: unknown): boolean => {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const skillName = ctx.helper.string("skillName", _skillName);
try { checkBladeburnerAccess(ctx);
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); const bladeburner = player.bladeburner;
} catch (e: any) { if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
throw helper.makeRuntimeErrorMsg("bladeburner.upgradeSkill", e); try {
} return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
}, } catch (e: any) {
getTeamSize: function (_type: unknown, _name: unknown): number { throw ctx.makeRuntimeErrorMsg(e);
updateRam("getTeamSize"); }
const type = helper.string("getTeamSize", "type", _type); },
const name = helper.string("getTeamSize", "name", _name); getTeamSize:
checkBladeburnerAccess("getTeamSize"); (ctx: NetscriptContext) =>
const bladeburner = player.bladeburner; (_type: unknown, _name: unknown): number => {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const type = ctx.helper.string("type", _type);
try { const name = ctx.helper.string("name", _name);
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); checkBladeburnerAccess(ctx);
} catch (e: any) { const bladeburner = player.bladeburner;
throw helper.makeRuntimeErrorMsg("bladeburner.getTeamSize", e); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
} try {
}, return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
setTeamSize: function (_type: unknown, _name: unknown, _size: unknown): number { } catch (e: any) {
updateRam("setTeamSize"); throw ctx.makeRuntimeErrorMsg(e);
const type = helper.string("setTeamSize", "type", _type); }
const name = helper.string("setTeamSize", "name", _name); },
const size = helper.number("setTeamSize", "size", _size); setTeamSize:
checkBladeburnerAccess("setTeamSize"); (ctx: NetscriptContext) =>
const bladeburner = player.bladeburner; (_type: unknown, _name: unknown, _size: unknown): number => {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const type = ctx.helper.string("type", _type);
try { const name = ctx.helper.string("name", _name);
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); const size = ctx.helper.number("size", _size);
} catch (e: any) { checkBladeburnerAccess(ctx);
throw helper.makeRuntimeErrorMsg("bladeburner.setTeamSize", e); const bladeburner = player.bladeburner;
} if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
}, try {
getCityEstimatedPopulation: function (_cityName: unknown): number { return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
updateRam("getCityEstimatedPopulation"); } catch (e: any) {
const cityName = helper.string("getCityEstimatedPopulation", "cityName", _cityName); throw ctx.makeRuntimeErrorMsg(e);
checkBladeburnerAccess("getCityEstimatedPopulation"); }
checkBladeburnerCity("getCityEstimatedPopulation", cityName); },
const bladeburner = player.bladeburner; getCityEstimatedPopulation:
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); (ctx: NetscriptContext) =>
return bladeburner.cities[cityName].popEst; (_cityName: unknown): number => {
}, const cityName = ctx.helper.string("cityName", _cityName);
getCityCommunities: function (_cityName: unknown): number { checkBladeburnerAccess(ctx);
updateRam("getCityCommunities"); checkBladeburnerCity(ctx, cityName);
const cityName = helper.string("getCityCommunities", "cityName", _cityName); const bladeburner = player.bladeburner;
checkBladeburnerAccess("getCityCommunities"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
checkBladeburnerCity("getCityCommunities", cityName); return bladeburner.cities[cityName].popEst;
const bladeburner = player.bladeburner; },
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); getCityCommunities:
return bladeburner.cities[cityName].comms; (ctx: NetscriptContext) =>
}, (_cityName: unknown): number => {
getCityChaos: function (_cityName: unknown): number { const cityName = ctx.helper.string("cityName", _cityName);
updateRam("getCityChaos"); checkBladeburnerAccess(ctx);
const cityName = helper.string("getCityChaos", "cityName", _cityName); checkBladeburnerCity(ctx, cityName);
checkBladeburnerAccess("getCityChaos"); const bladeburner = player.bladeburner;
checkBladeburnerCity("getCityChaos", cityName); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
const bladeburner = player.bladeburner; return bladeburner.cities[cityName].comms;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); },
return bladeburner.cities[cityName].chaos; getCityChaos:
}, (ctx: NetscriptContext) =>
getCity: function (): string { (_cityName: unknown): number => {
updateRam("getCity"); const cityName = ctx.helper.string("cityName", _cityName);
checkBladeburnerAccess("getCityChaos"); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].chaos;
},
getCity: (ctx: NetscriptContext) => (): string => {
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");
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);
const bladeburner = player.bladeburner; checkBladeburnerCity(ctx, cityName);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); const bladeburner = player.bladeburner;
bladeburner.city = cityName; if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return true; bladeburner.city = cityName;
}, return true;
getStamina: function (): [number, number] { },
updateRam("getStamina"); getStamina: (ctx: NetscriptContext) => (): [number, number] => {
checkBladeburnerAccess("getStamina"); 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");
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,132 +1,124 @@
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:
answer: any, (ctx: NetscriptContext) =>
_filename: unknown, (
_hostname: unknown = workerScript.hostname, answer: any,
{ returnReward }: CodingAttemptOptions = { returnReward: false }, _filename: unknown,
): boolean | string { _hostname: unknown = workerScript.hostname,
updateRam("attempt"); { returnReward }: CodingAttemptOptions = { returnReward: false },
const filename = helper.string("attempt", "filename", _filename); ): boolean | string => {
const hostname = helper.string("attempt", "hostname", _hostname); const filename = ctx.helper.string("filename", _filename);
const contract = getCodingContract("attempt", hostname, filename); const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "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
if (is2DArray(answer)) { if (is2DArray(answer)) {
const answerComponents = []; const answerComponents = [];
for (let i = 0; i < answer.length; ++i) { for (let i = 0; i < answer.length; ++i) {
answerComponents.push(["[", answer[i].toString(), "]"].join("")); answerComponents.push(["[", answer[i].toString(), "]"].join(""));
}
answer = answerComponents.join(",");
} else {
answer = String(answer);
}
const creward = contract.reward;
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
const serv = helper.getServer(hostname, "codingcontract.attempt");
if (contract.isSolution(answer)) {
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
workerScript.log(
"codingcontract.attempt",
() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`,
);
serv.removeContract(filename);
return returnReward ? reward : true;
} else {
++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) {
workerScript.log(
"codingcontract.attempt",
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
);
serv.removeContract(filename);
} else {
workerScript.log(
"codingcontract.attempt",
() =>
`Coding Contract attempt '${filename}' failed. ${
contract.getMaxNumTries() - contract.tries
} attempts remaining.`,
);
}
return returnReward ? "" : false;
}
},
getContractType: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string {
updateRam("getContractType");
const filename = helper.string("getContractType", "filename", _filename);
const hostname = helper.string("getContractType", "hostname", _hostname);
const contract = getCodingContract("getContractType", hostname, filename);
return contract.getType();
},
getData: function (_filename: unknown, _hostname: unknown = workerScript.hostname): any {
updateRam("getData");
const filename = helper.string("getContractType", "filename", _filename);
const hostname = helper.string("getContractType", "hostname", _hostname);
const contract = getCodingContract("getData", hostname, filename);
const data = contract.getData();
if (data.constructor === Array) {
// For two dimensional arrays, we have to copy the internal arrays using
// slice() as well. As of right now, no contract has arrays that have
// more than two dimensions
const copy = data.slice();
for (let i = 0; i < copy.length; ++i) {
if (data[i].constructor === Array) {
copy[i] = data[i].slice();
} }
answer = answerComponents.join(",");
} else {
answer = String(answer);
} }
return copy; const creward = contract.reward;
} else { if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
return data;
} const serv = ctx.helper.getServer(hostname);
}, if (contract.isSolution(answer)) {
getDescription: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string { const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
updateRam("getDescription"); ctx.log(() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
const filename = helper.string("getDescription", "filename", _filename); serv.removeContract(filename);
const hostname = helper.string("getDescription", "hostname", _hostname); return returnReward ? reward : true;
const contract = getCodingContract("getDescription", hostname, filename); } else {
return contract.getDescription(); ++contract.tries;
}, if (contract.tries >= contract.getMaxNumTries()) {
getNumTriesRemaining: function (_filename: unknown, _hostname: unknown = workerScript.hostname): number { ctx.log(() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`);
updateRam("getNumTriesRemaining"); serv.removeContract(filename);
const filename = helper.string("getNumTriesRemaining", "filename", _filename); } else {
const hostname = helper.string("getNumTriesRemaining", "hostname", _hostname); ctx.log(
const contract = getCodingContract("getNumTriesRemaining", hostname, filename); () =>
return contract.getMaxNumTries() - contract.tries; `Coding Contract attempt '${filename}' failed. ${
}, contract.getMaxNumTries() - contract.tries
} attempts remaining.`,
);
}
return returnReward ? "" : false;
}
},
getContractType:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
const filename = ctx.helper.string("filename", _filename);
const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getContractType", hostname, filename);
return contract.getType();
},
getData:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = workerScript.hostname): any => {
const filename = ctx.helper.string("filename", _filename);
const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getData", hostname, filename);
const data = contract.getData();
if (data.constructor === Array) {
// For two dimensional arrays, we have to copy the internal arrays using
// slice() as well. As of right now, no contract has arrays that have
// more than two dimensions
const copy = data.slice();
for (let i = 0; i < copy.length; ++i) {
if (data[i].constructor === Array) {
copy[i] = data[i].slice();
}
}
return copy;
} else {
return data;
}
},
getDescription:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
const filename = ctx.helper.string("filename", _filename);
const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getDescription", hostname, filename);
return contract.getDescription();
},
getNumTriesRemaining:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = workerScript.hostname): number => {
const filename = ctx.helper.string("filename", _filename);
const hostname = ctx.helper.string("hostname", _hostname);
const contract = getCodingContract(ctx, "getNumTriesRemaining", hostname, filename);
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,63 +18,60 @@ 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 => {
// this list is copied from Faction/ui/Root.tsx const faction = ctx.helper.string("faction", _faction);
// 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;
if (player.inGang()) return false; if (player.inGang()) return false;
if (!player.factions.includes(faction)) return false; if (!player.factions.includes(faction)) return false;
const isHacking = faction === FactionNames.NiteSec || faction === FactionNames.TheBlackHand; const isHacking = faction === FactionNames.NiteSec || faction === FactionNames.TheBlackHand;
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,242 +98,251 @@ 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);
const gang = player.gang; checkGangApiAccess(ctx);
if (gang === null) throw new Error("Should not be called without Gang"); const gang = player.gang;
const member = getGangMember("getMemberInformation", memberName); if (gang === null) throw new Error("Should not be called without Gang");
return { const member = getGangMember(ctx, memberName);
name: member.name, return {
task: member.task, name: member.name,
earnedRespect: member.earnedRespect, task: member.task,
hack: member.hack, earnedRespect: member.earnedRespect,
str: member.str, hack: member.hack,
def: member.def, str: member.str,
dex: member.dex, def: member.def,
agi: member.agi, dex: member.dex,
cha: member.cha, agi: member.agi,
cha: member.cha,
hack_exp: member.hack_exp, hack_exp: member.hack_exp,
str_exp: member.str_exp, str_exp: member.str_exp,
def_exp: member.def_exp, def_exp: member.def_exp,
dex_exp: member.dex_exp, dex_exp: member.dex_exp,
agi_exp: member.agi_exp, agi_exp: member.agi_exp,
cha_exp: member.cha_exp, cha_exp: member.cha_exp,
hack_mult: member.hack_mult, hack_mult: member.hack_mult,
str_mult: member.str_mult, str_mult: member.str_mult,
def_mult: member.def_mult, def_mult: member.def_mult,
dex_mult: member.dex_mult, dex_mult: member.dex_mult,
agi_mult: member.agi_mult, agi_mult: member.agi_mult,
cha_mult: member.cha_mult, cha_mult: member.cha_mult,
hack_asc_mult: member.calculateAscensionMult(member.hack_asc_points), hack_asc_mult: member.calculateAscensionMult(member.hack_asc_points),
str_asc_mult: member.calculateAscensionMult(member.str_asc_points), str_asc_mult: member.calculateAscensionMult(member.str_asc_points),
def_asc_mult: member.calculateAscensionMult(member.def_asc_points), def_asc_mult: member.calculateAscensionMult(member.def_asc_points),
dex_asc_mult: member.calculateAscensionMult(member.dex_asc_points), dex_asc_mult: member.calculateAscensionMult(member.dex_asc_points),
agi_asc_mult: member.calculateAscensionMult(member.agi_asc_points), agi_asc_mult: member.calculateAscensionMult(member.agi_asc_points),
cha_asc_mult: member.calculateAscensionMult(member.cha_asc_points), cha_asc_mult: member.calculateAscensionMult(member.cha_asc_points),
hack_asc_points: member.hack_asc_points, hack_asc_points: member.hack_asc_points,
str_asc_points: member.str_asc_points, str_asc_points: member.str_asc_points,
def_asc_points: member.def_asc_points, def_asc_points: member.def_asc_points,
dex_asc_points: member.dex_asc_points, dex_asc_points: member.dex_asc_points,
agi_asc_points: member.agi_asc_points, agi_asc_points: member.agi_asc_points,
cha_asc_points: member.cha_asc_points, cha_asc_points: member.cha_asc_points,
upgrades: member.upgrades.slice(), upgrades: member.upgrades.slice(),
augmentations: member.augmentations.slice(), augmentations: member.augmentations.slice(),
respectGain: member.calculateRespectGain(gang), respectGain: member.calculateRespectGain(gang),
wantedLevelGain: member.calculateWantedLevelGain(gang), wantedLevelGain: member.calculateWantedLevelGain(gang),
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);
const gang = player.gang; checkGangApiAccess(ctx);
if (gang === null) throw new Error("Should not be called without Gang"); const gang = player.gang;
const recruited = gang.recruitMember(memberName); if (gang === null) throw new Error("Should not be called without Gang");
if (recruited) { const recruited = gang.recruitMember(memberName);
workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`); if (recruited) {
} else { workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`);
workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`); } else {
} workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`);
}
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 gang = player.gang; const member = getGangMember(ctx, memberName);
if (gang === null) throw new Error("Should not be called without Gang"); const gang = player.gang;
if (!gang.getAllTaskNames().includes(taskName)) { if (gang === null) throw new Error("Should not be called without Gang");
workerScript.log( if (!gang.getAllTaskNames().includes(taskName)) {
"gang.setMemberTask", workerScript.log(
() => "gang.setMemberTask",
`Failed to assign Gang Member '${memberName}' to Invalid task '${taskName}'. '${memberName}' is now Unassigned`, () =>
); `Failed to assign Gang Member '${memberName}' to Invalid task '${taskName}'. '${memberName}' is now Unassigned`,
return member.assignToTask("Unassigned"); );
} return member.assignToTask("Unassigned");
const success = member.assignToTask(taskName); }
if (success) { const success = member.assignToTask(taskName);
workerScript.log( if (success) {
"gang.setMemberTask", workerScript.log(
() => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`, "gang.setMemberTask",
); () => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`,
} else { );
workerScript.log( } else {
"gang.setMemberTask", workerScript.log(
() => `Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`, "gang.setMemberTask",
); () =>
} `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 copy = Object.assign({}, task); const task = getGangTask(ctx, taskName);
copy.territory = Object.assign({}, task.territory); const copy = Object.assign({}, task);
return copy; copy.territory = Object.assign({}, task.territory);
}, return copy;
getEquipmentNames: function (): string[] { },
updateRam("getEquipmentNames"); getEquipmentNames: (ctx: NetscriptContext) => (): string[] => {
checkGangApiAccess("getEquipmentNames"); checkGangApiAccess(ctx);
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);
const gang = player.gang; checkGangApiAccess(ctx);
if (gang === null) throw new Error("Should not be called without Gang"); const gang = player.gang;
const upg = GangMemberUpgrades[equipName]; if (gang === null) throw new Error("Should not be called without Gang");
if (upg === null) return Infinity; const upg = GangMemberUpgrades[equipName];
return gang.getUpgradeCost(upg); if (upg === null) return Infinity;
}, return gang.getUpgradeCost(upg);
getEquipmentType: function (_equipName: unknown): string { },
updateRam("getEquipmentType"); getEquipmentType:
const equipName = helper.string("getEquipmentType", "equipName", _equipName); (ctx: NetscriptContext) =>
checkGangApiAccess("getEquipmentType"); (_equipName: unknown): string => {
const upg = GangMemberUpgrades[equipName]; const equipName = ctx.helper.string("equipName", _equipName);
if (upg == null) return ""; checkGangApiAccess(ctx);
return upg.getType(); const upg = GangMemberUpgrades[equipName];
}, if (upg == null) return "";
getEquipmentStats: function (_equipName: unknown): EquipmentStats { return upg.getType();
updateRam("getEquipmentStats"); },
const equipName = helper.string("getEquipmentStats", "equipName", _equipName); getEquipmentStats:
checkGangApiAccess("getEquipmentStats"); (ctx: NetscriptContext) =>
const equipment = GangMemberUpgrades[equipName]; (_equipName: unknown): EquipmentStats => {
if (!equipment) { const equipName = ctx.helper.string("equipName", _equipName);
throw helper.makeRuntimeErrorMsg("getEquipmentStats", `Invalid equipment: ${equipName}`); checkGangApiAccess(ctx);
} const equipment = GangMemberUpgrades[equipName];
const typecheck: EquipmentStats = equipment.mults; if (!equipment) {
return Object.assign({}, typecheck) as any; throw ctx.makeRuntimeErrorMsg(`Invalid equipment: ${equipName}`);
}, }
purchaseEquipment: function (_memberName: unknown, _equipName: unknown): boolean { const typecheck: EquipmentStats = equipment.mults;
updateRam("purchaseEquipment"); return Object.assign({}, typecheck) as any;
const memberName = helper.string("purchaseEquipment", "memberName", _memberName); },
const equipName = helper.string("purchaseEquipment", "equipName", _equipName); purchaseEquipment:
checkGangApiAccess("purchaseEquipment"); (ctx: NetscriptContext) =>
const gang = player.gang; (_memberName: unknown, _equipName: unknown): boolean => {
if (gang === null) throw new Error("Should not be called without Gang"); const memberName = ctx.helper.string("memberName", _memberName);
const member = getGangMember("purchaseEquipment", memberName); const equipName = ctx.helper.string("equipName", _equipName);
const equipment = GangMemberUpgrades[equipName]; checkGangApiAccess(ctx);
if (!equipment) return false; const gang = player.gang;
const res = member.buyUpgrade(equipment, player, gang); if (gang === null) throw new Error("Should not be called without Gang");
if (res) { const member = getGangMember(ctx, memberName);
workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`); const equipment = GangMemberUpgrades[equipName];
} else { if (!equipment) return false;
workerScript.log( const res = member.buyUpgrade(equipment, player, gang);
"gang.purchaseEquipment", if (res) {
() => `Failed to purchase '${equipName}' for Gang member '${memberName}'`, workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`);
); } else {
} workerScript.log(
"gang.purchaseEquipment",
() => `Failed to purchase '${equipName}' for Gang member '${memberName}'`,
);
}
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);
const gang = player.gang; checkGangApiAccess(ctx);
if (gang === null) throw new Error("Should not be called without Gang"); const gang = player.gang;
const member = getGangMember("ascendMember", memberName); if (gang === null) throw new Error("Should not be called without Gang");
if (!member.canAscend()) return; const member = getGangMember(ctx, memberName);
return gang.ascendMember(member, workerScript); if (!member.canAscend()) return;
}, return gang.ascendMember(member, workerScript);
getAscensionResult: function (_memberName: unknown): GangMemberAscension | undefined { },
updateRam("getAscensionResult"); getAscensionResult:
const memberName = helper.string("getAscensionResult", "memberName", _memberName); (ctx: NetscriptContext) =>
checkGangApiAccess("getAscensionResult"); (_memberName: unknown): GangMemberAscension | undefined => {
const gang = player.gang; const memberName = ctx.helper.string("memberName", _memberName);
if (gang === null) throw new Error("Should not be called without Gang"); checkGangApiAccess(ctx);
const member = getGangMember("getAscensionResult", memberName); const gang = player.gang;
if (!member.canAscend()) return; if (gang === null) throw new Error("Should not be called without Gang");
return { const member = getGangMember(ctx, memberName);
respect: member.earnedRespect, if (!member.canAscend()) return;
...member.getAscensionResults(), return {
}; respect: member.earnedRespect,
}, ...member.getAscensionResults(),
setTerritoryWarfare: function (_engage: unknown): void { };
updateRam("setTerritoryWarfare"); },
const engage = helper.boolean(_engage); setTerritoryWarfare:
checkGangApiAccess("setTerritoryWarfare"); (ctx: NetscriptContext) =>
const gang = player.gang; (_engage: unknown): void => {
if (gang === null) throw new Error("Should not be called without Gang"); const engage = ctx.helper.boolean(_engage);
if (engage) { checkGangApiAccess(ctx);
gang.territoryWarfareEngaged = true; const gang = player.gang;
workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare"); if (gang === null) throw new Error("Should not be called without Gang");
} else { if (engage) {
gang.territoryWarfareEngaged = false; gang.territoryWarfareEngaged = true;
workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare"); workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare");
} } else {
}, gang.territoryWarfareEngaged = false;
getChanceToWinClash: function (_otherGang: unknown): number { workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare");
updateRam("getChanceToWinClash"); }
const otherGang = helper.string("getChanceToWinClash", "otherGang", _otherGang); },
checkGangApiAccess("getChanceToWinClash"); getChanceToWinClash:
const gang = player.gang; (ctx: NetscriptContext) =>
if (gang === null) throw new Error("Should not be called without Gang"); (_otherGang: unknown): number => {
if (AllGangs[otherGang] == null) { const otherGang = ctx.helper.string("otherGang", _otherGang);
throw helper.makeRuntimeErrorMsg(`gang.getChanceToWinClash`, `Invalid gang: ${otherGang}`); checkGangApiAccess(ctx);
} const gang = player.gang;
if (gang === null) throw new Error("Should not be called without Gang");
if (AllGangs[otherGang] == null) {
throw ctx.makeRuntimeErrorMsg(`Invalid gang: ${otherGang}`);
}
const playerPower = AllGangs[gang.facName].power; const playerPower = AllGangs[gang.facName].power;
const otherPower = AllGangs[otherGang].power; const otherPower = AllGangs[otherGang].power;
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,104 +1,97 @@
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);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { checkGraftingAPIAccess(ctx);
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftPrice", `Invalid aug: ${augName}`); if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
} throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); }
return graftableAug.cost; const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
}, 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);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { checkGraftingAPIAccess(ctx);
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftTime", `Invalid aug: ${augName}`); if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
} throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); }
return calculateGraftingTimeWithBonus(player, graftableAug); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
}, 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);
if (player.city !== CityName.NewTokyo) { checkGraftingAPIAccess(ctx);
throw helper.makeRuntimeErrorMsg( if (player.city !== CityName.NewTokyo) {
"grafting.graftAugmentation", throw ctx.makeRuntimeErrorMsg("You must be in New Tokyo to begin grafting an Augmentation.");
"You must be in New Tokyo to begin grafting an Augmentation.", }
); if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
} ctx.log(() => `Invalid aug: ${augName}`);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { return false;
workerScript.log("grafting.graftAugmentation", () => `Invalid aug: ${augName}`); }
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;
} }
player.loseMoney(craftableAug.cost, "augmentations"); player.loseMoney(craftableAug.cost, "augmentations");
player.startGraftAugmentationWork(augName, craftableAug.time); player.startGraftAugmentationWork(augName, craftableAug.time);
if (focus) { if (focus) {
player.startFocusing(); player.startFocusing();
Router.toWork(); Router.toWork();
} else if (wasFocusing) { } else if (wasFocusing) {
player.stopFocusing(); player.stopFocusing();
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,162 +49,186 @@ 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 hasUpgraded = hasHacknetServers(player); const i = ctx.helper.number("i", _i);
const res: any = { const node = getHacknetNode(ctx, i);
name: node instanceof HacknetServer ? node.hostname : node.name, const hasUpgraded = hasHacknetServers(player);
level: node.level, const res: any = {
ram: node instanceof HacknetServer ? node.maxRam : node.ram, name: node instanceof HacknetServer ? node.hostname : node.name,
ramUsed: node instanceof HacknetServer ? node.ramUsed : undefined, level: node.level,
cores: node.cores, ram: node instanceof HacknetServer ? node.maxRam : node.ram,
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond, ramUsed: node instanceof HacknetServer ? node.ramUsed : undefined,
timeOnline: node.onlineTimeSeconds, cores: node.cores,
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated, production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
}; timeOnline: node.onlineTimeSeconds,
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
};
if (hasUpgraded && node instanceof HacknetServer) { if (hasUpgraded && node instanceof HacknetServer) {
res.cache = node.cache; res.cache = node.cache;
res.hashCapacity = node.hashCapacity; res.hashCapacity = node.hashCapacity;
} }
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);
return purchaseLevelUpgrade(player, node, n); const n = ctx.helper.number("n", _n);
}, const node = getHacknetNode(ctx, i);
upgradeRam: function (_i: unknown, _n: unknown = 1): boolean { return purchaseLevelUpgrade(player, node, n);
const i = helper.number("upgradeRam", "i", _i); },
const n = helper.number("upgradeRam", "n", _n); upgradeRam:
const node = getHacknetNode(i, "upgradeRam"); (ctx: NetscriptContext) =>
return purchaseRamUpgrade(player, node, n); (_i: unknown, _n: unknown = 1): boolean => {
}, const i = ctx.helper.number("i", _i);
upgradeCore: function (_i: unknown, _n: unknown = 1): boolean { const n = ctx.helper.number("n", _n);
const i = helper.number("upgradeCore", "i", _i); const node = getHacknetNode(ctx, i);
const n = helper.number("upgradeCore", "n", _n); return purchaseRamUpgrade(player, node, n);
const node = getHacknetNode(i, "upgradeCore"); },
return purchaseCoreUpgrade(player, node, n); upgradeCore:
}, (ctx: NetscriptContext) =>
upgradeCache: function (_i: unknown, _n: unknown = 1): boolean { (_i: unknown, _n: unknown = 1): boolean => {
const i = helper.number("upgradeCache", "i", _i); const i = ctx.helper.number("i", _i);
const n = helper.number("upgradeCache", "n", _n); const n = ctx.helper.number("n", _n);
if (!hasHacknetServers(player)) { const node = getHacknetNode(ctx, i);
return false; return purchaseCoreUpgrade(player, node, n);
} },
const node = getHacknetNode(i, "upgradeCache"); upgradeCache:
if (!(node instanceof HacknetServer)) { (ctx: NetscriptContext) =>
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers"); (_i: unknown, _n: unknown = 1): boolean => {
return false; const i = ctx.helper.number("i", _i);
} const n = ctx.helper.number("n", _n);
const res = purchaseCacheUpgrade(player, node, n); if (!hasHacknetServers(player)) {
if (res) { return false;
updateHashManagerCapacity(player); }
} const node = getHacknetNode(ctx, i);
return res; if (!(node instanceof HacknetServer)) {
}, workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
getLevelUpgradeCost: function (_i: unknown, _n: unknown = 1): number { return false;
const i = helper.number("getLevelUpgradeCost", "i", _i); }
const n = helper.number("getLevelUpgradeCost", "n", _n); const res = purchaseCacheUpgrade(player, node, n);
const node = getHacknetNode(i, "upgradeLevel"); if (res) {
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult); updateHashManagerCapacity(player);
}, }
getRamUpgradeCost: function (_i: unknown, _n: unknown = 1): number { return res;
const i = helper.number("getRamUpgradeCost", "i", _i); },
const n = helper.number("getRamUpgradeCost", "n", _n); getLevelUpgradeCost:
const node = getHacknetNode(i, "upgradeRam"); (ctx: NetscriptContext) =>
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult); (_i: unknown, _n: unknown = 1): number => {
}, const i = ctx.helper.number("i", _i);
getCoreUpgradeCost: function (_i: unknown, _n: unknown = 1): number { const n = ctx.helper.number("n", _n);
const i = helper.number("getCoreUpgradeCost", "i", _i); const node = getHacknetNode(ctx, i);
const n = helper.number("getCoreUpgradeCost", "n", _n); return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
const node = getHacknetNode(i, "upgradeCore"); },
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult); getRamUpgradeCost:
}, (ctx: NetscriptContext) =>
getCacheUpgradeCost: function (_i: unknown, _n: unknown = 1): number { (_i: unknown, _n: unknown = 1): number => {
const i = helper.number("getCacheUpgradeCost", "i", _i); const i = ctx.helper.number("i", _i);
const n = helper.number("getCacheUpgradeCost", "n", _n); const n = ctx.helper.number("n", _n);
if (!hasHacknetServers(player)) { const node = getHacknetNode(ctx, i);
return Infinity; return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
} },
const node = getHacknetNode(i, "upgradeCache"); getCoreUpgradeCost:
if (!(node instanceof HacknetServer)) { (ctx: NetscriptContext) =>
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers"); (_i: unknown, _n: unknown = 1): number => {
return -1; const i = ctx.helper.number("i", _i);
} const n = ctx.helper.number("n", _n);
return node.calculateCacheUpgradeCost(n); const node = getHacknetNode(ctx, i);
}, return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
numHashes: function (): number { },
getCacheUpgradeCost:
(ctx: NetscriptContext) =>
(_i: unknown, _n: unknown = 1): number => {
const i = ctx.helper.number("i", _i);
const n = ctx.helper.number("n", _n);
if (!hasHacknetServers(player)) {
return Infinity;
}
const node = getHacknetNode(ctx, i);
if (!(node instanceof HacknetServer)) {
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
return -1;
}
return node.calculateCacheUpgradeCost(n);
},
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) =>
if (!hasHacknetServers(player)) { (_upgName: unknown): number => {
return Infinity; const upgName = ctx.helper.string("upgName", _upgName);
} if (!hasHacknetServers(player)) {
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 => {
if (!hasHacknetServers(player)) { const upgName = ctx.helper.string("upgName", _upgName);
return false; const upgTarget = ctx.helper.string("upgTarget", _upgTarget);
} if (!hasHacknetServers(player)) {
return purchaseHashUpgrade(player, upgName, upgTarget); return false;
}, }
getHashUpgrades: function (): string[] { return purchaseHashUpgrade(player, upgName, upgTarget);
},
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) =>
const level = player.hashManager.upgrades[upgName]; (_upgName: unknown): number => {
if (level === undefined) { const upgName = ctx.helper.string("upgName", _upgName);
throw helper.makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`); const level = player.hashManager.upgrades[upgName];
} if (level === undefined) {
return level; throw ctx.makeRuntimeErrorMsg(`Invalid Hash Upgrade: ${upgName}`);
}, }
getStudyMult: function (): number { return level;
},
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,265 +47,268 @@ 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);
return player.sleeves[sleeveNumber].shockRecovery(player); checkSleeveNumber(ctx, sleeveNumber);
}, return player.sleeves[sleeveNumber].shockRecovery(player);
setToSynchronize: function (_sleeveNumber: unknown): boolean { },
updateRam("setToSynchronize"); setToSynchronize:
const sleeveNumber = helper.number("setToSynchronize", "sleeveNumber", _sleeveNumber); (ctx: NetscriptContext) =>
checkSleeveAPIAccess("setToSynchronize"); (_sleeveNumber: unknown): boolean => {
checkSleeveNumber("setToSynchronize", sleeveNumber); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
return player.sleeves[sleeveNumber].synchronize(player); checkSleeveAPIAccess(ctx);
}, checkSleeveNumber(ctx, sleeveNumber);
setToCommitCrime: function (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean { return player.sleeves[sleeveNumber].synchronize(player);
updateRam("setToCommitCrime"); },
const sleeveNumber = helper.number("setToCommitCrime", "sleeveNumber", _sleeveNumber); setToCommitCrime:
const crimeRoughName = helper.string("setToCommitCrime", "crimeName", _crimeRoughName); (ctx: NetscriptContext) =>
checkSleeveAPIAccess("setToCommitCrime"); (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean => {
checkSleeveNumber("setToCommitCrime", sleeveNumber); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const crime = findCrime(crimeRoughName); const crimeRoughName = ctx.helper.string("crimeName", _crimeRoughName);
if (crime === null) { checkSleeveAPIAccess(ctx);
return false; checkSleeveNumber(ctx, sleeveNumber);
} const crime = findCrime(crimeRoughName);
return player.sleeves[sleeveNumber].commitCrime(player, crime.name); if (crime === null) {
}, return false;
setToUniversityCourse: function (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean {
updateRam("setToUniversityCourse");
const sleeveNumber = helper.number("setToUniversityCourse", "sleeveNumber", _sleeveNumber);
const universityName = helper.string("setToUniversityCourse", "universityName", _universityName);
const className = helper.string("setToUniversityCourse", "className", _className);
checkSleeveAPIAccess("setToUniversityCourse");
checkSleeveNumber("setToUniversityCourse", sleeveNumber);
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
},
travel: function (_sleeveNumber: unknown, _cityName: unknown): boolean {
updateRam("travel");
const sleeveNumber = helper.number("travel", "sleeveNumber", _sleeveNumber);
const cityName = helper.string("travel", "cityName", _cityName);
checkSleeveAPIAccess("travel");
checkSleeveNumber("travel", sleeveNumber);
if (checkEnum(CityName, cityName)) {
return player.sleeves[sleeveNumber].travel(player, cityName);
} else {
throw helper.makeRuntimeErrorMsg("sleeve.setToCompanyWork", `Invalid city name: '${cityName}'.`);
}
},
setToCompanyWork: function (_sleeveNumber: unknown, acompanyName: unknown): boolean {
updateRam("setToCompanyWork");
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", _sleeveNumber);
const companyName = helper.string("setToCompanyWork", "companyName", acompanyName);
checkSleeveAPIAccess("setToCompanyWork");
checkSleeveNumber("setToCompanyWork", sleeveNumber);
// Cannot work at the same company that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
} }
const other = player.sleeves[i]; return player.sleeves[sleeveNumber].commitCrime(player, crime.name);
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) { },
throw helper.makeRuntimeErrorMsg( setToUniversityCourse:
"sleeve.setToCompanyWork", (ctx: NetscriptContext) =>
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`, (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const universityName = ctx.helper.string("universityName", _universityName);
const className = ctx.helper.string("className", _className);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
},
travel:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, _cityName: unknown): boolean => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const cityName = ctx.helper.string("cityName", _cityName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
if (checkEnum(CityName, cityName)) {
return player.sleeves[sleeveNumber].travel(player, cityName);
} else {
throw ctx.makeRuntimeErrorMsg(`Invalid city name: '${cityName}'.`);
}
},
setToCompanyWork:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, acompanyName: unknown): boolean => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const companyName = ctx.helper.string("companyName", acompanyName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same company that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
throw ctx.makeRuntimeErrorMsg(
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
);
}
}
return player.sleeves[sleeveNumber].workForCompany(player, companyName);
},
setToFactionWork:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, _factionName: unknown, _workType: unknown): boolean | undefined => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const factionName = ctx.helper.string("factionName", _factionName);
const workType = ctx.helper.string("workType", _workType);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same faction that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = player.sleeves[i];
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
throw ctx.makeRuntimeErrorMsg(
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
);
}
}
if (player.gang && player.gang.facName == factionName) {
throw ctx.makeRuntimeErrorMsg(
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
); );
} }
}
return player.sleeves[sleeveNumber].workForCompany(player, companyName); return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType);
}, },
setToFactionWork: function ( setToGymWorkout:
_sleeveNumber: unknown, (ctx: NetscriptContext) =>
_factionName: unknown, (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean => {
_workType: unknown, const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
): boolean | undefined { const gymName = ctx.helper.string("gymName", _gymName);
updateRam("setToFactionWork"); const stat = ctx.helper.string("stat", _stat);
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 return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat);
for (let i = 0; i < player.sleeves.length; ++i) { },
if (i === sleeveNumber) { getSleeveStats:
continue; (ctx: NetscriptContext) =>
(_sleeveNumber: unknown): SleeveSkills => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return getSleeveStats(sleeveNumber);
},
getTask:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): SleeveTask => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
task: SleeveTaskType[sl.currentTask],
crime: sl.crimeType,
location: sl.currentTaskLocation,
gymStatType: sl.gymStatType,
factionWorkType: FactionWorkType[sl.factionWorkType],
};
},
getInformation:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): SleeveInformation => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
tor: false,
city: sl.city,
hp: sl.hp,
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player.
jobTitle: Object.values(player.jobs),
maxHp: sl.max_hp,
mult: {
agility: sl.agility_mult,
agilityExp: sl.agility_exp_mult,
charisma: sl.charisma_mult,
charismaExp: sl.charisma_exp_mult,
companyRep: sl.company_rep_mult,
crimeMoney: sl.crime_money_mult,
crimeSuccess: sl.crime_success_mult,
defense: sl.defense_mult,
defenseExp: sl.defense_exp_mult,
dexterity: sl.dexterity_mult,
dexterityExp: sl.dexterity_exp_mult,
factionRep: sl.faction_rep_mult,
hacking: sl.hacking_mult,
hackingExp: sl.hacking_exp_mult,
strength: sl.strength_mult,
strengthExp: sl.strength_exp_mult,
workMoney: sl.work_money_mult,
},
timeWorked: sl.currentTaskTime,
earningsForSleeves: {
workHackExpGain: sl.earningsForSleeves.hack,
workStrExpGain: sl.earningsForSleeves.str,
workDefExpGain: sl.earningsForSleeves.def,
workDexExpGain: sl.earningsForSleeves.dex,
workAgiExpGain: sl.earningsForSleeves.agi,
workChaExpGain: sl.earningsForSleeves.cha,
workMoneyGain: sl.earningsForSleeves.money,
},
earningsForPlayer: {
workHackExpGain: sl.earningsForPlayer.hack,
workStrExpGain: sl.earningsForPlayer.str,
workDefExpGain: sl.earningsForPlayer.def,
workDexExpGain: sl.earningsForPlayer.dex,
workAgiExpGain: sl.earningsForPlayer.agi,
workChaExpGain: sl.earningsForPlayer.cha,
workMoneyGain: sl.earningsForPlayer.money,
},
earningsForTask: {
workHackExpGain: sl.earningsForTask.hack,
workStrExpGain: sl.earningsForTask.str,
workDefExpGain: sl.earningsForTask.def,
workDexExpGain: sl.earningsForTask.dex,
workAgiExpGain: sl.earningsForTask.agi,
workChaExpGain: sl.earningsForTask.cha,
workMoneyGain: sl.earningsForTask.money,
},
workRepGain: sl.getRepGain(player),
};
},
getSleeveAugmentations:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): string[] => {
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const augs = [];
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
augs.push(player.sleeves[sleeveNumber].augmentations[i].name);
} }
const other = player.sleeves[i]; return augs;
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) { },
throw helper.makeRuntimeErrorMsg( getSleevePurchasableAugs:
"sleeve.setToFactionWork", (ctx: NetscriptContext) =>
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`, (_sleeveNumber: unknown): AugmentPair[] => {
); const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i];
augs.push({
name: aug.name,
cost: aug.baseCost,
});
} }
}
if (player.gang && player.gang.facName == factionName) { return augs;
throw helper.makeRuntimeErrorMsg( },
"sleeve.setToFactionWork", purchaseSleeveAug:
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`, (ctx: NetscriptContext) =>
); (_sleeveNumber: unknown, _augName: unknown): boolean => {
} const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
const augName = ctx.helper.string("augName", _augName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType); if (getSleeveStats(sleeveNumber).shock > 0) {
}, throw ctx.makeRuntimeErrorMsg(`Sleeve shock too high: Sleeve ${sleeveNumber}`);
setToGymWorkout: function (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean { }
updateRam("setToGymWorkout");
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", _sleeveNumber);
const gymName = helper.string("setToGymWorkout", "gymName", _gymName);
const stat = helper.string("setToGymWorkout", "stat", _stat);
checkSleeveAPIAccess("setToGymWorkout");
checkSleeveNumber("setToGymWorkout", sleeveNumber);
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat); const aug = StaticAugmentations[augName];
}, if (!aug) {
getSleeveStats: function (_sleeveNumber: unknown): SleeveSkills { throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
updateRam("getSleeveStats"); }
const sleeveNumber = helper.number("getSleeveStats", "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("getSleeveStats");
checkSleeveNumber("getSleeveStats", sleeveNumber);
return getSleeveStats(sleeveNumber);
},
getTask: function (_sleeveNumber: unknown): SleeveTask {
updateRam("getTask");
const sleeveNumber = helper.number("getTask", "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("getTask");
checkSleeveNumber("getTask", sleeveNumber);
const sl = player.sleeves[sleeveNumber]; return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);
return { },
task: SleeveTaskType[sl.currentTask],
crime: sl.crimeType,
location: sl.currentTaskLocation,
gymStatType: sl.gymStatType,
factionWorkType: FactionWorkType[sl.factionWorkType],
};
},
getInformation: function (_sleeveNumber: unknown): SleeveInformation {
updateRam("getInformation");
const sleeveNumber = helper.number("getInformation", "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("getInformation");
checkSleeveNumber("getInformation", sleeveNumber);
const sl = player.sleeves[sleeveNumber];
return {
tor: false,
city: sl.city,
hp: sl.hp,
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player.
jobTitle: Object.values(player.jobs),
maxHp: sl.max_hp,
mult: {
agility: sl.agility_mult,
agilityExp: sl.agility_exp_mult,
charisma: sl.charisma_mult,
charismaExp: sl.charisma_exp_mult,
companyRep: sl.company_rep_mult,
crimeMoney: sl.crime_money_mult,
crimeSuccess: sl.crime_success_mult,
defense: sl.defense_mult,
defenseExp: sl.defense_exp_mult,
dexterity: sl.dexterity_mult,
dexterityExp: sl.dexterity_exp_mult,
factionRep: sl.faction_rep_mult,
hacking: sl.hacking_mult,
hackingExp: sl.hacking_exp_mult,
strength: sl.strength_mult,
strengthExp: sl.strength_exp_mult,
workMoney: sl.work_money_mult,
},
timeWorked: sl.currentTaskTime,
earningsForSleeves: {
workHackExpGain: sl.earningsForSleeves.hack,
workStrExpGain: sl.earningsForSleeves.str,
workDefExpGain: sl.earningsForSleeves.def,
workDexExpGain: sl.earningsForSleeves.dex,
workAgiExpGain: sl.earningsForSleeves.agi,
workChaExpGain: sl.earningsForSleeves.cha,
workMoneyGain: sl.earningsForSleeves.money,
},
earningsForPlayer: {
workHackExpGain: sl.earningsForPlayer.hack,
workStrExpGain: sl.earningsForPlayer.str,
workDefExpGain: sl.earningsForPlayer.def,
workDexExpGain: sl.earningsForPlayer.dex,
workAgiExpGain: sl.earningsForPlayer.agi,
workChaExpGain: sl.earningsForPlayer.cha,
workMoneyGain: sl.earningsForPlayer.money,
},
earningsForTask: {
workHackExpGain: sl.earningsForTask.hack,
workStrExpGain: sl.earningsForTask.str,
workDefExpGain: sl.earningsForTask.def,
workDexExpGain: sl.earningsForTask.dex,
workAgiExpGain: sl.earningsForTask.agi,
workChaExpGain: sl.earningsForTask.cha,
workMoneyGain: sl.earningsForTask.money,
},
workRepGain: sl.getRepGain(player),
};
},
getSleeveAugmentations: function (_sleeveNumber: unknown): string[] {
updateRam("getSleeveAugmentations");
const sleeveNumber = helper.number("getSleeveAugmentations", "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("getSleeveAugmentations");
checkSleeveNumber("getSleeveAugmentations", sleeveNumber);
const augs = [];
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
augs.push(player.sleeves[sleeveNumber].augmentations[i].name);
}
return augs;
},
getSleevePurchasableAugs: function (_sleeveNumber: unknown): AugmentPair[] {
updateRam("getSleevePurchasableAugs");
const sleeveNumber = helper.number("getSleevePurchasableAugs", "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess("getSleevePurchasableAugs");
checkSleeveNumber("getSleevePurchasableAugs", sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i];
augs.push({
name: aug.name,
cost: aug.baseCost,
});
}
return augs;
},
purchaseSleeveAug: function (_sleeveNumber: unknown, _augName: unknown): boolean {
updateRam("purchaseSleeveAug");
const sleeveNumber = helper.number("purchaseSleeveAug", "sleeveNumber", _sleeveNumber);
const augName = helper.string("purchaseSleeveAug", "augName", _augName);
checkSleeveAPIAccess("purchaseSleeveAug");
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
if (getSleeveStats(sleeveNumber).shock > 0) {
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
}
const aug = StaticAugmentations[augName];
if (!aug) {
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
}
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,301 +14,286 @@ 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);
const stock = SymbolToStockMap[symbol]; checkTixApiAccess(ctx);
if (stock == null) { const stock = SymbolToStockMap[symbol];
throw helper.makeRuntimeErrorMsg("getPosition", `Invalid stock symbol: ${symbol}`); if (stock == null) {
} throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: ${symbol}`);
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
},
getMaxShares: function (_symbol: unknown): number {
updateRam("getMaxShares");
const symbol = helper.string("getMaxShares", "symbol", _symbol);
checkTixApiAccess("getMaxShares");
const stock = getStockFromSymbol(symbol, "getMaxShares");
return stock.maxShares;
},
getPurchaseCost: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
updateRam("getPurchaseCost");
const symbol = helper.string("getPurchaseCost", "symbol", _symbol);
let shares = helper.number("getPurchaseCost", "shares", _shares);
const posType = helper.string("getPurchaseCost", "posType", _posType);
checkTixApiAccess("getPurchaseCost");
const stock = getStockFromSymbol(symbol, "getPurchaseCost");
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return Infinity;
}
const res = getBuyTransactionCost(stock, shares, pos);
if (res == null) {
return Infinity;
}
return res;
},
getSaleGain: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
updateRam("getSaleGain");
const symbol = helper.string("getSaleGain", "symbol", _symbol);
let shares = helper.number("getSaleGain", "shares", _shares);
const posType = helper.string("getSaleGain", "posType", _posType);
checkTixApiAccess("getSaleGain");
const stock = getStockFromSymbol(symbol, "getSaleGain");
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
if (res == null) {
return 0;
}
return res;
},
buy: function (_symbol: unknown, _shares: unknown): number {
updateRam("buy");
const symbol = helper.string("buy", "symbol", _symbol);
const shares = helper.number("buy", "shares", _shares);
checkTixApiAccess("buy");
const stock = getStockFromSymbol(symbol, "buy");
const res = buyStock(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0;
},
sell: function (_symbol: unknown, _shares: unknown): number {
updateRam("sell");
const symbol = helper.string("sell", "symbol", _symbol);
const shares = helper.number("sell", "shares", _shares);
checkTixApiAccess("sell");
const stock = getStockFromSymbol(symbol, "sell");
const res = sellStock(stock, shares, workerScript, {});
return res ? stock.getBidPrice() : 0;
},
short: function (_symbol: unknown, _shares: unknown): number {
updateRam("short");
const symbol = helper.string("short", "symbol", _symbol);
const shares = helper.number("short", "shares", _shares);
checkTixApiAccess("short");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helper.makeRuntimeErrorMsg(
"short",
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
} }
} return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
const stock = getStockFromSymbol(symbol, "short"); },
const res = shortStock(stock, shares, workerScript, {}); getMaxShares:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return res ? stock.getBidPrice() : 0; return stock.maxShares;
}, },
sellShort: function (_symbol: unknown, _shares: unknown): number { getPurchaseCost:
updateRam("sellShort"); (ctx: NetscriptContext) =>
const symbol = helper.string("sellShort", "symbol", _symbol); (_symbol: unknown, _shares: unknown, _posType: unknown): number => {
const shares = helper.number("sellShort", "shares", _shares); const symbol = ctx.helper.string("symbol", _symbol);
checkTixApiAccess("sellShort"); let shares = ctx.helper.number("shares", _shares);
if (player.bitNodeN !== 8) { const posType = ctx.helper.string("posType", _posType);
if (player.sourceFileLvl(8) <= 1) { checkTixApiAccess(ctx);
throw helper.makeRuntimeErrorMsg( const stock = getStockFromSymbol(ctx, symbol);
"sellShort", shares = Math.round(shares);
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
); let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return Infinity;
} }
}
const stock = getStockFromSymbol(symbol, "sellShort");
const res = sellShort(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0; const res = getBuyTransactionCost(stock, shares, pos);
}, if (res == null) {
placeOrder: function (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean { return Infinity;
updateRam("placeOrder"); }
const symbol = helper.string("placeOrder", "symbol", _symbol);
const shares = helper.number("placeOrder", "shares", _shares); return res;
const price = helper.number("placeOrder", "price", _price); },
const type = helper.string("placeOrder", "type", _type); getSaleGain:
const pos = helper.string("placeOrder", "pos", _pos); (ctx: NetscriptContext) =>
checkTixApiAccess("placeOrder"); (_symbol: unknown, _shares: unknown, _posType: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
let shares = ctx.helper.number("shares", _shares);
const posType = ctx.helper.string("posType", _posType);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
if (res == null) {
return 0;
}
return res;
},
buy:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
const res = buyStock(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0;
},
sell:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
const res = sellStock(stock, shares, workerScript, {});
return res ? stock.getBidPrice() : 0;
},
short:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
}
}
const stock = getStockFromSymbol(ctx, symbol);
const res = shortStock(stock, shares, workerScript, {});
return res ? stock.getBidPrice() : 0;
},
sellShort:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
}
}
const stock = getStockFromSymbol(ctx, symbol);
const res = sellShort(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0;
},
placeOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
const price = ctx.helper.number("price", _price);
const type = ctx.helper.string("type", _type);
const pos = ctx.helper.string("pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
}
}
const stock = getStockFromSymbol(ctx, symbol);
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
},
cancelOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
const symbol = ctx.helper.string("symbol", _symbol);
const shares = ctx.helper.number("shares", _shares);
const price = ctx.helper.number("price", _price);
const type = ctx.helper.string("type", _type);
const pos = ctx.helper.string("pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
}
}
const stock = getStockFromSymbol(ctx, symbol);
if (isNaN(shares) || isNaN(price)) {
throw ctx.makeRuntimeErrorMsg(`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`);
}
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, workerScript);
},
getOrders: (ctx: NetscriptContext) => (): any => {
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 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");
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
},
cancelOrder: function (
_symbol: unknown,
_shares: unknown,
_price: unknown,
_type: unknown,
_pos: unknown,
): boolean {
updateRam("cancelOrder");
const symbol = helper.string("cancelOrder", "symbol", _symbol);
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.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg(
"cancelOrder",
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(symbol, "cancelOrder");
if (isNaN(shares) || isNaN(price)) {
throw helper.makeRuntimeErrorMsg(
"cancelOrder",
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
);
}
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, workerScript);
},
getOrders: function (): any {
updateRam("getOrders");
checkTixApiAccess("getOrders");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg(
"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 => {
if (!player.has4SDataTixApi) { const symbol = ctx.helper.string("symbol", _symbol);
throw helper.makeRuntimeErrorMsg("getVolatility", "You don't have 4S Market Data TIX API Access!"); if (!player.has4SDataTixApi) {
} 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 => {
if (!player.has4SDataTixApi) { const symbol = ctx.helper.string("symbol", _symbol);
throw helper.makeRuntimeErrorMsg("getForecast", "You don't have 4S Market Data TIX API Access!"); if (!player.has4SDataTixApi) {
} 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;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData: function (): boolean {
updateRam("purchase4SMarketData");
let forecast = 50;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData: (ctx: NetscriptContext) => (): boolean => {
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,89 +10,81 @@ 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";
import { Terminal } from "../../src/Terminal"; import { Terminal } from "../../src/Terminal";
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) =>
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/; (newTheme: UserInterfaceTheme): void => {
const currentTheme = { ...Settings.theme }; const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
const errors: string[] = []; const currentTheme = { ...Settings.theme };
for (const key of Object.keys(newTheme)) { const errors: string[] = [];
if (!currentTheme[key]) { for (const key of Object.keys(newTheme)) {
// Invalid key if (!currentTheme[key]) {
errors.push(`Invalid key "${key}"`); // Invalid key
} else if (!hex.test(newTheme[key] ?? "")) { errors.push(`Invalid key "${key}"`);
errors.push(`Invalid color "${key}": ${newTheme[key]}`); } else if (!hex.test(newTheme[key] ?? "")) {
} else { errors.push(`Invalid color "${key}": ${newTheme[key]}`);
currentTheme[key] = newTheme[key]; } else {
currentTheme[key] = newTheme[key];
}
} }
}
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 {
workerScript.log("ui.setTheme", () => `Failed to set theme. Errors: ${errors.join(", ")}`);
}
},
setStyles: function (newStyles: IStyleSettings): void {
updateRam("setStyles");
const currentStyles = { ...Settings.styles };
const errors: string[] = [];
for (const key of Object.keys(newStyles)) {
if (!(currentStyles as any)[key]) {
// Invalid key
errors.push(`Invalid key "${key}"`);
} else { } else {
(currentStyles as any)[key] = (newStyles as any)[key]; ctx.log(() => `Failed to set theme. Errors: ${errors.join(", ")}`);
} }
} },
if (errors.length === 0) { setStyles:
Object.assign(Settings.styles, currentStyles); (ctx: NetscriptContext) =>
ThemeEvents.emit(); (newStyles: IStyleSettings): void => {
workerScript.log("ui.setStyles", () => `Successfully set styles`); const currentStyles = { ...Settings.styles };
} else { const errors: string[] = [];
workerScript.log("ui.setStyles", () => `Failed to set styles. Errors: ${errors.join(", ")}`); for (const key of Object.keys(newStyles)) {
} if (!(currentStyles as any)[key]) {
}, // Invalid key
errors.push(`Invalid key "${key}"`);
} else {
(currentStyles as any)[key] = (newStyles as any)[key];
}
}
resetTheme: function (): void { if (errors.length === 0) {
updateRam("resetTheme"); Object.assign(Settings.styles, currentStyles);
ThemeEvents.emit();
ctx.log(() => `Successfully set styles`);
} else {
ctx.log(() => `Failed to set styles. Errors: ${errors.join(", ")}`);
}
},
resetTheme: (ctx: NetscriptContext) => (): void => {
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";