Merge pull request #4236 from Snarling/typeFix

NETSCRIPT: Fix internal typechecking of functions
This commit is contained in:
hydroflame 2022-10-12 23:50:22 -04:00 committed by GitHub
commit 68e90b8e6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 3379 additions and 3841 deletions

@ -5,13 +5,15 @@ import { ScriptArg } from "./ScriptArg";
import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions"; import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions";
import { NSFull } from "src/NetscriptFunctions"; import { NSFull } from "src/NetscriptFunctions";
type ExternalFunction = (...args: unknown[]) => unknown; type ExternalFunction = (...args: any[]) => void;
export type ExternalAPILayer = { export type ExternalAPILayer = {
[key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[]; [key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[];
}; };
type InternalFunction<F extends (...args: unknown[]) => unknown> = (ctx: NetscriptContext) => F; type InternalFunction<F extends ExternalFunction> = (
ctx: NetscriptContext,
) => ((...args: unknown[]) => ReturnType<F>) & F;
export type InternalAPI<API> = { export type InternalAPI<API> = {
[Property in keyof API]: API[Property] extends ExternalFunction [Property in keyof API]: API[Property] extends ExternalFunction

@ -65,6 +65,39 @@ export const helpers = {
failOnHacknetServer, failOnHacknetServer,
}; };
export function assertObjectType<T extends object>(
ctx: NetscriptContext,
name: string,
obj: unknown,
desiredObject: T,
): asserts obj is T {
if (typeof obj !== "object" || obj === null) {
throw makeRuntimeErrorMsg(
ctx,
`Type ${obj === null ? "null" : typeof obj} provided for ${name}. Must be an object.`,
"TYPE",
);
}
const objHas = Object.prototype.hasOwnProperty.bind(obj);
for (const [key, val] of Object.entries(desiredObject)) {
if (!objHas(key)) {
throw makeRuntimeErrorMsg(
ctx,
`Object provided for argument ${name} is missing required property ${key}.`,
"TYPE",
);
}
const objVal = (obj as Record<string, unknown>)[key];
if (typeof val !== typeof objVal) {
throw makeRuntimeErrorMsg(
ctx,
`Incorrect type ${typeof objVal} provided for property ${key} on ${name} argument. Should be type ${typeof val}.`,
"TYPE",
);
}
}
}
const userFriendlyString = (v: unknown): string => { const userFriendlyString = (v: unknown): string => {
const clip = (s: string): string => { const clip = (s: string): string => {
if (s.length > 15) return s.slice(0, 12) + "..."; if (s.length > 15) return s.slice(0, 12) + "...";
@ -220,10 +253,7 @@ function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThread
} }
const requestedThreadsAsInt = requestedThreads | 0; const requestedThreadsAsInt = requestedThreads | 0;
if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) { if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) {
throw makeRuntimeErrorMsg( throw makeRuntimeErrorMsg(ctx, `Invalid thread count: ${requestedThreads}. Threads must be a positive number.`);
ctx,
`Invalid thread count passed to ${ctx.function}: ${requestedThreads}. Threads must be a positive number.`,
);
} }
if (requestedThreadsAsInt > threads) { if (requestedThreadsAsInt > threads) {
throw makeRuntimeErrorMsg( throw makeRuntimeErrorMsg(

@ -546,6 +546,7 @@ export const RamCosts: RamCostTree<Omit<NSFull, "args" | "enums">> = {
// Easter egg function // Easter egg function
break: 0, break: 0,
}, },
iKnowWhatImDoing: 0,
formulas: { formulas: {
mockServer: 0, mockServer: 0,

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
import { Player } from "@player"; import { Player } from "@player";
import { Bladeburner } from "../Bladeburner/Bladeburner"; import { Bladeburner } from "../Bladeburner/Bladeburner";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions"; import { Bladeburner as INetscriptBladeburner } from "../ScriptEditor/NetscriptDefinitions";
import { Action } from "src/Bladeburner/Action"; import { Action } from "src/Bladeburner/Action";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
import { BlackOperation } from "../Bladeburner/BlackOperation"; import { BlackOperation } from "../Bladeburner/BlackOperation";
@ -47,75 +47,69 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
}; };
return { return {
getContractNames: (ctx: NetscriptContext) => (): string[] => { getContractNames: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getContractNamesNetscriptFn(); return bladeburner.getContractNamesNetscriptFn();
}, },
getOperationNames: (ctx: NetscriptContext) => (): string[] => { getOperationNames: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getOperationNamesNetscriptFn(); return bladeburner.getOperationNamesNetscriptFn();
}, },
getBlackOpNames: (ctx: NetscriptContext) => (): string[] => { getBlackOpNames: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getBlackOpNamesNetscriptFn(); return bladeburner.getBlackOpNamesNetscriptFn();
}, },
getBlackOpRank: getBlackOpRank: (ctx) => (_blackOpName) => {
(ctx: NetscriptContext) => const blackOpName = helpers.string(ctx, "blackOpName", _blackOpName);
(_blackOpName: unknown): number => { checkBladeburnerAccess(ctx);
const blackOpName = helpers.string(ctx, "blackOpName", _blackOpName); const action = getBladeburnerActionObject(ctx, "blackops", blackOpName);
checkBladeburnerAccess(ctx); if (!(action instanceof BlackOperation)) throw new Error("action was not a black operation");
const action = getBladeburnerActionObject(ctx, "blackops", blackOpName); return action.reqdRank;
if (!(action instanceof BlackOperation)) throw new Error("action was not a black operation"); },
return action.reqdRank; getGeneralActionNames: (ctx) => () => {
},
getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getGeneralActionNamesNetscriptFn(); return bladeburner.getGeneralActionNamesNetscriptFn();
}, },
getSkillNames: (ctx: NetscriptContext) => (): string[] => { getSkillNames: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getSkillNamesNetscriptFn(); return bladeburner.getSkillNamesNetscriptFn();
}, },
startAction: startAction: (ctx) => (_type, _name) => {
(ctx: NetscriptContext) => const type = helpers.string(ctx, "type", _type);
(_type: unknown, _name: unknown): boolean => { const name = helpers.string(ctx, "name", _name);
const type = helpers.string(ctx, "type", _type); const bladeburner = getBladeburner(ctx);
const name = helpers.string(ctx, "name", _name); try {
const bladeburner = getBladeburner(ctx); return bladeburner.startActionNetscriptFn(type, name, ctx.workerScript);
try { } catch (e: unknown) {
return bladeburner.startActionNetscriptFn(type, name, ctx.workerScript); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} catch (e: unknown) { }
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); },
} stopBladeburnerAction: (ctx) => () => {
},
stopBladeburnerAction: (ctx: NetscriptContext) => (): void => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.resetAction(); return bladeburner.resetAction();
}, },
getCurrentAction: (ctx: NetscriptContext) => (): BladeburnerCurAction => { getCurrentAction: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.getTypeAndNameFromActionId(bladeburner.action); return bladeburner.getTypeAndNameFromActionId(bladeburner.action);
}, },
getActionTime: getActionTime: (ctx) => (_type, _name) => {
(ctx: NetscriptContext) => const type = helpers.string(ctx, "type", _type);
(_type: unknown, _name: unknown): number => { const name = helpers.string(ctx, "name", _name);
const type = helpers.string(ctx, "type", _type); const bladeburner = getBladeburner(ctx);
const name = helpers.string(ctx, "name", _name); try {
const bladeburner = getBladeburner(ctx); const time = bladeburner.getActionTimeNetscriptFn(Player, type, name);
try { if (typeof time === "string") {
const time = bladeburner.getActionTimeNetscriptFn(Player, type, name); const errorLogText = `Invalid action: type='${type}' name='${name}'`;
if (typeof time === "string") { helpers.log(ctx, () => errorLogText);
const errorLogText = `Invalid action: type='${type}' name='${name}'`; return -1;
helpers.log(ctx, () => errorLogText); } else {
return -1; return time;
} else {
return time;
}
} catch (e: unknown) {
throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} }
}, } catch (e: unknown) {
getActionCurrentTime: (ctx: NetscriptContext) => (): number => { throw helpers.makeRuntimeErrorMsg(ctx, String(e));
}
},
getActionCurrentTime: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
try { try {
const timecomputed = const timecomputed =
@ -126,84 +120,72 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} }
}, },
getActionEstimatedSuccessChance: getActionEstimatedSuccessChance: (ctx) => (_type, _name) => {
(ctx: NetscriptContext) => const type = helpers.string(ctx, "type", _type);
(_type: unknown, _name: unknown): [number, number] => { const name = helpers.string(ctx, "name", _name);
const type = helpers.string(ctx, "type", _type); const bladeburner = getBladeburner(ctx);
const name = helpers.string(ctx, "name", _name); try {
const bladeburner = getBladeburner(ctx); const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(Player, type, name);
try { if (typeof chance === "string") {
const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(Player, type, name); const errorLogText = `Invalid action: type='${type}' name='${name}'`;
if (typeof chance === "string") { helpers.log(ctx, () => errorLogText);
const errorLogText = `Invalid action: type='${type}' name='${name}'`; return [-1, -1];
helpers.log(ctx, () => errorLogText);
return [-1, -1];
} else {
return chance;
}
} catch (e: unknown) {
throw helpers.makeRuntimeErrorMsg(ctx, String(e));
}
},
getActionRepGain:
(ctx: NetscriptContext) =>
(_type: unknown, _name: unknown, _level: unknown): number => {
const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name);
const level = helpers.number(ctx, "level", _level);
checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
let rewardMultiplier;
if (level == null || isNaN(level)) {
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
} else { } else {
rewardMultiplier = Math.pow(action.rewardFac, level - 1); return chance;
} }
} catch (e: unknown) {
throw helpers.makeRuntimeErrorMsg(ctx, String(e));
}
},
getActionRepGain: (ctx) => (_type, _name, _level) => {
const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name);
const level = helpers.number(ctx, "level", _level);
checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
let rewardMultiplier;
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: getActionCountRemaining: (ctx) => (_type, _name) => {
(ctx: NetscriptContext) => const type = helpers.string(ctx, "type", _type);
(_type: unknown, _name: unknown): number => { const name = helpers.string(ctx, "name", _name);
const type = helpers.string(ctx, "type", _type); const bladeburner = getBladeburner(ctx);
const name = helpers.string(ctx, "name", _name); try {
const bladeburner = getBladeburner(ctx); return bladeburner.getActionCountRemainingNetscriptFn(type, name, ctx.workerScript);
try { } catch (e: unknown) {
return bladeburner.getActionCountRemainingNetscriptFn(type, name, ctx.workerScript); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} catch (e: unknown) { }
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); },
} getActionMaxLevel: (ctx) => (_type, _name) => {
}, const type = helpers.string(ctx, "type", _type);
getActionMaxLevel: const name = helpers.string(ctx, "name", _name);
(ctx: NetscriptContext) => checkBladeburnerAccess(ctx);
(_type: unknown, _name: unknown): number => { const action = getBladeburnerActionObject(ctx, type, name);
const type = helpers.string(ctx, "type", _type); return action.maxLevel;
const name = helpers.string(ctx, "name", _name); },
checkBladeburnerAccess(ctx); getActionCurrentLevel: (ctx) => (_type, _name) => {
const action = getBladeburnerActionObject(ctx, type, name); const type = helpers.string(ctx, "type", _type);
return action.maxLevel; const name = helpers.string(ctx, "name", _name);
}, checkBladeburnerAccess(ctx);
getActionCurrentLevel: const action = getBladeburnerActionObject(ctx, type, name);
(ctx: NetscriptContext) => return action.level;
(_type: unknown, _name: unknown): number => { },
const type = helpers.string(ctx, "type", _type); getActionAutolevel: (ctx) => (_type, _name) => {
const name = helpers.string(ctx, "name", _name); const type = helpers.string(ctx, "type", _type);
checkBladeburnerAccess(ctx); const name = helpers.string(ctx, "name", _name);
const action = getBladeburnerActionObject(ctx, type, name); checkBladeburnerAccess(ctx);
return action.level; const action = getBladeburnerActionObject(ctx, type, name);
}, return action.autoLevel;
getActionAutolevel: },
(ctx: NetscriptContext) =>
(_type: unknown, _name: unknown): boolean => {
const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx);
const action = getBladeburnerActionObject(ctx, type, name);
return action.autoLevel;
},
setActionAutolevel: setActionAutolevel:
(ctx: NetscriptContext) => (ctx) =>
(_type: unknown, _name: unknown, _autoLevel: unknown = true): void => { (_type, _name, _autoLevel = true) => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
const autoLevel = !!_autoLevel; const autoLevel = !!_autoLevel;
@ -212,8 +194,8 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
action.autoLevel = autoLevel; action.autoLevel = autoLevel;
}, },
setActionLevel: setActionLevel:
(ctx: NetscriptContext) => (ctx) =>
(_type: unknown, _name: unknown, _level: unknown = 1): void => { (_type, _name, _level = 1) => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
const level = helpers.number(ctx, "level", _level); const level = helpers.number(ctx, "level", _level);
@ -224,28 +206,26 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
} }
action.level = level; action.level = level;
}, },
getRank: (ctx: NetscriptContext) => (): number => { getRank: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.rank; return bladeburner.rank;
}, },
getSkillPoints: (ctx: NetscriptContext) => (): number => { getSkillPoints: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.skillPoints; return bladeburner.skillPoints;
}, },
getSkillLevel: getSkillLevel: (ctx) => (_skillName) => {
(ctx: NetscriptContext) => const skillName = helpers.string(ctx, "skillName", _skillName);
(_skillName: unknown): number => { const bladeburner = getBladeburner(ctx);
const skillName = helpers.string(ctx, "skillName", _skillName); try {
const bladeburner = getBladeburner(ctx); return bladeburner.getSkillLevelNetscriptFn(skillName, ctx.workerScript);
try { } catch (e: unknown) {
return bladeburner.getSkillLevelNetscriptFn(skillName, ctx.workerScript); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} catch (e: unknown) { }
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); },
}
},
getSkillUpgradeCost: getSkillUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_skillName: unknown, _count: unknown = 1): number => { (_skillName, _count = 1) => {
const skillName = helpers.string(ctx, "skillName", _skillName); const skillName = helpers.string(ctx, "skillName", _skillName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
@ -256,8 +236,8 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
} }
}, },
upgradeSkill: upgradeSkill:
(ctx: NetscriptContext) => (ctx) =>
(_skillName: unknown, _count: unknown = 1): boolean => { (_skillName, _count = 1) => {
const skillName = helpers.string(ctx, "skillName", _skillName); const skillName = helpers.string(ctx, "skillName", _skillName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
@ -267,85 +247,73 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} }
}, },
getTeamSize: getTeamSize: (ctx) => (_type, _name) => {
(ctx: NetscriptContext) => const type = helpers.string(ctx, "type", _type);
(_type: unknown, _name: unknown): number => { const name = helpers.string(ctx, "name", _name);
const type = helpers.string(ctx, "type", _type); const bladeburner = getBladeburner(ctx);
const name = helpers.string(ctx, "name", _name); try {
const bladeburner = getBladeburner(ctx); return bladeburner.getTeamSizeNetscriptFn(type, name, ctx.workerScript);
try { } catch (e: unknown) {
return bladeburner.getTeamSizeNetscriptFn(type, name, ctx.workerScript); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} catch (e: unknown) { }
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); },
} setTeamSize: (ctx) => (_type, _name, _size) => {
}, const type = helpers.string(ctx, "type", _type);
setTeamSize: const name = helpers.string(ctx, "name", _name);
(ctx: NetscriptContext) => const size = helpers.number(ctx, "size", _size);
(_type: unknown, _name: unknown, _size: unknown): number => { const bladeburner = getBladeburner(ctx);
const type = helpers.string(ctx, "type", _type); try {
const name = helpers.string(ctx, "name", _name); return bladeburner.setTeamSizeNetscriptFn(type, name, size, ctx.workerScript);
const size = helpers.number(ctx, "size", _size); } catch (e: unknown) {
const bladeburner = getBladeburner(ctx); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
try { }
return bladeburner.setTeamSizeNetscriptFn(type, name, size, ctx.workerScript); },
} catch (e: unknown) { getCityEstimatedPopulation: (ctx) => (_cityName) => {
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); const cityName = helpers.string(ctx, "cityName", _cityName);
} checkBladeburnerAccess(ctx);
}, checkBladeburnerCity(ctx, cityName);
getCityEstimatedPopulation: const bladeburner = Player.bladeburner;
(ctx: NetscriptContext) => if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
(_cityName: unknown): number => { return bladeburner.cities[cityName].popEst;
const cityName = helpers.string(ctx, "cityName", _cityName); },
checkBladeburnerAccess(ctx); getCityCommunities: (ctx) => (_cityName) => {
checkBladeburnerCity(ctx, cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
const bladeburner = Player.bladeburner; checkBladeburnerAccess(ctx);
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); checkBladeburnerCity(ctx, 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: (ctx) => (_cityName) => {
const cityName = helpers.string(ctx, "cityName", _cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
checkBladeburnerAccess(ctx); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName); checkBladeburnerCity(ctx, cityName);
const bladeburner = Player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].comms; return bladeburner.cities[cityName].chaos;
}, },
getCityChaos: getCity: (ctx) => () => {
(ctx: NetscriptContext) =>
(_cityName: unknown): number => {
const cityName = helpers.string(ctx, "cityName", _cityName);
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 => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.city; return bladeburner.city;
}, },
switchCity: switchCity: (ctx) => (_cityName) => {
(ctx: NetscriptContext) => const cityName = helpers.string(ctx, "cityName", _cityName);
(_cityName: unknown): boolean => { checkBladeburnerAccess(ctx);
const cityName = helpers.string(ctx, "cityName", _cityName); checkBladeburnerCity(ctx, 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: (ctx) => () => {
},
getStamina: (ctx: NetscriptContext) => (): [number, number] => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return [bladeburner.stamina, bladeburner.maxStamina]; return [bladeburner.stamina, bladeburner.maxStamina];
}, },
joinBladeburnerFaction: (ctx: NetscriptContext) => (): boolean => { joinBladeburnerFaction: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return bladeburner.joinBladeburnerFactionNetscriptFn(ctx.workerScript); return bladeburner.joinBladeburnerFactionNetscriptFn(ctx.workerScript);
}, },
joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => { joinBladeburnerDivision: (ctx) => () => {
if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) { if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) {
if (BitNodeMultipliers.BladeburnerRank === 0) { if (BitNodeMultipliers.BladeburnerRank === 0) {
return false; // Disabled in this bitnode return false; // Disabled in this bitnode
@ -369,7 +337,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
} }
return false; return false;
}, },
getBonusTime: (ctx: NetscriptContext) => (): number => { getBonusTime: (ctx) => () => {
const bladeburner = getBladeburner(ctx); const bladeburner = getBladeburner(ctx);
return Math.round(bladeburner.storedCycles / 5) * 1000; return Math.round(bladeburner.storedCycles / 5) * 1000;
}, },

