diff --git a/src/Company/data/companypositionnames.ts b/src/Company/data/companypositionnames.ts index cad436073..9dc1a5b04 100644 --- a/src/Company/data/companypositionnames.ts +++ b/src/Company/data/companypositionnames.ts @@ -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", diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts index f163c0d58..b6c2648a9 100644 --- a/src/Netscript/APIWrapper.ts +++ b/src/Netscript/APIWrapper.ts @@ -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>; +/** 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 = (ctx: NetscriptContext) => ((...args: unknown[]) => ReturnType) & F; +type Key = keyof API & string; -export type ExternalAPILayer = { - [key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[]; +export type ExternalAPI = { + [key in keyof API]: API[key] extends Enums + ? Enums + : key extends "args" + ? ScriptArg[] // "args" required to be ScriptArg[] + : API[key] extends APIFn + ? WrappedFn + : ExternalAPI; }; -type InternalFunction = ( - ctx: NetscriptContext, -) => ((...args: unknown[]) => ReturnType) & F; - export type InternalAPI = { - [Property in keyof API]: API[Property] extends ExternalFunction - ? InternalFunction - : 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 - : never; + : API[key] extends APIFn + ? InternalFn + : InternalAPI; }; +/** Any of the possible values on a internal API layer */ +type InternalValues = Enums | ScriptArg[] | InternalFn | InternalAPI; 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, args: ScriptArg[]): ExternalAPI { + function wrapAPILayer(eLayer: ExternalAPI, iLayer: InternalAPI, tree: string[]): ExternalAPI { + for (const [key, value] of Object.entries(iLayer) as [Key, 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, tree, key); + } else if (typeof value === "object") { + wrapAPILayer((eLayer[key] = {} as ExternalAPI[Key]), 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(eLayer: ExternalAPI, func: InternalFn, tree: string[], key: Key) { + 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, 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; -} diff --git a/src/Netscript/Environment.ts b/src/Netscript/Environment.ts index 3bb24bc7a..72a252b03 100644 --- a/src/Netscript/Environment.ts +++ b/src/Netscript/Environment.ts @@ -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 | null = null; } diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 00030c0c7..862cdcfc5 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -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 = { - [Property in keyof API]: API[Property] extends () => unknown ? number | (() => number) : RamCostTree; -}; +type RamCostTree = Omit< + { + [Property in keyof API]: API[Property] extends () => unknown ? number | (() => number) : RamCostTree; + }, + "enums" | "args" +>; /** Constants for assigning costs to ns functions */ export const RamCostConstants: Record = { @@ -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> = { +export const RamCosts: RamCostTree = { corporation, hacknet, stock, diff --git a/src/Netscript/WorkerScript.ts b/src/Netscript/WorkerScript.ts index ff9ad4d99..49e5931a5 100644 --- a/src/Netscript/WorkerScript.ts +++ b/src/Netscript/WorkerScript.ts @@ -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) { this.name = runningScriptObj.filename; this.hostname = runningScriptObj.server; diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 1d4f82846..7d45e2824 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -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; -export function NetscriptFunctions(workerScript: WorkerScript): NSFull { +export function NetscriptFunctions(workerScript: WorkerScript): ExternalAPI { return wrapAPI(workerScript, ns, workerScript.args.slice()); } @@ -968,7 +973,7 @@ const base: InternalAPI = { allFiles.sort(); return allFiles; }, - getRecentScripts: () => (): IRecentScript[] => { + getRecentScripts: () => (): RecentScript[] => { return recentScripts.map((rs) => ({ timeOfDeath: rs.timeOfDeath, ...helpers.createPublicRunningScript(rs.runningScript), diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index 17c087874..9c33e54ca 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -703,6 +703,10 @@ export function NetscriptCorporation(): InternalAPI { }; return { + enums: { + EmployeePositions, + IndustryType, + }, ...warehouseAPI, ...officeAPI, hasCorporation: () => () => !!Player.corporation, @@ -711,14 +715,6 @@ export function NetscriptCorporation(): InternalAPI { 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 { 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); diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts index 539e52c5a..796cd016e 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -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 { @@ -354,46 +354,50 @@ export function NetscriptFormulas(): InternalAPI { }, 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); }, }, }; diff --git a/src/NetscriptWorker.ts b/src/NetscriptWorker.ts index 07914260b..9e9524edf 100644 --- a/src/NetscriptWorker.ts +++ b/src/NetscriptWorker.ts @@ -79,9 +79,8 @@ async function startNetscript1Script(workerScript: WorkerScript): Promise 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; function wrapNS1Layer(int: Interpreter, intLayer: unknown, nsLayer = workerScript.env.vars as BasicObject) { for (const [name, entry] of Object.entries(nsLayer)) { if (typeof entry === "function") { diff --git a/src/Script/ScriptModule.ts b/src/Script/ScriptModule.ts index 96da00616..67bcd0d43 100644 --- a/src/Script/ScriptModule.ts +++ b/src/Script/ScriptModule.ts @@ -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) => unknown; autocomplete?: (data: AutocompleteData, flags: string[]) => unknown; } diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 0dbbdd28f..f79b65c22 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -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; + employeeProd: Record<`${EmployeePositions}`, number>; /** Positions of the employees */ - employeeJobs: Record; + employeeJobs: Record<`${EmployeePositions}`, number>; } /** diff --git a/src/utils/WorkType.ts b/src/utils/WorkType.ts index 19620eebe..79b741360 100644 --- a/src/utils/WorkType.ts +++ b/src/utils/WorkType.ts @@ -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", +} diff --git a/test/jest/Netscript/RamCalculation.test.ts b/test/jest/Netscript/RamCalculation.test.ts index 957c25fe6..7ac34d9fd 100644 --- a/test/jest/Netscript/RamCalculation.test.ts +++ b/test/jest/Netscript/RamCalculation.test.ts @@ -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, + ), ); }); }