diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index d748a8b77..2802d7fd1 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -4,47 +4,14 @@ import * as libarg from "arg"; import { getRamCost } from "./Netscript/RamCostGenerator"; import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter"; -import { Augmentations } from "./Augmentation/Augmentations"; -import { augmentationExists, installAugmentations } from "./Augmentation/AugmentationHelpers"; -import { prestigeAugmentation } from "./Prestige"; -import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { findCrime } from "./Crime/CrimeHelpers"; -import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Company } from "./Company/Company"; import { Companies } from "./Company/Companies"; import { CompanyPosition } from "./Company/CompanyPosition"; import { CompanyPositions } from "./Company/CompanyPositions"; import { CONSTANTS } from "./Constants"; import { DarkWebItems } from "./DarkWeb/DarkWebItems"; -import { - NewIndustry, - NewCity, - UnlockUpgrade, - LevelUpgrade, - IssueDividends, - SellMaterial, - SellProduct, - SetSmartSupply, - BuyMaterial, - AssignJob, - UpgradeOfficeSize, - ThrowParty, - PurchaseWarehouse, - UpgradeWarehouse, - BuyCoffee, - HireAdVert, - MakeProduct, - Research, - ExportMaterial, - CancelExportMaterial, - SetMaterialMarketTA1, - SetMaterialMarketTA2, - SetProductMarketTA1, - SetProductMarketTA2, -} from "./Corporation/Actions"; -import { CorporationUnlockUpgrades } from "./Corporation/data/CorporationUnlockUpgrades"; -import { CorporationUpgrades } from "./Corporation/data/CorporationUpgrades"; import { calculateHackingChance, calculateHackingExpGain, @@ -53,35 +20,16 @@ import { calculateGrowTime, calculateWeakenTime, } from "./Hacking"; -import { calculateServerGrowth } from "./Server/formulas/grow"; import { AllGangs } from "./Gang/AllGangs"; import { Factions, factionExists } from "./Faction/Factions"; -import { joinFaction, purchaseAugmentation } from "./Faction/FactionHelpers"; +import { joinFaction } from "./Faction/FactionHelpers"; import { netscriptCanGrow, netscriptCanHack, netscriptCanWeaken } from "./Hacking/netscriptCanHack"; -import { - calculateMoneyGainRate, - calculateLevelUpgradeCost, - calculateRamUpgradeCost, - calculateCoreUpgradeCost, - calculateNodeCost, -} from "./Hacknet/formulas/HacknetNodes"; -import { - calculateHashGainRate as HScalculateHashGainRate, - calculateLevelUpgradeCost as HScalculateLevelUpgradeCost, - calculateRamUpgradeCost as HScalculateRamUpgradeCost, - calculateCoreUpgradeCost as HScalculateCoreUpgradeCost, - calculateCacheUpgradeCost as HScalculateCacheUpgradeCost, - calculateServerCost as HScalculateServerCost, -} from "./Hacknet/formulas/HacknetServers"; -import { HacknetNodeConstants, HacknetServerConstants } from "./Hacknet/data/Constants"; import { HacknetServer } from "./Hacknet/HacknetServer"; import { CityName } from "./Locations/data/CityNames"; import { LocationName } from "./Locations/data/LocationNames"; import { Terminal } from "./Terminal"; -import { calculateSkill, calculateExp } from "./PersonObjects/formulas/skill"; -import { Message } from "./Message/Message"; import { Player } from "./Player"; import { Programs } from "./Programs/Programs"; import { Script } from "./Script/Script"; @@ -100,18 +48,12 @@ import { import { getPurchaseServerCost, getPurchaseServerLimit, getPurchaseServerMaxRam } from "./Server/ServerPurchases"; import { Server } from "./Server/Server"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; -import { buyStock, sellStock, shortStock, sellShort } from "./StockMarket/BuyingAndSelling"; import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing"; -import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "./StockMarket/StockMarket"; -import { getBuyTransactionCost, getSellTransactionGain } from "./StockMarket/StockMarketHelpers"; -import { OrderTypes } from "./StockMarket/data/OrderTypes"; -import { PositionTypes } from "./StockMarket/data/PositionTypes"; -import { StockSymbols } from "./StockMarket/data/StockSymbols"; -import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "./StockMarket/StockMarketCosts"; + import { isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers"; import { TextFile, getTextFile, createTextFile } from "./TextFile"; -import { NetscriptPorts, runScriptFromScript, startWorkerScript } from "./NetscriptWorker"; +import { NetscriptPorts, runScriptFromScript } from "./NetscriptWorker"; import { killWorkerScript } from "./Netscript/killWorkerScript"; import { workerScripts } from "./Netscript/WorkerScripts"; import { WorkerScript } from "./Netscript/WorkerScript"; @@ -120,7 +62,6 @@ import { Interpreter } from "./ThirdParty/JSInterpreter"; import { Router } from "./ui/GameRoot"; import { numeralWrapper } from "./ui/numeralFormat"; -import { is2DArray } from "./utils/helpers/is2DArray"; import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions"; import { SpecialServers } from "./Server/data/SpecialServers"; @@ -128,24 +69,21 @@ import { LogBoxEvents } from "./ui/React/LogBoxManager"; import { arrayToString } from "./utils/helpers/arrayToString"; import { isString } from "./utils/helpers/isString"; -import { OfficeSpace } from "./Corporation/OfficeSpace"; -import { Employee } from "./Corporation/Employee"; -import { Product } from "./Corporation/Product"; -import { Material } from "./Corporation/Material"; -import { Warehouse } from "./Corporation/Warehouse"; -import { IIndustry } from "./Corporation/IIndustry"; - import { Faction } from "./Faction/Faction"; -import { Augmentation } from "./Augmentation/Augmentation"; import { Page } from "./ui/Router"; -import { CodingContract } from "./CodingContracts"; -import { Stock } from "./StockMarket/Stock"; import { BaseServer } from "./Server/BaseServer"; import { INetscriptGang, NetscriptGang } from "./NetscriptFunctions/Gang"; import { INetscriptSleeve, NetscriptSleeve } from "./NetscriptFunctions/Sleeve"; import { INetscriptExtra, NetscriptExtra } from "./NetscriptFunctions/Extra"; import { INetscriptHacknet, NetscriptHacknet } from "./NetscriptFunctions/Hacknet"; +import { INetscriptBladeburner, NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner"; +import { INetscriptCodingContract, NetscriptCodingContract } from "./NetscriptFunctions/CodingContract"; +import { INetscriptCorporation, NetscriptCorporation } from "./NetscriptFunctions/Corporation"; +import { INetscriptFormulas, NetscriptFormulas } from "./NetscriptFunctions/Formulas"; +import { INetscriptAugmentations, NetscriptAugmentations } from "./NetscriptFunctions/Augmentations"; +import { INetscriptStockMarket, NetscriptStockMarket } from "./NetscriptFunctions/StockMarket"; + import { dialogBoxCreate } from "./ui/React/DialogBox"; import { SnackbarEvents } from "./ui/React/Snackbar"; @@ -184,11 +122,15 @@ function toNative(pseudoObj: any): any { return nativeObj; } -interface NS extends INetscriptExtra { +interface NS extends INetscriptExtra, INetscriptAugmentations, INetscriptStockMarket { [key: string]: any; hacknet: INetscriptHacknet; gang: INetscriptGang; sleeve: INetscriptSleeve; + bladeburner: INetscriptBladeburner; + codingcontract: INetscriptCodingContract; + corporation: INetscriptCorporation; + formulas: INetscriptFormulas; } function NetscriptFunctions(workerScript: WorkerScript): NS { @@ -238,7 +180,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { * @param {string} callingFnName - Name of calling function. For logging purposes * @returns {Server} The specified Server */ - const safeGetServer = function (ip: any, callingFnName: any = ""): BaseServer { + const safeGetServer = function (ip: string, callingFnName: string): BaseServer { const server = GetServer(ip); if (server == null) { throw makeRuntimeErrorMsg(callingFnName, `Invalid IP/hostname: ${ip}`); @@ -312,32 +254,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { return `Cannot find running script ${fn} on server ${ip} with args: ${arrayToString(scriptArgs)}`; }; - /** - * Checks if the player has TIX API access. Throws an error if the player does not - */ - const checkTixApiAccess = function (callingFn: any = ""): void { - if (!Player.hasWseAccount) { - throw makeRuntimeErrorMsg(callingFn, `You don't have WSE Access! Cannot use ${callingFn}()`); - } - if (!Player.hasTixApiAccess) { - throw makeRuntimeErrorMsg(callingFn, `You don't have TIX API Access! Cannot use ${callingFn}()`); - } - }; - - /** - * Gets a stock, given its symbol. Throws an error if the symbol is invalid - * @param {string} symbol - Stock's symbol - * @returns {Stock} stock object - */ - const getStockFromSymbol = function (symbol: any, callingFn: any = ""): Stock { - const stock = SymbolToStockMap[symbol]; - if (stock == null) { - throw makeRuntimeErrorMsg(callingFn, `Invalid stock symbol: '${symbol}'`); - } - - return stock; - }; - /** * Used to fail a function if the function's target is a Hacknet Server. * This is used for functions that should run on normal Servers, but not Hacknet Servers @@ -422,16 +338,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { return makeRuntimeRejectMsg(workerScript, rejectMsg); }; - const checkFormulasAccess = function (func: any, n: any): void { - if ((SourceFileFlags[5] < 1 && Player.bitNodeN !== 5) || (SourceFileFlags[n] < 1 && Player.bitNodeN !== n)) { - let extra = ""; - if (n !== 5) { - extra = ` and Source-File ${n}-1`; - } - throw makeRuntimeErrorMsg(`formulas.${func}`, `Requires Source-File 5-1${extra} to run.`); - } - }; - const checkSingularityAccess = function (func: any, n: any): void { if (Player.bitNodeN !== 4) { if (SourceFileFlags[4] < n) { @@ -440,60 +346,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { } }; - const checkBladeburnerAccess = function (func: any, skipjoined: any = false): void { - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Must have joined bladeburner"); - const apiAccess = - Player.bitNodeN === 7 || - Player.sourceFiles.some((a) => { - return a.n === 7; - }); - if (!apiAccess) { - const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`; - throw makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied); - } - if (!skipjoined) { - const bladeburnerAccess = bladeburner instanceof Bladeburner; - if (!bladeburnerAccess) { - const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`; - throw makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied); - } - } - }; - - const checkBladeburnerCity = function (func: any, city: any): void { - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Must have joined bladeburner"); - if (!bladeburner.cities.hasOwnProperty(city)) { - throw makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid city: ${city}`); - } - }; - - const getCodingContract = function (func: any, ip: any, fn: any): CodingContract { - const server = safeGetServer(ip, func); - const contract = server.getContract(fn); - if (contract == null) { - throw makeRuntimeErrorMsg(`codingcontract.${func}`, `Cannot find contract '${fn}' on server '${ip}'`); - } - - return contract; - }; - - const getBladeburnerActionObject = function (func: any, type: any, name: any): any { - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Must have joined bladeburner"); - const actionId = bladeburner.getActionIdFromTypeAndName(type, name); - if (!actionId) { - throw makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); - } - const actionObj = bladeburner.getActionObject(actionId); - if (!actionObj) { - throw makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); - } - - return actionObj; - }; - const getCompany = function (func: any, name: any): Company { const company = Companies[name]; if (company == null || !(company instanceof Company)) { @@ -510,78 +362,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { return Factions[name]; }; - const getAugmentation = function (func: any, name: any): Augmentation { - if (!augmentationExists(name)) { - throw makeRuntimeErrorMsg(func, `Invalid augmentation: '${name}'`); - } - - return Augmentations[name]; - }; - - function getDivision(divisionName: any): IIndustry { - const corporation = Player.corporation; - if (corporation === null) throw new Error("cannot be called without a corporation"); - const division = corporation.divisions.find((div) => div.name === divisionName); - if (division === undefined) throw new Error(`No division named '${divisionName}'`); - return division; - } - - function getOffice(divisionName: any, cityName: any): OfficeSpace { - const division = getDivision(divisionName); - if (!(cityName in division.offices)) throw new Error(`Invalid city name '${cityName}'`); - const office = division.offices[cityName]; - if (office === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`); - return office; - } - - function getWarehouse(divisionName: any, cityName: any): Warehouse { - const division = getDivision(divisionName); - if (!(cityName in division.warehouses)) throw new Error(`Invalid city name '${cityName}'`); - const warehouse = division.warehouses[cityName]; - if (warehouse === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`); - return warehouse; - } - - function getMaterial(divisionName: any, cityName: any, materialName: any): Material { - const warehouse = getWarehouse(divisionName, cityName); - const material = warehouse.materials[materialName]; - if (material === undefined) throw new Error(`Invalid material name: '${materialName}'`); - return material; - } - - function getProduct(divisionName: any, productName: any): Product { - const division = getDivision(divisionName); - const product = division.products[productName]; - if (product === undefined) throw new Error(`Invalid product name: '${productName}'`); - return product; - } - - function getEmployee(divisionName: any, cityName: any, employeeName: any): Employee { - const office = getOffice(divisionName, cityName); - const employee = office.employees.find((e) => e.name === employeeName); - if (employee === undefined) throw new Error(`Invalid employee name: '${employeeName}'`); - return employee; - } - - const runAfterReset = function (cbScript = null): void { - //Run a script after reset - if (cbScript && isString(cbScript)) { - const home = Player.getHomeComputer(); - for (const script of home.scripts) { - if (script.filename === cbScript) { - const ramUsage = script.ramUsage; - const ramAvailable = home.maxRam - home.ramUsed; - if (ramUsage > ramAvailable) { - return; // Not enough RAM - } - const runningScriptObj = new RunningScript(script, []); // No args - runningScriptObj.threads = 1; // Only 1 thread - startWorkerScript(runningScriptObj, home); - } - } - } - }; - const hack = function (ip: any, manual: any, { threads: requestedThreads, stock }: any = {}): Promise { if (ip === undefined) { throw makeRuntimeErrorMsg("hack", "Takes 1 argument."); @@ -713,12 +493,21 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { boolean: (v: any): boolean => { return !!v; // Just convert it to boolean. }, + getServer: safeGetServer, + checkSingularityAccess: checkSingularityAccess, + getFaction: getFaction, }; const gang = NetscriptGang(Player, workerScript, helper); const sleeve = NetscriptSleeve(Player, workerScript, helper); const extra = NetscriptExtra(Player, workerScript); const hacknet = NetscriptHacknet(Player, workerScript, helper); + const bladeburner = NetscriptBladeburner(Player, workerScript, helper); + const codingcontract = NetscriptCodingContract(Player, workerScript, helper); + const corporation = NetscriptCorporation(Player, workerScript, helper); + const formulas = NetscriptFormulas(Player, workerScript, helper); + const augmentations = NetscriptAugmentations(Player, workerScript, helper); + const stockmarket = NetscriptStockMarket(Player, workerScript, helper); const functions = { hacknet: hacknet, @@ -1217,7 +1006,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { throw makeRuntimeErrorMsg("kill", "Usage: kill(scriptname, server, [arg1], [arg2]...)"); } - const server = safeGetServer(ip); + const server = safeGetServer(ip, "kill"); const runningScriptObj = getRunningScript(filename, ip, "kill", scriptArgs); if (runningScriptObj == null) { workerScript.log("kill", getCannotFindRunningScriptErrorMessage(filename, ip, scriptArgs)); @@ -1782,317 +1571,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { return getRunningScript(fn, ip, "isRunning", scriptArgs) != null; } }, - getStockSymbols: function (): any { - updateDynamicRam("getStockSymbols", getRamCost("getStockSymbols")); - checkTixApiAccess("getStockSymbols"); - return Object.values(StockSymbols); - }, - getStockPrice: function (symbol: any): any { - updateDynamicRam("getStockPrice", getRamCost("getStockPrice")); - checkTixApiAccess("getStockPrice"); - const stock = getStockFromSymbol(symbol, "getStockPrice"); - - return stock.price; - }, - getStockAskPrice: function (symbol: any): any { - updateDynamicRam("getStockAskPrice", getRamCost("getStockAskPrice")); - checkTixApiAccess("getStockAskPrice"); - const stock = getStockFromSymbol(symbol, "getStockAskPrice"); - - return stock.getAskPrice(); - }, - getStockBidPrice: function (symbol: any): any { - updateDynamicRam("getStockBidPrice", getRamCost("getStockBidPrice")); - checkTixApiAccess("getStockBidPrice"); - const stock = getStockFromSymbol(symbol, "getStockBidPrice"); - - return stock.getBidPrice(); - }, - getStockPosition: function (symbol: any): any { - updateDynamicRam("getStockPosition", getRamCost("getStockPosition")); - checkTixApiAccess("getStockPosition"); - const stock = SymbolToStockMap[symbol]; - if (stock == null) { - throw makeRuntimeErrorMsg("getStockPosition", `Invalid stock symbol: ${symbol}`); - } - return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; - }, - getStockMaxShares: function (symbol: any): any { - updateDynamicRam("getStockMaxShares", getRamCost("getStockMaxShares")); - checkTixApiAccess("getStockMaxShares"); - const stock = getStockFromSymbol(symbol, "getStockMaxShares"); - - return stock.maxShares; - }, - getStockPurchaseCost: function (symbol: any, shares: any, posType: any): any { - updateDynamicRam("getStockPurchaseCost", getRamCost("getStockPurchaseCost")); - checkTixApiAccess("getStockPurchaseCost"); - const stock = getStockFromSymbol(symbol, "getStockPurchaseCost"); - shares = Math.round(shares); - - let pos; - const sanitizedPosType = posType.toLowerCase(); - if (sanitizedPosType.includes("l")) { - pos = PositionTypes.Long; - } else if (sanitizedPosType.includes("s")) { - pos = PositionTypes.Short; - } else { - return Infinity; - } - - const res = getBuyTransactionCost(stock, shares, pos); - if (res == null) { - return Infinity; - } - - return res; - }, - getStockSaleGain: function (symbol: any, shares: any, posType: any): any { - updateDynamicRam("getStockSaleGain", getRamCost("getStockSaleGain")); - checkTixApiAccess("getStockSaleGain"); - const stock = getStockFromSymbol(symbol, "getStockSaleGain"); - shares = Math.round(shares); - - let pos; - const sanitizedPosType = posType.toLowerCase(); - if (sanitizedPosType.includes("l")) { - pos = PositionTypes.Long; - } else if (sanitizedPosType.includes("s")) { - pos = PositionTypes.Short; - } else { - return 0; - } - - const res = getSellTransactionGain(stock, shares, pos); - if (res == null) { - return 0; - } - - return res; - }, - buyStock: function (symbol: any, shares: any): any { - updateDynamicRam("buyStock", getRamCost("buyStock")); - checkTixApiAccess("buyStock"); - const stock = getStockFromSymbol(symbol, "buyStock"); - const res = buyStock(stock, shares, workerScript, {}); - return res ? stock.price : 0; - }, - sellStock: function (symbol: any, shares: any): any { - updateDynamicRam("sellStock", getRamCost("sellStock")); - checkTixApiAccess("sellStock"); - const stock = getStockFromSymbol(symbol, "sellStock"); - const res = sellStock(stock, shares, workerScript, {}); - - return res ? stock.price : 0; - }, - shortStock: function (symbol: any, shares: any): any { - updateDynamicRam("shortStock", getRamCost("shortStock")); - checkTixApiAccess("shortStock"); - if (Player.bitNodeN !== 8) { - if (SourceFileFlags[8] <= 1) { - throw makeRuntimeErrorMsg( - "shortStock", - "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", - ); - } - } - const stock = getStockFromSymbol(symbol, "shortStock"); - const res = shortStock(stock, shares, workerScript, {}); - - return res ? stock.price : 0; - }, - sellShort: function (symbol: any, shares: any): any { - updateDynamicRam("sellShort", getRamCost("sellShort")); - checkTixApiAccess("sellShort"); - if (Player.bitNodeN !== 8) { - if (SourceFileFlags[8] <= 1) { - throw makeRuntimeErrorMsg( - "sellShort", - "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", - ); - } - } - const stock = getStockFromSymbol(symbol, "sellShort"); - const res = sellShort(stock, shares, workerScript, {}); - - return res ? stock.price : 0; - }, - placeOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any { - updateDynamicRam("placeOrder", getRamCost("placeOrder")); - checkTixApiAccess("placeOrder"); - if (Player.bitNodeN !== 8) { - if (SourceFileFlags[8] <= 2) { - throw makeRuntimeErrorMsg( - "placeOrder", - "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", - ); - } - } - const stock = getStockFromSymbol(symbol, "placeOrder"); - - let orderType; - let orderPos; - const ltype = type.toLowerCase(); - if (ltype.includes("limit") && ltype.includes("buy")) { - orderType = OrderTypes.LimitBuy; - } else if (ltype.includes("limit") && ltype.includes("sell")) { - orderType = OrderTypes.LimitSell; - } else if (ltype.includes("stop") && ltype.includes("buy")) { - orderType = OrderTypes.StopBuy; - } else if (ltype.includes("stop") && ltype.includes("sell")) { - orderType = OrderTypes.StopSell; - } else { - throw makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`); - } - - const lpos = pos.toLowerCase(); - if (lpos.includes("l")) { - orderPos = PositionTypes.Long; - } else if (lpos.includes("s")) { - orderPos = PositionTypes.Short; - } else { - throw makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`); - } - - return placeOrder(stock, shares, price, orderType, orderPos, workerScript); - }, - cancelOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any { - updateDynamicRam("cancelOrder", getRamCost("cancelOrder")); - checkTixApiAccess("cancelOrder"); - if (Player.bitNodeN !== 8) { - if (SourceFileFlags[8] <= 2) { - throw makeRuntimeErrorMsg( - "cancelOrder", - "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", - ); - } - } - const stock = getStockFromSymbol(symbol, "cancelOrder"); - if (isNaN(shares) || isNaN(price)) { - throw makeRuntimeErrorMsg( - "cancelOrder", - `Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`, - ); - } - let orderType; - let orderPos; - const ltype = type.toLowerCase(); - if (ltype.includes("limit") && ltype.includes("buy")) { - orderType = OrderTypes.LimitBuy; - } else if (ltype.includes("limit") && ltype.includes("sell")) { - orderType = OrderTypes.LimitSell; - } else if (ltype.includes("stop") && ltype.includes("buy")) { - orderType = OrderTypes.StopBuy; - } else if (ltype.includes("stop") && ltype.includes("sell")) { - orderType = OrderTypes.StopSell; - } else { - throw makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`); - } - - const lpos = pos.toLowerCase(); - if (lpos.includes("l")) { - orderPos = PositionTypes.Long; - } else if (lpos.includes("s")) { - orderPos = PositionTypes.Short; - } else { - throw makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`); - } - const params = { - stock: stock, - shares: shares, - price: price, - type: orderType, - pos: orderPos, - }; - return cancelOrder(params, workerScript); - }, - getOrders: function (): any { - updateDynamicRam("getOrders", getRamCost("getOrders")); - checkTixApiAccess("getOrders"); - if (Player.bitNodeN !== 8) { - if (SourceFileFlags[8] <= 2) { - throw makeRuntimeErrorMsg("getOrders", "You must either be in BitNode-8 or have Source-File 8 Level 3."); - } - } - - const orders: any = {}; - - const stockMarketOrders = StockMarket["Orders"]; - for (const symbol in stockMarketOrders) { - const orderBook = stockMarketOrders[symbol]; - if (orderBook.constructor === Array && orderBook.length > 0) { - orders[symbol] = []; - for (let i = 0; i < orderBook.length; ++i) { - orders[symbol].push({ - shares: orderBook[i].shares, - price: orderBook[i].price, - type: orderBook[i].type, - position: orderBook[i].pos, - }); - } - } - } - - return orders; - }, - getStockVolatility: function (symbol: any): any { - updateDynamicRam("getStockVolatility", getRamCost("getStockVolatility")); - if (!Player.has4SDataTixApi) { - throw makeRuntimeErrorMsg("getStockVolatility", "You don't have 4S Market Data TIX API Access!"); - } - const stock = getStockFromSymbol(symbol, "getStockVolatility"); - - return stock.mv / 100; // Convert from percentage to decimal - }, - getStockForecast: function (symbol: any): any { - updateDynamicRam("getStockForecast", getRamCost("getStockForecast")); - if (!Player.has4SDataTixApi) { - throw makeRuntimeErrorMsg("getStockForecast", "You don't have 4S Market Data TIX API Access!"); - } - const stock = getStockFromSymbol(symbol, "getStockForecast"); - - let forecast = 50; - stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag); - return forecast / 100; // Convert from percentage to decimal - }, - purchase4SMarketData: function () { - updateDynamicRam("purchase4SMarketData", getRamCost("purchase4SMarketData")); - checkTixApiAccess("purchase4SMarketData"); - - if (Player.has4SData) { - workerScript.log("purchase4SMarketData", "Already purchased 4S Market Data."); - return true; - } - - if (Player.money.lt(getStockMarket4SDataCost())) { - workerScript.log("purchase4SMarketData", "Not enough money to purchase 4S Market Data."); - return false; - } - - Player.has4SData = true; - Player.loseMoney(getStockMarket4SDataCost()); - workerScript.log("purchase4SMarketData", "Purchased 4S Market Data"); - return true; - }, - purchase4SMarketDataTixApi: function () { - updateDynamicRam("purchase4SMarketDataTixApi", getRamCost("purchase4SMarketDataTixApi")); - checkTixApiAccess("purchase4SMarketDataTixApi"); - - if (Player.has4SDataTixApi) { - workerScript.log("purchase4SMarketDataTixApi", "Already purchased 4S Market Data TIX API"); - return true; - } - - if (Player.money.lt(getStockMarket4STixApiCost())) { - workerScript.log("purchase4SMarketDataTixApi", "Not enough money to purchase 4S Market Data TIX API"); - return false; - } - - Player.has4SDataTixApi = true; - Player.loseMoney(getStockMarket4STixApiCost()); - workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API"); - return true; - }, + ...stockmarket, getPurchasedServerLimit: function (): any { updateDynamicRam("getPurchasedServerLimit", getRamCost("getPurchasedServerLimit")); @@ -3741,843 +3220,16 @@ function NetscriptFunctions(workerScript: WorkerScript): NS { return Object.assign({}, crime); }, - getOwnedAugmentations: function (purchased: any = false): any { - updateDynamicRam("getOwnedAugmentations", getRamCost("getOwnedAugmentations")); - checkSingularityAccess("getOwnedAugmentations", 3); - const res = []; - for (let i = 0; i < Player.augmentations.length; ++i) { - res.push(Player.augmentations[i].name); - } - if (purchased) { - for (let i = 0; i < Player.queuedAugmentations.length; ++i) { - res.push(Player.queuedAugmentations[i].name); - } - } - return res; - }, - getOwnedSourceFiles: function (): any { - updateDynamicRam("getOwnedSourceFiles", getRamCost("getOwnedSourceFiles")); - checkSingularityAccess("getOwnedSourceFiles", 3); - const res = []; - for (let i = 0; i < Player.sourceFiles.length; ++i) { - res.push({ - n: Player.sourceFiles[i].n, - lvl: Player.sourceFiles[i].lvl, - }); - } - return res; - }, - getAugmentationsFromFaction: function (facname: any): any { - updateDynamicRam("getAugmentationsFromFaction", getRamCost("getAugmentationsFromFaction")); - checkSingularityAccess("getAugmentationsFromFaction", 3); - const faction = getFaction("getAugmentationsFromFaction", facname); - // If player has a gang with this faction, return all augmentations. - if (Player.hasGangWith(facname)) { - const res = []; - for (const augName in Augmentations) { - const aug = Augmentations[augName]; - if (!aug.isSpecial) { - res.push(augName); - } - } + ...augmentations, - return res; - } - - return faction.augmentations.slice(); - }, - getAugmentationCost: function (name: any): any { - updateDynamicRam("getAugmentationCost", getRamCost("getAugmentationCost")); - checkSingularityAccess("getAugmentationCost", 3); - const aug = getAugmentation("getAugmentationCost", name); - return [aug.baseRepRequirement, aug.baseCost]; - }, - getAugmentationPrereq: function (name: any): any { - updateDynamicRam("getAugmentationPrereq", getRamCost("getAugmentationPrereq")); - checkSingularityAccess("getAugmentationPrereq", 3); - const aug = getAugmentation("getAugmentationPrereq", name); - return aug.prereqs.slice(); - }, - getAugmentationPrice: function (name: any): any { - updateDynamicRam("getAugmentationPrice", getRamCost("getAugmentationPrice")); - checkSingularityAccess("getAugmentationPrice", 3); - const aug = getAugmentation("getAugmentationPrice", name); - return aug.baseCost; - }, - getAugmentationRepReq: function (name: any): any { - updateDynamicRam("getAugmentationRepReq", getRamCost("getAugmentationRepReq")); - checkSingularityAccess("getAugmentationRepReq", 3); - const aug = getAugmentation("getAugmentationRepReq", name); - return aug.baseRepRequirement; - }, - getAugmentationStats: function (name: any): any { - updateDynamicRam("getAugmentationStats", getRamCost("getAugmentationStats")); - checkSingularityAccess("getAugmentationStats", 3); - const aug = getAugmentation("getAugmentationStats", name); - return Object.assign({}, aug.mults); - }, - purchaseAugmentation: function (faction: any, name: any): any { - updateDynamicRam("purchaseAugmentation", getRamCost("purchaseAugmentation")); - checkSingularityAccess("purchaseAugmentation", 3); - const fac = getFaction("purchaseAugmentation", faction); - const aug = getAugmentation("purchaseAugmentation", name); - - let augs = []; - if (Player.hasGangWith(faction)) { - for (const augName in Augmentations) { - const tempAug = Augmentations[augName]; - if (!tempAug.isSpecial) { - augs.push(augName); - } - } - } else { - augs = fac.augmentations; - } - - if (!augs.includes(name)) { - workerScript.log("purchaseAugmentation", `Faction '${faction}' does not have the '${name}' augmentation.`); - return false; - } - - const isNeuroflux = aug.name === AugmentationNames.NeuroFluxGovernor; - if (!isNeuroflux) { - for (let j = 0; j < Player.queuedAugmentations.length; ++j) { - if (Player.queuedAugmentations[j].name === aug.name) { - workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`); - return false; - } - } - for (let j = 0; j < Player.augmentations.length; ++j) { - if (Player.augmentations[j].name === aug.name) { - workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`); - return false; - } - } - } - - if (fac.playerReputation < aug.baseRepRequirement) { - workerScript.log("purchaseAugmentation", `You do not have enough reputation with '${fac.name}'.`); - return false; - } - - const res = purchaseAugmentation(aug, fac, true); - workerScript.log("purchaseAugmentation", res); - if (isString(res) && res.startsWith("You purchased")) { - Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); - return true; - } else { - return false; - } - }, - softReset: function (cbScript: any): any { - updateDynamicRam("softReset", getRamCost("softReset")); - checkSingularityAccess("softReset", 3); - - workerScript.log("softReset", "Soft resetting. This will cause this script to be killed"); - setTimeout(() => { - prestigeAugmentation(); - runAfterReset(cbScript); - }, 0); - - // Prevent workerScript from "finishing execution naturally" - workerScript.running = false; - killWorkerScript(workerScript); - }, - installAugmentations: function (cbScript: any): any { - updateDynamicRam("installAugmentations", getRamCost("installAugmentations")); - checkSingularityAccess("installAugmentations", 3); - - if (Player.queuedAugmentations.length === 0) { - workerScript.log("installAugmentations", "You do not have any Augmentations to be installed."); - return false; - } - Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); - workerScript.log("installAugmentations", "Installing Augmentations. This will cause this script to be killed"); - setTimeout(() => { - installAugmentations(); - runAfterReset(cbScript); - }, 0); - - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - killWorkerScript(workerScript); - }, - - // Gang API gang: gang, - - // Bladeburner API - bladeburner: { - getContractNames: function (): any { - updateDynamicRam("getContractNames", getRamCost("bladeburner", "getContractNames")); - checkBladeburnerAccess("getContractNames"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getContractNamesNetscriptFn(); - }, - getOperationNames: function (): any { - updateDynamicRam("getOperationNames", getRamCost("bladeburner", "getOperationNames")); - checkBladeburnerAccess("getOperationNames"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getOperationNamesNetscriptFn(); - }, - getBlackOpNames: function (): any { - updateDynamicRam("getBlackOpNames", getRamCost("bladeburner", "getBlackOpNames")); - checkBladeburnerAccess("getBlackOpNames"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getBlackOpNamesNetscriptFn(); - }, - getBlackOpRank: function (name: any = ""): any { - updateDynamicRam("getBlackOpRank", getRamCost("bladeburner", "getBlackOpRank")); - checkBladeburnerAccess("getBlackOpRank"); - const action: any = getBladeburnerActionObject("getBlackOpRank", "blackops", name); - return action.reqdRank; - }, - getGeneralActionNames: function (): any { - updateDynamicRam("getGeneralActionNames", getRamCost("bladeburner", "getGeneralActionNames")); - checkBladeburnerAccess("getGeneralActionNames"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getGeneralActionNamesNetscriptFn(); - }, - getSkillNames: function (): any { - updateDynamicRam("getSkillNames", getRamCost("bladeburner", "getSkillNames")); - checkBladeburnerAccess("getSkillNames"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getSkillNamesNetscriptFn(); - }, - startAction: function (type: any = "", name: any = ""): any { - updateDynamicRam("startAction", getRamCost("bladeburner", "startAction")); - checkBladeburnerAccess("startAction"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.startActionNetscriptFn(Player, type, name, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.startAction", e); - } - }, - stopBladeburnerAction: function (): any { - updateDynamicRam("stopBladeburnerAction", getRamCost("bladeburner", "stopBladeburnerAction")); - checkBladeburnerAccess("stopBladeburnerAction"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.resetAction(); - }, - getCurrentAction: function (): any { - updateDynamicRam("getCurrentAction", getRamCost("bladeburner", "getCurrentAction")); - checkBladeburnerAccess("getCurrentAction"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.getTypeAndNameFromActionId(bladeburner.action); - }, - getActionTime: function (type: any = "", name: any = ""): any { - updateDynamicRam("getActionTime", getRamCost("bladeburner", "getActionTime")); - checkBladeburnerAccess("getActionTime"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getActionTimeNetscriptFn(Player, type, name, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getActionTime", e); - } - }, - getActionEstimatedSuccessChance: function (type: any = "", name: any = ""): any { - updateDynamicRam( - "getActionEstimatedSuccessChance", - getRamCost("bladeburner", "getActionEstimatedSuccessChance"), - ); - checkBladeburnerAccess("getActionEstimatedSuccessChance"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(Player, type, name, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e); - } - }, - getActionRepGain: function (type: any = "", name: any = "", level: any): any { - updateDynamicRam("getActionRepGain", getRamCost("bladeburner", "getActionRepGain")); - checkBladeburnerAccess("getActionRepGain"); - const action = getBladeburnerActionObject("getActionRepGain", 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; - }, - getActionCountRemaining: function (type: any = "", name: any = ""): any { - updateDynamicRam("getActionCountRemaining", getRamCost("bladeburner", "getActionCountRemaining")); - checkBladeburnerAccess("getActionCountRemaining"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e); - } - }, - getActionMaxLevel: function (type: any = "", name: any = ""): any { - updateDynamicRam("getActionMaxLevel", getRamCost("bladeburner", "getActionMaxLevel")); - checkBladeburnerAccess("getActionMaxLevel"); - const action = getBladeburnerActionObject("getActionMaxLevel", type, name); - return action.maxLevel; - }, - getActionCurrentLevel: function (type: any = "", name: any = ""): any { - updateDynamicRam("getActionCurrentLevel", getRamCost("bladeburner", "getActionCurrentLevel")); - checkBladeburnerAccess("getActionCurrentLevel"); - const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); - return action.level; - }, - getActionAutolevel: function (type: any = "", name: any = ""): any { - updateDynamicRam("getActionAutolevel", getRamCost("bladeburner", "getActionAutolevel")); - checkBladeburnerAccess("getActionAutolevel"); - const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); - return action.autoLevel; - }, - setActionAutolevel: function (type: any = "", name: any = "", autoLevel: any = true): any { - updateDynamicRam("setActionAutolevel", getRamCost("bladeburner", "setActionAutolevel")); - checkBladeburnerAccess("setActionAutolevel"); - const action = getBladeburnerActionObject("setActionAutolevel", type, name); - action.autoLevel = autoLevel; - }, - setActionLevel: function (type: any = "", name: any = "", level: any = 1): any { - updateDynamicRam("setActionLevel", getRamCost("bladeburner", "setActionLevel")); - checkBladeburnerAccess("setActionLevel"); - const action = getBladeburnerActionObject("setActionLevel", type, name); - if (level < 1 || level > action.maxLevel) { - throw makeRuntimeErrorMsg( - "bladeburner.setActionLevel", - `Level must be between 1 and ${action.maxLevel}, is ${level}`, - ); - } - action.level = level; - }, - getRank: function (): any { - updateDynamicRam("getRank", getRamCost("bladeburner", "getRank")); - checkBladeburnerAccess("getRank"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.rank; - }, - getSkillPoints: function (): any { - updateDynamicRam("getSkillPoints", getRamCost("bladeburner", "getSkillPoints")); - checkBladeburnerAccess("getSkillPoints"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.skillPoints; - }, - getSkillLevel: function (skillName: any = ""): any { - updateDynamicRam("getSkillLevel", getRamCost("bladeburner", "getSkillLevel")); - checkBladeburnerAccess("getSkillLevel"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getSkillLevel", e); - } - }, - getSkillUpgradeCost: function (skillName: any = ""): any { - updateDynamicRam("getSkillUpgradeCost", getRamCost("bladeburner", "getSkillUpgradeCost")); - checkBladeburnerAccess("getSkillUpgradeCost"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e); - } - }, - upgradeSkill: function (skillName: any): any { - updateDynamicRam("upgradeSkill", getRamCost("bladeburner", "upgradeSkill")); - checkBladeburnerAccess("upgradeSkill"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.upgradeSkill", e); - } - }, - getTeamSize: function (type: any = "", name: any = ""): any { - updateDynamicRam("getTeamSize", getRamCost("bladeburner", "getTeamSize")); - checkBladeburnerAccess("getTeamSize"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.getTeamSize", e); - } - }, - setTeamSize: function (type: any = "", name: any = "", size: any): any { - updateDynamicRam("setTeamSize", getRamCost("bladeburner", "setTeamSize")); - checkBladeburnerAccess("setTeamSize"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - try { - return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); - } catch (e: any) { - throw makeRuntimeErrorMsg("bladeburner.setTeamSize", e); - } - }, - getCityEstimatedPopulation: function (cityName: any): any { - updateDynamicRam("getCityEstimatedPopulation", getRamCost("bladeburner", "getCityEstimatedPopulation")); - checkBladeburnerAccess("getCityEstimatedPopulation"); - checkBladeburnerCity("getCityEstimatedPopulation", cityName); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.cities[cityName].popEst; - }, - getCityEstimatedCommunities: function (cityName: any): any { - updateDynamicRam("getCityEstimatedCommunities", getRamCost("bladeburner", "getCityEstimatedCommunities")); - checkBladeburnerAccess("getCityEstimatedCommunities"); - checkBladeburnerCity("getCityEstimatedCommunities", cityName); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.cities[cityName].commsEst; - }, - getCityChaos: function (cityName: any): any { - updateDynamicRam("getCityChaos", getRamCost("bladeburner", "getCityChaos")); - checkBladeburnerAccess("getCityChaos"); - checkBladeburnerCity("getCityChaos", cityName); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.cities[cityName].chaos; - }, - getCity: function (): any { - updateDynamicRam("getCity", getRamCost("bladeburner", "getCity")); - checkBladeburnerAccess("getCityChaos"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.city; - }, - switchCity: function (cityName: any): any { - updateDynamicRam("switchCity", getRamCost("bladeburner", "switchCity")); - checkBladeburnerAccess("switchCity"); - checkBladeburnerCity("switchCity", cityName); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return (bladeburner.city = cityName); - }, - getStamina: function (): any { - updateDynamicRam("getStamina", getRamCost("bladeburner", "getStamina")); - checkBladeburnerAccess("getStamina"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return [bladeburner.stamina, bladeburner.maxStamina]; - }, - joinBladeburnerFaction: function (): any { - updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction")); - checkBladeburnerAccess("joinBladeburnerFaction", true); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript); - }, - joinBladeburnerDivision: function (): any { - updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision")); - if (Player.bitNodeN === 7 || SourceFileFlags[7] > 0) { - if (Player.bitNodeN === 8) { - return false; - } - if (Player.bladeburner instanceof Bladeburner) { - return true; // Already member - } else if ( - Player.strength >= 100 && - Player.defense >= 100 && - Player.dexterity >= 100 && - Player.agility >= 100 - ) { - Player.bladeburner = new Bladeburner(Player); - workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division"); - - return true; - } else { - workerScript.log( - "joinBladeburnerDivision", - "You do not meet the requirements for joining the Bladeburner division", - ); - return false; - } - } - }, - getBonusTime: function (): any { - updateDynamicRam("getBonusTime", getRamCost("bladeburner", "getBonusTime")); - checkBladeburnerAccess("getBonusTime"); - const bladeburner = Player.bladeburner; - if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return Math.round(bladeburner.storedCycles / 5); - }, - }, // End Bladeburner - - // Hi, if you're reading this you're a bit nosy. - // There's a corporation API but it's very imbalanced right now. - // It's here so players can test with if they want. - corporation: { - expandIndustry: function (industryName: any, divisionName: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - NewIndustry(corporation, industryName, divisionName); - }, - expandCity: function (divisionName: any, cityName: any): any { - const division = getDivision(divisionName); - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - NewCity(corporation, division, cityName); - }, - unlockUpgrade: function (upgradeName: any): any { - const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); - if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - UnlockUpgrade(corporation, upgrade); - }, - levelUpgrade: function (upgradeName: any): any { - const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); - if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - LevelUpgrade(corporation, upgrade); - }, - issueDividends: function (percent: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - IssueDividends(corporation, percent); - }, - sellMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any, price: any): any { - const material = getMaterial(divisionName, cityName, materialName); - SellMaterial(material, amt, price); - }, - sellProduct: function (divisionName: any, cityName: any, productName: any, amt: any, price: any, all: any): any { - const product = getProduct(divisionName, productName); - SellProduct(product, cityName, amt, price, all); - }, - discontinueProduct: function (divisionName: any, productName: any): any { - getDivision(divisionName).discontinueProduct(getProduct(divisionName, productName)); - }, - setSmartSupply: function (divisionName: any, cityName: any, enabled: any): any { - const warehouse = getWarehouse(divisionName, cityName); - SetSmartSupply(warehouse, enabled); - }, - // setSmartSupplyUseLeftovers: function (): any {}, - buyMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any): any { - const material = getMaterial(divisionName, cityName, materialName); - BuyMaterial(material, amt); - }, - employees: function (divisionName: any, cityName: any): any { - const office = getOffice(divisionName, cityName); - return office.employees.map((e) => Object.assign({}, e)); - }, - assignJob: function (divisionName: any, cityName: any, employeeName: any, job: any): any { - const employee = getEmployee(divisionName, cityName, employeeName); - AssignJob(employee, job); - }, - hireEmployee: function (divisionName: any, cityName: any): any { - const office = getOffice(divisionName, cityName); - office.hireRandomEmployee(); - }, - upgradeOfficeSize: function (divisionName: any, cityName: any, size: any): any { - const office = getOffice(divisionName, cityName); - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - UpgradeOfficeSize(corporation, office, size); - }, - throwParty: function (divisionName: any, cityName: any, costPerEmployee: any): any { - const office = getOffice(divisionName, cityName); - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - ThrowParty(corporation, office, costPerEmployee); - }, - purchaseWarehouse: function (divisionName: any, cityName: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - PurchaseWarehouse(corporation, getDivision(divisionName), cityName); - }, - upgradeWarehouse: function (divisionName: any, cityName: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName)); - }, - buyCoffee: function (divisionName: any, cityName: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - BuyCoffee(corporation, getDivision(divisionName), getOffice(divisionName, cityName)); - }, - hireAdVert: function (divisionName: any): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - HireAdVert(corporation, getDivision(divisionName), getOffice(divisionName, "Sector-12")); - }, - makeProduct: function ( - divisionName: any, - cityName: any, - productName: any, - designInvest: any, - marketingInvest: any, - ): any { - const corporation = Player.corporation; - if (corporation === null) throw new Error("Should not be called without a corporation"); - MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest); - }, - research: function (divisionName: any, researchName: any): any { - Research(getDivision(divisionName), researchName); - }, - exportMaterial: function ( - sourceDivision: any, - sourceCity: any, - targetDivision: any, - targetCity: any, - materialName: any, - amt: any, - ): any { - ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + ""); - }, - cancelExportMaterial: function ( - sourceDivision: any, - sourceCity: any, - targetDivision: any, - targetCity: any, - materialName: any, - amt: any, - ): any { - CancelExportMaterial( - targetDivision, - targetCity, - getMaterial(sourceDivision, sourceCity, materialName), - amt + "", - ); - }, - setMaterialMarketTA1: function (divisionName: any, cityName: any, materialName: any, on: any): any { - SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on); - }, - setMaterialMarketTA2: function (divisionName: any, cityName: any, materialName: any, on: any) { - SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on); - }, - setProductMarketTA1: function (divisionName: any, productName: any, on: any): any { - SetProductMarketTA1(getProduct(divisionName, productName), on); - }, - setProductMarketTA2: function (divisionName: any, productName: any, on: any) { - SetProductMarketTA2(getProduct(divisionName, productName), on); - }, - // If you modify these objects you will affect them for real, it's not - // copies. - getDivision: function (divisionName: any): any { - return getDivision(divisionName); - }, - getOffice: function (divisionName: any, cityName: any): any { - return getOffice(divisionName, cityName); - }, - getWarehouse: function (divisionName: any, cityName: any): any { - return getWarehouse(divisionName, cityName); - }, - getMaterial: function (divisionName: any, cityName: any, materialName: any): any { - return getMaterial(divisionName, cityName, materialName); - }, - getProduct: function (divisionName: any, productName: any): any { - return getProduct(divisionName, productName); - }, - getEmployee: function (divisionName: any, cityName: any, employeeName: any): any { - return getEmployee(divisionName, cityName, employeeName); - }, - }, // End Corporation API - - // Coding Contract API - codingcontract: { - attempt: function (answer: any, fn: any, ip: any = workerScript.hostname, { returnReward }: any = {}): any { - updateDynamicRam("attempt", getRamCost("codingcontract", "attempt")); - const contract = getCodingContract("attempt", ip, fn); - - // Convert answer to string. If the answer is a 2D array, then we have to - // manually add brackets for the inner arrays - if (is2DArray(answer)) { - const answerComponents = []; - for (let i = 0; i < answer.length; ++i) { - answerComponents.push(["[", answer[i].toString(), "]"].join("")); - } - - answer = answerComponents.join(","); - } else { - answer = String(answer); - } - - const creward = contract.reward; - if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward"); - - const serv = safeGetServer(ip, "codingcontract.attempt"); - if (contract.isSolution(answer)) { - const reward = Player.gainCodingContractReward(creward, contract.getDifficulty()); - workerScript.log("attempt", `Successfully completed Coding Contract '${fn}'. Reward: ${reward}`); - serv.removeContract(fn); - return returnReward ? reward : true; - } else { - ++contract.tries; - if (contract.tries >= contract.getMaxNumTries()) { - workerScript.log("attempt", `Coding Contract attempt '${fn}' failed. Contract is now self-destructing`); - serv.removeContract(fn); - } else { - workerScript.log( - "attempt", - `Coding Contract attempt '${fn}' failed. ${ - contract.getMaxNumTries() - contract.tries - } attempts remaining.`, - ); - } - - return returnReward ? "" : false; - } - }, - getContractType: function (fn: any, ip: any = workerScript.hostname): any { - updateDynamicRam("getContractType", getRamCost("codingcontract", "getContractType")); - const contract = getCodingContract("getContractType", ip, fn); - return contract.getType(); - }, - getData: function (fn: any, ip: any = workerScript.hostname): any { - updateDynamicRam("getData", getRamCost("codingcontract", "getData")); - const contract = getCodingContract("getData", ip, fn); - const data = contract.getData(); - if (data.constructor === Array) { - // For two dimensional arrays, we have to copy the internal arrays using - // slice() as well. As of right now, no contract has arrays that have - // more than two dimensions - const copy = data.slice(); - for (let i = 0; i < copy.length; ++i) { - if (data[i].constructor === Array) { - copy[i] = data[i].slice(); - } - } - - return copy; - } else { - return data; - } - }, - getDescription: function (fn: any, ip: any = workerScript.hostname): any { - updateDynamicRam("getDescription", getRamCost("codingcontract", "getDescription")); - const contract = getCodingContract("getDescription", ip, fn); - return contract.getDescription(); - }, - getNumTriesRemaining: function (fn: any, ip: any = workerScript.hostname): any { - updateDynamicRam("getNumTriesRemaining", getRamCost("codingcontract", "getNumTriesRemaining")); - const contract = getCodingContract("getNumTriesRemaining", ip, fn); - return contract.getMaxNumTries() - contract.tries; - }, - }, // End coding contracts - + bladeburner: bladeburner, + codingcontract: codingcontract, sleeve: sleeve, - formulas: { - basic: { - calculateSkill: function (exp: any, mult: any = 1): any { - checkFormulasAccess("basic.calculateSkill", 5); - return calculateSkill(exp, mult); - }, - calculateExp: function (skill: any, mult: any = 1): any { - checkFormulasAccess("basic.calculateExp", 5); - return calculateExp(skill, mult); - }, - hackChance: function (server: any, player: any): any { - checkFormulasAccess("basic.hackChance", 5); - return calculateHackingChance(server, player); - }, - hackExp: function (server: any, player: any): any { - checkFormulasAccess("basic.hackExp", 5); - return calculateHackingExpGain(server, player); - }, - hackPercent: function (server: any, player: any): any { - checkFormulasAccess("basic.hackPercent", 5); - return calculatePercentMoneyHacked(server, player); - }, - growPercent: function (server: any, threads: any, player: any, cores: any = 1): any { - checkFormulasAccess("basic.growPercent", 5); - return calculateServerGrowth(server, threads, player, cores); - }, - hackTime: function (server: any, player: any): any { - checkFormulasAccess("basic.hackTime", 5); - return calculateHackingTime(server, player); - }, - growTime: function (server: any, player: any): any { - checkFormulasAccess("basic.growTime", 5); - return calculateGrowTime(server, player); - }, - weakenTime: function (server: any, player: any): any { - checkFormulasAccess("basic.weakenTime", 5); - return calculateWeakenTime(server, player); - }, - }, - hacknetNodes: { - moneyGainRate: function (level: any, ram: any, cores: any, mult: any = 1): any { - checkFormulasAccess("hacknetNodes.moneyGainRate", 5); - return calculateMoneyGainRate(level, ram, cores, mult); - }, - levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetNodes.levelUpgradeCost", 5); - return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult); - }, - ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetNodes.ramUpgradeCost", 5); - return calculateRamUpgradeCost(startingRam, extraLevels, costMult); - }, - coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetNodes.coreUpgradeCost", 5); - return calculateCoreUpgradeCost(startingCore, extraCores, costMult); - }, - hacknetNodeCost: function (n: any, mult: any): any { - checkFormulasAccess("hacknetNodes.hacknetNodeCost", 5); - return calculateNodeCost(n, mult); - }, - constants: function (): any { - checkFormulasAccess("hacknetNodes.constants", 5); - return Object.assign({}, HacknetNodeConstants); - }, - }, - hacknetServers: { - hashGainRate: function (level: any, ramUsed: any, maxRam: any, cores: any, mult: any = 1): any { - checkFormulasAccess("hacknetServers.hashGainRate", 9); - return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult); - }, - levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetServers.levelUpgradeCost", 9); - return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult); - }, - ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetServers.ramUpgradeCost", 9); - return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult); - }, - coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any { - checkFormulasAccess("hacknetServers.coreUpgradeCost", 9); - return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult); - }, - cacheUpgradeCost: function (startingCache: any, extraCache: any = 1): any { - checkFormulasAccess("hacknetServers.cacheUpgradeCost", 9); - return HScalculateCacheUpgradeCost(startingCache, extraCache); - }, - hashUpgradeCost: function (upgName: any, level: any): any { - checkFormulasAccess("hacknetServers.hashUpgradeCost", 9); - const upg = Player.hashManager.getUpgrade(upgName); - if (!upg) { - throw makeRuntimeErrorMsg( - "formulas.hacknetServers.calculateHashUpgradeCost", - `Invalid Hash Upgrade: ${upgName}`, - ); - } - return upg.getCost(level); - }, - hacknetServerCost: function (n: any, mult: any): any { - checkFormulasAccess("hacknetServers.hacknetServerCost", 9); - return HScalculateServerCost(n, mult); - }, - constants: function (): any { - checkFormulasAccess("hacknetServers.constants", 9); - return Object.assign({}, HacknetServerConstants); - }, - }, - }, // end formulas + corporation: corporation, + + formulas: formulas, flags: function (data: any): any { data = toNative(data); // We always want the help flag. diff --git a/src/NetscriptFunctions/Augmentations.ts b/src/NetscriptFunctions/Augmentations.ts new file mode 100644 index 000000000..100ea6714 --- /dev/null +++ b/src/NetscriptFunctions/Augmentations.ts @@ -0,0 +1,224 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { purchaseAugmentation } from "../Faction/FactionHelpers"; +import { startWorkerScript } from "../NetscriptWorker"; +import { Augmentation } from "../Augmentation/Augmentation"; +import { Augmentations } from "../Augmentation/Augmentations"; +import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers"; +import { prestigeAugmentation } from "../Prestige"; +import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; +import { killWorkerScript } from "../Netscript/killWorkerScript"; +import { CONSTANTS } from "../Constants"; +import { isString } from "../utils/helpers/isString"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { RunningScript } from "../Script/RunningScript"; + +export interface INetscriptAugmentations { + getOwnedAugmentations(purchased?: any): any; + getOwnedSourceFiles(): any; + getAugmentationsFromFaction(facname: any): any; + getAugmentationCost(name: any): any; + getAugmentationPrereq(name: any): any; + getAugmentationPrice(name: any): any; + getAugmentationRepReq(name: any): any; + getAugmentationStats(name: any): any; + purchaseAugmentation(faction: any, name: any): any; + softReset(cbScript: any): any; + installAugmentations(cbScript: any): any; +} + +export function NetscriptAugmentations( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptAugmentations { + const getAugmentation = function (func: any, name: any): Augmentation { + if (!augmentationExists(name)) { + throw helper.makeRuntimeErrorMsg(func, `Invalid augmentation: '${name}'`); + } + + return Augmentations[name]; + }; + const runAfterReset = function (cbScript = null): void { + //Run a script after reset + if (cbScript && isString(cbScript)) { + const home = player.getHomeComputer(); + for (const script of home.scripts) { + if (script.filename === cbScript) { + const ramUsage = script.ramUsage; + const ramAvailable = home.maxRam - home.ramUsed; + if (ramUsage > ramAvailable) { + return; // Not enough RAM + } + const runningScriptObj = new RunningScript(script, []); // No args + runningScriptObj.threads = 1; // Only 1 thread + startWorkerScript(runningScriptObj, home); + } + } + } + }; + return { + getOwnedAugmentations: function (purchased: any = false): any { + helper.updateDynamicRam("getOwnedAugmentations", getRamCost("getOwnedAugmentations")); + helper.checkSingularityAccess("getOwnedAugmentations", 3); + const res = []; + for (let i = 0; i < player.augmentations.length; ++i) { + res.push(player.augmentations[i].name); + } + if (purchased) { + for (let i = 0; i < player.queuedAugmentations.length; ++i) { + res.push(player.queuedAugmentations[i].name); + } + } + return res; + }, + getOwnedSourceFiles: function (): any { + helper.updateDynamicRam("getOwnedSourceFiles", getRamCost("getOwnedSourceFiles")); + helper.checkSingularityAccess("getOwnedSourceFiles", 3); + const res = []; + for (let i = 0; i < player.sourceFiles.length; ++i) { + res.push({ + n: player.sourceFiles[i].n, + lvl: player.sourceFiles[i].lvl, + }); + } + return res; + }, + getAugmentationsFromFaction: function (facname: any): any { + helper.updateDynamicRam("getAugmentationsFromFaction", getRamCost("getAugmentationsFromFaction")); + helper.checkSingularityAccess("getAugmentationsFromFaction", 3); + const faction = helper.getFaction("getAugmentationsFromFaction", facname); + + // If player has a gang with this faction, return all augmentations. + if (player.hasGangWith(facname)) { + const res = []; + for (const augName in Augmentations) { + const aug = Augmentations[augName]; + if (!aug.isSpecial) { + res.push(augName); + } + } + + return res; + } + + return faction.augmentations.slice(); + }, + getAugmentationCost: function (name: any): any { + helper.updateDynamicRam("getAugmentationCost", getRamCost("getAugmentationCost")); + helper.checkSingularityAccess("getAugmentationCost", 3); + const aug = getAugmentation("getAugmentationCost", name); + return [aug.baseRepRequirement, aug.baseCost]; + }, + getAugmentationPrereq: function (name: any): any { + helper.updateDynamicRam("getAugmentationPrereq", getRamCost("getAugmentationPrereq")); + helper.checkSingularityAccess("getAugmentationPrereq", 3); + const aug = getAugmentation("getAugmentationPrereq", name); + return aug.prereqs.slice(); + }, + getAugmentationPrice: function (name: any): any { + helper.updateDynamicRam("getAugmentationPrice", getRamCost("getAugmentationPrice")); + helper.checkSingularityAccess("getAugmentationPrice", 3); + const aug = getAugmentation("getAugmentationPrice", name); + return aug.baseCost; + }, + getAugmentationRepReq: function (name: any): any { + helper.updateDynamicRam("getAugmentationRepReq", getRamCost("getAugmentationRepReq")); + helper.checkSingularityAccess("getAugmentationRepReq", 3); + const aug = getAugmentation("getAugmentationRepReq", name); + return aug.baseRepRequirement; + }, + getAugmentationStats: function (name: any): any { + helper.updateDynamicRam("getAugmentationStats", getRamCost("getAugmentationStats")); + helper.checkSingularityAccess("getAugmentationStats", 3); + const aug = getAugmentation("getAugmentationStats", name); + return Object.assign({}, aug.mults); + }, + purchaseAugmentation: function (faction: any, name: any): any { + helper.updateDynamicRam("purchaseAugmentation", getRamCost("purchaseAugmentation")); + helper.checkSingularityAccess("purchaseAugmentation", 3); + const fac = helper.getFaction("purchaseAugmentation", faction); + const aug = getAugmentation("purchaseAugmentation", name); + + let augs = []; + if (player.hasGangWith(faction)) { + for (const augName in Augmentations) { + const tempAug = Augmentations[augName]; + if (!tempAug.isSpecial) { + augs.push(augName); + } + } + } else { + augs = fac.augmentations; + } + + if (!augs.includes(name)) { + workerScript.log("purchaseAugmentation", `Faction '${faction}' does not have the '${name}' augmentation.`); + return false; + } + + const isNeuroflux = aug.name === AugmentationNames.NeuroFluxGovernor; + if (!isNeuroflux) { + for (let j = 0; j < player.queuedAugmentations.length; ++j) { + if (player.queuedAugmentations[j].name === aug.name) { + workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`); + return false; + } + } + for (let j = 0; j < player.augmentations.length; ++j) { + if (player.augmentations[j].name === aug.name) { + workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`); + return false; + } + } + } + + if (fac.playerReputation < aug.baseRepRequirement) { + workerScript.log("purchaseAugmentation", `You do not have enough reputation with '${fac.name}'.`); + return false; + } + + const res = purchaseAugmentation(aug, fac, true); + workerScript.log("purchaseAugmentation", res); + if (isString(res) && res.startsWith("You purchased")) { + player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); + return true; + } else { + return false; + } + }, + softReset: function (cbScript: any): any { + helper.updateDynamicRam("softReset", getRamCost("softReset")); + helper.checkSingularityAccess("softReset", 3); + + workerScript.log("softReset", "Soft resetting. This will cause this script to be killed"); + setTimeout(() => { + prestigeAugmentation(); + runAfterReset(cbScript); + }, 0); + + // Prevent workerScript from "finishing execution naturally" + workerScript.running = false; + killWorkerScript(workerScript); + }, + installAugmentations: function (cbScript: any): any { + helper.updateDynamicRam("installAugmentations", getRamCost("installAugmentations")); + helper.checkSingularityAccess("installAugmentations", 3); + + if (player.queuedAugmentations.length === 0) { + workerScript.log("installAugmentations", "You do not have any Augmentations to be installed."); + return false; + } + player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); + workerScript.log("installAugmentations", "Installing Augmentations. This will cause this script to be killed"); + setTimeout(() => { + installAugmentations(); + runAfterReset(cbScript); + }, 0); + + workerScript.running = false; // Prevent workerScript from "finishing execution naturally" + killWorkerScript(workerScript); + }, + }; +} diff --git a/src/NetscriptFunctions/Bladeburner.ts b/src/NetscriptFunctions/Bladeburner.ts new file mode 100644 index 000000000..b5aba6b0b --- /dev/null +++ b/src/NetscriptFunctions/Bladeburner.ts @@ -0,0 +1,403 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Bladeburner } from "../Bladeburner/Bladeburner"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; + +export interface INetscriptBladeburner { + getContractNames(): any; + getOperationNames(): any; + getBlackOpNames(): any; + getBlackOpRank(name?: any): any; + getGeneralActionNames(): any; + getSkillNames(): any; + startAction(type?: any, name?: any): any; + stopBladeburnerAction(): any; + getCurrentAction(): any; + getActionTime(type?: any, name?: any): any; + getActionEstimatedSuccessChance(type?: any, name?: any): any; + getActionRepGain(type?: any, name?: any, level?: any): any; + getActionCountRemaining(type?: any, name?: any): any; + getActionMaxLevel(type?: any, name?: any): any; + getActionCurrentLevel(type?: any, name?: any): any; + getActionAutolevel(type?: any, name?: any): any; + setActionAutolevel(type?: any, name?: any, autoLevel?: any): any; + setActionLevel(type?: any, name?: any, level?: any): any; + getRank(): any; + getSkillPoints(): any; + getSkillLevel(skillName?: any): any; + getSkillUpgradeCost(skillName?: any): any; + upgradeSkill(skillName: any): any; + getTeamSize(type?: any, name?: any): any; + setTeamSize(type?: any, name?: any, size?: any): any; + getCityEstimatedPopulation(cityName: any): any; + getCityEstimatedCommunities(cityName: any): any; + getCityChaos(cityName: any): any; + getCity(): any; + switchCity(cityName: any): any; + getStamina(): any; + joinBladeburnerFaction(): any; + joinBladeburnerDivision(): any; + getBonusTime(): any; +} + +export function NetscriptBladeburner( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptBladeburner { + const checkBladeburnerAccess = function (func: any, skipjoined: any = false): void { + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Must have joined bladeburner"); + const apiAccess = + player.bitNodeN === 7 || + player.sourceFiles.some((a) => { + return a.n === 7; + }); + if (!apiAccess) { + const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`; + throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied); + } + if (!skipjoined) { + const bladeburnerAccess = bladeburner instanceof Bladeburner; + if (!bladeburnerAccess) { + const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`; + throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied); + } + } + }; + + const checkBladeburnerCity = function (func: any, city: any): void { + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Must have joined bladeburner"); + if (!bladeburner.cities.hasOwnProperty(city)) { + throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid city: ${city}`); + } + }; + + const getBladeburnerActionObject = function (func: any, type: any, name: any): any { + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Must have joined bladeburner"); + const actionId = bladeburner.getActionIdFromTypeAndName(type, name); + if (!actionId) { + throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); + } + const actionObj = bladeburner.getActionObject(actionId); + if (!actionObj) { + throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`); + } + + return actionObj; + }; + + return { + getContractNames: function (): any { + helper.updateDynamicRam("getContractNames", getRamCost("bladeburner", "getContractNames")); + checkBladeburnerAccess("getContractNames"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getContractNamesNetscriptFn(); + }, + getOperationNames: function (): any { + helper.updateDynamicRam("getOperationNames", getRamCost("bladeburner", "getOperationNames")); + checkBladeburnerAccess("getOperationNames"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getOperationNamesNetscriptFn(); + }, + getBlackOpNames: function (): any { + helper.updateDynamicRam("getBlackOpNames", getRamCost("bladeburner", "getBlackOpNames")); + checkBladeburnerAccess("getBlackOpNames"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getBlackOpNamesNetscriptFn(); + }, + getBlackOpRank: function (name: any = ""): any { + helper.updateDynamicRam("getBlackOpRank", getRamCost("bladeburner", "getBlackOpRank")); + checkBladeburnerAccess("getBlackOpRank"); + const action: any = getBladeburnerActionObject("getBlackOpRank", "blackops", name); + return action.reqdRank; + }, + getGeneralActionNames: function (): any { + helper.updateDynamicRam("getGeneralActionNames", getRamCost("bladeburner", "getGeneralActionNames")); + checkBladeburnerAccess("getGeneralActionNames"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getGeneralActionNamesNetscriptFn(); + }, + getSkillNames: function (): any { + helper.updateDynamicRam("getSkillNames", getRamCost("bladeburner", "getSkillNames")); + checkBladeburnerAccess("getSkillNames"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getSkillNamesNetscriptFn(); + }, + startAction: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("startAction", getRamCost("bladeburner", "startAction")); + checkBladeburnerAccess("startAction"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.startActionNetscriptFn(player, type, name, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.startAction", e); + } + }, + stopBladeburnerAction: function (): any { + helper.updateDynamicRam("stopBladeburnerAction", getRamCost("bladeburner", "stopBladeburnerAction")); + checkBladeburnerAccess("stopBladeburnerAction"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.resetAction(); + }, + getCurrentAction: function (): any { + helper.updateDynamicRam("getCurrentAction", getRamCost("bladeburner", "getCurrentAction")); + checkBladeburnerAccess("getCurrentAction"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.getTypeAndNameFromActionId(bladeburner.action); + }, + getActionTime: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getActionTime", getRamCost("bladeburner", "getActionTime")); + checkBladeburnerAccess("getActionTime"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getActionTime", e); + } + }, + getActionEstimatedSuccessChance: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam( + "getActionEstimatedSuccessChance", + getRamCost("bladeburner", "getActionEstimatedSuccessChance"), + ); + checkBladeburnerAccess("getActionEstimatedSuccessChance"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e); + } + }, + getActionRepGain: function (type: any = "", name: any = "", level: any): any { + helper.updateDynamicRam("getActionRepGain", getRamCost("bladeburner", "getActionRepGain")); + checkBladeburnerAccess("getActionRepGain"); + const action = getBladeburnerActionObject("getActionRepGain", 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; + }, + getActionCountRemaining: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getActionCountRemaining", getRamCost("bladeburner", "getActionCountRemaining")); + checkBladeburnerAccess("getActionCountRemaining"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e); + } + }, + getActionMaxLevel: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getActionMaxLevel", getRamCost("bladeburner", "getActionMaxLevel")); + checkBladeburnerAccess("getActionMaxLevel"); + const action = getBladeburnerActionObject("getActionMaxLevel", type, name); + return action.maxLevel; + }, + getActionCurrentLevel: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getActionCurrentLevel", getRamCost("bladeburner", "getActionCurrentLevel")); + checkBladeburnerAccess("getActionCurrentLevel"); + const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); + return action.level; + }, + getActionAutolevel: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getActionAutolevel", getRamCost("bladeburner", "getActionAutolevel")); + checkBladeburnerAccess("getActionAutolevel"); + const action = getBladeburnerActionObject("getActionCurrentLevel", type, name); + return action.autoLevel; + }, + setActionAutolevel: function (type: any = "", name: any = "", autoLevel: any = true): any { + helper.updateDynamicRam("setActionAutolevel", getRamCost("bladeburner", "setActionAutolevel")); + checkBladeburnerAccess("setActionAutolevel"); + const action = getBladeburnerActionObject("setActionAutolevel", type, name); + action.autoLevel = autoLevel; + }, + setActionLevel: function (type: any = "", name: any = "", level: any = 1): any { + helper.updateDynamicRam("setActionLevel", getRamCost("bladeburner", "setActionLevel")); + checkBladeburnerAccess("setActionLevel"); + const action = getBladeburnerActionObject("setActionLevel", type, name); + if (level < 1 || level > action.maxLevel) { + throw helper.makeRuntimeErrorMsg( + "bladeburner.setActionLevel", + `Level must be between 1 and ${action.maxLevel}, is ${level}`, + ); + } + action.level = level; + }, + getRank: function (): any { + helper.updateDynamicRam("getRank", getRamCost("bladeburner", "getRank")); + checkBladeburnerAccess("getRank"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.rank; + }, + getSkillPoints: function (): any { + helper.updateDynamicRam("getSkillPoints", getRamCost("bladeburner", "getSkillPoints")); + checkBladeburnerAccess("getSkillPoints"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.skillPoints; + }, + getSkillLevel: function (skillName: any = ""): any { + helper.updateDynamicRam("getSkillLevel", getRamCost("bladeburner", "getSkillLevel")); + checkBladeburnerAccess("getSkillLevel"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getSkillLevel", e); + } + }, + getSkillUpgradeCost: function (skillName: any = ""): any { + helper.updateDynamicRam("getSkillUpgradeCost", getRamCost("bladeburner", "getSkillUpgradeCost")); + checkBladeburnerAccess("getSkillUpgradeCost"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e); + } + }, + upgradeSkill: function (skillName: any): any { + helper.updateDynamicRam("upgradeSkill", getRamCost("bladeburner", "upgradeSkill")); + checkBladeburnerAccess("upgradeSkill"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.upgradeSkill", e); + } + }, + getTeamSize: function (type: any = "", name: any = ""): any { + helper.updateDynamicRam("getTeamSize", getRamCost("bladeburner", "getTeamSize")); + checkBladeburnerAccess("getTeamSize"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.getTeamSize", e); + } + }, + setTeamSize: function (type: any = "", name: any = "", size: any): any { + helper.updateDynamicRam("setTeamSize", getRamCost("bladeburner", "setTeamSize")); + checkBladeburnerAccess("setTeamSize"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + try { + return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); + } catch (e: any) { + throw helper.makeRuntimeErrorMsg("bladeburner.setTeamSize", e); + } + }, + getCityEstimatedPopulation: function (cityName: any): any { + helper.updateDynamicRam("getCityEstimatedPopulation", getRamCost("bladeburner", "getCityEstimatedPopulation")); + checkBladeburnerAccess("getCityEstimatedPopulation"); + checkBladeburnerCity("getCityEstimatedPopulation", cityName); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.cities[cityName].popEst; + }, + getCityEstimatedCommunities: function (cityName: any): any { + helper.updateDynamicRam("getCityEstimatedCommunities", getRamCost("bladeburner", "getCityEstimatedCommunities")); + checkBladeburnerAccess("getCityEstimatedCommunities"); + checkBladeburnerCity("getCityEstimatedCommunities", cityName); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.cities[cityName].commsEst; + }, + getCityChaos: function (cityName: any): any { + helper.updateDynamicRam("getCityChaos", getRamCost("bladeburner", "getCityChaos")); + checkBladeburnerAccess("getCityChaos"); + checkBladeburnerCity("getCityChaos", cityName); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.cities[cityName].chaos; + }, + getCity: function (): any { + helper.updateDynamicRam("getCity", getRamCost("bladeburner", "getCity")); + checkBladeburnerAccess("getCityChaos"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.city; + }, + switchCity: function (cityName: any): any { + helper.updateDynamicRam("switchCity", getRamCost("bladeburner", "switchCity")); + checkBladeburnerAccess("switchCity"); + checkBladeburnerCity("switchCity", cityName); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return (bladeburner.city = cityName); + }, + getStamina: function (): any { + helper.updateDynamicRam("getStamina", getRamCost("bladeburner", "getStamina")); + checkBladeburnerAccess("getStamina"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return [bladeburner.stamina, bladeburner.maxStamina]; + }, + joinBladeburnerFaction: function (): any { + helper.updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction")); + checkBladeburnerAccess("joinBladeburnerFaction", true); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript); + }, + joinBladeburnerDivision: function (): any { + helper.updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision")); + if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) { + if (player.bitNodeN === 8) { + return false; + } + if (player.bladeburner instanceof Bladeburner) { + return true; // Already member + } else if ( + player.strength >= 100 && + player.defense >= 100 && + player.dexterity >= 100 && + player.agility >= 100 + ) { + player.bladeburner = new Bladeburner(player); + workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division"); + + return true; + } else { + workerScript.log( + "joinBladeburnerDivision", + "You do not meet the requirements for joining the Bladeburner division", + ); + return false; + } + } + }, + getBonusTime: function (): any { + helper.updateDynamicRam("getBonusTime", getRamCost("bladeburner", "getBonusTime")); + checkBladeburnerAccess("getBonusTime"); + const bladeburner = player.bladeburner; + if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); + return Math.round(bladeburner.storedCycles / 5); + }, + }; +} diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts new file mode 100644 index 000000000..2717e2132 --- /dev/null +++ b/src/NetscriptFunctions/CodingContract.ts @@ -0,0 +1,109 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { is2DArray } from "../utils/helpers/is2DArray"; +import { CodingContract } from "../CodingContracts"; + +export interface INetscriptCodingContract { + attempt(answer: any, fn: any, ip?: any, options?: { returnReward: any }): any; + getContractType(fn: any, ip?: any): any; + getData(fn: any, ip?: any): any; + getDescription(fn: any, ip?: any): any; + getNumTriesRemaining(fn: any, ip?: any): any; +} + +export function NetscriptCodingContract( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptCodingContract { + const getCodingContract = function (func: any, ip: any, fn: any): CodingContract { + const server = helper.getServer(ip, func); + const contract = server.getContract(fn); + if (contract == null) { + throw helper.makeRuntimeErrorMsg(`codingcontract.${func}`, `Cannot find contract '${fn}' on server '${ip}'`); + } + + return contract; + }; + + return { + attempt: function (answer: any, fn: any, ip: any = workerScript.hostname, { returnReward }: any = {}): any { + helper.updateDynamicRam("attempt", getRamCost("codingcontract", "attempt")); + const contract = getCodingContract("attempt", ip, fn); + + // Convert answer to string. If the answer is a 2D array, then we have to + // manually add brackets for the inner arrays + if (is2DArray(answer)) { + const answerComponents = []; + for (let i = 0; i < answer.length; ++i) { + answerComponents.push(["[", answer[i].toString(), "]"].join("")); + } + + answer = answerComponents.join(","); + } else { + answer = String(answer); + } + + const creward = contract.reward; + if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward"); + + const serv = helper.getServer(ip, "codingcontract.attempt"); + if (contract.isSolution(answer)) { + const reward = player.gainCodingContractReward(creward, contract.getDifficulty()); + workerScript.log("attempt", `Successfully completed Coding Contract '${fn}'. Reward: ${reward}`); + serv.removeContract(fn); + return returnReward ? reward : true; + } else { + ++contract.tries; + if (contract.tries >= contract.getMaxNumTries()) { + workerScript.log("attempt", `Coding Contract attempt '${fn}' failed. Contract is now self-destructing`); + serv.removeContract(fn); + } else { + workerScript.log( + "attempt", + `Coding Contract attempt '${fn}' failed. ${contract.getMaxNumTries() - contract.tries} attempts remaining.`, + ); + } + + return returnReward ? "" : false; + } + }, + getContractType: function (fn: any, ip: any = workerScript.hostname): any { + helper.updateDynamicRam("getContractType", getRamCost("codingcontract", "getContractType")); + const contract = getCodingContract("getContractType", ip, fn); + return contract.getType(); + }, + getData: function (fn: any, ip: any = workerScript.hostname): any { + helper.updateDynamicRam("getData", getRamCost("codingcontract", "getData")); + const contract = getCodingContract("getData", ip, fn); + const data = contract.getData(); + if (data.constructor === Array) { + // For two dimensional arrays, we have to copy the internal arrays using + // slice() as well. As of right now, no contract has arrays that have + // more than two dimensions + const copy = data.slice(); + for (let i = 0; i < copy.length; ++i) { + if (data[i].constructor === Array) { + copy[i] = data[i].slice(); + } + } + + return copy; + } else { + return data; + } + }, + getDescription: function (fn: any, ip: any = workerScript.hostname): any { + helper.updateDynamicRam("getDescription", getRamCost("codingcontract", "getDescription")); + const contract = getCodingContract("getDescription", ip, fn); + return contract.getDescription(); + }, + getNumTriesRemaining: function (fn: any, ip: any = workerScript.hostname): any { + helper.updateDynamicRam("getNumTriesRemaining", getRamCost("codingcontract", "getNumTriesRemaining")); + const contract = getCodingContract("getNumTriesRemaining", ip, fn); + return contract.getMaxNumTries() - contract.tries; + }, + }; +} diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts new file mode 100644 index 000000000..102397bcd --- /dev/null +++ b/src/NetscriptFunctions/Corporation.ts @@ -0,0 +1,305 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; + +import { OfficeSpace } from "../Corporation/OfficeSpace"; +import { Employee } from "../Corporation/Employee"; +import { Product } from "../Corporation/Product"; +import { Material } from "../Corporation/Material"; +import { Warehouse } from "../Corporation/Warehouse"; +import { IIndustry } from "../Corporation/IIndustry"; + +import { + NewIndustry, + NewCity, + UnlockUpgrade, + LevelUpgrade, + IssueDividends, + SellMaterial, + SellProduct, + SetSmartSupply, + BuyMaterial, + AssignJob, + UpgradeOfficeSize, + ThrowParty, + PurchaseWarehouse, + UpgradeWarehouse, + BuyCoffee, + HireAdVert, + MakeProduct, + Research, + ExportMaterial, + CancelExportMaterial, + SetMaterialMarketTA1, + SetMaterialMarketTA2, + SetProductMarketTA1, + SetProductMarketTA2, +} from "../Corporation/Actions"; +import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades"; +import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades"; + +export interface INetscriptCorporation { + expandIndustry(industryName: any, divisionName: any): any; + expandCity(divisionName: any, cityName: any): any; + unlockUpgrade(upgradeName: any): any; + levelUpgrade(upgradeName: any): any; + issueDividends(percent: any): any; + sellMaterial(divisionName: any, cityName: any, materialName: any, amt: any, price: any): any; + sellProduct(divisionName: any, cityName: any, productName: any, amt: any, price: any, all: any): any; + discontinueProduct(divisionName: any, productName: any): any; + setSmartSupply(divisionName: any, cityName: any, enabled: any): any; + buyMaterial(divisionName: any, cityName: any, materialName: any, amt: any): any; + employees(divisionName: any, cityName: any): any; + assignJob(divisionName: any, cityName: any, employeeName: any, job: any): any; + hireEmployee(divisionName: any, cityName: any): any; + upgradeOfficeSize(divisionName: any, cityName: any, size: any): any; + throwParty(divisionName: any, cityName: any, costPerEmployee: any): any; + purchaseWarehouse(divisionName: any, cityName: any): any; + upgradeWarehouse(divisionName: any, cityName: any): any; + buyCoffee(divisionName: any, cityName: any): any; + hireAdVert(divisionName: any): any; + makeProduct(divisionName: any, cityName: any, productName: any, designInvest: any, marketingInvest: any): any; + research(divisionName: any, researchName: any): any; + exportMaterial( + sourceDivision: any, + sourceCity: any, + targetDivision: any, + targetCity: any, + materialName: any, + amt: any, + ): any; + cancelExportMaterial( + sourceDivision: any, + sourceCity: any, + targetDivision: any, + targetCity: any, + materialName: any, + amt: any, + ): any; + setMaterialMarketTA1(divisionName: any, cityName: any, materialName: any, on: any): any; + setMaterialMarketTA2(divisionName: any, cityName: any, materialName: any, on: any): any; + setProductMarketTA1(divisionName: any, productName: any, on: any): any; + setProductMarketTA2(divisionName: any, productName: any, on: any): any; + getDivision(divisionName: any): any; + getOffice(divisionName: any, cityName: any): any; + getWarehouse(divisionName: any, cityName: any): any; + getMaterial(divisionName: any, cityName: any, materialName: any): any; + getProduct(divisionName: any, productName: any): any; + getEmployee(divisionName: any, cityName: any, employeeName: any): any; +} + +export function NetscriptCorporation( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptCorporation { + function getDivision(divisionName: any): IIndustry { + const corporation = player.corporation; + if (corporation === null) throw new Error("cannot be called without a corporation"); + const division = corporation.divisions.find((div) => div.name === divisionName); + if (division === undefined) throw new Error(`No division named '${divisionName}'`); + return division; + } + + function getOffice(divisionName: any, cityName: any): OfficeSpace { + const division = getDivision(divisionName); + if (!(cityName in division.offices)) throw new Error(`Invalid city name '${cityName}'`); + const office = division.offices[cityName]; + if (office === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`); + return office; + } + + function getWarehouse(divisionName: any, cityName: any): Warehouse { + const division = getDivision(divisionName); + if (!(cityName in division.warehouses)) throw new Error(`Invalid city name '${cityName}'`); + const warehouse = division.warehouses[cityName]; + if (warehouse === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`); + return warehouse; + } + + function getMaterial(divisionName: any, cityName: any, materialName: any): Material { + const warehouse = getWarehouse(divisionName, cityName); + const material = warehouse.materials[materialName]; + if (material === undefined) throw new Error(`Invalid material name: '${materialName}'`); + return material; + } + + function getProduct(divisionName: any, productName: any): Product { + const division = getDivision(divisionName); + const product = division.products[productName]; + if (product === undefined) throw new Error(`Invalid product name: '${productName}'`); + return product; + } + + function getEmployee(divisionName: any, cityName: any, employeeName: any): Employee { + const office = getOffice(divisionName, cityName); + const employee = office.employees.find((e) => e.name === employeeName); + if (employee === undefined) throw new Error(`Invalid employee name: '${employeeName}'`); + return employee; + } + // Hi, if you're reading this you're a bit nosy. + // There's a corporation API but it's very imbalanced right now. + // It's here so players can test with if they want. + return { + expandIndustry: function (industryName: any, divisionName: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + NewIndustry(corporation, industryName, divisionName); + }, + expandCity: function (divisionName: any, cityName: any): any { + const division = getDivision(divisionName); + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + NewCity(corporation, division, cityName); + }, + unlockUpgrade: function (upgradeName: any): any { + const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); + if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + UnlockUpgrade(corporation, upgrade); + }, + levelUpgrade: function (upgradeName: any): any { + const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); + if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + LevelUpgrade(corporation, upgrade); + }, + issueDividends: function (percent: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + IssueDividends(corporation, percent); + }, + sellMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any, price: any): any { + const material = getMaterial(divisionName, cityName, materialName); + SellMaterial(material, amt, price); + }, + sellProduct: function (divisionName: any, cityName: any, productName: any, amt: any, price: any, all: any): any { + const product = getProduct(divisionName, productName); + SellProduct(product, cityName, amt, price, all); + }, + discontinueProduct: function (divisionName: any, productName: any): any { + getDivision(divisionName).discontinueProduct(getProduct(divisionName, productName)); + }, + setSmartSupply: function (divisionName: any, cityName: any, enabled: any): any { + const warehouse = getWarehouse(divisionName, cityName); + SetSmartSupply(warehouse, enabled); + }, + // setSmartSupplyUseLeftovers: function (): any {}, + buyMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any): any { + const material = getMaterial(divisionName, cityName, materialName); + BuyMaterial(material, amt); + }, + employees: function (divisionName: any, cityName: any): any { + const office = getOffice(divisionName, cityName); + return office.employees.map((e) => Object.assign({}, e)); + }, + assignJob: function (divisionName: any, cityName: any, employeeName: any, job: any): any { + const employee = getEmployee(divisionName, cityName, employeeName); + AssignJob(employee, job); + }, + hireEmployee: function (divisionName: any, cityName: any): any { + const office = getOffice(divisionName, cityName); + office.hireRandomEmployee(); + }, + upgradeOfficeSize: function (divisionName: any, cityName: any, size: any): any { + const office = getOffice(divisionName, cityName); + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + UpgradeOfficeSize(corporation, office, size); + }, + throwParty: function (divisionName: any, cityName: any, costPerEmployee: any): any { + const office = getOffice(divisionName, cityName); + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + ThrowParty(corporation, office, costPerEmployee); + }, + purchaseWarehouse: function (divisionName: any, cityName: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + PurchaseWarehouse(corporation, getDivision(divisionName), cityName); + }, + upgradeWarehouse: function (divisionName: any, cityName: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName)); + }, + buyCoffee: function (divisionName: any, cityName: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + BuyCoffee(corporation, getDivision(divisionName), getOffice(divisionName, cityName)); + }, + hireAdVert: function (divisionName: any): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + HireAdVert(corporation, getDivision(divisionName), getOffice(divisionName, "Sector-12")); + }, + makeProduct: function ( + divisionName: any, + cityName: any, + productName: any, + designInvest: any, + marketingInvest: any, + ): any { + const corporation = player.corporation; + if (corporation === null) throw new Error("Should not be called without a corporation"); + MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest); + }, + research: function (divisionName: any, researchName: any): any { + Research(getDivision(divisionName), researchName); + }, + exportMaterial: function ( + sourceDivision: any, + sourceCity: any, + targetDivision: any, + targetCity: any, + materialName: any, + amt: any, + ): any { + ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + ""); + }, + cancelExportMaterial: function ( + sourceDivision: any, + sourceCity: any, + targetDivision: any, + targetCity: any, + materialName: any, + amt: any, + ): any { + CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + ""); + }, + setMaterialMarketTA1: function (divisionName: any, cityName: any, materialName: any, on: any): any { + SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on); + }, + setMaterialMarketTA2: function (divisionName: any, cityName: any, materialName: any, on: any) { + SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on); + }, + setProductMarketTA1: function (divisionName: any, productName: any, on: any): any { + SetProductMarketTA1(getProduct(divisionName, productName), on); + }, + setProductMarketTA2: function (divisionName: any, productName: any, on: any) { + SetProductMarketTA2(getProduct(divisionName, productName), on); + }, + // If you modify these objects you will affect them for real, it's not + // copies. + getDivision: function (divisionName: any): any { + return getDivision(divisionName); + }, + getOffice: function (divisionName: any, cityName: any): any { + return getOffice(divisionName, cityName); + }, + getWarehouse: function (divisionName: any, cityName: any): any { + return getWarehouse(divisionName, cityName); + }, + getMaterial: function (divisionName: any, cityName: any, materialName: any): any { + return getMaterial(divisionName, cityName, materialName); + }, + getProduct: function (divisionName: any, productName: any): any { + return getProduct(divisionName, productName); + }, + getEmployee: function (divisionName: any, cityName: any, employeeName: any): any { + return getEmployee(divisionName, cityName, employeeName); + }, + }; +} diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts new file mode 100644 index 000000000..e908299f1 --- /dev/null +++ b/src/NetscriptFunctions/Formulas.ts @@ -0,0 +1,187 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { calculateServerGrowth } from "../Server/formulas/grow"; +import { + calculateMoneyGainRate, + calculateLevelUpgradeCost, + calculateRamUpgradeCost, + calculateCoreUpgradeCost, + calculateNodeCost, +} from "../Hacknet/formulas/HacknetNodes"; +import { + calculateHashGainRate as HScalculateHashGainRate, + calculateLevelUpgradeCost as HScalculateLevelUpgradeCost, + calculateRamUpgradeCost as HScalculateRamUpgradeCost, + calculateCoreUpgradeCost as HScalculateCoreUpgradeCost, + calculateCacheUpgradeCost as HScalculateCacheUpgradeCost, + calculateServerCost as HScalculateServerCost, +} from "../Hacknet/formulas/HacknetServers"; +import { HacknetNodeConstants, HacknetServerConstants } from "../Hacknet/data/Constants"; +import { calculateSkill, calculateExp } from "../PersonObjects/formulas/skill"; +import { + calculateHackingChance, + calculateHackingExpGain, + calculatePercentMoneyHacked, + calculateHackingTime, + calculateGrowTime, + calculateWeakenTime, +} from "../Hacking"; + +export interface INetscriptFormulas { + basic: { + calculateSkill(exp: any, mult?: any): any; + calculateExp(skill: any, mult?: any): any; + hackChance(server: any, player: any): any; + hackExp(server: any, player: any): any; + hackPercent(server: any, player: any): any; + growPercent(server: any, threads: any, player: any, cores?: any): any; + hackTime(server: any, player: any): any; + growTime(server: any, player: any): any; + weakenTime(server: any, player: any): any; + }; + hacknetNodes: { + moneyGainRate(level: any, ram: any, cores: any, mult?: any): any; + levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any; + ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any; + coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any; + hacknetNodeCost(n: any, mult: any): any; + constants(): any; + }; + hacknetServers: { + hashGainRate(level: any, ramUsed: any, maxRam: any, cores: any, mult?: any): any; + levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any; + ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any; + coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any; + cacheUpgradeCost(startingCache: any, extraCache?: any): any; + hashUpgradeCost(upgName: any, level: any): any; + hacknetServerCost(n: any, mult: any): any; + constants(): any; + }; +} + +export function NetscriptFormulas( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptFormulas { + const checkFormulasAccess = function (func: any, n: any): void { + if ( + (player.sourceFileLvl(5) < 1 && player.bitNodeN !== 5) || + (player.sourceFileLvl(n) < 1 && player.bitNodeN !== n) + ) { + let extra = ""; + if (n !== 5) { + extra = ` and Source-File ${n}-1`; + } + throw helper.makeRuntimeErrorMsg(`formulas.${func}`, `Requires Source-File 5-1${extra} to run.`); + } + }; + return { + basic: { + calculateSkill: function (exp: any, mult: any = 1): any { + checkFormulasAccess("basic.calculateSkill", 5); + return calculateSkill(exp, mult); + }, + calculateExp: function (skill: any, mult: any = 1): any { + checkFormulasAccess("basic.calculateExp", 5); + return calculateExp(skill, mult); + }, + hackChance: function (server: any, player: any): any { + checkFormulasAccess("basic.hackChance", 5); + return calculateHackingChance(server, player); + }, + hackExp: function (server: any, player: any): any { + checkFormulasAccess("basic.hackExp", 5); + return calculateHackingExpGain(server, player); + }, + hackPercent: function (server: any, player: any): any { + checkFormulasAccess("basic.hackPercent", 5); + return calculatePercentMoneyHacked(server, player); + }, + growPercent: function (server: any, threads: any, player: any, cores: any = 1): any { + checkFormulasAccess("basic.growPercent", 5); + return calculateServerGrowth(server, threads, player, cores); + }, + hackTime: function (server: any, player: any): any { + checkFormulasAccess("basic.hackTime", 5); + return calculateHackingTime(server, player); + }, + growTime: function (server: any, player: any): any { + checkFormulasAccess("basic.growTime", 5); + return calculateGrowTime(server, player); + }, + weakenTime: function (server: any, player: any): any { + checkFormulasAccess("basic.weakenTime", 5); + return calculateWeakenTime(server, player); + }, + }, + hacknetNodes: { + moneyGainRate: function (level: any, ram: any, cores: any, mult: any = 1): any { + checkFormulasAccess("hacknetNodes.moneyGainRate", 5); + return calculateMoneyGainRate(level, ram, cores, mult); + }, + levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetNodes.levelUpgradeCost", 5); + return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult); + }, + ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetNodes.ramUpgradeCost", 5); + return calculateRamUpgradeCost(startingRam, extraLevels, costMult); + }, + coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetNodes.coreUpgradeCost", 5); + return calculateCoreUpgradeCost(startingCore, extraCores, costMult); + }, + hacknetNodeCost: function (n: any, mult: any): any { + checkFormulasAccess("hacknetNodes.hacknetNodeCost", 5); + return calculateNodeCost(n, mult); + }, + constants: function (): any { + checkFormulasAccess("hacknetNodes.constants", 5); + return Object.assign({}, HacknetNodeConstants); + }, + }, + hacknetServers: { + hashGainRate: function (level: any, ramUsed: any, maxRam: any, cores: any, mult: any = 1): any { + checkFormulasAccess("hacknetServers.hashGainRate", 9); + return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult); + }, + levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetServers.levelUpgradeCost", 9); + return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult); + }, + ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetServers.ramUpgradeCost", 9); + return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult); + }, + coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any { + checkFormulasAccess("hacknetServers.coreUpgradeCost", 9); + return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult); + }, + cacheUpgradeCost: function (startingCache: any, extraCache: any = 1): any { + checkFormulasAccess("hacknetServers.cacheUpgradeCost", 9); + return HScalculateCacheUpgradeCost(startingCache, extraCache); + }, + hashUpgradeCost: function (upgName: any, level: any): any { + checkFormulasAccess("hacknetServers.hashUpgradeCost", 9); + const upg = player.hashManager.getUpgrade(upgName); + if (!upg) { + throw helper.makeRuntimeErrorMsg( + "formulas.hacknetServers.calculateHashUpgradeCost", + `Invalid Hash Upgrade: ${upgName}`, + ); + } + return upg.getCost(level); + }, + hacknetServerCost: function (n: any, mult: any): any { + checkFormulasAccess("hacknetServers.hacknetServerCost", 9); + return HScalculateServerCost(n, mult); + }, + constants: function (): any { + checkFormulasAccess("hacknetServers.constants", 9); + return Object.assign({}, HacknetServerConstants); + }, + }, + }; +} diff --git a/src/NetscriptFunctions/INetscriptHelper.ts b/src/NetscriptFunctions/INetscriptHelper.ts index 58bbab059..bf17cfe03 100644 --- a/src/NetscriptFunctions/INetscriptHelper.ts +++ b/src/NetscriptFunctions/INetscriptHelper.ts @@ -1,7 +1,13 @@ +import { BaseServer } from "../Server/BaseServer"; +import { Faction } from "../Faction/Faction"; + export interface INetscriptHelper { updateDynamicRam(functionName: string, ram: number): void; makeRuntimeErrorMsg(functionName: string, message: string): void; string(funcName: string, argName: string, v: any): string; number(funcName: string, argName: string, v: any): number; boolean(v: any): boolean; + getServer(ip: any, fn: any): BaseServer; + checkSingularityAccess(func: string, n: number): void; + getFaction(func: string, name: string): Faction; } diff --git a/src/NetscriptFunctions/StockMarket.ts b/src/NetscriptFunctions/StockMarket.ts new file mode 100644 index 000000000..2fe6c8b8e --- /dev/null +++ b/src/NetscriptFunctions/StockMarket.ts @@ -0,0 +1,377 @@ +import { INetscriptHelper } from "./INetscriptHelper"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling"; +import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "../StockMarket/StockMarket"; +import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers"; +import { OrderTypes } from "../StockMarket/data/OrderTypes"; +import { PositionTypes } from "../StockMarket/data/PositionTypes"; +import { StockSymbols } from "../StockMarket/data/StockSymbols"; +import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "../StockMarket/StockMarketCosts"; +import { Stock } from "../StockMarket/Stock"; + +export interface INetscriptStockMarket { + getStockSymbols(): any; + getStockPrice(symbol: any): any; + getStockAskPrice(symbol: any): any; + getStockBidPrice(symbol: any): any; + getStockPosition(symbol: any): any; + getStockMaxShares(symbol: any): any; + getStockPurchaseCost(symbol: any, shares: any, posType: any): any; + getStockSaleGain(symbol: any, shares: any, posType: any): any; + buyStock(symbol: any, shares: any): any; + sellStock(symbol: any, shares: any): any; + shortStock(symbol: any, shares: any): any; + sellShort(symbol: any, shares: any): any; + placeOrder(symbol: any, shares: any, price: any, type: any, pos: any): any; + cancelOrder(symbol: any, shares: any, price: any, type: any, pos: any): any; + getOrders(): any; + getStockVolatility(symbol: any): any; + getStockForecast(symbol: any): any; + purchase4SMarketData(): void; + purchase4SMarketDataTixApi(): void; +} + +export function NetscriptStockMarket( + player: IPlayer, + workerScript: WorkerScript, + helper: INetscriptHelper, +): INetscriptStockMarket { + /** + * Checks if the player has TIX API access. Throws an error if the player does not + */ + const checkTixApiAccess = function (callingFn: string): void { + if (!player.hasWseAccount) { + throw helper.makeRuntimeErrorMsg(callingFn, `You don't have WSE Access! Cannot use ${callingFn}()`); + } + if (!player.hasTixApiAccess) { + throw helper.makeRuntimeErrorMsg(callingFn, `You don't have TIX API Access! Cannot use ${callingFn}()`); + } + }; + + const getStockFromSymbol = function (symbol: string, callingFn: string): Stock { + const stock = SymbolToStockMap[symbol]; + if (stock == null) { + throw helper.makeRuntimeErrorMsg(callingFn, `Invalid stock symbol: '${symbol}'`); + } + + return stock; + }; + return { + getStockSymbols: function (): any { + helper.updateDynamicRam("getStockSymbols", getRamCost("getStockSymbols")); + checkTixApiAccess("getStockSymbols"); + return Object.values(StockSymbols); + }, + getStockPrice: function (symbol: any): any { + helper.updateDynamicRam("getStockPrice", getRamCost("getStockPrice")); + checkTixApiAccess("getStockPrice"); + const stock = getStockFromSymbol(symbol, "getStockPrice"); + + return stock.price; + }, + getStockAskPrice: function (symbol: any): any { + helper.updateDynamicRam("getStockAskPrice", getRamCost("getStockAskPrice")); + checkTixApiAccess("getStockAskPrice"); + const stock = getStockFromSymbol(symbol, "getStockAskPrice"); + + return stock.getAskPrice(); + }, + getStockBidPrice: function (symbol: any): any { + helper.updateDynamicRam("getStockBidPrice", getRamCost("getStockBidPrice")); + checkTixApiAccess("getStockBidPrice"); + const stock = getStockFromSymbol(symbol, "getStockBidPrice"); + + return stock.getBidPrice(); + }, + getStockPosition: function (symbol: any): any { + helper.updateDynamicRam("getStockPosition", getRamCost("getStockPosition")); + checkTixApiAccess("getStockPosition"); + const stock = SymbolToStockMap[symbol]; + if (stock == null) { + throw helper.makeRuntimeErrorMsg("getStockPosition", `Invalid stock symbol: ${symbol}`); + } + return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; + }, + getStockMaxShares: function (symbol: any): any { + helper.updateDynamicRam("getStockMaxShares", getRamCost("getStockMaxShares")); + checkTixApiAccess("getStockMaxShares"); + const stock = getStockFromSymbol(symbol, "getStockMaxShares"); + + return stock.maxShares; + }, + getStockPurchaseCost: function (symbol: any, shares: any, posType: any): any { + helper.updateDynamicRam("getStockPurchaseCost", getRamCost("getStockPurchaseCost")); + checkTixApiAccess("getStockPurchaseCost"); + const stock = getStockFromSymbol(symbol, "getStockPurchaseCost"); + shares = Math.round(shares); + + let pos; + const sanitizedPosType = posType.toLowerCase(); + if (sanitizedPosType.includes("l")) { + pos = PositionTypes.Long; + } else if (sanitizedPosType.includes("s")) { + pos = PositionTypes.Short; + } else { + return Infinity; + } + + const res = getBuyTransactionCost(stock, shares, pos); + if (res == null) { + return Infinity; + } + + return res; + }, + getStockSaleGain: function (symbol: any, shares: any, posType: any): any { + helper.updateDynamicRam("getStockSaleGain", getRamCost("getStockSaleGain")); + checkTixApiAccess("getStockSaleGain"); + const stock = getStockFromSymbol(symbol, "getStockSaleGain"); + shares = Math.round(shares); + + let pos; + const sanitizedPosType = posType.toLowerCase(); + if (sanitizedPosType.includes("l")) { + pos = PositionTypes.Long; + } else if (sanitizedPosType.includes("s")) { + pos = PositionTypes.Short; + } else { + return 0; + } + + const res = getSellTransactionGain(stock, shares, pos); + if (res == null) { + return 0; + } + + return res; + }, + buyStock: function (symbol: any, shares: any): any { + helper.updateDynamicRam("buyStock", getRamCost("buyStock")); + checkTixApiAccess("buyStock"); + const stock = getStockFromSymbol(symbol, "buyStock"); + const res = buyStock(stock, shares, workerScript, {}); + return res ? stock.price : 0; + }, + sellStock: function (symbol: any, shares: any): any { + helper.updateDynamicRam("sellStock", getRamCost("sellStock")); + checkTixApiAccess("sellStock"); + const stock = getStockFromSymbol(symbol, "sellStock"); + const res = sellStock(stock, shares, workerScript, {}); + + return res ? stock.price : 0; + }, + shortStock: function (symbol: any, shares: any): any { + helper.updateDynamicRam("shortStock", getRamCost("shortStock")); + checkTixApiAccess("shortStock"); + if (player.bitNodeN !== 8) { + if (player.sourceFileLvl(8) <= 1) { + throw helper.makeRuntimeErrorMsg( + "shortStock", + "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", + ); + } + } + const stock = getStockFromSymbol(symbol, "shortStock"); + const res = shortStock(stock, shares, workerScript, {}); + + return res ? stock.price : 0; + }, + sellShort: function (symbol: any, shares: any): any { + helper.updateDynamicRam("sellShort", getRamCost("sellShort")); + checkTixApiAccess("sellShort"); + if (player.bitNodeN !== 8) { + if (player.sourceFileLvl(8) <= 1) { + throw helper.makeRuntimeErrorMsg( + "sellShort", + "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", + ); + } + } + const stock = getStockFromSymbol(symbol, "sellShort"); + const res = sellShort(stock, shares, workerScript, {}); + + return res ? stock.price : 0; + }, + placeOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any { + helper.updateDynamicRam("placeOrder", getRamCost("placeOrder")); + checkTixApiAccess("placeOrder"); + if (player.bitNodeN !== 8) { + if (player.sourceFileLvl(8) <= 2) { + throw helper.makeRuntimeErrorMsg( + "placeOrder", + "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", + ); + } + } + const stock = getStockFromSymbol(symbol, "placeOrder"); + + let orderType; + let orderPos; + const ltype = type.toLowerCase(); + if (ltype.includes("limit") && ltype.includes("buy")) { + orderType = OrderTypes.LimitBuy; + } else if (ltype.includes("limit") && ltype.includes("sell")) { + orderType = OrderTypes.LimitSell; + } else if (ltype.includes("stop") && ltype.includes("buy")) { + orderType = OrderTypes.StopBuy; + } else if (ltype.includes("stop") && ltype.includes("sell")) { + orderType = OrderTypes.StopSell; + } else { + throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`); + } + + const lpos = pos.toLowerCase(); + if (lpos.includes("l")) { + orderPos = PositionTypes.Long; + } else if (lpos.includes("s")) { + orderPos = PositionTypes.Short; + } else { + throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`); + } + + return placeOrder(stock, shares, price, orderType, orderPos, workerScript); + }, + cancelOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any { + helper.updateDynamicRam("cancelOrder", getRamCost("cancelOrder")); + checkTixApiAccess("cancelOrder"); + if (player.bitNodeN !== 8) { + if (player.sourceFileLvl(8) <= 2) { + throw helper.makeRuntimeErrorMsg( + "cancelOrder", + "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", + ); + } + } + const stock = getStockFromSymbol(symbol, "cancelOrder"); + if (isNaN(shares) || isNaN(price)) { + throw helper.makeRuntimeErrorMsg( + "cancelOrder", + `Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`, + ); + } + let orderType; + let orderPos; + const ltype = type.toLowerCase(); + if (ltype.includes("limit") && ltype.includes("buy")) { + orderType = OrderTypes.LimitBuy; + } else if (ltype.includes("limit") && ltype.includes("sell")) { + orderType = OrderTypes.LimitSell; + } else if (ltype.includes("stop") && ltype.includes("buy")) { + orderType = OrderTypes.StopBuy; + } else if (ltype.includes("stop") && ltype.includes("sell")) { + orderType = OrderTypes.StopSell; + } else { + throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`); + } + + const lpos = pos.toLowerCase(); + if (lpos.includes("l")) { + orderPos = PositionTypes.Long; + } else if (lpos.includes("s")) { + orderPos = PositionTypes.Short; + } else { + throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`); + } + const params = { + stock: stock, + shares: shares, + price: price, + type: orderType, + pos: orderPos, + }; + return cancelOrder(params, workerScript); + }, + getOrders: function (): any { + helper.updateDynamicRam("getOrders", getRamCost("getOrders")); + checkTixApiAccess("getOrders"); + if (player.bitNodeN !== 8) { + if (player.sourceFileLvl(8) <= 2) { + throw helper.makeRuntimeErrorMsg( + "getOrders", + "You must either be in BitNode-8 or have Source-File 8 Level 3.", + ); + } + } + + const orders: any = {}; + + const stockMarketOrders = StockMarket["Orders"]; + for (const symbol in stockMarketOrders) { + const orderBook = stockMarketOrders[symbol]; + if (orderBook.constructor === Array && orderBook.length > 0) { + orders[symbol] = []; + for (let i = 0; i < orderBook.length; ++i) { + orders[symbol].push({ + shares: orderBook[i].shares, + price: orderBook[i].price, + type: orderBook[i].type, + position: orderBook[i].pos, + }); + } + } + } + + return orders; + }, + getStockVolatility: function (symbol: any): any { + helper.updateDynamicRam("getStockVolatility", getRamCost("getStockVolatility")); + if (!player.has4SDataTixApi) { + throw helper.makeRuntimeErrorMsg("getStockVolatility", "You don't have 4S Market Data TIX API Access!"); + } + const stock = getStockFromSymbol(symbol, "getStockVolatility"); + + return stock.mv / 100; // Convert from percentage to decimal + }, + getStockForecast: function (symbol: any): any { + helper.updateDynamicRam("getStockForecast", getRamCost("getStockForecast")); + if (!player.has4SDataTixApi) { + throw helper.makeRuntimeErrorMsg("getStockForecast", "You don't have 4S Market Data TIX API Access!"); + } + const stock = getStockFromSymbol(symbol, "getStockForecast"); + + let forecast = 50; + stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag); + return forecast / 100; // Convert from percentage to decimal + }, + purchase4SMarketData: function () { + helper.updateDynamicRam("purchase4SMarketData", getRamCost("purchase4SMarketData")); + checkTixApiAccess("purchase4SMarketData"); + + if (player.has4SData) { + workerScript.log("purchase4SMarketData", "Already purchased 4S Market Data."); + return true; + } + + if (player.money.lt(getStockMarket4SDataCost())) { + workerScript.log("purchase4SMarketData", "Not enough money to purchase 4S Market Data."); + return false; + } + + player.has4SData = true; + player.loseMoney(getStockMarket4SDataCost()); + workerScript.log("purchase4SMarketData", "Purchased 4S Market Data"); + return true; + }, + purchase4SMarketDataTixApi: function () { + helper.updateDynamicRam("purchase4SMarketDataTixApi", getRamCost("purchase4SMarketDataTixApi")); + checkTixApiAccess("purchase4SMarketDataTixApi"); + + if (player.has4SDataTixApi) { + workerScript.log("purchase4SMarketDataTixApi", "Already purchased 4S Market Data TIX API"); + return true; + } + + if (player.money.lt(getStockMarket4STixApiCost())) { + workerScript.log("purchase4SMarketDataTixApi", "Not enough money to purchase 4S Market Data TIX API"); + return false; + } + + player.has4SDataTixApi = true; + player.loseMoney(getStockMarket4STixApiCost()); + workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API"); + return true; + }, + }; +} diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 295decdd6..d71137f38 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -276,4 +276,5 @@ export interface IPlayer { setBitNodeNumber(n: number): void; getMult(name: string): number; setMult(name: string, mult: number): void; + sourceFileLvl(n: number): number; } diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index 175f27725..c19d9850f 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -283,6 +283,7 @@ export class PlayerObject implements IPlayer { setBitNodeNumber: (n: number) => void; getMult: (name: string) => number; setMult: (name: string, mult: number) => void; + sourceFileLvl: (n: number) => number; constructor() { //Skills and stats @@ -576,6 +577,7 @@ export class PlayerObject implements IPlayer { this.getMult = generalMethods.getMult; this.setMult = generalMethods.setMult; + this.sourceFileLvl = generalMethods.sourceFileLvl; } /** diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index c2057b5a4..62812dda8 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -2654,3 +2654,10 @@ export function setMult(this: IPlayer, name: string, mult: number): void { if (!this.hasOwnProperty(name)) return; (this as any)[name] = mult; } + +export function sourceFileLvl(this: IPlayer, n: number): number { + for (const sf of this.sourceFiles) { + if (sf.n === n) return sf.lvl; + } + return 0; +}