@ -2,17 +2,12 @@ import { Player as player } from "../Player";
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 "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers, assertObjectType } from "../Netscript/NetscriptHelpers";
import { codingContractTypesMetadata } from "../data/codingcontracttypes"; import { codingContractTypesMetadata } from "../data/codingcontracttypes";
import { generateDummyContract } from "../CodingContractGenerator"; import { generateDummyContract } from "../CodingContractGenerator";
export function NetscriptCodingContract(): InternalAPI<ICodingContract> { export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
const getCodingContract = function ( const getCodingContract = function (ctx: NetscriptContext, hostname: string, filename: string): CodingContract {
ctx: NetscriptContext,
func: string,
hostname: string,
filename: string,
): CodingContract {
const server = helpers.getServer(ctx, hostname); const server = helpers.getServer(ctx, hostname);
const contract = server.getContract(filename); const contract = server.getContract(filename);
if (contract == null) { if (contract == null) {
@ -23,102 +18,86 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
}; };
return { return {
attempt: attempt: (ctx) => (answer, _filename, _hostname?, opts?) => {
(ctx: NetscriptContext) => const filename = helpers.string(ctx, "filename", _filename);
( const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
answer: unknown, const contract = getCodingContract(ctx, hostname, filename);
_filename: unknown,
_hostname: unknown = ctx.workerScript.hostname,
{ returnReward }: CodingAttemptOptions = { returnReward: false },
): boolean | string => {
const filename = helpers.string(ctx, "filename", _filename);
const hostname = helpers.string(ctx, "hostname", _hostname);
const contract = getCodingContract(ctx, "attempt", hostname, filename);
if (typeof answer !== "number" && typeof answer !== "string" && !Array.isArray(answer)) const optsValidator: CodingAttemptOptions = { returnReward: true };
throw new Error("The answer provided was not a number, string, or array"); opts ??= optsValidator;
assertObjectType(ctx, "opts", opts, optsValidator);
if (typeof answer !== "number" && typeof answer !== "string" && !Array.isArray(answer))
throw new Error("The answer provided was not a number, string, or array");
// Convert answer to string. // Convert answer to string.
const answerStr = typeof answer === "string" ? answer : JSON.stringify(answer); const answerStr = typeof answer === "string" ? answer : JSON.stringify(answer);
const creward = contract.reward; const creward = contract.reward;
const serv = helpers.getServer(ctx, hostname); const serv = helpers.getServer(ctx, hostname);
if (contract.isSolution(answerStr)) { if (contract.isSolution(answerStr)) {
const reward = player.gainCodingContractReward(creward, contract.getDifficulty()); const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
helpers.log(ctx, () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`); helpers.log(ctx, () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
serv.removeContract(filename);
return opts.returnReward ? reward : true;
} else {
++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) {
helpers.log(ctx, () => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`);
serv.removeContract(filename); serv.removeContract(filename);
return returnReward ? reward : true;
} else { } else {
++contract.tries; helpers.log(
if (contract.tries >= contract.getMaxNumTries()) { ctx,
helpers.log(ctx, () => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`); () =>
serv.removeContract(filename); `Coding Contract attempt '${filename}' failed. ${
} else { contract.getMaxNumTries() - contract.tries
helpers.log( } attempts remaining.`,
ctx, );
() =>
`Coding Contract attempt '${filename}' failed. ${
contract.getMaxNumTries() - contract.tries
} attempts remaining.`,
);
}
return returnReward ? "" : false;
} }
},
getContractType:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): string => {
const filename = helpers.string(ctx, "filename", _filename);
const hostname = helpers.string(ctx, "hostname", _hostname);
const contract = getCodingContract(ctx, "getContractType", hostname, filename);
return contract.getType();
},
getData:
(ctx: NetscriptContext) =>
(_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): unknown => {
const filename = helpers.string(ctx, "filename", _filename);
const hostname = helpers.string(ctx, "hostname", _hostname);
const contract = getCodingContract(ctx, "getData", hostname, filename);
const data = contract.getData();
if (Array.isArray(data)) {
// 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; return opts.returnReward ? "" : false;
} else { }
return data; },
getContractType: (ctx) => (_filename, _hostname?) => {
const filename = helpers.string(ctx, "filename", _filename);
const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
const contract = getCodingContract(ctx, hostname, filename);
return contract.getType();
},
getData: (ctx) => (_filename, _hostname?) => {
const filename = helpers.string(ctx, "filename", _filename);
const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
const contract = getCodingContract(ctx, hostname, filename);
const data = contract.getData();
if (Array.isArray(data)) {
// 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();
}
} }
},
getDescription: return copy;
(ctx: NetscriptContext) => } else return data;
(_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): string => { },
const filename = helpers.string(ctx, "filename", _filename); getDescription: (ctx) => (_filename, _hostname?) => {
const hostname = helpers.string(ctx, "hostname", _hostname); const filename = helpers.string(ctx, "filename", _filename);
const contract = getCodingContract(ctx, "getDescription", hostname, filename); const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
return contract.getDescription(); const contract = getCodingContract(ctx, hostname, filename);
}, return contract.getDescription();
getNumTriesRemaining: },
(ctx: NetscriptContext) => getNumTriesRemaining: (ctx) => (_filename, _hostname?) => {
(_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): number => { const filename = helpers.string(ctx, "filename", _filename);
const filename = helpers.string(ctx, "filename", _filename); const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, hostname, filename);
const contract = getCodingContract(ctx, "getNumTriesRemaining", hostname, filename); return contract.getMaxNumTries() - contract.tries;
return contract.getMaxNumTries() - contract.tries; },
}, createDummyContract: (ctx) => (_type) => {
createDummyContract: const type = helpers.string(ctx, "type", _type);
(ctx: NetscriptContext) => generateDummyContract(type);
(_type: unknown): void => { },
const type = helpers.string(ctx, "type", _type); getContractTypes: () => () => codingContractTypesMetadata.map((c) => c.name),
generateDummyContract(type);
},
getContractTypes: () => (): string[] => codingContractTypesMetadata.map((c) => c.name),
}; };
} }

File diff suppressed because it is too large Load Diff

@ -1,9 +1,10 @@
import { Player as player } from "../Player"; import { Player } from "../Player";
import { Exploit } from "../Exploits/Exploit"; import { Exploit } from "../Exploits/Exploit";
import * as bcrypt from "bcryptjs"; import * as bcrypt from "bcryptjs";
import { Apr1Events as devMenu } from "../ui/Apr1"; import { Apr1Events as devMenu } from "../ui/Apr1";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { Terminal } from "../Terminal";
export interface INetscriptExtra { export interface INetscriptExtra {
heart: { heart: {
@ -14,40 +15,39 @@ export interface INetscriptExtra {
bypass(doc: Document): void; bypass(doc: Document): void;
alterReality(): void; alterReality(): void;
rainbow(guess: string): void; rainbow(guess: string): void;
iKnowWhatImDoing(): void;
} }
export function NetscriptExtra(): InternalAPI<INetscriptExtra> { export function NetscriptExtra(): InternalAPI<INetscriptExtra> {
return { return {
heart: { heart: {
// Easter egg function // Easter egg function
break: () => (): number => { break: () => () => {
return player.karma; return Player.karma;
}, },
}, },
openDevMenu: () => (): void => { openDevMenu: () => () => {
devMenu.emit(); devMenu.emit();
}, },
exploit: () => (): void => { exploit: () => () => {
player.giveExploit(Exploit.UndocumentedFunctionCall); Player.giveExploit(Exploit.UndocumentedFunctionCall);
}, },
bypass: bypass: (ctx) => (doc) => {
(ctx: NetscriptContext) => // reset both fields first
(doc: unknown): void => { type temporary = { completely_unused_field: unknown };
// reset both fields first const d = doc as temporary;
type temporary = { completely_unused_field: unknown }; d.completely_unused_field = undefined;
const d = doc as temporary; const real_document = document as unknown as temporary;
d.completely_unused_field = undefined; real_document.completely_unused_field = undefined;
const real_document = document as unknown as temporary; // set one to true and check that it affected the other.
real_document.completely_unused_field = undefined; real_document.completely_unused_field = true;
// set one to true and check that it affected the other. if (d.completely_unused_field && ctx.workerScript.ramUsage === 1.6) {
real_document.completely_unused_field = true; Player.giveExploit(Exploit.Bypass);
if (d.completely_unused_field && ctx.workerScript.ramUsage === 1.6) { }
player.giveExploit(Exploit.Bypass); d.completely_unused_field = undefined;
} real_document.completely_unused_field = undefined;
d.completely_unused_field = undefined; },
real_document.completely_unused_field = undefined; alterReality: () => () => {
},
alterReality: () => (): void => {
// We need to trick webpack into not optimizing a variable that is guaranteed to be false (and doesn't use prototypes) // We need to trick webpack into not optimizing a variable that is guaranteed to be false (and doesn't use prototypes)
let x = false; let x = false;
const recur = function (depth: number): void { const recur = function (depth: number): void {
@ -59,25 +59,28 @@ export function NetscriptExtra(): InternalAPI<INetscriptExtra> {
console.warn("I am sure that this variable is false."); console.warn("I am sure that this variable is false.");
if (x !== false) { if (x !== false) {
console.warn("Reality has been altered!"); console.warn("Reality has been altered!");
player.giveExploit(Exploit.RealityAlteration); Player.giveExploit(Exploit.RealityAlteration);
} }
}, },
rainbow: rainbow: (ctx) => (guess) => {
(ctx: NetscriptContext) => function tryGuess(): boolean {
(guess: unknown): boolean => { // eslint-disable-next-line no-sync
function tryGuess(): boolean { const verified = bcrypt.compareSync(
// eslint-disable-next-line no-sync helpers.string(ctx, "guess", guess),
const verified = bcrypt.compareSync( "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO",
helpers.string(ctx, "guess", guess), );
"$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", if (verified) {
); Player.giveExploit(Exploit.INeedARainbow);
if (verified) { return true;
player.giveExploit(Exploit.INeedARainbow);
return true;
}
return false;
} }
return tryGuess(); return false;
}, }
return tryGuess();
},
iKnowWhatImDoing: (ctx) => () => {
helpers.log(ctx, () => "Unlocking unsupported feature: window.tprintRaw");
// @ts-ignore window has no tprintRaw property defined
window.tprintRaw = Terminal.printRaw.bind(Terminal);
},
}; };
} }

@ -26,11 +26,7 @@ import {
calculateWeakenTime, calculateWeakenTime,
} from "../Hacking"; } from "../Hacking";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { import { Formulas as IFormulas } from "../ScriptEditor/NetscriptDefinitions";
Formulas as IFormulas,
HacknetNodeConstants as DefHacknetNodeConstants,
HacknetServerConstants as DefHacknetServerConstants,
} from "../ScriptEditor/NetscriptDefinitions";
import { import {
calculateRespectGain, calculateRespectGain,
calculateWantedLevelGain, calculateWantedLevelGain,
@ -43,7 +39,6 @@ import { favorToRep as calculateFavorToRep, repToFavor as calculateRepToFavor }
import { repFromDonation } from "../Faction/formulas/donation"; import { repFromDonation } from "../Faction/formulas/donation";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { WorkStats } from "../Work/WorkStats";
import { calculateCrimeWorkStats } from "../Work/formulas/Crime"; import { calculateCrimeWorkStats } from "../Work/formulas/Crime";
import { Crimes } from "../Crime/Crimes"; import { Crimes } from "../Crime/Crimes";
import { calculateClassEarnings } from "../Work/formulas/Class"; import { calculateClassEarnings } from "../Work/formulas/Class";
@ -52,7 +47,6 @@ import { LocationName } from "../Locations/data/LocationNames";
import { calculateFactionExp, calculateFactionRep } from "../Work/formulas/Faction"; import { calculateFactionExp, calculateFactionRep } from "../Work/formulas/Faction";
import { FactionWorkType } from "../Work/data/FactionWorkType"; import { FactionWorkType } from "../Work/data/FactionWorkType";
import { Player as INetscriptPlayer, Server as IServerDef } from "../ScriptEditor/NetscriptDefinitions";
import { defaultMultipliers } from "../PersonObjects/Multipliers"; import { defaultMultipliers } from "../PersonObjects/Multipliers";
export function NetscriptFormulas(): InternalAPI<IFormulas> { export function NetscriptFormulas(): InternalAPI<IFormulas> {
@ -62,7 +56,7 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
} }
}; };
return { return {
mockServer: () => (): IServerDef => ({ mockServer: () => () => ({
cpuCores: 0, cpuCores: 0,
ftpPortOpen: false, ftpPortOpen: false,
hasAdminRights: false, hasAdminRights: false,
@ -88,7 +82,7 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
requiredHackingSkill: 0, requiredHackingSkill: 0,
serverGrowth: 0, serverGrowth: 0,
}), }),
mockPlayer: () => (): INetscriptPlayer => ({ mockPlayer: () => () => ({
hp: { current: 0, max: 0 }, hp: { current: 0, max: 0 },
skills: { skills: {
hacking: 0, hacking: 0,
@ -125,41 +119,35 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
entropy: 0, entropy: 0,
}), }),
reputation: { reputation: {
calculateFavorToRep: calculateFavorToRep: (ctx) => (_favor) => {
(ctx: NetscriptContext) => const favor = helpers.number(ctx, "favor", _favor);
(_favor: unknown): number => { checkFormulasAccess(ctx);
const favor = helpers.number(ctx, "favor", _favor); return calculateFavorToRep(favor);
checkFormulasAccess(ctx); },
return calculateFavorToRep(favor); calculateRepToFavor: (ctx) => (_rep) => {
}, const rep = helpers.number(ctx, "rep", _rep);
calculateRepToFavor: checkFormulasAccess(ctx);
(ctx: NetscriptContext) => return calculateRepToFavor(rep);
(_rep: unknown): number => { },
const rep = helpers.number(ctx, "rep", _rep); repFromDonation: (ctx) => (_amount, _player) => {
checkFormulasAccess(ctx); const amount = helpers.number(ctx, "amount", _amount);
return calculateRepToFavor(rep); const player = helpers.player(ctx, _player);
}, checkFormulasAccess(ctx);
repFromDonation: return repFromDonation(amount, player);
(ctx: NetscriptContext) => },
(_amount: unknown, _player: unknown): number => {
const amount = helpers.number(ctx, "amount", _amount);
const player = helpers.player(ctx, _player);
checkFormulasAccess(ctx);
return repFromDonation(amount, player);
},
}, },
skills: { skills: {
calculateSkill: calculateSkill:
(ctx: NetscriptContext) => (ctx) =>
(_exp: unknown, _mult: unknown = 1): number => { (_exp, _mult = 1) => {
const exp = helpers.number(ctx, "exp", _exp); const exp = helpers.number(ctx, "exp", _exp);
const mult = helpers.number(ctx, "mult", _mult); const mult = helpers.number(ctx, "mult", _mult);
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return calculateSkill(exp, mult); return calculateSkill(exp, mult);
}, },
calculateExp: calculateExp:
(ctx: NetscriptContext) => (ctx) =>
(_skill: unknown, _mult: unknown = 1): number => { (_skill, _mult = 1) => {
const skill = helpers.number(ctx, "skill", _skill); const skill = helpers.number(ctx, "skill", _skill);
const mult = helpers.number(ctx, "mult", _mult); const mult = helpers.number(ctx, "mult", _mult);
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
@ -167,33 +155,27 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
}, },
}, },
hacking: { hacking: {
hackChance: hackChance: (ctx) => (_server, _player) => {
(ctx: NetscriptContext) => const server = helpers.server(ctx, _server);
(_server: unknown, _player: unknown): number => { const player = helpers.player(ctx, _player);
const server = helpers.server(ctx, _server); checkFormulasAccess(ctx);
const player = helpers.player(ctx, _player); return calculateHackingChance(server, player);
checkFormulasAccess(ctx); },
return calculateHackingChance(server, player); hackExp: (ctx) => (_server, _player) => {
}, const server = helpers.server(ctx, _server);
hackExp: const player = helpers.player(ctx, _player);
(ctx: NetscriptContext) => checkFormulasAccess(ctx);
(_server: unknown, _player: unknown): number => { return calculateHackingExpGain(server, player);
const server = helpers.server(ctx, _server); },
const player = helpers.player(ctx, _player); hackPercent: (ctx) => (_server, _player) => {
checkFormulasAccess(ctx); const server = helpers.server(ctx, _server);
return calculateHackingExpGain(server, player); const player = helpers.player(ctx, _player);
}, checkFormulasAccess(ctx);
hackPercent: return calculatePercentMoneyHacked(server, player);
(ctx: NetscriptContext) => },
(_server: unknown, _player: unknown): number => {
const server = helpers.server(ctx, _server);
const player = helpers.player(ctx, _player);
checkFormulasAccess(ctx);
return calculatePercentMoneyHacked(server, player);
},
growPercent: growPercent:
(ctx: NetscriptContext) => (ctx) =>
(_server: unknown, _threads: unknown, _player: unknown, _cores: unknown = 1): number => { (_server, _threads, _player, _cores = 1) => {
const server = helpers.server(ctx, _server); const server = helpers.server(ctx, _server);
const player = helpers.player(ctx, _player); const player = helpers.player(ctx, _player);
const threads = helpers.number(ctx, "threads", _threads); const threads = helpers.number(ctx, "threads", _threads);
@ -201,35 +183,29 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return calculateServerGrowth(server, threads, player, cores); return calculateServerGrowth(server, threads, player, cores);
}, },
hackTime: hackTime: (ctx) => (_server, _player) => {
(ctx: NetscriptContext) => const server = helpers.server(ctx, _server);
(_server: unknown, _player: unknown): number => { const player = helpers.player(ctx, _player);
const server = helpers.server(ctx, _server); checkFormulasAccess(ctx);
const player = helpers.player(ctx, _player); return calculateHackingTime(server, player) * 1000;
checkFormulasAccess(ctx); },
return calculateHackingTime(server, player) * 1000; growTime: (ctx) => (_server, _player) => {
}, const server = helpers.server(ctx, _server);
growTime: const player = helpers.player(ctx, _player);
(ctx: NetscriptContext) => checkFormulasAccess(ctx);
(_server: unknown, _player: unknown): number => { return calculateGrowTime(server, player) * 1000;
const server = helpers.server(ctx, _server); },
const player = helpers.player(ctx, _player); weakenTime: (ctx) => (_server, _player) => {
checkFormulasAccess(ctx); const server = helpers.server(ctx, _server);
return calculateGrowTime(server, player) * 1000; const player = helpers.player(ctx, _player);
}, checkFormulasAccess(ctx);
weakenTime: return calculateWeakenTime(server, player) * 1000;
(ctx: NetscriptContext) => },
(_server: unknown, _player: unknown): number => {
const server = helpers.server(ctx, _server);
const player = helpers.player(ctx, _player);
checkFormulasAccess(ctx);
return calculateWeakenTime(server, player) * 1000;
},
}, },
hacknetNodes: { hacknetNodes: {
moneyGainRate: moneyGainRate:
(ctx: NetscriptContext) => (ctx) =>
(_level: unknown, _ram: unknown, _cores: unknown, _mult: unknown = 1): number => { (_level, _ram, _cores, _mult = 1) => {
const level = helpers.number(ctx, "level", _level); const level = helpers.number(ctx, "level", _level);
const ram = helpers.number(ctx, "ram", _ram); const ram = helpers.number(ctx, "ram", _ram);
const cores = helpers.number(ctx, "cores", _cores); const cores = helpers.number(ctx, "cores", _cores);
@ -238,8 +214,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return calculateMoneyGainRate(level, ram, cores, mult); return calculateMoneyGainRate(level, ram, cores, mult);
}, },
levelUpgradeCost: levelUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { (_startingLevel, _extraLevels = 1, _costMult = 1) => {
const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel); const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel);
const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
@ -247,8 +223,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult); return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
}, },
ramUpgradeCost: ramUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { (_startingRam, _extraLevels = 1, _costMult = 1) => {
const startingRam = helpers.number(ctx, "startingRam", _startingRam); const startingRam = helpers.number(ctx, "startingRam", _startingRam);
const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
@ -256,31 +232,29 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return calculateRamUpgradeCost(startingRam, extraLevels, costMult); return calculateRamUpgradeCost(startingRam, extraLevels, costMult);
}, },
coreUpgradeCost: coreUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number => { (_startingCore, _extraCores = 1, _costMult = 1) => {
const startingCore = helpers.number(ctx, "startingCore", _startingCore); const startingCore = helpers.number(ctx, "startingCore", _startingCore);
const extraCores = helpers.number(ctx, "extraCores", _extraCores); const extraCores = helpers.number(ctx, "extraCores", _extraCores);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return calculateCoreUpgradeCost(startingCore, extraCores, costMult); return calculateCoreUpgradeCost(startingCore, extraCores, costMult);
}, },
hacknetNodeCost: hacknetNodeCost: (ctx) => (_n, _mult) => {
(ctx: NetscriptContext) => const n = helpers.number(ctx, "n", _n);
(_n: unknown, _mult: unknown): number => { const mult = helpers.number(ctx, "mult", _mult);
const n = helpers.number(ctx, "n", _n); checkFormulasAccess(ctx);
const mult = helpers.number(ctx, "mult", _mult); return calculateNodeCost(n, mult);
checkFormulasAccess(ctx); },
return calculateNodeCost(n, mult); constants: (ctx) => () => {
},
constants: (ctx: NetscriptContext) => (): DefHacknetNodeConstants => {
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return Object.assign({}, HacknetNodeConstants); return Object.assign({}, HacknetNodeConstants);
}, },
}, },
hacknetServers: { hacknetServers: {
hashGainRate: hashGainRate:
(ctx: NetscriptContext) => (ctx) =>
(_level: unknown, _ramUsed: unknown, _maxRam: unknown, _cores: unknown, _mult: unknown = 1): number => { (_level, _ramUsed, _maxRam, _cores, _mult = 1) => {
const level = helpers.number(ctx, "level", _level); const level = helpers.number(ctx, "level", _level);
const ramUsed = helpers.number(ctx, "ramUsed", _ramUsed); const ramUsed = helpers.number(ctx, "ramUsed", _ramUsed);
const maxRam = helpers.number(ctx, "maxRam", _maxRam); const maxRam = helpers.number(ctx, "maxRam", _maxRam);
@ -290,8 +264,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult); return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult);
}, },
levelUpgradeCost: levelUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { (_startingLevel, _extraLevels = 1, _costMult = 1) => {
const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel); const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel);
const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
@ -299,8 +273,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult); return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
}, },
ramUpgradeCost: ramUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { (_startingRam, _extraLevels = 1, _costMult = 1) => {
const startingRam = helpers.number(ctx, "startingRam", _startingRam); const startingRam = helpers.number(ctx, "startingRam", _startingRam);
const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
@ -308,8 +282,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult); return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult);
}, },
coreUpgradeCost: coreUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number => { (_startingCore, _extraCores = 1, _costMult = 1) => {
const startingCore = helpers.number(ctx, "startingCore", _startingCore); const startingCore = helpers.number(ctx, "startingCore", _startingCore);
const extraCores = helpers.number(ctx, "extraCores", _extraCores); const extraCores = helpers.number(ctx, "extraCores", _extraCores);
const costMult = helpers.number(ctx, "costMult", _costMult); const costMult = helpers.number(ctx, "costMult", _costMult);
@ -317,117 +291,97 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult); return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult);
}, },
cacheUpgradeCost: cacheUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_startingCache: unknown, _extraCache: unknown = 1): number => { (_startingCache, _extraCache = 1) => {
const startingCache = helpers.number(ctx, "startingCache", _startingCache); const startingCache = helpers.number(ctx, "startingCache", _startingCache);
const extraCache = helpers.number(ctx, "extraCache", _extraCache); const extraCache = helpers.number(ctx, "extraCache", _extraCache);
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return HScalculateCacheUpgradeCost(startingCache, extraCache); return HScalculateCacheUpgradeCost(startingCache, extraCache);
}, },
hashUpgradeCost: hashUpgradeCost: (ctx) => (_upgName, _level) => {
(ctx: NetscriptContext) => const upgName = helpers.string(ctx, "upgName", _upgName);
(_upgName: unknown, _level: unknown): number => { const level = helpers.number(ctx, "level", _level);
const upgName = helpers.string(ctx, "upgName", _upgName); checkFormulasAccess(ctx);
const level = helpers.number(ctx, "level", _level); const upg = player.hashManager.getUpgrade(upgName);
checkFormulasAccess(ctx); if (!upg) {
const upg = player.hashManager.getUpgrade(upgName); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`);
if (!upg) { }
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`); return upg.getCost(level);
} },
return upg.getCost(level);
},
hacknetServerCost: hacknetServerCost:
(ctx: NetscriptContext) => (ctx) =>
(_n: unknown, _mult: unknown = 1): number => { (_n, _mult = 1) => {
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const mult = helpers.number(ctx, "mult", _mult); const mult = helpers.number(ctx, "mult", _mult);
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return HScalculateServerCost(n, mult); return HScalculateServerCost(n, mult);
}, },
constants: (ctx: NetscriptContext) => (): DefHacknetServerConstants => { constants: (ctx) => () => {
checkFormulasAccess(ctx); checkFormulasAccess(ctx);
return Object.assign({}, HacknetServerConstants); return Object.assign({}, HacknetServerConstants);
}, },
}, },
gang: { gang: {
wantedPenalty: wantedPenalty: (ctx) => (_gang) => {
(ctx: NetscriptContext) => const gang = helpers.gang(ctx, _gang);
(_gang: unknown): number => { checkFormulasAccess(ctx);
const gang = helpers.gang(ctx, _gang); return calculateWantedPenalty(gang);
checkFormulasAccess(ctx); },
return calculateWantedPenalty(gang); respectGain: (ctx) => (_gang, _member, _task) => {
}, const gang = helpers.gang(ctx, _gang);
respectGain: const member = helpers.gangMember(ctx, _member);
(ctx: NetscriptContext) => const task = helpers.gangTask(ctx, _task);
(_gang: unknown, _member: unknown, _task: unknown): number => { checkFormulasAccess(ctx);
const gang = helpers.gang(ctx, _gang); return calculateRespectGain(gang, member, task);
const member = helpers.gangMember(ctx, _member); },
const task = helpers.gangTask(ctx, _task); wantedLevelGain: (ctx) => (_gang, _member, _task) => {
checkFormulasAccess(ctx); const gang = helpers.gang(ctx, _gang);
return calculateRespectGain(gang, member, task); const member = helpers.gangMember(ctx, _member);
}, const task = helpers.gangTask(ctx, _task);
wantedLevelGain: checkFormulasAccess(ctx);
(ctx: NetscriptContext) => return calculateWantedLevelGain(gang, member, task);
(_gang: unknown, _member: unknown, _task: unknown): number => { },
const gang = helpers.gang(ctx, _gang); moneyGain: (ctx) => (_gang, _member, _task) => {
const member = helpers.gangMember(ctx, _member); const gang = helpers.gang(ctx, _gang);
const task = helpers.gangTask(ctx, _task); const member = helpers.gangMember(ctx, _member);
checkFormulasAccess(ctx); const task = helpers.gangTask(ctx, _task);
return calculateWantedLevelGain(gang, member, task); checkFormulasAccess(ctx);
}, return calculateMoneyGain(gang, member, task);
moneyGain: },
(ctx: NetscriptContext) => ascensionPointsGain: (ctx) => (_exp) => {
(_gang: unknown, _member: unknown, _task: unknown): number => { const exp = helpers.number(ctx, "exp", _exp);
const gang = helpers.gang(ctx, _gang); checkFormulasAccess(ctx);
const member = helpers.gangMember(ctx, _member); return calculateAscensionPointsGain(exp);
const task = helpers.gangTask(ctx, _task); },
checkFormulasAccess(ctx); ascensionMultiplier: (ctx) => (_points) => {
return calculateMoneyGain(gang, member, task); const points = helpers.number(ctx, "points", _points);
}, checkFormulasAccess(ctx);
ascensionPointsGain: return calculateAscensionMult(points);
(ctx: NetscriptContext) => },
(_exp: unknown): number => {
const exp = helpers.number(ctx, "exp", _exp);
checkFormulasAccess(ctx);
return calculateAscensionPointsGain(exp);
},
ascensionMultiplier:
(ctx: NetscriptContext) =>
(_points: unknown): number => {
const points = helpers.number(ctx, "points", _points);
checkFormulasAccess(ctx);
return calculateAscensionMult(points);
},
}, },
work: { work: {
crimeGains: crimeGains: (ctx) => (_crimeType) => {
(ctx: NetscriptContext) => const crimeType = helpers.string(ctx, "crimeType", _crimeType);
(_crimeType: unknown): WorkStats => { const crime = Object.values(Crimes).find((c) => String(c.type) === crimeType);
const crimeType = helpers.string(ctx, "crimeType", _crimeType); if (!crime) throw new Error(`Invalid crime type: ${crimeType}`);
const crime = Object.values(Crimes).find((c) => String(c.type) === crimeType); return calculateCrimeWorkStats(crime);
if (!crime) throw new Error(`Invalid crime type: ${crimeType}`); },
return calculateCrimeWorkStats(crime); classGains: (ctx) => (_person, _classType, _locationName) => {
}, const person = helpers.player(ctx, _person);
classGains: const classType = helpers.string(ctx, "classType", _classType);
(ctx: NetscriptContext) => const locationName = helpers.string(ctx, "locationName", _locationName);
(_person: unknown, _classType: unknown, _locationName: unknown): WorkStats => { return calculateClassEarnings(person, classType as ClassType, locationName as LocationName);
const person = helpers.player(ctx, _person); },
const classType = helpers.string(ctx, "classType", _classType); factionGains: (ctx) => (_player, _workType, _favor) => {
const locationName = helpers.string(ctx, "locationName", _locationName); const player = helpers.player(ctx, _player);
return calculateClassEarnings(person, classType as ClassType, locationName as LocationName); const workType = helpers.string(ctx, "_workType", _workType) as FactionWorkType;
}, const favor = helpers.number(ctx, "favor", _favor);
factionGains: const exp = calculateFactionExp(player, workType);
(ctx: NetscriptContext) => const rep = calculateFactionRep(player, workType, favor);
(_player: unknown, _workType: unknown, _favor: unknown): WorkStats => { exp.reputation = rep;
const player = helpers.player(ctx, _player); return exp;
const workType = helpers.string(ctx, "_workType", _workType) as FactionWorkType; },
const favor = helpers.number(ctx, "favor", _favor); // companyGains: (ctx) => (_player) {
const exp = calculateFactionExp(player, workType);
const rep = calculateFactionRep(player, workType, favor);
exp.reputation = rep;
return exp;
},
// companyGains: (ctx: NetscriptContext) =>_player: unknown (): WorkStats {
// const player = helpers.player(ctx, _player); // const player = helpers.player(ctx, _player);
// }, // },

@ -9,16 +9,7 @@ import { GangMember } from "../Gang/GangMember";
import { GangMemberTask } from "../Gang/GangMemberTask"; import { GangMemberTask } from "../Gang/GangMemberTask";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { import { Gang as IGang, EquipmentStats, GangOtherInfoObject } from "../ScriptEditor/NetscriptDefinitions";
Gang as IGang,
GangGenInfo,
GangOtherInfo,
GangMemberInfo,
GangMemberAscension,
EquipmentStats,
GangTaskStats,
GangOtherInfoObject,
} from "../ScriptEditor/NetscriptDefinitions";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
export function NetscriptGang(): InternalAPI<IGang> { export function NetscriptGang(): InternalAPI<IGang> {
@ -44,28 +35,26 @@ export function NetscriptGang(): InternalAPI<IGang> {
}; };
return { return {
createGang: createGang: (ctx) => (_faction) => {
(ctx: NetscriptContext) => const faction = helpers.string(ctx, "faction", _faction);
(_faction: unknown): boolean => { // this list is copied from Faction/ui/Root.tsx
const faction = helpers.string(ctx, "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.gang) return false; if (Player.gang) 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: () => (): boolean => { inGang: () => () => {
return Player.gang ? true : false; return Player.gang ? true : false;
}, },
getMemberNames: (ctx: NetscriptContext) => (): string[] => { getMemberNames: (ctx) => () => {
const gang = getGang(ctx); const gang = getGang(ctx);
return gang.members.map((member) => member.name); return gang.members.map((member) => member.name);
}, },
getGangInformation: (ctx: NetscriptContext) => (): GangGenInfo => { getGangInformation: (ctx) => () => {
const gang = getGang(ctx); const gang = getGang(ctx);
return { return {
faction: gang.facName, faction: gang.facName,
@ -82,7 +71,7 @@ export function NetscriptGang(): InternalAPI<IGang> {
wantedPenalty: gang.getWantedPenalty(), wantedPenalty: gang.getWantedPenalty(),
}; };
}, },
getOtherGangInformation: (ctx: NetscriptContext) => (): GangOtherInfo => { getOtherGangInformation: (ctx) => () => {
getGang(ctx); getGang(ctx);
const cpy: Record<string, GangOtherInfoObject> = {}; const cpy: Record<string, GangOtherInfoObject> = {};
for (const gang of Object.keys(AllGangs)) { for (const gang of Object.keys(AllGangs)) {
@ -91,231 +80,206 @@ export function NetscriptGang(): InternalAPI<IGang> {
return cpy; return cpy;
}, },
getMemberInformation: getMemberInformation: (ctx) => (_memberName) => {
(ctx: NetscriptContext) => const memberName = helpers.string(ctx, "memberName", _memberName);
(_memberName: unknown): GangMemberInfo => { const gang = getGang(ctx);
const memberName = helpers.string(ctx, "memberName", _memberName); const member = getGangMember(ctx, memberName);
const gang = getGang(ctx); 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: (ctx: NetscriptContext) => (): boolean => { canRecruitMember: (ctx) => () => {
const gang = getGang(ctx); const gang = getGang(ctx);
return gang.canRecruitMember(); return gang.canRecruitMember();
}, },
recruitMember: recruitMember: (ctx) => (_memberName) => {
(ctx: NetscriptContext) => const memberName = helpers.string(ctx, "memberName", _memberName);
(_memberName: unknown): boolean => { const gang = getGang(ctx);
const memberName = helpers.string(ctx, "memberName", _memberName); const recruited = gang.recruitMember(memberName);
const gang = getGang(ctx); if (recruited) {
const recruited = gang.recruitMember(memberName); ctx.workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`);
if (recruited) { } else {
ctx.workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`); ctx.workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`);
} else { }
ctx.workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`);
}
return recruited; return recruited;
}, },
getTaskNames: (ctx: NetscriptContext) => (): string[] => { getTaskNames: (ctx) => () => {
const gang = getGang(ctx); const gang = getGang(ctx);
const tasks = gang.getAllTaskNames(); const tasks = gang.getAllTaskNames();
tasks.unshift("Unassigned"); tasks.unshift("Unassigned");
return tasks; return tasks;
}, },
setMemberTask: setMemberTask: (ctx) => (_memberName, _taskName) => {
(ctx: NetscriptContext) => const memberName = helpers.string(ctx, "memberName", _memberName);
(_memberName: unknown, _taskName: unknown): boolean => { const taskName = helpers.string(ctx, "taskName", _taskName);
const memberName = helpers.string(ctx, "memberName", _memberName); const gang = getGang(ctx);
const taskName = helpers.string(ctx, "taskName", _taskName); const member = getGangMember(ctx, memberName);
const gang = getGang(ctx); if (!gang.getAllTaskNames().includes(taskName)) {
const member = getGangMember(ctx, memberName); ctx.workerScript.log(
if (!gang.getAllTaskNames().includes(taskName)) { "gang.setMemberTask",
ctx.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); ctx.workerScript.log(
if (success) { "gang.setMemberTask",
ctx.workerScript.log( () => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`,
"gang.setMemberTask", );
() => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`, } else {
); ctx.workerScript.log(
} else { "gang.setMemberTask",
ctx.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: getTaskStats: (ctx) => (_taskName) => {
(ctx: NetscriptContext) => const taskName = helpers.string(ctx, "taskName", _taskName);
(_taskName: unknown): GangTaskStats => { getGang(ctx);
const taskName = helpers.string(ctx, "taskName", _taskName); const task = getGangTask(ctx, taskName);
getGang(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: (ctx) => () => {
},
getEquipmentNames: (ctx: NetscriptContext) => (): string[] => {
getGang(ctx); getGang(ctx);
return Object.keys(GangMemberUpgrades); return Object.keys(GangMemberUpgrades);
}, },
getEquipmentCost: getEquipmentCost: (ctx) => (_equipName) => {
(ctx: NetscriptContext) => const equipName = helpers.string(ctx, "equipName", _equipName);
(_equipName: unknown): number => { const gang = getGang(ctx);
const equipName = helpers.string(ctx, "equipName", _equipName); const upg = GangMemberUpgrades[equipName];
const gang = getGang(ctx); if (upg === null) return Infinity;
const upg = GangMemberUpgrades[equipName]; return gang.getUpgradeCost(upg);
if (upg === null) return Infinity; },
return gang.getUpgradeCost(upg); getEquipmentType: (ctx) => (_equipName) => {
}, const equipName = helpers.string(ctx, "equipName", _equipName);
getEquipmentType: getGang(ctx);
(ctx: NetscriptContext) => const upg = GangMemberUpgrades[equipName];
(_equipName: unknown): string => { if (upg == null) return "";
const equipName = helpers.string(ctx, "equipName", _equipName); return upg.getType();
getGang(ctx); },
const upg = GangMemberUpgrades[equipName]; getEquipmentStats: (ctx) => (_equipName) => {
if (upg == null) return ""; const equipName = helpers.string(ctx, "equipName", _equipName);
return upg.getType(); getGang(ctx);
}, const equipment = GangMemberUpgrades[equipName];
getEquipmentStats: if (!equipment) {
(ctx: NetscriptContext) => throw helpers.makeRuntimeErrorMsg(ctx, `Invalid equipment: ${equipName}`);
(_equipName: unknown): EquipmentStats => { }
const equipName = helpers.string(ctx, "equipName", _equipName); const typecheck: EquipmentStats = equipment.mults;
getGang(ctx); return Object.assign({}, typecheck);
const equipment = GangMemberUpgrades[equipName]; },
if (!equipment) { purchaseEquipment: (ctx) => (_memberName, _equipName) => {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid equipment: ${equipName}`); const memberName = helpers.string(ctx, "memberName", _memberName);
} const equipName = helpers.string(ctx, "equipName", _equipName);
const typecheck: EquipmentStats = equipment.mults; getGang(ctx);
return Object.assign({}, typecheck) as any; const member = getGangMember(ctx, memberName);
}, const equipment = GangMemberUpgrades[equipName];
purchaseEquipment: if (!equipment) return false;
(ctx: NetscriptContext) => const res = member.buyUpgrade(equipment);
(_memberName: unknown, _equipName: unknown): boolean => { if (res) {
const memberName = helpers.string(ctx, "memberName", _memberName); ctx.workerScript.log(
const equipName = helpers.string(ctx, "equipName", _equipName); "gang.purchaseEquipment",
getGang(ctx); () => `Purchased '${equipName}' for Gang member '${memberName}'`,
const member = getGangMember(ctx, memberName); );
const equipment = GangMemberUpgrades[equipName]; } else {
if (!equipment) return false; ctx.workerScript.log(
const res = member.buyUpgrade(equipment); "gang.purchaseEquipment",
if (res) { () => `Failed to purchase '${equipName}' for Gang member '${memberName}'`,
ctx.workerScript.log( );
"gang.purchaseEquipment", }
() => `Purchased '${equipName}' for Gang member '${memberName}'`,
);
} else {
ctx.workerScript.log(
"gang.purchaseEquipment",
() => `Failed to purchase '${equipName}' for Gang member '${memberName}'`,
);
}
return res; return res;
}, },
ascendMember: ascendMember: (ctx) => (_memberName) => {
(ctx: NetscriptContext) => const memberName = helpers.string(ctx, "memberName", _memberName);
(_memberName: unknown): GangMemberAscension | undefined => { const gang = getGang(ctx);
const memberName = helpers.string(ctx, "memberName", _memberName); const member = getGangMember(ctx, memberName);
const gang = getGang(ctx); if (!member.canAscend()) return;
const member = getGangMember(ctx, memberName); return gang.ascendMember(member, ctx.workerScript);
if (!member.canAscend()) return; },
return gang.ascendMember(member, ctx.workerScript); getAscensionResult: (ctx) => (_memberName) => {
}, const memberName = helpers.string(ctx, "memberName", _memberName);
getAscensionResult: getGang(ctx);
(ctx: NetscriptContext) => const member = getGangMember(ctx, memberName);
(_memberName: unknown): GangMemberAscension | undefined => { if (!member.canAscend()) return;
const memberName = helpers.string(ctx, "memberName", _memberName); return {
getGang(ctx); respect: member.earnedRespect,
const member = getGangMember(ctx, memberName); ...member.getAscensionResults(),
if (!member.canAscend()) return; };
return { },
respect: member.earnedRespect, setTerritoryWarfare: (ctx) => (_engage) => {
...member.getAscensionResults(), const engage = !!_engage;
}; const gang = getGang(ctx);
}, if (engage) {
setTerritoryWarfare: gang.territoryWarfareEngaged = true;
(ctx: NetscriptContext) => ctx.workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare");
(_engage: unknown): void => { } else {
const engage = !!_engage; gang.territoryWarfareEngaged = false;
const gang = getGang(ctx); ctx.workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare");
if (engage) { }
gang.territoryWarfareEngaged = true; },
ctx.workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare"); getChanceToWinClash: (ctx) => (_otherGang) => {
} else { const otherGang = helpers.string(ctx, "otherGang", _otherGang);
gang.territoryWarfareEngaged = false; const gang = getGang(ctx);
ctx.workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare"); if (AllGangs[otherGang] == null) {
} throw helpers.makeRuntimeErrorMsg(ctx, `Invalid gang: ${otherGang}`);
}, }
getChanceToWinClash:
(ctx: NetscriptContext) =>
(_otherGang: unknown): number => {
const otherGang = helpers.string(ctx, "otherGang", _otherGang);
const gang = getGang(ctx);
if (AllGangs[otherGang] == null) {
throw helpers.makeRuntimeErrorMsg(ctx, `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: (ctx: NetscriptContext) => (): number => { getBonusTime: (ctx) => () => {
const gang = getGang(ctx); const gang = getGang(ctx);
return Math.round(gang.storedCycles / 5) * 1000; return Math.round(gang.storedCycles / 5) * 1000;
}, },

@ -21,39 +21,35 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
}; };
return { return {
getAugmentationGraftPrice: getAugmentationGraftPrice: (ctx) => (_augName) => {
(ctx: NetscriptContext) => const augName = helpers.string(ctx, "augName", _augName);
(_augName: unknown): number => { checkGraftingAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
checkGraftingAPIAccess(ctx); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { }
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
} return graftableAug.cost;
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); },
return graftableAug.cost;
},
getAugmentationGraftTime: getAugmentationGraftTime: (ctx) => (_augName) => {
(ctx: NetscriptContext) => const augName = helpers.string(ctx, "augName", _augName);
(_augName: string): number => { checkGraftingAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
checkGraftingAPIAccess(ctx); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { }
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
} return calculateGraftingTimeWithBonus(graftableAug);
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); },
return calculateGraftingTimeWithBonus(graftableAug);
},
getGraftableAugmentations: (ctx: NetscriptContext) => (): string[] => { getGraftableAugmentations: (ctx) => () => {
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
const graftableAugs = getGraftingAvailableAugs(); const graftableAugs = getGraftingAvailableAugs();
return graftableAugs; return graftableAugs;
}, },
graftAugmentation: graftAugmentation:
(ctx: NetscriptContext) => (ctx) =>
(_augName: string, _focus: unknown = true): boolean => { (_augName, _focus = true) => {
const augName = helpers.string(ctx, "augName", _augName); const augName = helpers.string(ctx, "augName", _augName);
const focus = !!_focus; const focus = !!_focus;
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);

@ -50,76 +50,74 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
}; };
return { return {
numNodes: () => (): number => { numNodes: () => () => {
return player.hacknetNodes.length; return player.hacknetNodes.length;
}, },
maxNumNodes: () => (): number => { maxNumNodes: () => () => {
if (hasHacknetServers()) { if (hasHacknetServers()) {
return HacknetServerConstants.MaxServers; return HacknetServerConstants.MaxServers;
} }
return Infinity; return Infinity;
}, },
purchaseNode: () => (): number => { purchaseNode: () => () => {
return purchaseHacknet(); return purchaseHacknet();
}, },
getPurchaseNodeCost: () => (): number => { getPurchaseNodeCost: () => () => {
if (hasHacknetServers()) { if (hasHacknetServers()) {
return getCostOfNextHacknetServer(); return getCostOfNextHacknetServer();
} else { } else {
return getCostOfNextHacknetNode(); return getCostOfNextHacknetNode();
} }
}, },
getNodeStats: getNodeStats: (ctx) => (_i) => {
(ctx: NetscriptContext) => const i = helpers.number(ctx, "i", _i);
(_i: unknown): NodeStats => { const node = getHacknetNode(ctx, i);
const i = helpers.number(ctx, "i", _i); const hasUpgraded = hasHacknetServers();
const node = getHacknetNode(ctx, i); const res: NodeStats = {
const hasUpgraded = hasHacknetServers(); name: node instanceof HacknetServer ? node.hostname : node.name,
const res: NodeStats = { level: node.level,
name: node instanceof HacknetServer ? node.hostname : node.name, ram: node instanceof HacknetServer ? node.maxRam : node.ram,
level: node.level, cores: node.cores,
ram: node instanceof HacknetServer ? node.maxRam : node.ram, production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
cores: node.cores, timeOnline: node.onlineTimeSeconds,
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond, totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
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;
res.ramUsed = node.ramUsed; res.ramUsed = node.ramUsed;
} }
return res; return res;
}, },
upgradeLevel: upgradeLevel:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): boolean => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseLevelUpgrade(node, n); return purchaseLevelUpgrade(node, n);
}, },
upgradeRam: upgradeRam:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): boolean => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseRamUpgrade(node, n); return purchaseRamUpgrade(node, n);
}, },
upgradeCore: upgradeCore:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): boolean => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseCoreUpgrade(node, n); return purchaseCoreUpgrade(node, n);
}, },
upgradeCache: upgradeCache:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): boolean => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
@ -137,32 +135,32 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
return res; return res;
}, },
getLevelUpgradeCost: getLevelUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): number => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return node.calculateLevelUpgradeCost(n, player.mults.hacknet_node_level_cost); return node.calculateLevelUpgradeCost(n, player.mults.hacknet_node_level_cost);
}, },
getRamUpgradeCost: getRamUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): number => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return node.calculateRamUpgradeCost(n, player.mults.hacknet_node_ram_cost); return node.calculateRamUpgradeCost(n, player.mults.hacknet_node_ram_cost);
}, },
getCoreUpgradeCost: getCoreUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): number => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return node.calculateCoreUpgradeCost(n, player.mults.hacknet_node_core_cost); return node.calculateCoreUpgradeCost(n, player.mults.hacknet_node_core_cost);
}, },
getCacheUpgradeCost: getCacheUpgradeCost:
(ctx: NetscriptContext) => (ctx) =>
(_i: unknown, _n: unknown = 1): number => { (_i, _n = 1) => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
@ -175,21 +173,21 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
} }
return node.calculateCacheUpgradeCost(n); return node.calculateCacheUpgradeCost(n);
}, },
numHashes: () => (): number => { numHashes: () => () => {
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
return 0; return 0;
} }
return player.hashManager.hashes; return player.hashManager.hashes;
}, },
hashCapacity: () => (): number => { hashCapacity: () => () => {
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
return 0; return 0;
} }
return player.hashManager.capacity; return player.hashManager.capacity;
}, },
hashCost: hashCost:
(ctx: NetscriptContext) => (ctx) =>
(_upgName: unknown, _count: unknown = 1): number => { (_upgName, _count = 1) => {
const upgName = helpers.string(ctx, "upgName", _upgName); const upgName = helpers.string(ctx, "upgName", _upgName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
@ -199,8 +197,8 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
return player.hashManager.getUpgradeCost(upgName, count); return player.hashManager.getUpgradeCost(upgName, count);
}, },
spendHashes: spendHashes:
(ctx: NetscriptContext) => (ctx) =>
(_upgName: unknown, _upgTarget: unknown = "", _count: unknown = 1): boolean => { (_upgName, _upgTarget = "", _count = 1) => {
const upgName = helpers.string(ctx, "upgName", _upgName); const upgName = helpers.string(ctx, "upgName", _upgName);
const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget); const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
@ -209,29 +207,27 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
} }
return purchaseHashUpgrade(upgName, upgTarget, count); return purchaseHashUpgrade(upgName, upgTarget, count);
}, },
getHashUpgrades: () => (): string[] => { getHashUpgrades: () => () => {
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
return []; return [];
} }
return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name); return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name);
}, },
getHashUpgradeLevel: getHashUpgradeLevel: (ctx) => (_upgName) => {
(ctx: NetscriptContext) => const upgName = helpers.string(ctx, "upgName", _upgName);
(_upgName: unknown): number => { const level = player.hashManager.upgrades[upgName];
const upgName = helpers.string(ctx, "upgName", _upgName); if (level === undefined) {
const level = player.hashManager.upgrades[upgName]; throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`);
if (level === undefined) { }
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`); return level;
} },
return level; getStudyMult: () => () => {
},
getStudyMult: () => (): number => {
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
return 1; return 1;
} }
return player.hashManager.getStudyMult(); return player.hashManager.getStudyMult();
}, },
getTrainingMult: () => (): number => { getTrainingMult: () => () => {
if (!hasHacknetServers()) { if (!hasHacknetServers()) {
return 1; return 1;
} }

@ -1,8 +1,4 @@
import { import { Infiltration as IInfiltration, InfiltrationLocation } from "../ScriptEditor/NetscriptDefinitions";
Infiltration as IInfiltration,
InfiltrationLocation,
PossibleInfiltrationLocation,
} from "../ScriptEditor/NetscriptDefinitions";
import { Location } from "../Locations/Location"; import { Location } from "../Locations/Location";
import { Locations } from "../Locations/Locations"; import { Locations } from "../Locations/Locations";
import { calculateDifficulty, calculateReward } from "../Infiltration/formulas/game"; import { calculateDifficulty, calculateReward } from "../Infiltration/formulas/game";
@ -44,17 +40,15 @@ export function NetscriptInfiltration(): InternalAPI<IInfiltration> {
}; };
}; };
return { return {
getPossibleLocations: () => (): PossibleInfiltrationLocation[] => { getPossibleLocations: () => () => {
return getLocationsWithInfiltrations.map((l) => ({ return getLocationsWithInfiltrations.map((l) => ({
city: l.city ?? "", city: l.city ?? "",
name: String(l.name), name: String(l.name),
})); }));
}, },
getInfiltration: getInfiltration: (ctx) => (_location) => {
(ctx: NetscriptContext) => const location = helpers.string(ctx, "location", _location);
(_location: unknown): InfiltrationLocation => { return calculateInfiltrationData(ctx, location);
const location = helpers.string(ctx, "location", _location); },
return calculateInfiltrationData(ctx, location);
},
}; };
} }

File diff suppressed because it is too large Load Diff

@ -4,13 +4,7 @@ import { CityName } from "../Locations/data/CityNames";
import { findCrime } from "../Crime/CrimeHelpers"; import { findCrime } from "../Crime/CrimeHelpers";
import { Augmentation } from "../Augmentation/Augmentation"; import { Augmentation } from "../Augmentation/Augmentation";
import { import { Sleeve as ISleeve, SleeveSkills } from "../ScriptEditor/NetscriptDefinitions";
AugmentPair,
Sleeve as ISleeve,
SleeveInformation,
SleeveSkills,
SleeveTask,
} from "../ScriptEditor/NetscriptDefinitions";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork"; import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork";
@ -19,7 +13,7 @@ import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyW
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
export function NetscriptSleeve(): InternalAPI<ISleeve> { export function NetscriptSleeve(): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext): void { const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
if (Player.bitNodeN !== 10 && !Player.sourceFileLvl(10)) { if (Player.bitNodeN !== 10 && !Player.sourceFileLvl(10)) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
@ -28,7 +22,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
} }
}; };
const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void { const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number) {
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}`;
helpers.log(ctx, () => msg); helpers.log(ctx, () => msg);
@ -52,282 +46,248 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
}; };
return { return {
getNumSleeves: (ctx: NetscriptContext) => (): number => { getNumSleeves: (ctx) => () => {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
return Player.sleeves.length; return Player.sleeves.length;
}, },
setToShockRecovery: setToShockRecovery: (ctx) => (_sleeveNumber) => {
(ctx: NetscriptContext) => const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
(_sleeveNumber: unknown): boolean => { checkSleeveAPIAccess(ctx);
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
checkSleeveAPIAccess(ctx); return Player.sleeves[sleeveNumber].shockRecovery();
checkSleeveNumber(ctx, sleeveNumber); },
return Player.sleeves[sleeveNumber].shockRecovery(); setToSynchronize: (ctx) => (_sleeveNumber) => {
}, const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
setToSynchronize: checkSleeveAPIAccess(ctx);
(ctx: NetscriptContext) => checkSleeveNumber(ctx, sleeveNumber);
(_sleeveNumber: unknown): boolean => { return Player.sleeves[sleeveNumber].synchronize();
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); },
checkSleeveAPIAccess(ctx); setToCommitCrime: (ctx) => (_sleeveNumber, _crimeRoughName) => {
checkSleeveNumber(ctx, sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
return Player.sleeves[sleeveNumber].synchronize(); const crimeRoughName = helpers.string(ctx, "crimeName", _crimeRoughName);
}, checkSleeveAPIAccess(ctx);
setToCommitCrime: checkSleeveNumber(ctx, sleeveNumber);
(ctx: NetscriptContext) => const crime = findCrime(crimeRoughName);
(_sleeveNumber: unknown, _crimeRoughName: unknown): boolean => { if (crime === null) {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); return false;
const crimeRoughName = helpers.string(ctx, "crimeName", _crimeRoughName); }
checkSleeveAPIAccess(ctx); return Player.sleeves[sleeveNumber].commitCrime(crime.name);
checkSleeveNumber(ctx, sleeveNumber); },
const crime = findCrime(crimeRoughName); setToUniversityCourse: (ctx) => (_sleeveNumber, _universityName, _className) => {
if (crime === null) { const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
return false; const universityName = helpers.string(ctx, "universityName", _universityName);
} const className = helpers.string(ctx, "className", _className);
return Player.sleeves[sleeveNumber].commitCrime(crime.name); checkSleeveAPIAccess(ctx);
}, checkSleeveNumber(ctx, sleeveNumber);
setToUniversityCourse: return Player.sleeves[sleeveNumber].takeUniversityCourse(universityName, className);
(ctx: NetscriptContext) => },
(_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean => { travel: (ctx) => (_sleeveNumber, _cityName) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const universityName = helpers.string(ctx, "universityName", _universityName); const cityName = helpers.string(ctx, "cityName", _cityName);
const className = helpers.string(ctx, "className", _className); checkSleeveAPIAccess(ctx);
checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber);
checkSleeveNumber(ctx, sleeveNumber); if (checkEnum(CityName, cityName)) {
return Player.sleeves[sleeveNumber].takeUniversityCourse(universityName, className); return Player.sleeves[sleeveNumber].travel(cityName);
}, } else {
travel: throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`);
(ctx: NetscriptContext) => }
(_sleeveNumber: unknown, _cityName: unknown): boolean => { },
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); setToCompanyWork: (ctx) => (_sleeveNumber, acompanyName) => {
const cityName = helpers.string(ctx, "cityName", _cityName); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx); const companyName = helpers.string(ctx, "companyName", acompanyName);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveAPIAccess(ctx);
if (checkEnum(CityName, cityName)) { checkSleeveNumber(ctx, sleeveNumber);
return Player.sleeves[sleeveNumber].travel(cityName);
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`);
}
},
setToCompanyWork:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, acompanyName: unknown): boolean => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const companyName = helpers.string(ctx, "companyName", acompanyName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same company that another sleeve is working at // Cannot work at the same company that another sleeve is working at
for (let i = 0; i < Player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) { if (i === sleeveNumber) {
continue; continue;
}
const other = Player.sleeves[i];
if (isSleeveCompanyWork(other.currentWork) && other.currentWork.companyName === companyName) {
throw helpers.makeRuntimeErrorMsg(
ctx,
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
);
}
} }
const other = Player.sleeves[i];
return Player.sleeves[sleeveNumber].workForCompany(companyName); if (isSleeveCompanyWork(other.currentWork) && other.currentWork.companyName === companyName) {
},
setToFactionWork:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, _factionName: unknown, _workType: unknown): boolean | undefined => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const factionName = helpers.string(ctx, "factionName", _factionName);
const workType = helpers.string(ctx, "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 (isSleeveFactionWork(other.currentWork) && other.currentWork.factionName === factionName) {
throw helpers.makeRuntimeErrorMsg(
ctx,
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
);
}
}
if (Player.gang && Player.gang.facName == factionName) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`, `Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
); );
} }
}
return Player.sleeves[sleeveNumber].workForFaction(factionName, workType); return Player.sleeves[sleeveNumber].workForCompany(companyName);
}, },
setToGymWorkout: setToFactionWork: (ctx) => (_sleeveNumber, _factionName, _workType) => {
(ctx: NetscriptContext) => const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
(_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean => { const factionName = helpers.string(ctx, "factionName", _factionName);
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const workType = helpers.string(ctx, "workType", _workType);
const gymName = helpers.string(ctx, "gymName", _gymName); checkSleeveAPIAccess(ctx);
const stat = helpers.string(ctx, "stat", _stat); checkSleeveNumber(ctx, sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return Player.sleeves[sleeveNumber].workoutAtGym(gymName, stat); // Cannot work at the same faction that another sleeve is working at
}, for (let i = 0; i < Player.sleeves.length; ++i) {
getSleeveStats: if (i === sleeveNumber) {
(ctx: NetscriptContext) => continue;
(_sleeveNumber: unknown): SleeveSkills => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return getSleeveStats(sleeveNumber);
},
getTask:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): SleeveTask | null => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const sl = Player.sleeves[sleeveNumber];
if (sl.currentWork === null) return null;
return sl.currentWork.APICopy();
},
getInformation:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): SleeveInformation => {
const sleeveNumber = helpers.number(ctx, "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),
mult: {
agility: sl.mults.agility,
agilityExp: sl.mults.agility_exp,
charisma: sl.mults.charisma,
charismaExp: sl.mults.charisma_exp,
companyRep: sl.mults.company_rep,
crimeMoney: sl.mults.crime_money,
crimeSuccess: sl.mults.crime_success,
defense: sl.mults.defense,
defenseExp: sl.mults.defense_exp,
dexterity: sl.mults.dexterity,
dexterityExp: sl.mults.dexterity_exp,
factionRep: sl.mults.faction_rep,
hacking: sl.mults.hacking,
hackingExp: sl.mults.hacking_exp,
strength: sl.mults.strength,
strengthExp: sl.mults.strength_exp,
workMoney: sl.mults.work_money,
},
};
},
getSleeveAugmentations:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown): string[] => {
const sleeveNumber = helpers.number(ctx, "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);
} }
return augs; const other = Player.sleeves[i];
}, if (isSleeveFactionWork(other.currentWork) && other.currentWork.factionName === factionName) {
getSleevePurchasableAugs: throw helpers.makeRuntimeErrorMsg(
(ctx: NetscriptContext) => ctx,
(_sleeveNumber: unknown): AugmentPair[] => { `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); );
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const purchasableAugs = Player.sleeves[sleeveNumber].findPurchasableAugs();
const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i];
augs.push({
name: aug.name,
cost: aug.baseCost,
});
} }
}
return augs; if (Player.gang && Player.gang.facName == factionName) {
}, throw helpers.makeRuntimeErrorMsg(
purchaseSleeveAug: ctx,
(ctx: NetscriptContext) => `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
(_sleeveNumber: unknown, _augName: unknown): boolean => { );
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); }
const augName = helpers.string(ctx, "augName", _augName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
if (getSleeveStats(sleeveNumber).shock > 0) { return Player.sleeves[sleeveNumber].workForFaction(factionName, workType);
throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`); },
} setToGymWorkout: (ctx) => (_sleeveNumber, _gymName, _stat) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const gymName = helpers.string(ctx, "gymName", _gymName);
const stat = helpers.string(ctx, "stat", _stat);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const aug = StaticAugmentations[augName]; return Player.sleeves[sleeveNumber].workoutAtGym(gymName, stat);
if (!aug) { },
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); getSleeveStats: (ctx) => (_sleeveNumber) => {
} const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return getSleeveStats(sleeveNumber);
},
getTask: (ctx) => (_sleeveNumber) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return Player.sleeves[sleeveNumber].tryBuyAugmentation(aug); const sl = Player.sleeves[sleeveNumber];
}, if (sl.currentWork === null) return null;
getSleeveAugmentationPrice: return sl.currentWork.APICopy();
(ctx: NetscriptContext) => },
(_augName: unknown): number => { getInformation: (ctx) => (_sleeveNumber) => {
checkSleeveAPIAccess(ctx); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const augName = helpers.string(ctx, "augName", _augName); checkSleeveAPIAccess(ctx);
const aug: Augmentation = StaticAugmentations[augName]; checkSleeveNumber(ctx, sleeveNumber);
return aug.baseCost;
},
getSleeveAugmentationRepReq:
(ctx: NetscriptContext) =>
(_augName: unknown): number => {
checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName];
return aug.getCost().repCost;
},
setToBladeburnerAction:
(ctx: NetscriptContext) =>
(_sleeveNumber: unknown, _action: unknown, _contract?: unknown): boolean => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const action = helpers.string(ctx, "action", _action);
let contract: string;
if (typeof _contract === "undefined") {
contract = "------";
} else {
contract = helpers.string(ctx, "contract", _contract);
}
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot Take on Contracts if another sleeve is performing that action const sl = Player.sleeves[sleeveNumber];
if (action === "Take on contracts") { return {
for (let i = 0; i < Player.sleeves.length; ++i) { tor: false,
if (i === sleeveNumber) { city: sl.city,
continue; hp: sl.hp,
} jobs: Object.keys(Player.jobs), // technically sleeves have the same jobs as the player.
const other = Player.sleeves[i]; jobTitle: Object.values(Player.jobs),
if (isSleeveBladeburnerWork(other.currentWork) && other.currentWork.actionName === contract) {
throw helpers.makeRuntimeErrorMsg( mult: {
ctx, agility: sl.mults.agility,
`Sleeve ${sleeveNumber} cannot take on contracts because Sleeve ${i} is already performing that action.`, agilityExp: sl.mults.agility_exp,
); charisma: sl.mults.charisma,
} charismaExp: sl.mults.charisma_exp,
companyRep: sl.mults.company_rep,
crimeMoney: sl.mults.crime_money,
crimeSuccess: sl.mults.crime_success,
defense: sl.mults.defense,
defenseExp: sl.mults.defense_exp,
dexterity: sl.mults.dexterity,
dexterityExp: sl.mults.dexterity_exp,
factionRep: sl.mults.faction_rep,
hacking: sl.mults.hacking,
hackingExp: sl.mults.hacking_exp,
strength: sl.mults.strength,
strengthExp: sl.mults.strength_exp,
workMoney: sl.mults.work_money,
},
};
},
getSleeveAugmentations: (ctx) => (_sleeveNumber) => {
const sleeveNumber = helpers.number(ctx, "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);
}
return augs;
},
getSleevePurchasableAugs: (ctx) => (_sleeveNumber) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const purchasableAugs = Player.sleeves[sleeveNumber].findPurchasableAugs();
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: (ctx) => (_sleeveNumber, _augName) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const augName = helpers.string(ctx, "augName", _augName);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
if (getSleeveStats(sleeveNumber).shock > 0) {
throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`);
}
const aug = StaticAugmentations[augName];
if (!aug) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
}
return Player.sleeves[sleeveNumber].tryBuyAugmentation(aug);
},
getSleeveAugmentationPrice: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName];
return aug.baseCost;
},
getSleeveAugmentationRepReq: (ctx) => (_augName) => {
checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName];
return aug.getCost().repCost;
},
setToBladeburnerAction: (ctx) => (_sleeveNumber, _action, _contract?) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const action = helpers.string(ctx, "action", _action);
let contract: string;
if (typeof _contract === "undefined") {
contract = "------";
} else {
contract = helpers.string(ctx, "contract", _contract);
}
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
// Cannot Take on Contracts if another sleeve is performing that action
if (action === "Take on contracts") {
for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) {
continue;
}
const other = Player.sleeves[i];
if (isSleeveBladeburnerWork(other.currentWork) && other.currentWork.actionName === contract) {
throw helpers.makeRuntimeErrorMsg(
ctx,
`Sleeve ${sleeveNumber} cannot take on contracts because Sleeve ${i} is already performing that action.`,
);
} }
} }
}
return Player.sleeves[sleeveNumber].bladeburner(action, contract); return Player.sleeves[sleeveNumber].bladeburner(action, contract);
}, },
}; };
} }

