NETSCRIPT: Expose more enums for player use (#198)

* Add support for enums at other ns layers
* APIWrapper: simplified wrapping algorithm and modified to just use cloneDeep to copy enums instead of recursively wrapping enums as if they were new API layers
* Improve APIWrapper typing
* Changed some typings at RamCostGenerator to allow for enums at different levels without enums needing a ram cost
* Added enums to ns.corporation, removed getter functions that were being used instead.
* Add FactionWorkType for player use
* Add ClassType and CompanyWorkPos enums
* Change netscriptDefinitions to expect members of these new enums where appropriate.
This commit is contained in:
Snarling 2022-11-09 13:46:21 -05:00 committed by GitHub
parent 8e0e0eaa88
commit b275f88053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 355 additions and 285 deletions

@ -1,4 +1,4 @@
// Defs for job titles, stored in arrays and categorized by job "type"
// TODO: Convert all to CompanyPosNames[] and make everything that uses these expect a CompanyPosNames enum member.
export const SoftwareCompanyPositions: string[] = [
"Software Engineering Intern",

@ -2,30 +2,40 @@ import { getRamCost } from "./RamCostGenerator";
import type { WorkerScript } from "./WorkerScript";
import { helpers } from "./NetscriptHelpers";
import { ScriptArg } from "./ScriptArg";
import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions";
import { NSFull } from "src/NetscriptFunctions";
import { cloneDeep } from "lodash";
type ExternalFunction = (...args: any[]) => void;
/** Generic type for an enums object */
type Enums = Record<string, Record<string, string>>;
/** Permissive type for the documented API functions */
type APIFn = (...args: any[]) => void;
/** Type for the actual wrapped function given to the player */
type WrappedFn = (...args: unknown[]) => unknown;
/** Type for internal, unwrapped ctx function that produces an APIFunction */
type InternalFn<F extends APIFn> = (ctx: NetscriptContext) => ((...args: unknown[]) => ReturnType<F>) & F;
type Key<API> = keyof API & string;
export type ExternalAPILayer = {
[key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[];
export type ExternalAPI<API> = {
[key in keyof API]: API[key] extends Enums
? Enums
: key extends "args"
? ScriptArg[] // "args" required to be ScriptArg[]
: API[key] extends APIFn
? WrappedFn
: ExternalAPI<API[key]>;
};
type InternalFunction<F extends ExternalFunction> = (
ctx: NetscriptContext,
) => ((...args: unknown[]) => ReturnType<F>) & F;
export type InternalAPI<API> = {
[Property in keyof API]: API[Property] extends ExternalFunction
? InternalFunction<API[Property]>
: API[Property] extends NSEnums
? NSEnums
: API[Property] extends ScriptArg[]
[key in keyof API]: API[key] extends Enums
? API[key] & Enums
: key extends "args"
? ScriptArg[]
: API[Property] extends object
? InternalAPI<API[Property]>
: never;
: API[key] extends APIFn
? InternalFn<API[key]>
: InternalAPI<API[key]>;
};
/** Any of the possible values on a internal API layer */
type InternalValues = Enums | ScriptArg[] | InternalFn<APIFn> | InternalAPI<unknown>;
export type NetscriptContext = {
workerScript: WorkerScript;
@ -33,77 +43,36 @@ export type NetscriptContext = {
functionPath: string;
};
function wrapFunction(
wrappedAPI: ExternalAPILayer,
workerScript: WorkerScript,
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
...tree: string[]
): void {
const functionPath = tree.join(".");
const functionName = tree.pop();
if (typeof functionName !== "string") {
throw helpers.makeBasicErrorMsg(workerScript, "Failure occurred while wrapping netscript api", "INITIALIZATION");
}
const ctx = {
workerScript,
function: functionName,
functionPath,
};
function wrappedFunction(...args: unknown[]): unknown {
helpers.checkEnvFlags(ctx);
helpers.updateDynamicRam(ctx, getRamCost(...tree, ctx.function));
return func(ctx)(...args);
}
const parent = getNestedProperty(wrappedAPI, tree);
Object.defineProperty(parent, functionName, {
value: wrappedFunction,
writable: true,
enumerable: true,
});
}
export function wrapAPI(workerScript: WorkerScript, namespace: object, args: ScriptArg[]): NSFull {
const wrappedAPI = wrapAPILayer({}, workerScript, namespace);
wrappedAPI.args = args;
return wrappedAPI as unknown as NSFull;
}
export function wrapAPILayer(
wrappedAPI: ExternalAPILayer,
workerScript: WorkerScript,
namespace: object,
...tree: string[]
) {
for (const [key, value] of Object.entries(namespace)) {
if (typeof value === "function") {
wrapFunction(wrappedAPI, workerScript, value, ...tree, key);
} else if (Array.isArray(value)) {
setNestedProperty(wrappedAPI, value.slice(), key);
} else if (typeof value === "object") {
wrapAPILayer(wrappedAPI, workerScript, value, ...tree, key);
} else {
setNestedProperty(wrappedAPI, value, ...tree, key);
export function wrapAPI(ws: WorkerScript, internalAPI: InternalAPI<NSFull>, args: ScriptArg[]): ExternalAPI<NSFull> {
function wrapAPILayer<API>(eLayer: ExternalAPI<API>, iLayer: InternalAPI<API>, tree: string[]): ExternalAPI<API> {
for (const [key, value] of Object.entries(iLayer) as [Key<API>, InternalValues][]) {
if (key === "enums") {
(eLayer[key] as Enums) = cloneDeep(value as Enums);
} else if (key === "args") continue;
// Args are added in wrapAPI function and should only exist at top level
else if (typeof value === "function") {
wrapFunction(eLayer, value as InternalFn<APIFn>, tree, key);
} else if (typeof value === "object") {
wrapAPILayer((eLayer[key] = {} as ExternalAPI<API>[Key<API>]), value, [...tree, key as string]);
} else {
console.warn(`Unexpected data while wrapping API.`, "tree:", tree, "key:", key, "value:", value);
throw new Error("Error while wrapping netscript API. See console.");
}
}
return eLayer;
}
function wrapFunction<API>(eLayer: ExternalAPI<API>, func: InternalFn<APIFn>, tree: string[], key: Key<API>) {
const arrayPath = [...tree, key];
const functionPath = arrayPath.join(".");
const ctx = { workerScript: ws, function: key, functionPath };
function wrappedFunction(...args: unknown[]): unknown {
helpers.checkEnvFlags(ctx);
helpers.updateDynamicRam(ctx, getRamCost(...tree, key));
return func(ctx)(...args);
}
(eLayer[key] as WrappedFn) = wrappedFunction;
}
const wrappedAPI = wrapAPILayer({ args } as ExternalAPI<NSFull>, internalAPI, []);
return wrappedAPI;
}
function setNestedProperty(root: any, value: unknown, ...tree: string[]): void {
let target = root;
const key = tree.pop();
if (!key) throw new Error("Failure occurred while wrapping netscript api (setNestedProperty)");
for (const branch of tree) {
target[branch] ??= {};
target = target[branch];
}
target[key] = value;
}
function getNestedProperty(root: any, tree: string[]): unknown {
let target = root;
for (const branch of tree) {
target[branch] ??= {};
target = target[branch];
}
return target;
}

@ -1,4 +1,5 @@
import { NS } from "../ScriptEditor/NetscriptDefinitions";
import { ExternalAPI } from "./APIWrapper";
/**
* The environment in which a script runs. The environment holds
@ -13,5 +14,5 @@ export class Environment {
runningFn = "";
/** Environment variables (currently only Netscript functions) */
vars: NS | null = null;
vars: ExternalAPI<NS> | null = null;
}

@ -2,9 +2,12 @@ import { Player } from "@player";
import { NSFull } from "../NetscriptFunctions";
/** This type assumes any value that isn't an API layer or a function has been omitted (args and enum) */
type RamCostTree<API> = {
[Property in keyof API]: API[Property] extends () => unknown ? number | (() => number) : RamCostTree<API[Property]>;
};
type RamCostTree<API> = Omit<
{
[Property in keyof API]: API[Property] extends () => unknown ? number | (() => number) : RamCostTree<API[Property]>;
},
"enums" | "args"
>;
/** Constants for assigning costs to ns functions */
export const RamCostConstants: Record<string, number> = {
@ -414,7 +417,7 @@ const corporation = {
* An error will be generated if there are missing OR additional ram costs defined.
* To avoid errors, define every function in NetscriptDefinition.d.ts and NetscriptFunctions,
* and have a ram cost associated here. */
export const RamCosts: RamCostTree<Omit<NSFull, "args" | "enums">> = {
export const RamCosts: RamCostTree<NSFull> = {
corporation,
hacknet,
stock,

@ -16,6 +16,7 @@ import { BaseServer } from "../Server/BaseServer";
import { NS } from "../ScriptEditor/NetscriptDefinitions";
import { ScriptDeath } from "./ScriptDeath";
import { ScriptArg } from "./ScriptArg";
import { ExternalAPI } from "./APIWrapper";
export class WorkerScript {
/** Script's arguments */
@ -82,7 +83,7 @@ export class WorkerScript {
/** Function called when the script ends. */
atExit?: () => void;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NS) {
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => ExternalAPI<NS>) {
this.name = runningScriptObj.filename;
this.hostname = runningScriptObj.server;

@ -61,7 +61,7 @@ import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket";
import { NetscriptGrafting } from "./NetscriptFunctions/Grafting";
import { NS, RecentScript as IRecentScript, BasicHGWOptions, ProcessInfo } from "./ScriptEditor/NetscriptDefinitions";
import { NS, RecentScript, BasicHGWOptions, ProcessInfo, NSEnums } from "./ScriptEditor/NetscriptDefinitions";
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
import { dialogBoxCreate } from "./ui/React/DialogBox";
@ -72,22 +72,27 @@ import { Flags } from "./NetscriptFunctions/Flags";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
import { recentScripts } from "./Netscript/RecentScripts";
import { InternalAPI, wrapAPI } from "./Netscript/APIWrapper";
import { ExternalAPI, InternalAPI, wrapAPI } from "./Netscript/APIWrapper";
import { INetscriptExtra } from "./NetscriptFunctions/Extra";
import { ScriptDeath } from "./Netscript/ScriptDeath";
import { getBitNodeMultipliers } from "./BitNode/BitNode";
import { assert, arrayAssert, stringAssert, objectAssert } from "./utils/helpers/typeAssertion";
import { CrimeType } from "./utils/WorkType";
import { CompanyPosNames, CrimeType } from "./utils/WorkType";
import { cloneDeep } from "lodash";
import { FactionWorkType } from "./Work/data/FactionWorkType";
import { ClassType } from "./Work/ClassWork";
export const enums = {
export const enums: NSEnums = {
toast: ToastVariant,
CrimeType,
FactionWorkType,
ClassType,
CompanyPosNames,
};
export type NSFull = Readonly<NS & INetscriptExtra>;
export function NetscriptFunctions(workerScript: WorkerScript): NSFull {
export function NetscriptFunctions(workerScript: WorkerScript): ExternalAPI<NSFull> {
return wrapAPI(workerScript, ns, workerScript.args.slice());
}
@ -968,7 +973,7 @@ const base: InternalAPI<NS> = {
allFiles.sort();
return allFiles;
},
getRecentScripts: () => (): IRecentScript[] => {
getRecentScripts: () => (): RecentScript[] => {
return recentScripts.map((rs) => ({
timeOfDeath: rs.timeOfDeath,
...helpers.createPublicRunningScript(rs.runningScript),

@ -703,6 +703,10 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
};
return {
enums: {
EmployeePositions,
IndustryType,
},
...warehouseAPI,
...officeAPI,
hasCorporation: () => () => !!Player.corporation,
@ -711,14 +715,6 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
checkAccess(ctx);
return [...CorporationConstants.AllMaterials];
},
getIndustryTypes: (ctx) => () => {
checkAccess(ctx);
return Object.values(IndustryType);
},
getEmployeePositions: (ctx) => () => {
checkAccess(ctx);
return Object.values(EmployeePositions);
},
getUnlockables: (ctx) => () => {
checkAccess(ctx);
return [...CorporationConstants.AllUnlocks];
@ -776,9 +772,6 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
if (!corporation.public) throw helpers.makeRuntimeErrorMsg(ctx, `Your company has not gone public!`);
IssueDividends(corporation, rate);
},
// If you modify these objects you will affect them for real, it's not
// copies.
getDivision: (ctx) => (_divisionName) => {
checkAccess(ctx);
const divisionName = helpers.string(ctx, "divisionName", _divisionName);

@ -51,7 +51,7 @@ import { FactionWorkType } from "../Work/data/FactionWorkType";
import { defaultMultipliers } from "../PersonObjects/Multipliers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CrimeType } from "../utils/WorkType";
import { CompanyPosNames, CrimeType } from "../utils/WorkType";
import { CompanyPositions } from "../Company/CompanyPositions";
export function NetscriptFormulas(): InternalAPI<IFormulas> {
@ -354,46 +354,50 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
},
work: {
crimeSuccessChance: (ctx) => (_person, _crimeType) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return Crimes[crimeType].successRate(person);
},
crimeGains: (ctx) => (_person, _crimeType) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return calculateCrimeWorkStats(person, Crimes[crimeType]);
},
classGains: (ctx) => (_person, _classType, _locationName) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const classType = helpers.string(ctx, "classType", _classType);
if (!checkEnum(ClassType, classType)) throw new Error(`Invalid class type: ${classType}`);
const locationName = helpers.string(ctx, "locationName", _locationName);
return calculateClassEarnings(person, classType as ClassType, locationName as LocationName);
if (!checkEnum(LocationName, locationName)) throw new Error(`Invalid location name: ${locationName}`);
return calculateClassEarnings(person, classType, locationName);
},
factionGains: (ctx) => (_player, _workType, _favor) => {
checkFormulasAccess(ctx);
const player = helpers.person(ctx, _player);
const workType = helpers.string(ctx, "_workType", _workType) as FactionWorkType;
const workType = helpers.string(ctx, "_workType", _workType);
if (!checkEnum(FactionWorkType, workType)) throw new Error(`Invalid faction work type: ${workType}`);
const favor = helpers.number(ctx, "favor", _favor);
const exp = calculateFactionExp(player, workType);
const rep = calculateFactionRep(player, workType, favor);
exp.reputation = rep;
return exp;
},
companyGains: (ctx) => (_player, _companyName, _positionName, _favor) => {
const player = helpers.person(ctx, _player);
CompanyPositions;
companyGains: (ctx) => (_person, _companyName, _positionName, _favor) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const positionName = helpers.string(ctx, "_positionName", _positionName);
const position = Object.values(CompanyPositions).find((c) => c.name === positionName);
if (!position) throw new Error(`Invalid position name: ${positionName}`);
if (!checkEnum(CompanyPosNames, positionName)) throw new Error(`Invalid company position: ${positionName}`);
const position = CompanyPositions[positionName];
const companyName = helpers.string(ctx, "_companyName", _companyName);
const company = Object.values(Companies).find((c) => c.name === companyName);
if (!company) throw new Error(`Invalid company name: ${companyName}`);
const favor = helpers.number(ctx, "favor", _favor);
return calculateCompanyWorkStats(player, company, position, favor);
return calculateCompanyWorkStats(person, company, position, favor);
},
},
};

@ -79,9 +79,8 @@ async function startNetscript1Script(workerScript: WorkerScript): Promise<void>
throw `Error processing Imports in ${workerScript.name}@${workerScript.hostname}:\n\n${e}`;
}
interface BasicObject {
[key: string]: any;
}
//TODO: Make NS1 wrapping type safe instead of using BasicObject
type BasicObject = Record<string, any>;
function wrapNS1Layer(int: Interpreter, intLayer: unknown, nsLayer = workerScript.env.vars as BasicObject) {
for (const [name, entry] of Object.entries(nsLayer)) {
if (typeof entry === "function") {

@ -1,6 +1,7 @@
import { ExternalAPI } from "../Netscript/APIWrapper";
import { AutocompleteData, NS } from "../ScriptEditor/NetscriptDefinitions";
export interface ScriptModule {
main?: (ns: NS) => unknown;
main?: (ns: ExternalAPI<NS>) => unknown;
autocomplete?: (data: AutocompleteData, flags: string[]) => unknown;
}

@ -1768,7 +1768,7 @@ export interface Singularity {
* @param focus - Acquire player focus on this work operation. Optional. Defaults to true.
* @returns True if the player starts working, and false otherwise.
*/
workForFaction(faction: string, workType: "hacking" | "field" | "security", focus?: boolean): boolean;
workForFaction(faction: string, workType: FactionWorkType | `${FactionWorkType}`, focus?: boolean): boolean;
/**
* Get faction reputation.
@ -1887,7 +1887,7 @@ export interface Singularity {
* @param focus - Acquire player focus on this crime. Optional. Defaults to true.
* @returns The number of milliseconds it takes to attempt the specified crime.
*/
commitCrime(crime: CrimeType | CrimeNames, focus?: boolean): number;
commitCrime(crime: CrimeType | `${CrimeType}`, focus?: boolean): number;
/**
* Get chance to successfully commit a crime.
@ -1900,7 +1900,7 @@ export interface Singularity {
* @param crime - Name of crime.
* @returns Chance of success at committing the specified crime.
*/
getCrimeChance(crime: CrimeType | CrimeNames): number;
getCrimeChance(crime: CrimeType | `${CrimeType}`): number;
/**
* Get stats related to a crime.
@ -1913,7 +1913,7 @@ export interface Singularity {
* @param crime - Name of crime.
* @returns The stats of the crime.
*/
getCrimeStats(crime: CrimeType | CrimeNames): CrimeStats;
getCrimeStats(crime: CrimeType | `${CrimeType}`): CrimeStats;
/**
* Get a list of owned augmentation.
@ -3572,7 +3572,7 @@ export interface sleeve {
* @param name - Name of the crime.
* @returns True if this action was set successfully, false otherwise.
*/
setToCommitCrime(sleeveNumber: number, crimeType: CrimeType | CrimeNames): boolean;
setToCommitCrime(sleeveNumber: number, crimeType: CrimeType | `${CrimeType}`): boolean;
/**
* Set a sleeve to work for a faction.
@ -3586,7 +3586,11 @@ export interface sleeve {
* @param factionWorkType - Name of the action to perform for this faction.
* @returns True if the sleeve started working on this faction, false otherwise, can also throw on errors
*/
setToFactionWork(sleeveNumber: number, factionName: string, factionWorkType: string): boolean | undefined;
setToFactionWork(
sleeveNumber: number,
factionName: string,
factionWorkType: FactionWorkType | `${FactionWorkType}`,
): boolean | undefined;
/**
* Set a sleeve to work for a company.
@ -3811,10 +3815,15 @@ export interface WorkStats {
*/
interface WorkFormulas {
crimeSuccessChance(person: Person, crimeType: CrimeType | CrimeNames): number;
crimeGains(person: Person, crimeType: CrimeType | CrimeNames): WorkStats;
classGains(person: Person, classType: string, locationName: string): WorkStats;
factionGains(person: Person, workType: string, favor: number): WorkStats;
companyGains(person: Person, companyName: string, workType: string, favor: number): WorkStats;
crimeGains(person: Person, crimeType: CrimeType | `${CrimeType}`): WorkStats;
classGains(person: Person, classType: ClassType | `${ClassType}`, locationName: string): WorkStats;
factionGains(person: Person, workType: FactionWorkType | `${FactionWorkType}`, favor: number): WorkStats;
companyGains(
person: Person,
companyName: string,
workType: CompanyPosNames | `${CompanyPosNames}`,
favor: number,
): WorkStats;
}
/**
@ -6592,7 +6601,7 @@ export interface NS {
* @param variant - Type of toast. Must be one of success, info, warning, error. Defaults to success.
* @param duration - Duration of toast in ms. Can also be `null` to create a persistent toast. Defaults to 2000.
*/
toast(msg: string, variant?: ToastTypes | ToastVariant, duration?: number | null): void;
toast(msg: string, variant?: ToastVariant | `${ToastVariant}`, duration?: number | null): void;
/**
* Download a file from the internet.
@ -6791,33 +6800,7 @@ export interface NS {
enums: NSEnums;
}
/** @public */
type EmployeePosName =
| "Operations"
| "Engineer"
| "Business"
| "Management"
| "Research & Development"
| "Training"
| "Unassigned";
/** @public */
type IndustryTypeName =
| "Energy"
| "Water Utilities"
| "Agriculture"
| "Fishing"
| "Mining"
| "Food"
| "Tobacco"
| "Chemical"
| "Pharmaceutical"
| "Computer Hardware"
| "Robotics"
| "Software"
| "Healthcare"
| "RealEstate";
// BASE ENUMS
/** @public */
declare enum ToastVariant {
SUCCESS = "success",
@ -6825,8 +6808,6 @@ declare enum ToastVariant {
ERROR = "error",
INFO = "info",
}
/** @public */
export type ToastTypes = `${ToastVariant}`;
/** @public */
declare enum CrimeType {
@ -6843,13 +6824,107 @@ declare enum CrimeType {
ASSASSINATION = "ASSASSINATION",
HEIST = "HEIST",
}
/** @public */
type CrimeNames = `${CrimeType}`;
declare enum FactionWorkType {
HACKING = "HACKING",
FIELD = "FIELD",
SECURITY = "SECURITY",
}
// TODO: split ClassType enum into separate enums for gym and uni so they can actually be used for player input.
/** @public */
declare enum ClassType {
StudyComputerScience = "STUDYCOMPUTERSCIENCE",
DataStructures = "DATASTRUCTURES",
Networks = "NETWORKS",
Algorithms = "ALGORITHMS",
Management = "MANAGEMENT",
Leadership = "LEADERSHIP",
GymStrength = "GYMSTRENGTH",
GymDefense = "GYMDEFENSE",
GymDexterity = "GYMDEXTERITY",
GymAgility = "GYMAGILITY",
}
declare enum CompanyPosNames {
sw0 = "Software Engineering Intern",
sw1 = "Junior Software Engineer",
sw2 = "Senior Software Engineer",
sw3 = "Lead Software Developer",
sw4 = "Head of Software",
sw5 = "Head of Engineering",
sw6 = "Vice President of Technology",
sw7 = "Chief Technology Officer",
IT0 = "IT Intern",
IT1 = "IT Analyst",
IT2 = "IT Manager",
IT3 = "Systems Administrator",
secEng = "Security Engineer",
netEng0 = "Network Engineer",
netEng1 = "Network Administrator",
bus0 = "Business Intern",
bus1 = "Business Analyst",
bus2 = "Business Manager",
bus3 = "Operations Manager",
bus4 = "Chief Financial Officer",
bus5 = "Chief Executive Officer",
sec0 = "Police Officer",
sec1 = "Police Chief",
sec2 = "Security Guard",
sec3 = "Security Officer",
sec4 = "Security Supervisor",
sec5 = "Head of Security",
agent0 = "Field Agent",
agent1 = "Secret Agent",
agent2 = "Special Operative",
waiter = "Waiter",
employee = "Employee",
softCons0 = "Software Consultant",
softCons1 = "Senior Software Consultant",
busCons0 = "Business Consultant",
busCons1 = "Senior Business Consultant",
waiterPT = "Part-time Waiter",
employeePT = "Part-time Employee",
}
// CORP ENUMS
/** @public */
declare enum EmployeePositions {
Operations = "Operations",
Engineer = "Engineer",
Business = "Business",
Management = "Management",
RandD = "Research & Development",
Training = "Training",
Unassigned = "Unassigned",
}
/** @public */
declare enum IndustryType {
Energy = "Energy",
Utilities = "Water Utilities",
Agriculture = "Agriculture",
Fishing = "Fishing",
Mining = "Mining",
Food = "Food",
Tobacco = "Tobacco",
Chemical = "Chemical",
Pharmaceutical = "Pharmaceutical",
Computers = "Computer Hardware",
Robotics = "Robotics",
Software = "Software",
Healthcare = "Healthcare",
RealEstate = "RealEstate",
}
/** @public */
export type NSEnums = {
toast: typeof ToastVariant;
CrimeType: typeof CrimeType;
FactionWorkType: typeof FactionWorkType;
ClassType: typeof ClassType;
CompanyPosNames: typeof CompanyPosNames;
};
/**
@ -6867,7 +6942,11 @@ export interface OfficeAPI {
* @param employeePosition - Position to place into. Defaults to "Unassigned".
* @returns True if an employee was hired, false otherwise
*/
hireEmployee(divisionName: string, cityName: string, employeePosition?: EmployeePosName): boolean;
hireEmployee(
divisionName: string,
cityName: string,
employeePosition?: EmployeePositions | `${EmployeePositions}`,
): boolean;
/**
* Upgrade office size.
* @param divisionName - Name of the division
@ -7179,164 +7258,131 @@ export interface WarehouseAPI {
* @public
*/
export interface Corporation extends WarehouseAPI, OfficeAPI {
/** Enums specific to the corporation game mechanic. */
enums: {
EmployeePositions: typeof EmployeePositions;
IndustryType: typeof IndustryType;
};
/** Returns whether the player has a corporation. Does not require API access.
* @returns whether the player has a corporation */
hasCorporation(): boolean;
/** Create a Corporation
* @param divisionName - Name of the division
* @param corporationName - Name of the corporation
* @param selfFund - If you should self fund, defaults to true, false will only work on Bitnode 3
* @returns true if created and false if not */
createCorporation(corporationName: string, selfFund: boolean): boolean;
/**
* Check if you have a one time unlockable upgrade
/** Check if you have a one time unlockable upgrade
* @param upgradeName - Name of the upgrade
* @returns true if unlocked and false if not
*/
* @returns true if unlocked and false if not */
hasUnlockUpgrade(upgradeName: string): boolean;
/**
* Gets the cost to unlock a one time unlockable upgrade
/** Gets the cost to unlock a one time unlockable upgrade
* @param upgradeName - Name of the upgrade
* @returns cost of the upgrade
*/
* @returns cost of the upgrade */
getUnlockUpgradeCost(upgradeName: string): number;
/**
* Get the level of a levelable upgrade
/** Get the level of a levelable upgrade
* @param upgradeName - Name of the upgrade
* @returns the level of the upgrade
*/
* @returns the level of the upgrade */
getUpgradeLevel(upgradeName: string): number;
/**
* Gets the cost to unlock the next level of a levelable upgrade
/** Gets the cost to unlock the next level of a levelable upgrade
* @param upgradeName - Name of the upgrade
* @returns cost of the upgrade
*/
* @returns cost of the upgrade */
getUpgradeLevelCost(upgradeName: string): number;
/**
* Gets the cost to expand into a new industry
/** Gets the cost to expand into a new industry
* @param industryName - Name of the industry
* @returns cost
*/
getExpandIndustryCost(industryName: string): number;
/**
* Gets the cost to expand into a new city
* @returns cost
*/
* @returns cost */
getExpandIndustryCost(industryName: IndustryType | `${IndustryType}`): number;
/** Gets the cost to expand into a new city
* @returns cost */
getExpandCityCost(): number;
/**
* Get an offer for investment based on you companies current valuation
* @returns An offer of investment
*/
/** Get an offer for investment based on you companies current valuation
* @returns An offer of investment */
getInvestmentOffer(): InvestmentOffer;
/**
* Get list of materials
* @returns material names
*/
/** Get list of materials
* @returns material names */
getMaterialNames(): string[];
/**
* Get list of industry types
* @returns industry names
*/
getIndustryTypes(): IndustryTypeName[];
/**
* Get list of industry types
* @returns industry names
*/
getEmployeePositions(): EmployeePosName[];
/**
* Get list of one-time unlockable upgrades
* @returns unlockable upgrades names
*/
/** Get list of one-time unlockable upgrades
* @returns unlockable upgrades names */
getUnlockables(): string[];
/**
* Get list of upgrade names
* @returns upgrade names
*/
/** Get list of upgrade names
* @returns upgrade names */
getUpgradeNames(): string[];
/**
* Get list of research names
* @returns research names
*/
/** Get list of research names
* @returns research names */
getResearchNames(): string[];
/**
* Accept investment based on you companies current valuation
/** Accept investment based on you companies current valuation
* @remarks
* Is based on current valuation and will not honer a specific Offer
* @returns An offer of investment
*/
* @returns An offer of investment */
acceptInvestmentOffer(): boolean;
/**
* Go public
/** Go public
* @param numShares - number of shares you would like to issue for your IPO
* @returns true if you successfully go public, false if not
*/
* @returns true if you successfully go public, false if not */
goPublic(numShares: number): boolean;
/**
* Bribe a faction
/** Bribe a faction
* @param factionName - Faction name
* @param amountCash - Amount of money to bribe
* @returns True if successful, false if not
*/
* @returns True if successful, false if not */
bribe(factionName: string, amountCash: number): boolean;
/**
* Get corporation data
* @returns Corporation data
*/
/** Get corporation data
* @returns Corporation data */
getCorporation(): CorporationInfo;
/**
* Get division data
/** Get division data
* @param divisionName - Name of the division
* @returns Division data
*/
* @returns Division data */
getDivision(divisionName: string): Division;
/**
* Expand to a new industry
/** Expand to a new industry
* @param industryType - Name of the industry
* @param divisionName - Name of the division */
expandIndustry(industryType: IndustryType | `${IndustryType}`, divisionName: string): void;
/** Expand to a new city
* @param divisionName - Name of the division
*/
expandIndustry(industryType: string, divisionName: string): void;
/**
* Expand to a new city
* @param divisionName - Name of the division
* @param cityName - Name of the city
*/
* @param cityName - Name of the city */
expandCity(divisionName: string, cityName: string): void;
/**
* Unlock an upgrade
* @param upgradeName - Name of the upgrade
*/
/** Unlock an upgrade
* @param upgradeName - Name of the upgrade */
unlockUpgrade(upgradeName: string): void;
/**
* Level an upgrade.
* @param upgradeName - Name of the upgrade
*/
/** Level an upgrade.
* @param upgradeName - Name of the upgrade */
levelUpgrade(upgradeName: string): void;
/**
* Issue dividends
* @param rate - Fraction of profit to issue as dividends.
*/
/** Issue dividends
* @param rate - Fraction of profit to issue as dividends. */
issueDividends(rate: number): void;
/**
* Buyback Shares
* @param amount - Amount of shares to buy back.
*
*/
/** Buyback Shares
* @param amount - Amount of shares to buy back. */
buyBackShares(amount: number): void;
/**
* Sell Shares
* @param amount - Amount of shares to sell.
*
*/
/** Sell Shares
* @param amount - Amount of shares to sell. */
sellShares(amount: number): void;
/**
* Get bonus time.
*
/** Get bonus time.
* Bonus time is accumulated when the game is offline or if the game is inactive in the browser.
*
* Bonus time makes the game progress faster.
*
* @returns Bonus time for the Corporation mechanic in milliseconds.
*/
* @returns Bonus time for the Corporation mechanic in milliseconds. */
getBonusTime(): number;
}
@ -7491,9 +7537,9 @@ export interface Office {
/** Average morale of the employees */
avgMor: number;
/** Production of the employees */
employeeProd: Record<EmployeePosName, number>;
employeeProd: Record<`${EmployeePositions}`, number>;
/** Positions of the employees */
employeeJobs: Record<EmployeePosName, number>;
employeeJobs: Record<`${EmployeePositions}`, number>;
}
/**

@ -12,3 +12,44 @@ export enum CrimeType {
ASSASSINATION = "ASSASSINATION", //"assassinate a high-profile target",
HEIST = "HEIST", //"pull off the ultimate heist",
}
export enum CompanyPosNames {
sw0 = "Software Engineering Intern",
sw1 = "Junior Software Engineer",
sw2 = "Senior Software Engineer",
sw3 = "Lead Software Developer",
sw4 = "Head of Software",
sw5 = "Head of Engineering",
sw6 = "Vice President of Technology",
sw7 = "Chief Technology Officer",
IT0 = "IT Intern",
IT1 = "IT Analyst",
IT2 = "IT Manager",
IT3 = "Systems Administrator",
secEng = "Security Engineer",
netEng0 = "Network Engineer",
netEng1 = "Network Administrator",
bus0 = "Business Intern",
bus1 = "Business Analyst",
bus2 = "Business Manager",
bus3 = "Operations Manager",
bus4 = "Chief Financial Officer",
bus5 = "Chief Executive Officer",
sec0 = "Police Officer",
sec1 = "Police Chief",
sec2 = "Security Guard",
sec3 = "Security Officer",
sec4 = "Security Supervisor",
sec5 = "Head of Security",
agent0 = "Field Agent",
agent1 = "Secret Agent",
agent2 = "Special Operative",
waiter = "Waiter",
employee = "Employee",
softCons0 = "Software Consultant",
softCons1 = "Senior Software Consultant",
busCons0 = "Business Consultant",
busCons1 = "Senior Business Consultant",
waiterPT = "Part-time Waiter",
employeePT = "Part-time Employee",
}

@ -120,6 +120,8 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
const expectedRam = grabCost(ramLayer, newPath);
it(`${fnName}()`, () => combinedRamCheck(val, newPath, expectedRam, extraLayerCost));
}
//Skip enums layers
else if (key === "enums") return;
//A layer should be the only other option.
else testLayer(val, ramLayer[key] as RamLayer, newPath, 0);
});
@ -141,7 +143,12 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
it(`SF4.${lvl} check for x${lvlToMult[lvl]} costs`, () => {
sf4.lvl = lvl;
singObjects.forEach((obj) =>
combinedRamCheck(obj.fn, ["singularity", obj.name], obj.baseRam * lvlToMult[lvl], 0),
combinedRamCheck(
obj.fn as PotentiallyAsyncFunction,
["singularity", obj.name],
obj.baseRam * lvlToMult[lvl],
0,
),
);
});
}