@ -4,11 +4,7 @@ import { staneksGift } from "../CotMG/Helper";
import { Fragments, FragmentById } from "../CotMG/Fragment"; import { Fragments, FragmentById } from "../CotMG/Fragment";
import { FragmentType } from "../CotMG/FragmentType"; import { FragmentType } from "../CotMG/FragmentType";
import { import { Stanek as IStanek } from "../ScriptEditor/NetscriptDefinitions";
Fragment as IFragment,
ActiveFragment as IActiveFragment,
Stanek as IStanek,
} from "../ScriptEditor/NetscriptDefinitions";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { NetscriptContext, InternalAPI } from "../Netscript/APIWrapper"; import { NetscriptContext, InternalAPI } from "../Netscript/APIWrapper";
import { applyAugmentation } from "../Augmentation/AugmentationHelpers"; import { applyAugmentation } from "../Augmentation/AugmentationHelpers";
@ -25,125 +21,114 @@ export function NetscriptStanek(): InternalAPI<IStanek> {
} }
return { return {
giftWidth: (ctx: NetscriptContext) => giftWidth: (ctx) => () => {
function (): number { checkStanekAPIAccess(ctx);
checkStanekAPIAccess(ctx); return staneksGift.width();
return staneksGift.width(); },
}, giftHeight: (ctx) => () => {
giftHeight: (ctx: NetscriptContext) => checkStanekAPIAccess(ctx);
function (): number { return staneksGift.height();
checkStanekAPIAccess(ctx); },
return staneksGift.height(); chargeFragment: (ctx) => (_rootX, _rootY) => {
}, //Get the fragment object using the given coordinates
chargeFragment: (ctx: NetscriptContext) => const rootX = helpers.number(ctx, "rootX", _rootX);
function (_rootX: unknown, _rootY: unknown): Promise<void> { const rootY = helpers.number(ctx, "rootY", _rootY);
//Get the fragment object using the given coordinates checkStanekAPIAccess(ctx);
const rootX = helpers.number(ctx, "rootX", _rootX); const fragment = staneksGift.findFragment(rootX, rootY);
const rootY = helpers.number(ctx, "rootY", _rootY); //Check whether the selected fragment can ge charged
checkStanekAPIAccess(ctx); if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `No fragment with root (${rootX}, ${rootY}).`);
const fragment = staneksGift.findFragment(rootX, rootY); if (fragment.fragment().type == FragmentType.Booster) {
//Check whether the selected fragment can ge charged throw helpers.makeRuntimeErrorMsg(
if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `No fragment with root (${rootX}, ${rootY}).`); ctx,
if (fragment.fragment().type == FragmentType.Booster) { `The fragment with root (${rootX}, ${rootY}) is a Booster Fragment and thus cannot be charged.`,
throw helpers.makeRuntimeErrorMsg( );
}
//Charge the fragment
const time = staneksGift.inBonus() ? 200 : 1000;
return helpers.netscriptDelay(ctx, time).then(function () {
staneksGift.charge(fragment, ctx.workerScript.scriptRef.threads);
helpers.log(ctx, () => `Charged fragment with ${ctx.workerScript.scriptRef.threads} threads.`);
return Promise.resolve();
});
},
fragmentDefinitions: (ctx) => () => {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Returned ${Fragments.length} fragments`);
return Fragments.map((f) => f.copy());
},
activeFragments: (ctx) => () => {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Returned ${staneksGift.fragments.length} fragments`);
return staneksGift.fragments.map((af) => {
return { ...af.copy(), ...af.fragment().copy() };
});
},
clearGift: (ctx) => () => {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Cleared Stanek's Gift.`);
staneksGift.clear();
},
canPlaceFragment: (ctx) => (_rootX, _rootY, _rotation, _fragmentId) => {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
const rotation = helpers.number(ctx, "rotation", _rotation);
const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId);
checkStanekAPIAccess(ctx);
const fragment = FragmentById(fragmentId);
if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`);
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
return can;
},
placeFragment: (ctx) => (_rootX, _rootY, _rotation, _fragmentId) => {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
const rotation = helpers.number(ctx, "rotation", _rotation);
const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId);
checkStanekAPIAccess(ctx);
const fragment = FragmentById(fragmentId);
if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`);
return staneksGift.place(rootX, rootY, rotation, fragment);
},
getFragment: (ctx) => (_rootX, _rootY) => {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
checkStanekAPIAccess(ctx);
const fragment = staneksGift.findFragment(rootX, rootY);
if (fragment !== undefined) return fragment.copy();
return undefined;
},
removeFragment: (ctx) => (_rootX, _rootY) => {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
checkStanekAPIAccess(ctx);
return staneksGift.delete(rootX, rootY);
},
acceptGift: (ctx) => () => {
//Check if the player is eligible to join the church
if (
player.canAccessCotMG() &&
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length == 0 &&
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length == 0
) {
//Attempt to join CotMG
joinFaction(Factions[FactionNames.ChurchOfTheMachineGod]);
//Attempt to install the first Stanek aug
if (
!player.hasAugmentation(AugmentationNames.StaneksGift1) &&
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
) {
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
helpers.log(
ctx, ctx,
`The fragment with root (${rootX}, ${rootY}) is a Booster Fragment and thus cannot be charged.`, () => `'${FactionNames.ChurchOfTheMachineGod}' joined and '${AugmentationNames.StaneksGift1}' installed.`,
); );
} }
//Charge the fragment }
const time = staneksGift.inBonus() ? 200 : 1000; //Return true iff the player is in CotMG and has the first Stanek aug installed
return helpers.netscriptDelay(ctx, time).then(function () { return (
staneksGift.charge(fragment, ctx.workerScript.scriptRef.threads); Factions[FactionNames.ChurchOfTheMachineGod].isMember &&
helpers.log(ctx, () => `Charged fragment with ${ctx.workerScript.scriptRef.threads} threads.`); player.hasAugmentation(AugmentationNames.StaneksGift1, true)
return Promise.resolve(); );
}); },
},
fragmentDefinitions: (ctx: NetscriptContext) =>
function (): IFragment[] {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Returned ${Fragments.length} fragments`);
return Fragments.map((f) => f.copy());
},
activeFragments: (ctx: NetscriptContext) =>
function (): IActiveFragment[] {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Returned ${staneksGift.fragments.length} fragments`);
return staneksGift.fragments.map((af) => {
return { ...af.copy(), ...af.fragment().copy() };
});
},
clearGift: (ctx: NetscriptContext) =>
function (): void {
checkStanekAPIAccess(ctx);
helpers.log(ctx, () => `Cleared Stanek's Gift.`);
staneksGift.clear();
},
canPlaceFragment: (ctx: NetscriptContext) =>
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
const rotation = helpers.number(ctx, "rotation", _rotation);
const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId);
checkStanekAPIAccess(ctx);
const fragment = FragmentById(fragmentId);
if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`);
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
return can;
},
placeFragment: (ctx: NetscriptContext) =>
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
const rotation = helpers.number(ctx, "rotation", _rotation);
const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId);
checkStanekAPIAccess(ctx);
const fragment = FragmentById(fragmentId);
if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`);
return staneksGift.place(rootX, rootY, rotation, fragment);
},
getFragment: (ctx: NetscriptContext) =>
function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
checkStanekAPIAccess(ctx);
const fragment = staneksGift.findFragment(rootX, rootY);
if (fragment !== undefined) return fragment.copy();
return undefined;
},
removeFragment: (ctx: NetscriptContext) =>
function (_rootX: unknown, _rootY: unknown): boolean {
const rootX = helpers.number(ctx, "rootX", _rootX);
const rootY = helpers.number(ctx, "rootY", _rootY);
checkStanekAPIAccess(ctx);
return staneksGift.delete(rootX, rootY);
},
acceptGift: (ctx: NetscriptContext) =>
function (): boolean {
//Check if the player is eligible to join the church
if (
player.canAccessCotMG() &&
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length == 0 &&
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length == 0
) {
//Attempt to join CotMG
joinFaction(Factions[FactionNames.ChurchOfTheMachineGod]);
//Attempt to install the first Stanek aug
if (
!player.hasAugmentation(AugmentationNames.StaneksGift1) &&
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
) {
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
helpers.log(
ctx,
() => `'${FactionNames.ChurchOfTheMachineGod}' joined and '${AugmentationNames.StaneksGift1}' installed.`,
);
}
}
//Return true iff the player is in CotMG and has the first Stanek aug installed
return (
Factions[FactionNames.ChurchOfTheMachineGod].isMember &&
player.hasAugmentation(AugmentationNames.StaneksGift1, true)
);
},
}; };
} }

@ -37,284 +37,258 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
}; };
return { return {
hasWSEAccount: () => (): boolean => { hasWSEAccount: () => () => {
return player.hasWseAccount; return player.hasWseAccount;
}, },
hasTIXAPIAccess: () => (): boolean => { hasTIXAPIAccess: () => () => {
return player.hasTixApiAccess; return player.hasTixApiAccess;
}, },
has4SData: () => (): boolean => { has4SData: () => () => {
return player.has4SData; return player.has4SData;
}, },
has4SDataTIXAPI: () => (): boolean => { has4SDataTIXAPI: () => () => {
return player.has4SDataTixApi; return player.has4SDataTixApi;
}, },
getSymbols: (ctx: NetscriptContext) => (): string[] => { getSymbols: (ctx) => () => {
checkTixApiAccess(ctx); checkTixApiAccess(ctx);
return Object.values(StockSymbols); return Object.values(StockSymbols);
}, },
getPrice: getPrice: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): number => { checkTixApiAccess(ctx);
const symbol = helpers.string(ctx, "symbol", _symbol); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.price; return stock.price;
}, },
getAskPrice: getAskPrice: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): number => { checkTixApiAccess(ctx);
const symbol = helpers.string(ctx, "symbol", _symbol); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getAskPrice(); return stock.getAskPrice();
}, },
getBidPrice: getBidPrice: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): number => { checkTixApiAccess(ctx);
const symbol = helpers.string(ctx, "symbol", _symbol); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getBidPrice(); return stock.getBidPrice();
}, },
getPosition: getPosition: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): [number, number, number, number] => { checkTixApiAccess(ctx);
const symbol = helpers.string(ctx, "symbol", _symbol); const stock = SymbolToStockMap[symbol];
checkTixApiAccess(ctx); if (stock == null) {
const stock = SymbolToStockMap[symbol]; throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: ${symbol}`);
if (stock == null) { }
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: ${symbol}`); return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
} },
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; getMaxShares: (ctx) => (_symbol) => {
}, const symbol = helpers.string(ctx, "symbol", _symbol);
getMaxShares: checkTixApiAccess(ctx);
(ctx: NetscriptContext) => const stock = getStockFromSymbol(ctx, symbol);
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.maxShares; return stock.maxShares;
}, },
getPurchaseCost: getPurchaseCost: (ctx) => (_symbol, _shares, _posType) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown, _shares: unknown, _posType: unknown): number => { let shares = helpers.number(ctx, "shares", _shares);
const symbol = helpers.string(ctx, "symbol", _symbol); const posType = helpers.string(ctx, "posType", _posType);
let shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx);
const posType = helpers.string(ctx, "posType", _posType); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx); shares = Math.round(shares);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares);
let pos; let pos;
const sanitizedPosType = posType.toLowerCase(); const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) { if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long; pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) { } else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short; pos = PositionTypes.Short;
} else { } else {
return Infinity; return Infinity;
} }
const res = getBuyTransactionCost(stock, shares, pos); const res = getBuyTransactionCost(stock, shares, pos);
if (res == null) { if (res == null) {
return Infinity; return Infinity;
} }
return res; return res;
}, },
getSaleGain: getSaleGain: (ctx) => (_symbol, _shares, _posType) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown, _shares: unknown, _posType: unknown): number => { let shares = helpers.number(ctx, "shares", _shares);
const symbol = helpers.string(ctx, "symbol", _symbol); const posType = helpers.string(ctx, "posType", _posType);
let shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx);
const posType = helpers.string(ctx, "posType", _posType); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx); shares = Math.round(shares);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares);
let pos; let pos;
const sanitizedPosType = posType.toLowerCase(); const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) { if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long; pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) { } else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short; pos = PositionTypes.Short;
} else { } else {
return 0; return 0;
} }
const res = getSellTransactionGain(stock, shares, pos); const res = getSellTransactionGain(stock, shares, pos);
if (res == null) { if (res == null) {
return 0; return 0;
} }
return res; return res;
}, },
buyStock: buyStock: (ctx) => (_symbol, _shares) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown, _shares: unknown): number => { const shares = helpers.number(ctx, "shares", _shares);
const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx);
const shares = helpers.number(ctx, "shares", _shares); const stock = getStockFromSymbol(ctx, symbol);
checkTixApiAccess(ctx); const res = buyStock(stock, shares, ctx, {});
const stock = getStockFromSymbol(ctx, symbol); return res ? stock.getAskPrice() : 0;
const res = buyStock(stock, shares, ctx, {}); },
return res ? stock.getAskPrice() : 0; sellStock: (ctx) => (_symbol, _shares) => {
}, const symbol = helpers.string(ctx, "symbol", _symbol);
sellStock: const shares = helpers.number(ctx, "shares", _shares);
(ctx: NetscriptContext) => checkTixApiAccess(ctx);
(_symbol: unknown, _shares: unknown): number => { const stock = getStockFromSymbol(ctx, symbol);
const symbol = helpers.string(ctx, "symbol", _symbol); const res = sellStock(stock, shares, ctx, {});
const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
const res = sellStock(stock, shares, ctx, {});
return res ? stock.getBidPrice() : 0; return res ? stock.getBidPrice() : 0;
}, },
buyShort: buyShort: (ctx) => (_symbol, _shares) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown, _shares: unknown): number => { const shares = helpers.number(ctx, "shares", _shares);
const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx);
const shares = helpers.number(ctx, "shares", _shares); if (player.bitNodeN !== 8) {
checkTixApiAccess(ctx); if (player.sourceFileLvl(8) <= 1) {
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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, ctx, {});
return res ? stock.getBidPrice() : 0;
},
sellShort:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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, ctx, {});
return res ? stock.getAskPrice() : 0;
},
placeOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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 helpers.makeRuntimeErrorMsg(ctx, `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 helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, ctx);
},
cancelOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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 helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`, "You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
); );
} }
let orderType; }
let orderPos; const stock = getStockFromSymbol(ctx, symbol);
const ltype = type.toLowerCase(); const res = shortStock(stock, shares, ctx, {});
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 helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase(); return res ? stock.getBidPrice() : 0;
if (lpos.includes("l")) { },
orderPos = PositionTypes.Long; sellShort: (ctx) => (_symbol, _shares) => {
} else if (lpos.includes("s")) { const symbol = helpers.string(ctx, "symbol", _symbol);
orderPos = PositionTypes.Short; const shares = helpers.number(ctx, "shares", _shares);
} else { checkTixApiAccess(ctx);
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`); if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
} }
const params = { }
stock: stock, const stock = getStockFromSymbol(ctx, symbol);
shares: shares, const res = sellShort(stock, shares, ctx, {});
price: price,
type: orderType, return res ? stock.getAskPrice() : 0;
pos: orderPos, },
}; placeOrder: (ctx) => (_symbol, _shares, _price, _type, _pos) => {
return cancelOrder(params, ctx); const symbol = helpers.string(ctx, "symbol", _symbol);
}, const shares = helpers.number(ctx, "shares", _shares);
getOrders: (ctx: NetscriptContext) => (): StockOrder => { const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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 helpers.makeRuntimeErrorMsg(ctx, `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 helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, ctx);
},
cancelOrder: (ctx) => (_symbol, _shares, _price, _type, _pos) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"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 helpers.makeRuntimeErrorMsg(
ctx,
`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 helpers.makeRuntimeErrorMsg(ctx, `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 helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, ctx);
},
getOrders: (ctx) => () => {
checkTixApiAccess(ctx); checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) { if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) { if (player.sourceFileLvl(8) <= 2) {
@ -342,31 +316,27 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
return orders; return orders;
}, },
getVolatility: getVolatility: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): number => { if (!player.has4SDataTixApi) {
const symbol = helpers.string(ctx, "symbol", _symbol); throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
if (!player.has4SDataTixApi) { }
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!"); const stock = getStockFromSymbol(ctx, symbol);
}
const stock = getStockFromSymbol(ctx, symbol);
return stock.mv / 100; // Convert from percentage to decimal return stock.mv / 100; // Convert from percentage to decimal
}, },
getForecast: getForecast: (ctx) => (_symbol) => {
(ctx: NetscriptContext) => const symbol = helpers.string(ctx, "symbol", _symbol);
(_symbol: unknown): number => { if (!player.has4SDataTixApi) {
const symbol = helpers.string(ctx, "symbol", _symbol); throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
if (!player.has4SDataTixApi) { }
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!"); const stock = getStockFromSymbol(ctx, symbol);
}
const stock = getStockFromSymbol(ctx, symbol);
let forecast = 50; let forecast = 50;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag); stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal return forecast / 100; // Convert from percentage to decimal
}, },
purchase4SMarketData: (ctx: NetscriptContext) => (): boolean => { purchase4SMarketData: (ctx) => () => {
if (player.has4SData) { if (player.has4SData) {
helpers.log(ctx, () => "Already purchased 4S Market Data."); helpers.log(ctx, () => "Already purchased 4S Market Data.");
return true; return true;
@ -382,7 +352,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased 4S Market Data"); helpers.log(ctx, () => "Purchased 4S Market Data");
return true; return true;
}, },
purchase4SMarketDataTixApi: (ctx: NetscriptContext) => (): boolean => { purchase4SMarketDataTixApi: (ctx) => () => {
checkTixApiAccess(ctx); checkTixApiAccess(ctx);
if (player.has4SDataTixApi) { if (player.has4SDataTixApi) {
@ -400,7 +370,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased 4S Market Data TIX API"); helpers.log(ctx, () => "Purchased 4S Market Data TIX API");
return true; return true;
}, },
purchaseWseAccount: (ctx: NetscriptContext) => (): boolean => { purchaseWseAccount: (ctx) => () => {
if (player.hasWseAccount) { if (player.hasWseAccount) {
helpers.log(ctx, () => "Already purchased WSE Account"); helpers.log(ctx, () => "Already purchased WSE Account");
return true; return true;
@ -417,7 +387,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased WSE Account Access"); helpers.log(ctx, () => "Purchased WSE Account Access");
return true; return true;
}, },
purchaseTixApi: (ctx: NetscriptContext) => (): boolean => { purchaseTixApi: (ctx) => () => {
if (player.hasTixApiAccess) { if (player.hasTixApiAccess) {
helpers.log(ctx, () => "Already purchased TIX API"); helpers.log(ctx, () => "Already purchased TIX API");
return true; return true;

@ -1,38 +1,34 @@
import { import { UserInterface as IUserInterface } from "../ScriptEditor/NetscriptDefinitions";
GameInfo,
IStyleSettings,
UserInterface as IUserInterface,
UserInterfaceTheme,
} from "../ScriptEditor/NetscriptDefinitions";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { ThemeEvents } from "../Themes/ui/Theme"; import { ThemeEvents } from "../Themes/ui/Theme";
import { defaultTheme } from "../Themes/Themes"; 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 "../Netscript/APIWrapper"; import { InternalAPI } from "../Netscript/APIWrapper";
import { Terminal } from "../../src/Terminal"; import { Terminal } from "../../src/Terminal";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers, assertObjectType } from "../Netscript/NetscriptHelpers";
export function NetscriptUserInterface(): InternalAPI<IUserInterface> { export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
return { return {
windowSize: () => (): [number, number] => { windowSize: () => () => {
return [window.innerWidth, window.innerHeight]; return [window.innerWidth, window.innerHeight];
}, },
getTheme: () => (): UserInterfaceTheme => { getTheme: () => () => {
return { ...Settings.theme }; return { ...Settings.theme };
}, },
getStyles: () => (): IStyleSettings => { getStyles: () => () => {
return { ...Settings.styles }; return { ...Settings.styles };
}, },
setTheme: setTheme: (ctx) => (newTheme) => {
(ctx: NetscriptContext) => const themeValidator: Record<string, string | undefined> = {};
(newTheme: UserInterfaceTheme): void => { assertObjectType(ctx, "newTheme", newTheme, themeValidator);
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/; const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
const currentTheme = { ...Settings.theme }; const currentTheme = { ...Settings.theme };
const errors: string[] = []; const errors: string[] = [];
if (typeof newTheme !== "object")
for (const key of Object.keys(newTheme)) { for (const key of Object.keys(newTheme)) {
if (!currentTheme[key]) { if (!currentTheme[key]) {
// Invalid key // Invalid key
@ -44,51 +40,51 @@ export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
} }
} }
if (errors.length === 0) { if (errors.length === 0) {
Object.assign(Settings.theme, currentTheme); Object.assign(Settings.theme, currentTheme);
ThemeEvents.emit(); ThemeEvents.emit();
helpers.log(ctx, () => `Successfully set theme`); helpers.log(ctx, () => `Successfully set theme`);
} else {
helpers.log(ctx, () => `Failed to set theme. Errors: ${errors.join(", ")}`);
}
},
setStyles: (ctx) => (newStyles) => {
const styleValidator: Record<string, string | number | undefined> = {};
assertObjectType(ctx, "newStyles", newStyles, styleValidator);
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 {
helpers.log(ctx, () => `Failed to set theme. Errors: ${errors.join(", ")}`); (currentStyles as any)[key] = newStyles[key];
} }
}, }
setStyles: if (errors.length === 0) {
(ctx: NetscriptContext) => Object.assign(Settings.styles, currentStyles);
(newStyles: IStyleSettings): void => { ThemeEvents.emit();
const currentStyles = { ...Settings.styles }; helpers.log(ctx, () => `Successfully set styles`);
const errors: string[] = []; } else {
for (const key of Object.keys(newStyles)) { helpers.log(ctx, () => `Failed to set styles. Errors: ${errors.join(", ")}`);
if (!(currentStyles as any)[key]) { }
// Invalid key },
errors.push(`Invalid key "${key}"`);
} else {
(currentStyles as any)[key] = (newStyles as any)[key];
}
}
if (errors.length === 0) { resetTheme: (ctx) => () => {
Object.assign(Settings.styles, currentStyles);
ThemeEvents.emit();
helpers.log(ctx, () => `Successfully set styles`);
} else {
helpers.log(ctx, () => `Failed to set styles. Errors: ${errors.join(", ")}`);
}
},
resetTheme: (ctx: NetscriptContext) => (): void => {
Settings.theme = { ...defaultTheme }; Settings.theme = { ...defaultTheme };
ThemeEvents.emit(); ThemeEvents.emit();
helpers.log(ctx, () => `Reinitialized theme to default`); helpers.log(ctx, () => `Reinitialized theme to default`);
}, },
resetStyles: (ctx: NetscriptContext) => (): void => { resetStyles: (ctx) => () => {
Settings.styles = { ...defaultStyles }; Settings.styles = { ...defaultStyles };
ThemeEvents.emit(); ThemeEvents.emit();
helpers.log(ctx, () => `Reinitialized styles to default`); helpers.log(ctx, () => `Reinitialized styles to default`);
}, },
getGameInfo: () => (): GameInfo => { 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";
@ -102,7 +98,7 @@ export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
return gameInfo; return gameInfo;
}, },
clearTerminal: (ctx: NetscriptContext) => (): void => { clearTerminal: (ctx) => () => {
helpers.log(ctx, () => `Clearing terminal`); helpers.log(ctx, () => `Clearing terminal`);
Terminal.clear(); Terminal.clear();
}, },

@ -1,28 +1,39 @@
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
type PortData = string | number;
export interface IPort { export interface IPort {
write: (value: unknown) => unknown; write: (value: unknown) => PortData | null;
tryWrite: (value: unknown) => boolean; tryWrite: (value: unknown) => boolean;
read: () => unknown; read: () => PortData;
peek: () => unknown; peek: () => PortData;
full: () => boolean; full: () => boolean;
empty: () => boolean; empty: () => boolean;
clear: () => void; clear: () => void;
} }
export function NetscriptPort(): IPort { export function NetscriptPort(): IPort {
const data: unknown[] = []; const data: PortData[] = [];
return { return {
write: (value: unknown): unknown => { write: (value) => {
if (typeof value !== "number" && typeof value !== "string") {
throw new Error(
`port.write: Tried to write type ${typeof value}. Only string and number types may be written to ports.`,
);
}
data.push(value); data.push(value);
if (data.length > Settings.MaxPortCapacity) { if (data.length > Settings.MaxPortCapacity) {
return data.shift(); return data.shift() as PortData;
} }
return null; return null;
}, },
tryWrite: (value: unknown): boolean => { tryWrite: (value) => {
if (typeof value != "number" && typeof value != "string") {
throw new Error(
`port.write: Tried to write type ${typeof value}. Only string and number types may be written to ports.`,
);
}
if (data.length >= Settings.MaxPortCapacity) { if (data.length >= Settings.MaxPortCapacity) {
return false; return false;
} }
@ -30,31 +41,25 @@ export function NetscriptPort(): IPort {
return true; return true;
}, },
read: (): unknown => { read: () => {
if (data.length === 0) { if (data.length === 0) return "NULL PORT DATA";
return "NULL PORT DATA"; return data.shift() as PortData;
}
return data.shift();
}, },
peek: (): unknown => { peek: () => {
if (data.length === 0) { if (data.length === 0) return "NULL PORT DATA";
return "NULL PORT DATA"; return data[0];
} else {
const foo = data.slice();
return foo[0];
}
}, },
full: (): boolean => { full: () => {
return data.length == Settings.MaxPortCapacity; return data.length == Settings.MaxPortCapacity;
}, },
empty: (): boolean => { empty: () => {
return data.length === 0; return data.length === 0;
}, },
clear: (): void => { clear: () => {
data.length = 0; data.length = 0;
}, },
}; };

@ -961,7 +961,7 @@ export interface NetscriptPort {
* *
* @returns The data popped off the queue if it was full. * @returns The data popped off the queue if it was full.
*/ */
write(value: string | number): null | string | number; write(value: string | number): PortData | null;
/** /**
* Attempt to write data to the port. * Attempt to write data to the port.
@ -981,7 +981,7 @@ export interface NetscriptPort {
* If the port is empty, then the string NULL PORT DATA will be returned. * If the port is empty, then the string NULL PORT DATA will be returned.
* @returns the data read. * @returns the data read.
*/ */
read(): string | number; read(): PortData;
/** /**
* Retrieve the first element from the port without removing it. * Retrieve the first element from the port without removing it.
@ -993,7 +993,7 @@ export interface NetscriptPort {
* the port is empty, the string NULL PORT DATA will be returned. * the port is empty, the string NULL PORT DATA will be returned.
* @returns the data read * @returns the data read
*/ */
peek(): string | number; peek(): PortData;
/** /**
* Check if the port is full. * Check if the port is full.

@ -1,3 +1,2 @@
import { Terminal as TTerminal } from "./Terminal/Terminal"; import { Terminal as TTerminal } from "./Terminal/Terminal";
export const Terminal = new TTerminal(); export const Terminal = new TTerminal();

@ -1,4 +1,4 @@
// This works for both enums and regular objects. /** Verifies that a supplied value is a member of the provided object/enum. Works for enums as well as enum-like objects (const {} as const). */
export function checkEnum<T extends Record<string, unknown>>(obj: T, value: unknown): value is T[keyof T] { export function checkEnum<T extends Record<string, unknown>>(obj: T, value: unknown): value is T[keyof T] {
return Object.values(obj).includes(value); return Object.values(obj).includes(value);
} }