diff --git a/doc/source/basicgameplay/stockmarket.rst b/doc/source/basicgameplay/stockmarket.rst index fb47bf251..b84cd8894 100644 --- a/doc/source/basicgameplay/stockmarket.rst +++ b/doc/source/basicgameplay/stockmarket.rst @@ -73,13 +73,16 @@ be sold at $98.01, and so on. This is an important concept to keep in mind if you are trying to purchase/sell a large number of shares, as **it can negatively affect your profits**. +.. note:: On the Stock Market UI, the displayed profit of a position does not take + this "influencing" into account. + Transactions Influencing Stock Forecast ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In addition to influencing stock price, buying or selling a large number of shares of a stock will also influence that stock's "forecast". The forecast is the likelihood that the stock will increase or decrease in price. The magnitude of this effect depends on the number of shares being transacted. More shares will have a bigger effect on the -stock forecast. +stock forecast. .. _gameplay_stock_market_order_types: @@ -131,7 +134,36 @@ A Stop Order to buy will execute if the stock's price <= order's price A Stop Order to sell will execute if the stock's price >= order's price. +.. note:: Limit and Stop orders do **not** take into account the fact that + transactions can influence a stock's price. If a stock's price + changes mid-transaction, a limit/stop order will continue to execute + even if its conditions are no longer met. + Automating the Stock Market --------------------------- You can write scripts to perform automatic and algorithmic trading on the Stock Market. See :ref:`netscript_tixapi` for more details. + +Under the Hood +-------------- +Stock prices are updated very ~6 seconds. + +Whether a stock's price moves up or down is determined by RNG. However, +stocks have properties that can influence the way their price moves. These properties +are hidden, although some of them can be made visible by purchasing the +Four Sigma (4S) Market Data upgrade. Some examples of these properties are: + +* Volatility +* Likelihood of increasing or decreasing +* How easily a stock's price/forecast is influenced by transactions +* Spread percentage +* Maximum price (not a real maximum, more of a "soft cap") + +Each stock has its own unique values for these properties. + +Offline Progression +------------------- +The Stock Market does not change or process anything while the game has closed. +However, it does accumulate time when offline. This accumulated time allows +the stock market to run 50% faster when the game is opened again. This means +that stock prices will update every ~4 seconds instead of 6. diff --git a/src/Augmentation/AugmentationHelpers.js b/src/Augmentation/AugmentationHelpers.js index 37836a77a..ef7649a85 100644 --- a/src/Augmentation/AugmentationHelpers.js +++ b/src/Augmentation/AugmentationHelpers.js @@ -7,7 +7,6 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers" import { CONSTANTS } from "../Constants"; import { Factions, factionExists } from "../Faction/Factions"; -import { hasBladeburnerSF } from "../NetscriptFunctions"; import { addWorkerScript } from "../NetscriptWorker"; import { Player } from "../Player"; import { prestigeAugmentation } from "../Prestige"; @@ -2094,7 +2093,7 @@ function installAugmentations(cbScript=null) { } var runningScriptObj = new RunningScript(script, []); //No args runningScriptObj.threads = 1; //Only 1 thread - home.runScript(runningScriptObj, Player); + home.runScript(runningScriptObj, Player.hacknet_node_money_mult); addWorkerScript(runningScriptObj, home); } } diff --git a/src/Constants.ts b/src/Constants.ts index 3f836d321..07d52ee41 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -38,60 +38,6 @@ export let CONSTANTS: IMap = { // NeuroFlux Governor Augmentation cost multiplier NeuroFluxGovernorLevelMult: 1.14, - // RAM Costs for Netscript functions - ScriptBaseRamCost: 1.6, - ScriptDomRamCost: 25, - ScriptWhileRamCost: 0, - ScriptForRamCost: 0, - ScriptIfRamCost: 0, - ScriptHackRamCost: 0.1, - ScriptHackAnalyzeRamCost: 1, - ScriptGrowRamCost: 0.15, - ScriptGrowthAnalyzeRamCost: 1, - ScriptWeakenRamCost: 0.15, - ScriptScanRamCost: 0.2, - ScriptPortProgramRamCost: 0.05, - ScriptRunRamCost: 1.0, - ScriptExecRamCost: 1.3, - ScriptSpawnRamCost: 2.0, - ScriptScpRamCost: 0.6, - ScriptKillRamCost: 0.5, - ScriptHasRootAccessRamCost: 0.05, - ScriptGetHostnameRamCost: 0.05, - ScriptGetHackingLevelRamCost: 0.05, - ScriptGetMultipliersRamCost: 4.0, - ScriptGetServerRamCost: 0.1, - ScriptFileExistsRamCost: 0.1, - ScriptIsRunningRamCost: 0.1, - ScriptHacknetNodesRamCost: 4.0, - ScriptHNUpgLevelRamCost: 0.4, - ScriptHNUpgRamRamCost: 0.6, - ScriptHNUpgCoreRamCost: 0.8, - ScriptGetStockRamCost: 2.0, - ScriptBuySellStockRamCost: 2.5, - ScriptGetPurchaseServerRamCost: 0.25, - ScriptPurchaseServerRamCost: 2.25, - ScriptGetPurchasedServerLimit: 0.05, - ScriptGetPurchasedServerMaxRam: 0.05, - ScriptRoundRamCost: 0.05, - ScriptReadWriteRamCost: 1.0, - ScriptArbScriptRamCost: 1.0, - ScriptGetScriptRamCost: 0.1, - ScriptGetHackTimeRamCost: 0.05, - ScriptGetFavorToDonate: 0.10, - ScriptCodingContractBaseRamCost: 10, - ScriptSleeveBaseRamCost: 4, - - ScriptSingularityFn1RamCost: 1, - ScriptSingularityFn2RamCost: 2, - ScriptSingularityFn3RamCost: 3, - - ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4 - - ScriptGangApiBaseRamCost: 4, - - ScriptBladeburnerApiBaseRamCost: 4, - NumNetscriptPorts: 20, // Server-related constants @@ -287,6 +233,8 @@ export let CONSTANTS: IMap = { * Re-sleeves can no longer have the NeuroFlux Governor augmentation ** This is just a temporary patch until the mechanic gets re-worked + * Adjusted RAM costs of Netscript Singularity functions (mostly increased) + * Netscript Singularity functions no longer cost extra RAM outside of BitNode-4 * Corporation employees no longer have an "age" stat * Bug Fix: Corporation employees stats should no longer become negative * Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios diff --git a/src/Hacking/netscriptCanHack.ts b/src/Hacking/netscriptCanHack.ts index 875fd22f5..f2b8fc6a2 100644 --- a/src/Hacking/netscriptCanHack.ts +++ b/src/Hacking/netscriptCanHack.ts @@ -6,34 +6,35 @@ */ import { IReturnStatus } from "../types"; -import { HacknetServer } from "../Hacknet/HacknetServer"; +//import { HacknetServer } from "../Hacknet/HacknetServer"; import { IPlayer } from "../PersonObjects/IPlayer"; import { Server } from "../Server/Server"; -function baseCheck(server: Server | HacknetServer, fnName: string): IReturnStatus { - if (server instanceof HacknetServer) { +function baseCheck(server: Server, fnName: string): IReturnStatus { + const hostname = server.hostname; + + if (!("requiredHackingSkill" in server)) { return { res: false, - msg: `Cannot ${fnName} ${server.hostname} server because it is a Hacknet Node` + msg: `Cannot ${fnName} ${hostname} server because it is a Hacknet Node` } } if (server.hasAdminRights === false) { return { res: false, - msg: `Cannot ${fnName} ${server.hostname} server because you do not have root access`, + msg: `Cannot ${fnName} ${hostname} server because you do not have root access`, } } return { res: true } } -export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IReturnStatus { +export function netscriptCanHack(server: Server, p: IPlayer): IReturnStatus { const initialCheck = baseCheck(server, "hack"); if (!initialCheck.res) { return initialCheck; } - let s = server; - + let s = server; if (s.requiredHackingSkill > p.hacking_skill) { return { res: false, @@ -44,10 +45,10 @@ export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IR return { res: true } } -export function netscriptCanGrow(server: Server | HacknetServer): IReturnStatus { +export function netscriptCanGrow(server: Server): IReturnStatus { return baseCheck(server, "grow"); } -export function netscriptCanWeaken(server: Server | HacknetServer): IReturnStatus { +export function netscriptCanWeaken(server: Server): IReturnStatus { return baseCheck(server, "weaken"); } diff --git a/src/Hacknet/HacknetHelpers.jsx b/src/Hacknet/HacknetHelpers.jsx index a426a28a3..fedc4361c 100644 --- a/src/Hacknet/HacknetHelpers.jsx +++ b/src/Hacknet/HacknetHelpers.jsx @@ -1,35 +1,41 @@ -import { HacknetNode, - BaseCostForHacknetNode, - HacknetNodePurchaseNextMult, - HacknetNodeMaxLevel, - HacknetNodeMaxRam, - HacknetNodeMaxCores } from "./HacknetNode"; -import { HacknetServer, - BaseCostForHacknetServer, - HacknetServerPurchaseMult, - HacknetServerMaxLevel, - HacknetServerMaxRam, - HacknetServerMaxCores, - HacknetServerMaxCache, - MaxNumberHacknetServers } from "./HacknetServer"; -import { HashManager } from "./HashManager"; -import { HashUpgrades } from "./HashUpgrades"; +import { + HacknetNode, + BaseCostForHacknetNode, + HacknetNodePurchaseNextMult, + HacknetNodeMaxLevel, + HacknetNodeMaxRam, + HacknetNodeMaxCores +} from "./HacknetNode"; +import { + HacknetServer, + BaseCostForHacknetServer, + HacknetServerPurchaseMult, + HacknetServerMaxLevel, + HacknetServerMaxRam, + HacknetServerMaxCores, + HacknetServerMaxCache, + MaxNumberHacknetServers +} from "./HacknetServer"; +import { HashManager } from "./HashManager"; +import { HashUpgrades } from "./HashUpgrades"; -import { generateRandomContract } from "../CodingContractGenerator"; -import { iTutorialSteps, iTutorialNextStep, - ITutorial} from "../InteractiveTutorial"; -import { Player } from "../Player"; -import { AddToAllServers, - AllServers } from "../Server/AllServers"; -import { GetServerByHostname } from "../Server/ServerHelpers"; -import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; -import { Page, routing } from "../ui/navigationTracking"; +import { generateRandomContract } from "../CodingContractGenerator"; +import { + iTutorialSteps, + iTutorialNextStep, + ITutorial +} from "../InteractiveTutorial"; +import { Player } from "../Player"; +import { AddToAllServers, AllServers } from "../Server/AllServers"; +import { GetServerByHostname } from "../Server/ServerHelpers"; +import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; +import { Page, routing } from "../ui/navigationTracking"; -import {getElementById} from "../../utils/uiHelpers/getElementById"; +import { getElementById } from "../../utils/uiHelpers/getElementById"; -import React from "react"; -import ReactDOM from "react-dom"; -import { HacknetRoot } from "./ui/Root"; +import React from "react"; +import ReactDOM from "react-dom"; +import { HacknetRoot } from "./ui/Root"; let hacknetNodesDiv; function hacknetNodesInit() { @@ -66,7 +72,7 @@ export function purchaseHacknet() { if (!Player.canAfford(cost)) { return -1; } Player.loseMoney(cost); const server = Player.createHacknetServer(); - Player.hashManager.updateCapacity(Player); + Player.hashManager.updateCapacity(Player.hacknetNodes); return numOwned; } else { @@ -79,8 +85,7 @@ export function purchaseHacknet() { // Auto generate a name for the Node const name = "hacknet-node-" + numOwned; - const node = new HacknetNode(name); - node.updateMoneyGainRate(Player); + const node = new HacknetNode(name, Player.hacknet_node_money_mult); Player.loseMoney(cost); Player.hacknetNodes.push(node); @@ -116,26 +121,26 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player))) { + if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player.hacknet_node_level_cost_mult))) { return 0; } let min = 1; let max = maxLevel - 1; let levelsToMax = maxLevel - nodeObj.level; - if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player))) { + if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.hacknet_node_level_cost_mult))) { return levelsToMax; } while (min <= max) { var curr = (min + max) / 2 | 0; if (curr !== maxLevel && - Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player)) && - Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player))) { + Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult)) && + Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player.hacknet_node_level_cost_mult))) { return Math.min(levelsToMax, curr); - } else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player))) { + } else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) { max = curr - 1; - } else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player))) { + } else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) { min = curr + 1; } else { return Math.min(levelsToMax, curr); @@ -149,7 +154,7 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player))) { + if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player.hacknet_node_ram_cost_mult))) { return 0; } @@ -159,13 +164,13 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { } else { levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram)); } - if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player))) { + if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player.hacknet_node_ram_cost_mult))) { return levelsToMax; } //We'll just loop until we find the max for (let i = levelsToMax-1; i >= 0; --i) { - if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player))) { + if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player.hacknet_node_ram_cost_mult))) { return i; } } @@ -177,14 +182,14 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player))) { + if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player.hacknet_node_core_cost_mult))) { return 0; } let min = 1; let max = maxLevel - 1; const levelsToMax = maxLevel - nodeObj.cores; - if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player))) { + if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.hacknet_node_core_cost_mult))) { return levelsToMax; } @@ -192,12 +197,12 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { while (min <= max) { let curr = (min + max) / 2 | 0; if (curr != maxLevel && - Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player)) && - Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player))) { + Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult)) && + Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player.hacknet_node_core_cost_mult))) { return Math.min(levelsToMax, curr); - } else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player))) { + } else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) { max = curr - 1; - } else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player))) { + } else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) { min = curr + 1; } else { return Math.min(levelsToMax, curr); @@ -242,7 +247,134 @@ export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { return 0; } -// Initial construction of Hacknet Nodes UI +export function purchaseLevelUpgrade(node, levels=1) { + const sanitizedLevels = Math.round(levels); + const cost = node.calculateLevelUpgradeCost(sanitizedLevels, Player.hacknet_node_level_cost_mult); + if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { + return false; + } + + const isServer = (node instanceof HacknetServer); + + // If we're at max level, return false + if (node.level >= (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) { + return false; + } + + // If the number of specified upgrades would exceed the max level, calculate + // the maximum number of upgrades and use that + if (node.level + sanitizedLevels > (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) { + const diff = Math.max(0, (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel) - node.level); + return purchaseLevelUpgrade(node, diff); + } + + if (!Player.canAfford(cost)) { + return false; + } + + Player.loseMoney(cost); + node.upgradeLevel(sanitizedLevels, Player.hacknet_node_money_mult); + + return true; +} + +export function purchaseRamUpgrade(node, levels=1) { + const sanitizedLevels = Math.round(levels); + const cost = node.calculateRamUpgradeCost(sanitizedLevels, Player.hacknet_node_ram_cost_mult); + if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { + return false; + } + + const isServer = (node instanceof HacknetServer); + + // Fail if we're already at max + if (node.ram >= (isServer ? HacknetServerMaxRam : HacknetNodeMaxRam)) { + return false; + } + + // If the number of specified upgrades would exceed the max RAM, calculate the + // max possible number of upgrades and use that + if (isServer) { + if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) { + const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / node.maxRam))); + return purchaseRamUpgrade(node, diff); + } + } else { + if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) { + const diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / node.ram))); + return purchaseRamUpgrade(node, diff); + } + } + + + if (!Player.canAfford(cost)) { + return false; + } + + Player.loseMoney(cost); + node.upgradeRam(sanitizedLevels, Player.hacknet_node_money_mult); + + return true; +} + +export function purchaseCoreUpgrade(node, levels=1) { + const sanitizedLevels = Math.round(levels); + const cost = node.calculateCoreUpgradeCost(sanitizedLevels, Player.hacknet_node_core_cost_mult); + if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { + return false; + } + + const isServer = (node instanceof HacknetServer); + + // Fail if we're already at max + if (node.cores >= (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) { + return false; + } + + // If the specified number of upgrades would exceed the max Cores, calculate + // the max possible number of upgrades and use that + if (node.cores + sanitizedLevels > (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) { + const diff = Math.max(0, (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores) - node.cores); + return purchaseCoreUpgrade(node, diff); + } + + if (!Player.canAfford(cost)) { + return false; + } + + Player.loseMoney(cost); + node.upgradeCore(sanitizedLevels, Player.hacknet_node_money_mult); + + return true; +} + +export function purchaseCacheUpgrade(node, levels=1) { + const sanitizedLevels = Math.round(levels); + const cost = node.calculateCoreUpgradeCost(sanitizedLevels); + if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { + return false; + } + + if (!(node instanceof HacknetServer)) { + console.warn(`purchaseCacheUpgrade() called for a non-HacknetNode`); + return false; + } + + // Fail if we're already at max + if (node.cache + sanitizedLevels > HacknetServerMaxCache) { + const diff = Math.max(0, HacknetServerMaxCache - node.cache); + return purchaseCacheUpgrade(node, diff); + } + + if (!Player.canAfford(cost)) { + return false; + } + + Player.loseMoney(cost); + node.upgradeCache(sanitizedLevels); +} + +// Create/Refresh Hacknet Nodes UI export function renderHacknetNodesUI() { if (!routing.isOn(Page.HacknetNodes)) { return; } @@ -303,6 +435,37 @@ function processAllHacknetServerEarnings(numCycles) { return hashes; } +export function updateHashManagerCapacity() { + if (!(Player.hashManager instanceof HashManager)) { + console.error(`Player does not have a HashManager`); + return; + } + + const nodes = Player.hacknetNodes; + if (nodes.length === 0) { + Player.hashManager.updateCapacity(0); + return; + } + + let total = 0; + for (let i = 0; i < nodes.length; ++i) { + if (typeof nodes[i] !== "string") { + Player.hashManager.updateCapacity(0); + return; + } + + const h = AllServers[nodes[i]]; + if (!(h instanceof HacknetServer)) { + Player.hashManager.updateCapacity(0); + return; + } + + total += h.hashCapacity; + } + + Player.hashManager.updateCapacity(total); +} + export function purchaseHashUpgrade(upgName, upgTarget) { if (!(Player.hashManager instanceof HashManager)) { console.error(`Player does not have a HashManager`); diff --git a/src/Hacknet/HacknetNode.ts b/src/Hacknet/HacknetNode.ts index 51b9b2025..fe1d94615 100644 --- a/src/Hacknet/HacknetNode.ts +++ b/src/Hacknet/HacknetNode.ts @@ -62,12 +62,14 @@ export class HacknetNode implements IHacknetNode { // Total money earned by this Node totalMoneyGenerated: number = 0; - constructor(name: string="") { + constructor(name: string="", prodMult: number=1) { this.name = name; + + this.updateMoneyGainRate(prodMult); } // Get the cost to upgrade this Node's number of cores - calculateCoreUpgradeCost(levels: number=1, p: IPlayer): number { + calculateCoreUpgradeCost(levels: number=1, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -86,13 +88,13 @@ export class HacknetNode implements IHacknetNode { ++currentCores; } - totalCost *= p.hacknet_node_core_cost_mult; + totalCost *= costMult; return totalCost; } // Get the cost to upgrade this Node's level - calculateLevelUpgradeCost(levels: number=1, p: IPlayer): number { + calculateLevelUpgradeCost(levels: number=1, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -110,11 +112,11 @@ export class HacknetNode implements IHacknetNode { ++currLevel; } - return BaseCostForHacknetNode / 2 * totalMultiplier * p.hacknet_node_level_cost_mult; + return BaseCostForHacknetNode / 2 * totalMultiplier * costMult; } // Get the cost to upgrade this Node's RAM - calculateRamUpgradeCost(levels: number=1, p: IPlayer): number { + calculateRamUpgradeCost(levels: number=1, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -138,7 +140,7 @@ export class HacknetNode implements IHacknetNode { ++numUpgrades; } - totalCost *= p.hacknet_node_ram_cost_mult; + totalCost *= costMult; return totalCost; } @@ -161,112 +163,37 @@ export class HacknetNode implements IHacknetNode { // Upgrade this Node's number of cores, if possible // Returns a boolean indicating whether new cores were successfully bought - purchaseCoreUpgrade(levels: number=1, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p); - if (isNaN(cost) || sanitizedLevels < 0) { - return false; - } - - // Fail if we're already at max - if (this.cores >= HacknetNodeMaxCores) { - return false; - } - - // If the specified number of upgrades would exceed the max Cores, calculate - // the max possible number of upgrades and use that - if (this.cores + sanitizedLevels > HacknetNodeMaxCores) { - const diff = Math.max(0, HacknetNodeMaxCores - this.cores); - return this.purchaseCoreUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - this.cores = Math.round(this.cores + sanitizedLevels); // Just in case of floating point imprecision - this.updateMoneyGainRate(p); - - return true; + upgradeCore(levels: number=1, prodMult: number): void { + this.cores = Math.min(HacknetNodeMaxCores, Math.round(this.cores + levels)); + this.updateMoneyGainRate(prodMult); } // Upgrade this Node's level, if possible // Returns a boolean indicating whether the level was successfully updated - purchaseLevelUpgrade(levels: number=1, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p); - if (isNaN(cost) || sanitizedLevels < 0) { - return false; - } - - // If we're at max level, return false - if (this.level >= HacknetNodeMaxLevel) { - return false; - } - - // If the number of specified upgrades would exceed the max level, calculate - // the maximum number of upgrades and use that - if (this.level + sanitizedLevels > HacknetNodeMaxLevel) { - var diff = Math.max(0, HacknetNodeMaxLevel - this.level); - return this.purchaseLevelUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - this.level = Math.round(this.level + sanitizedLevels); // Just in case of floating point imprecision - this.updateMoneyGainRate(p); - - return true; + upgradeLevel(levels: number=1, prodMult: number): void { + this.level = Math.min(HacknetNodeMaxLevel, Math.round(this.level + levels)); + this.updateMoneyGainRate(prodMult); } // Upgrade this Node's RAM, if possible // Returns a boolean indicating whether the RAM was successfully upgraded - purchaseRamUpgrade(levels: number=1, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateRamUpgradeCost(sanitizedLevels, p); - if (isNaN(cost) || sanitizedLevels < 0) { - return false; - } - - // Fail if we're already at max - if (this.ram >= HacknetNodeMaxRam) { - return false; - } - - // If the number of specified upgrades would exceed the max RAM, calculate the - // max possible number of upgrades and use that - if (this.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) { - var diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / this.ram))); - return this.purchaseRamUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - for (let i = 0; i < sanitizedLevels; ++i) { + upgradeRam(levels: number=1, prodMult: number): void { + for (let i = 0; i < levels; ++i) { this.ram *= 2; // Ram is always doubled } this.ram = Math.round(this.ram); // Handle any floating point precision issues - this.updateMoneyGainRate(p); - - return true; + this.updateMoneyGainRate(prodMult); } // Re-calculate this Node's production and update the moneyGainRatePerSecond prop - updateMoneyGainRate(p: IPlayer): void { + updateMoneyGainRate(prodMult: number): void { //How much extra $/s is gained per level var gainPerLevel = HacknetNodeMoneyGainPerLevel; this.moneyGainRatePerSecond = (this.level * gainPerLevel) * Math.pow(1.035, this.ram - 1) * ((this.cores + 5) / 6) * - p.hacknet_node_money_mult * + prodMult * BitNodeMultipliers.HacknetNodeMoney; if (isNaN(this.moneyGainRatePerSecond)) { this.moneyGainRatePerSecond = 0; diff --git a/src/Hacknet/HacknetServer.ts b/src/Hacknet/HacknetServer.ts index 7d85243a9..1c0d42c60 100644 --- a/src/Hacknet/HacknetServer.ts +++ b/src/Hacknet/HacknetServer.ts @@ -6,15 +6,16 @@ import { CONSTANTS } from "../Constants"; import { IHacknetNode } from "./IHacknetNode"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { IPlayer } from "../PersonObjects/IPlayer"; import { BaseServer } from "../Server/BaseServer"; import { RunningScript } from "../Script/RunningScript"; import { createRandomIp } from "../../utils/IPAddress"; -import { Generic_fromJSON, - Generic_toJSON, - Reviver } from "../../utils/JSONReviver"; +import { + Generic_fromJSON, + Generic_toJSON, + Reviver +} from "../../utils/JSONReviver"; // Constants for Hacknet Server stats/production export const HacknetServerHashesPerLevel: number = 0.001; @@ -46,7 +47,6 @@ interface IConstructorParams { isConnectedTo?: boolean; maxRam?: number; organizationName?: string; - player?: IPlayer; } export class HacknetServer extends BaseServer implements IHacknetNode { @@ -81,10 +81,6 @@ export class HacknetServer extends BaseServer implements IHacknetNode { this.maxRam = 1; this.updateHashCapacity(); - - if (params.player) { - this.updateHashRate(params.player); - } } calculateCacheUpgradeCost(levels: number): number { @@ -109,7 +105,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode { return totalCost; } - calculateCoreUpgradeCost(levels: number, p: IPlayer): number { + calculateCoreUpgradeCost(levels: number, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -127,12 +123,12 @@ export class HacknetServer extends BaseServer implements IHacknetNode { ++currentCores; } totalCost *= BaseCostForHacknetServerCore; - totalCost *= p.hacknet_node_core_cost_mult; + totalCost *= costMult; return totalCost; } - calculateLevelUpgradeCost(levels: number, p: IPlayer): number { + calculateLevelUpgradeCost(levels: number, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -150,10 +146,10 @@ export class HacknetServer extends BaseServer implements IHacknetNode { ++currLevel; } - return 10 * BaseCostForHacknetServer * totalMultiplier * p.hacknet_node_level_cost_mult; + return 10 * BaseCostForHacknetServer * totalMultiplier * costMult; } - calculateRamUpgradeCost(levels: number, p: IPlayer): number { + calculateRamUpgradeCost(levels: number, costMult: number): number { const sanitizedLevels = Math.round(levels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -175,7 +171,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode { currentRam *= 2; ++numUpgrades; } - totalCost *= p.hacknet_node_ram_cost_mult; + totalCost *= costMult; return totalCost; } @@ -189,124 +185,30 @@ export class HacknetServer extends BaseServer implements IHacknetNode { } // Returns a boolean indicating whether the cache was successfully upgraded - purchaseCacheUpgrade(levels: number, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateCacheUpgradeCost(levels); - if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) { - return false; - } - - if (this.cache >= HacknetServerMaxCache) { - return false; - } - - // If the specified number of upgrades would exceed the max, try to purchase - // the maximum possible - if (this.cache + levels > HacknetServerMaxCache) { - const diff = Math.max(0, HacknetServerMaxCache - this.cache); - return this.purchaseCacheUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - this.cache = Math.round(this.cache + sanitizedLevels); + upgradeCache(levels: number): void { + this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels)); this.updateHashCapacity(); - - return true; } // Returns a boolean indicating whether the number of cores was successfully upgraded - purchaseCoreUpgrade(levels: number, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p); - if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) { - return false; - } - - if (this.cores >= HacknetServerMaxCores) { - return false; - } - - // If the specified number of upgrades would exceed the max, try to purchase - // the maximum possible - if (this.cores + sanitizedLevels > HacknetServerMaxCores) { - const diff = Math.max(0, HacknetServerMaxCores - this.cores); - return this.purchaseCoreUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - this.cores = Math.round(this.cores + sanitizedLevels); - this.updateHashRate(p); - - return true; + upgradeCore(levels: number, prodMult: number): void { + this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels)); + this.updateHashRate(prodMult); } // Returns a boolean indicating whether the level was successfully upgraded - purchaseLevelUpgrade(levels: number, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p); - if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) { - return false; - } - - if (this.level >= HacknetServerMaxLevel) { - return false; - } - - // If the specified number of upgrades would exceed the max, try to purchase the - // maximum possible - if (this.level + sanitizedLevels > HacknetServerMaxLevel) { - const diff = Math.max(0, HacknetServerMaxLevel - this.level); - return this.purchaseLevelUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - this.level = Math.round(this.level + sanitizedLevels); - this.updateHashRate(p); - - return true; + upgradeLevel(levels: number, prodMult: number): void { + this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels)); + this.updateHashRate(prodMult); } // Returns a boolean indicating whether the RAM was successfully upgraded - purchaseRamUpgrade(levels: number, p: IPlayer): boolean { - const sanitizedLevels = Math.round(levels); - const cost = this.calculateRamUpgradeCost(sanitizedLevels, p); - if(isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) { - return false; - } - - if (this.maxRam >= HacknetServerMaxRam) { - return false; - } - - // If the specified number of upgrades would exceed the max, try to purchase - // just the maximum possible - if (this.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) { - const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / this.maxRam))); - return this.purchaseRamUpgrade(diff, p); - } - - if (!p.canAfford(cost)) { - return false; - } - - p.loseMoney(cost); - for (let i = 0; i < sanitizedLevels; ++i) { + upgradeRam(levels: number, prodMult: number): boolean { + for (let i = 0; i < levels; ++i) { this.maxRam *= 2; } - this.maxRam = Math.round(this.maxRam); - this.updateHashRate(p); + this.maxRam = Math.min(HacknetServerMaxRam, Math.round(this.maxRam)); + this.updateHashRate(prodMult); return true; } @@ -314,10 +216,10 @@ export class HacknetServer extends BaseServer implements IHacknetNode { /** * Whenever a script is run, we must update this server's hash rate */ - runScript(script: RunningScript, p?: IPlayer): void { + runScript(script: RunningScript, prodMult?: number): void { super.runScript(script); - if (p) { - this.updateHashRate(p); + if (prodMult != null && typeof prodMult === "number") { + this.updateHashRate(prodMult); } } @@ -325,7 +227,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode { this.hashCapacity = 32 * Math.pow(2, this.cache); } - updateHashRate(p: IPlayer): void { + updateHashRate(prodMult: number): void { const baseGain = HacknetServerHashesPerLevel * this.level; const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam)); const coreMultiplier = 1 + (this.cores - 1) / 5; @@ -333,7 +235,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode { const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio; - this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney; + this.hashRate = hashRate * prodMult * BitNodeMultipliers.HacknetNodeMoney; if (isNaN(this.hashRate)) { this.hashRate = 0; diff --git a/src/Hacknet/HashManager.ts b/src/Hacknet/HashManager.ts index 11417a2a1..1cadef89d 100644 --- a/src/Hacknet/HashManager.ts +++ b/src/Hacknet/HashManager.ts @@ -6,12 +6,9 @@ * his hashes, and contains method for grabbing the data/multipliers from * those upgrades */ -import { HacknetServer } from "./HacknetServer"; import { HashUpgrades } from "./HashUpgrades"; import { IMap } from "../types"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { AllServers } from "../Server/AllServers"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; @@ -84,14 +81,14 @@ export class HashManager { return upg.getCost(currLevel); } - prestige(p: IPlayer): void { + prestige(): void { for (const name in HashUpgrades) { this.upgrades[name] = 0; } this.hashes = 0; - if (p != null) { - this.updateCapacity(p); - } + + // When prestiging, player's hacknet nodes are always reset. So capacity = 0 + this.updateCapacity(0); } /** @@ -118,33 +115,11 @@ export class HashManager { this.hashes = Math.min(this.hashes, this.capacity); } - updateCapacity(p: IPlayer): void { - if (p.hacknetNodes.length <= 0) { + updateCapacity(newCap: number): void { + if (newCap < 0) { this.capacity = 0; - return; } - - // Make sure the Player's `hacknetNodes` property actually holds Hacknet Servers - const ip: string = p.hacknetNodes[0]; - if (typeof ip !== "string") { - this.capacity = 0; - return; - } - - const hserver = AllServers[ip]; - if (!(hserver instanceof HacknetServer)) { - this.capacity = 0; - return; - } - - - let total: number = 0; - for (let i = 0; i < p.hacknetNodes.length; ++i) { - const h = AllServers[p.hacknetNodes[i]]; - total += h.hashCapacity; - } - - this.capacity = total; + this.capacity = Math.max(newCap, 0); } /** diff --git a/src/Hacknet/IHacknetNode.ts b/src/Hacknet/IHacknetNode.ts index 32ec81f53..5f00ffee1 100644 --- a/src/Hacknet/IHacknetNode.ts +++ b/src/Hacknet/IHacknetNode.ts @@ -1,16 +1,14 @@ // Interface for a Hacknet Node. Implemented by both a basic Hacknet Node, // and the upgraded Hacknet Server in BitNode-9 -import { IPlayer } from "../PersonObjects/IPlayer"; - export interface IHacknetNode { cores: number; level: number; onlineTimeSeconds: number; - calculateCoreUpgradeCost: (levels: number, p: IPlayer) => number; - calculateLevelUpgradeCost: (levels: number, p: IPlayer) => number; - calculateRamUpgradeCost: (levels: number, p: IPlayer) => number; - purchaseCoreUpgrade: (levels: number, p: IPlayer) => boolean; - purchaseLevelUpgrade: (levels: number, p: IPlayer) => boolean; - purchaseRamUpgrade: (levels: number, p: IPlayer) => boolean; + calculateCoreUpgradeCost: (levels: number, costMult: number) => number; + calculateLevelUpgradeCost: (levels: number, costMult: number) => number; + calculateRamUpgradeCost: (levels: number, costMult: number) => number; + upgradeCore: (levels: number, prodMult: number) => void; + upgradeLevel: (levels: number, prodMult: number) => void; + upgradeRam: (levels: number, prodMult: number) => void; } diff --git a/src/Hacknet/ui/HacknetNode.jsx b/src/Hacknet/ui/HacknetNode.jsx index 6ba40d09a..5b6369adc 100644 --- a/src/Hacknet/ui/HacknetNode.jsx +++ b/src/Hacknet/ui/HacknetNode.jsx @@ -4,12 +4,19 @@ */ import React from "react"; -import { HacknetNodeMaxLevel, - HacknetNodeMaxRam, - HacknetNodeMaxCores } from "../HacknetNode"; -import { getMaxNumberLevelUpgrades, - getMaxNumberRamUpgrades, - getMaxNumberCoreUpgrades } from "../HacknetHelpers"; +import { + HacknetNodeMaxLevel, + HacknetNodeMaxRam, + HacknetNodeMaxCores +} from "../HacknetNode"; +import { + getMaxNumberLevelUpgrades, + getMaxNumberRamUpgrades, + getMaxNumberCoreUpgrades, + purchaseLevelUpgrade, + purchaseRamUpgrade, + purchaseCoreUpgrade, +} from "../HacknetHelpers"; import { Player } from "../../Player"; @@ -35,7 +42,7 @@ export class HacknetNode extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player); + const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; if (Player.money.lt(upgradeLevelCost)) { upgradeLevelClass = "std-button-disabled"; @@ -48,7 +55,7 @@ export class HacknetNode extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel); } - node.purchaseLevelUpgrade(numUpgrades, Player); + purchaseLevelUpgrade(node, numUpgrades); recalculate(); return false; } @@ -66,7 +73,7 @@ export class HacknetNode extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player); + const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; if (Player.money.lt(upgradeRamCost)) { upgradeRamClass = "std-button-disabled"; @@ -79,7 +86,7 @@ export class HacknetNode extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam); } - node.purchaseRamUpgrade(numUpgrades, Player); + purchaseRamUpgrade(node, numUpgrades); recalculate(); return false; } @@ -97,7 +104,7 @@ export class HacknetNode extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player); + const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; if (Player.money.lt(upgradeCoreCost)) { upgradeCoresClass = "std-button-disabled"; @@ -110,7 +117,7 @@ export class HacknetNode extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores); } - node.purchaseCoreUpgrade(numUpgrades, Player); + purchaseCoreUpgrade(node, numUpgrades); recalculate(); return false; } diff --git a/src/Hacknet/ui/HacknetServer.jsx b/src/Hacknet/ui/HacknetServer.jsx index 5e69dc387..c03415d28 100644 --- a/src/Hacknet/ui/HacknetServer.jsx +++ b/src/Hacknet/ui/HacknetServer.jsx @@ -4,14 +4,23 @@ */ import React from "react"; -import { HacknetServerMaxLevel, - HacknetServerMaxRam, - HacknetServerMaxCores, - HacknetServerMaxCache } from "../HacknetServer"; -import { getMaxNumberLevelUpgrades, - getMaxNumberRamUpgrades, - getMaxNumberCoreUpgrades, - getMaxNumberCacheUpgrades } from "../HacknetHelpers"; +import { + HacknetServerMaxLevel, + HacknetServerMaxRam, + HacknetServerMaxCores, + HacknetServerMaxCache +} from "../HacknetServer"; +import { + getMaxNumberLevelUpgrades, + getMaxNumberRamUpgrades, + getMaxNumberCoreUpgrades, + getMaxNumberCacheUpgrades, + purchaseLevelUpgrade, + purchaseRamUpgrade, + purchaseCoreUpgrade, + purchaseCacheUpgrade, + updateHashManagerCapacity, +} from "../HacknetHelpers"; import { Player } from "../../Player"; @@ -37,7 +46,7 @@ export class HacknetServer extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player); + const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; if (Player.money.lt(upgradeLevelCost)) { upgradeLevelClass = "std-button-disabled"; @@ -50,7 +59,7 @@ export class HacknetServer extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel); } - node.purchaseLevelUpgrade(numUpgrades, Player); + purchaseLevelUpgrade(node, numUpgrades); recalculate(); return false; } @@ -69,7 +78,7 @@ export class HacknetServer extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player); + const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; if (Player.money.lt(upgradeRamCost)) { upgradeRamClass = "std-button-disabled"; @@ -82,7 +91,7 @@ export class HacknetServer extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam); } - node.purchaseRamUpgrade(numUpgrades, Player); + purchaseRamUpgrade(node, numUpgrades); recalculate(); return false; } @@ -101,7 +110,7 @@ export class HacknetServer extends React.Component { multiplier = Math.min(levelsToMax, purchaseMult); } - const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player); + const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; if (Player.money.lt(upgradeCoreCost)) { upgradeCoresClass = "std-button-disabled"; @@ -114,7 +123,7 @@ export class HacknetServer extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores); } - node.purchaseCoreUpgrade(numUpgrades, Player); + purchaseCoreUpgrade(node, numUpgrades); recalculate(); return false; } @@ -146,9 +155,9 @@ export class HacknetServer extends React.Component { if (purchaseMult === "MAX") { numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache); } - node.purchaseCacheUpgrade(numUpgrades, Player); + purchaseCacheUpgrade(node, numUpgrades); recalculate(); - Player.hashManager.updateCapacity(Player); + updateHashManagerCapacity(); return false; } diff --git a/src/Locations/LocationsHelpers.ts b/src/Locations/LocationsHelpers.ts index 69943e0b4..f50a27c2a 100644 --- a/src/Locations/LocationsHelpers.ts +++ b/src/Locations/LocationsHelpers.ts @@ -2,37 +2,42 @@ * Location and traveling-related helper functions. * Mostly used for UI */ -import { CONSTANTS } from "../Constants"; +import { CONSTANTS } from "../Constants"; -import { CityName } from "./data/CityNames"; +import { CityName } from "./data/CityNames"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { AllServers, - AddToAllServers } from "../Server/AllServers"; -import { Server } from "../Server/Server"; -import { getPurchaseServerCost, - purchaseRamForHomeComputer, - purchaseServer } from "../Server/ServerPurchases"; -import { SpecialServerIps } from "../Server/SpecialServerIps"; -import { Settings } from "../Settings/Settings"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { + AddToAllServers, + createUniqueRandomIp, +} from "../Server/AllServers"; +import { safetlyCreateUniqueServer } from "../Server/ServerHelpers"; +import { + getPurchaseServerCost, + purchaseRamForHomeComputer, + purchaseServer +} from "../Server/ServerPurchases"; +import { SpecialServerIps } from "../Server/SpecialServerIps"; +import { Settings } from "../Settings/Settings"; -import { numeralWrapper } from "../ui/numeralFormat"; +import { numeralWrapper } from "../ui/numeralFormat"; -import { dialogBoxCreate } from "../../utils/DialogBox"; -import { createRandomIp } from "../../utils/IPAddress"; -import { yesNoBoxGetYesButton, - yesNoBoxGetNoButton, - yesNoBoxClose, - yesNoBoxCreate, - yesNoTxtInpBoxGetYesButton, - yesNoTxtInpBoxGetNoButton, - yesNoTxtInpBoxClose, - yesNoTxtInpBoxCreate } from "../../utils/YesNoBox"; +import { dialogBoxCreate } from "../../utils/DialogBox"; +import { + yesNoBoxGetYesButton, + yesNoBoxGetNoButton, + yesNoBoxClose, + yesNoBoxCreate, + yesNoTxtInpBoxGetYesButton, + yesNoTxtInpBoxGetNoButton, + yesNoTxtInpBoxClose, + yesNoTxtInpBoxCreate +} from "../../utils/YesNoBox"; -import { createElement } from "../../utils/uiHelpers/createElement"; -import { createPopup } from "../../utils/uiHelpers/createPopup"; -import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton"; -import { removeElementById } from "../../utils/uiHelpers/removeElementById"; +import { createElement } from "../../utils/uiHelpers/createElement"; +import { createPopup } from "../../utils/uiHelpers/createPopup"; +import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton"; +import { removeElementById } from "../../utils/uiHelpers/removeElementById"; /** * Create a pop-up box that lets the player confirm traveling to a different city @@ -271,8 +276,8 @@ export function purchaseTorRouter(p: IPlayer) { } p.loseMoney(CONSTANTS.TorRouterCost); - const darkweb = new Server({ - ip: createRandomIp(), hostname:"darkweb", organizationName:"", + const darkweb = safetlyCreateUniqueServer({ + ip: createUniqueRandomIp(), hostname:"darkweb", organizationName:"", isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1 }); AddToAllServers(darkweb); diff --git a/src/Netscript/Environment.ts b/src/Netscript/Environment.ts new file mode 100644 index 000000000..97de95001 --- /dev/null +++ b/src/Netscript/Environment.ts @@ -0,0 +1,72 @@ +/** + * The environment in which a script runs. The environment holds + * Netscript functions and arguments for that script. + */ +import { IMap } from "../types"; + +export class Environment { + /** + * Parent environment. Used to implement "scope" + */ + parent: Environment | null = null; + + /** + * Whether or not the script that uses this Environment should stop running + */ + stopFlag: boolean = false; + + /** + * Environment variables (currently only Netscript functions) + */ + vars: IMap = {}; + + constructor(parent: Environment | null) { + if (parent instanceof Environment) { + this.vars = Object.assign({}, parent.vars); + } + + this.parent = parent; + } + + /** + * Finds the scope where the variable with the given name is defined + */ + lookup(name: string): Environment | null { + let scope: Environment | null = this; + while (scope) { + if (Object.prototype.hasOwnProperty.call(scope.vars, name)) { + return scope; + } + scope = scope.parent; + } + + return null; + } + + //Get the current value of a variable + get(name: string): any { + if (name in this.vars) { + return this.vars[name]; + } + + throw new Error(`Undefined variable ${name}`); + } + + //Sets the value of a variable in any scope + set(name: string, value: any) { + var scope = this.lookup(name); + + //If scope has a value, then this variable is already set in a higher scope, so + //set is there. Otherwise, create a new variable in the local scope + if (scope !== null) { + return scope.vars[name] = value; + } else { + return this.vars[name] = value; + } + } + + //Creates (or overwrites) a variable in the current scope + def(name: string, value: any) { + return this.vars[name] = value; + } +} diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts new file mode 100644 index 000000000..93b12426f --- /dev/null +++ b/src/Netscript/RamCostGenerator.ts @@ -0,0 +1,332 @@ +import { IMap } from "../types"; + +// TODO remember to update RamCalculations.js and WorkerScript.js + +// RAM costs for Netscript functions +export const RamCostConstants: IMap = { + ScriptBaseRamCost: 1.6, + ScriptDomRamCost: 25, + ScriptHackRamCost: 0.1, + ScriptHackAnalyzeRamCost: 1, + ScriptGrowRamCost: 0.15, + ScriptGrowthAnalyzeRamCost: 1, + ScriptWeakenRamCost: 0.15, + ScriptScanRamCost: 0.2, + ScriptPortProgramRamCost: 0.05, + ScriptRunRamCost: 1.0, + ScriptExecRamCost: 1.3, + ScriptSpawnRamCost: 2.0, + ScriptScpRamCost: 0.6, + ScriptKillRamCost: 0.5, + ScriptHasRootAccessRamCost: 0.05, + ScriptGetHostnameRamCost: 0.05, + ScriptGetHackingLevelRamCost: 0.05, + ScriptGetMultipliersRamCost: 4.0, + ScriptGetServerRamCost: 0.1, + ScriptFileExistsRamCost: 0.1, + ScriptIsRunningRamCost: 0.1, + ScriptHacknetNodesRamCost: 4.0, + ScriptHNUpgLevelRamCost: 0.4, + ScriptHNUpgRamRamCost: 0.6, + ScriptHNUpgCoreRamCost: 0.8, + ScriptGetStockRamCost: 2.0, + ScriptBuySellStockRamCost: 2.5, + ScriptGetPurchaseServerRamCost: 0.25, + ScriptPurchaseServerRamCost: 2.25, + ScriptGetPurchasedServerLimit: 0.05, + ScriptGetPurchasedServerMaxRam: 0.05, + ScriptRoundRamCost: 0.05, + ScriptReadWriteRamCost: 1.0, + ScriptArbScriptRamCost: 1.0, + ScriptGetScriptRamCost: 0.1, + ScriptGetHackTimeRamCost: 0.05, + ScriptGetFavorToDonate: 0.10, + ScriptCodingContractBaseRamCost: 10, + ScriptSleeveBaseRamCost: 4, + + ScriptSingularityFn1RamCost: 2, + ScriptSingularityFn2RamCost: 3, + ScriptSingularityFn3RamCost: 5, + + ScriptGangApiBaseRamCost: 4, + + ScriptBladeburnerApiBaseRamCost: 4, +} + +export const RamCosts: IMap = { + hacknet: { + numNodes: () => 0, + purchaseNode: () => 0, + getPurchaseNodeCost: () => 0, + getNodeStats: () => 0, + upgradeLevel: () => 0, + upgradeRam: () => 0, + upgradeCore: () => 0, + upgradeCache: () => 0, + getLevelUpgradeCost: () => 0, + getRamUpgradeCost: () => 0, + getCoreUpgradeCost: () => 0, + getCacheUpgradeCost: () => 0, + numHashes: () => 0, + hashCost: () => 0, + spendHashes: () => 0, + }, + sprintf: () => 0, + vsprintf: () => 0, + scan: () => RamCostConstants.ScriptScanRamCost, + hack: () => RamCostConstants.ScriptHackRamCost, + hackAnalyzeThreads: () => RamCostConstants.ScriptHackAnalyzeRamCost, + hackAnalyzePercent: () => RamCostConstants.ScriptHackAnalyzeRamCost, + hackChance: () => RamCostConstants.ScriptHackAnalyzeRamCost, + sleep: () => 0, + grow: () => RamCostConstants.ScriptGrowRamCost, + growthAnalyze: () => RamCostConstants.ScriptGrowthAnalyzeRamCost, + weaken: () => RamCostConstants.ScriptWeakenRamCost, + print: () => 0, + tprint: () => 0, + clearLog: () => 0, + disableLog: () => 0, + enableLog: () => 0, + isLogEnabled: () => 0, + getScriptLogs: () => 0, + nuke: () => RamCostConstants.ScriptPortProgramRamCost, + brutessh: () => RamCostConstants.ScriptPortProgramRamCost, + ftpcrack: () => RamCostConstants.ScriptPortProgramRamCost, + relaysmtp: () => RamCostConstants.ScriptPortProgramRamCost, + httpworm: () => RamCostConstants.ScriptPortProgramRamCost, + sqlinject: () => RamCostConstants.ScriptPortProgramRamCost, + run: () => RamCostConstants.ScriptRunRamCost, + exec: () => RamCostConstants.ScriptExecRamCost, + spawn: () => RamCostConstants.ScriptSpawnRamCost, + kill: () => RamCostConstants.ScriptKillRamCost, + killall: () => RamCostConstants.ScriptKillRamCost, + exit: () => 0, + scp: () => RamCostConstants.ScriptScpRamCost, + ls: () => RamCostConstants.ScriptScanRamCost, + ps: () => RamCostConstants.ScriptScanRamCost, + hasRootAccess: () => RamCostConstants.ScriptHasRootAccessRamCost, + getIp: () => RamCostConstants.ScriptGetHostnameRamCost, + getHostname: () => RamCostConstants.ScriptGetHostnameRamCost, + getHackingLevel: () => RamCostConstants.ScriptGetHackingLevelRamCost, + getHackingMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost, + getHacknetMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost, + getBitNodeMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost, + getServerMoneyAvailable: () => RamCostConstants.ScriptGetServerRamCost, + getServerSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost, + getServerBaseSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost, + getServerMinSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost, + getServerRequiredHackingLevel: () => RamCostConstants.ScriptGetServerRamCost, + getServerMaxMoney: () => RamCostConstants.ScriptGetServerRamCost, + getServerGrowth: () => RamCostConstants.ScriptGetServerRamCost, + getServerNumPortsRequired: () => RamCostConstants.ScriptGetServerRamCost, + getServerRam: () => RamCostConstants.ScriptGetServerRamCost, + serverExists: () => RamCostConstants.ScriptGetServerRamCost, + fileExists: () => RamCostConstants.ScriptFileExistsRamCost, + isRunning: () => RamCostConstants.ScriptIsRunningRamCost, + getStockSymbols: () => RamCostConstants.ScriptGetStockRamCost, + getStockPrice: () => RamCostConstants.ScriptGetStockRamCost, + getStockAskPrice: () => RamCostConstants.ScriptGetStockRamCost, + getStockBidPrice: () => RamCostConstants.ScriptGetStockRamCost, + getStockPosition: () => RamCostConstants.ScriptGetStockRamCost, + getStockMaxShares: () => RamCostConstants.ScriptGetStockRamCost, + getStockPurchaseCost: () => RamCostConstants.ScriptGetStockRamCost, + getStockSaleGain: () => RamCostConstants.ScriptGetStockRamCost, + buyStock: () => RamCostConstants.ScriptBuySellStockRamCost, + sellStock: () => RamCostConstants.ScriptBuySellStockRamCost, + shortStock: () => RamCostConstants.ScriptBuySellStockRamCost, + sellShort: () => RamCostConstants.ScriptBuySellStockRamCost, + placeOrder: () => RamCostConstants.ScriptBuySellStockRamCost, + cancelOrder: () => RamCostConstants.ScriptBuySellStockRamCost, + getOrders: () => RamCostConstants.ScriptBuySellStockRamCost, + getStockVolatility: () => RamCostConstants.ScriptBuySellStockRamCost, + getStockForecast: () => RamCostConstants.ScriptBuySellStockRamCost, + purchase4SMarketData: () => RamCostConstants.ScriptBuySellStockRamCost, + purchase4SMarketDataTixApi: () => RamCostConstants.ScriptBuySellStockRamCost, + getPurchasedServerLimit: () => RamCostConstants.ScriptGetPurchasedServerLimit, + getPurchasedServerMaxRam: () => RamCostConstants.ScriptGetPurchasedServerMaxRam, + getPurchasedServerCost: () => RamCostConstants.ScriptGetPurchaseServerRamCost, + purchaseServer: () => RamCostConstants.ScriptPurchaseServerRamCost, + deleteServer: () => RamCostConstants.ScriptPurchaseServerRamCost, + getPurchasedServers: () => RamCostConstants.ScriptPurchaseServerRamCost, + write: () => RamCostConstants.ScriptReadWriteRamCost, + tryWrite: () => RamCostConstants.ScriptReadWriteRamCost, + read: () => RamCostConstants.ScriptReadWriteRamCost, + peek: () => RamCostConstants.ScriptReadWriteRamCost, + clear: () => RamCostConstants.ScriptReadWriteRamCost, + getPortHandle: () => RamCostConstants.ScriptReadWriteRamCost * 10, + rm: () => RamCostConstants.ScriptReadWriteRamCost, + scriptRunning: () => RamCostConstants.ScriptArbScriptRamCost, + scriptKill: () => RamCostConstants.ScriptArbScriptRamCost, + getScriptName: () => 0, + getScriptRam: () => RamCostConstants.ScriptGetScriptRamCost, + getHackTime: () => RamCostConstants.ScriptGetHackTimeRamCost, + getGrowTime: () => RamCostConstants.ScriptGetHackTimeRamCost, + getWeakenTime: () => RamCostConstants.ScriptGetHackTimeRamCost, + getScriptIncome: () => RamCostConstants.ScriptGetScriptRamCost, + getScriptExpGain: () => RamCostConstants.ScriptGetScriptRamCost, + nFormat: () => 0, + getTimeSinceLastAug: () => RamCostConstants.ScriptGetHackTimeRamCost, + prompt: () => 0, + wget: () => 0, + getFavorToDonate: () => RamCostConstants.ScriptGetFavorToDonate, + + // Singularity Functions + universityCourse: () => RamCostConstants.ScriptSingularityFn1RamCost, + gymWorkout: () => RamCostConstants.ScriptSingularityFn1RamCost, + travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost, + purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost, + purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost, + getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, + getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, + isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, + stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2, + upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost, + getUpgradeHomeRamCost: () => RamCostConstants.ScriptSingularityFn2RamCost / 2, + workForCompany: () => RamCostConstants.ScriptSingularityFn2RamCost, + applyToCompany: () => RamCostConstants.ScriptSingularityFn2RamCost, + getCompanyRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3, + getCompanyFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3, + getCompanyFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4, + checkFactionInvitations: () => RamCostConstants.ScriptSingularityFn2RamCost, + joinFaction: () => RamCostConstants.ScriptSingularityFn2RamCost, + workForFaction: () => RamCostConstants.ScriptSingularityFn2RamCost, + getFactionRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3, + getFactionFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3, + getFactionFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4, + donateToFaction: () => RamCostConstants.ScriptSingularityFn3RamCost, + createProgram: () => RamCostConstants.ScriptSingularityFn3RamCost, + commitCrime: () => RamCostConstants.ScriptSingularityFn3RamCost, + getCrimeChance: () => RamCostConstants.ScriptSingularityFn3RamCost, + getOwnedAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost, + getOwnedSourceFiles: () => RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationsFromFaction: () => RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationPrereq: () => RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationCost: () => RamCostConstants.ScriptSingularityFn3RamCost, + purchaseAugmentation: () => RamCostConstants.ScriptSingularityFn3RamCost, + installAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost, + + // Gang API + gang : { + getMemberNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4, + getGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getOtherGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getMemberInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + canRecruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 4, + recruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getTaskNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4, + setMemberTask: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getEquipmentNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4, + getEquipmentCost: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getEquipmentType: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + purchaseEquipment: () => RamCostConstants.ScriptGangApiBaseRamCost, + ascendMember: () => RamCostConstants.ScriptGangApiBaseRamCost, + setTerritoryWarfare: () => RamCostConstants.ScriptGangApiBaseRamCost / 2, + getChanceToWinClash: () => RamCostConstants.ScriptGangApiBaseRamCost, + getBonusTime: () => 0, + }, + + // Bladeburner API + bladeburner : { + getContractNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getOperationNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getBlackOpNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getBlackOpRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2, + getGeneralActionNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getSkillNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + startAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + stopBladeburnerAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2, + getCurrentAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4, + getActionTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionEstimatedSuccessChance: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionRepGain: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionCountRemaining: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionMaxLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionCurrentLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setActionLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillPoints: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillUpgradeCost: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + upgradeSkill: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityEstimatedPopulation: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityEstimatedCommunities: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityChaos: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + switchCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getStamina: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + joinBladeburnerFaction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + joinBladeburnerDivision: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getBonusTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost, + }, + + // Coding Contract API + codingcontract : { + attempt: () => RamCostConstants.ScriptCodingContractBaseRamCost, + getContractType: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getData: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getDescription: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getNumTriesRemaining: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2, + }, + + // Duplicate Sleeve API + sleeve : { + getNumSleeves: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToShockRecovery: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToSynchronize: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToCommitCrime: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToUniversityCourse: () => RamCostConstants.ScriptSleeveBaseRamCost, + travel: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToCompanyWork: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToFactionWork: () => RamCostConstants.ScriptSleeveBaseRamCost, + setToGymWorkout: () => RamCostConstants.ScriptSleeveBaseRamCost, + getSleeveStats: () => RamCostConstants.ScriptSleeveBaseRamCost, + getTask: () => RamCostConstants.ScriptSleeveBaseRamCost, + getInformation: () => RamCostConstants.ScriptSleeveBaseRamCost, + getSleeveAugmentations: () => RamCostConstants.ScriptSleeveBaseRamCost, + getSleevePurchasableAugs: () => RamCostConstants.ScriptSleeveBaseRamCost, + purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost, + }, + + heart: { + // Easter egg function + break : () => 0, + } +} + +export function getRamCost(...args: string[]): number { + if (args.length === 0) { + console.warn(`No arguments passed to getRamCost()`); + return 0; + } + + let curr = RamCosts[args[0]]; + for (let i = 1; i < args.length; ++i) { + if (curr == null) { + console.warn(`Invalid function passed to getRamCost: ${args}`); + return 0; + } + + const currType = typeof curr; + if (currType === "function" || currType === "number") { + break; + } + + curr = curr[args[i]]; + } + + const currType = typeof curr; + if (currType === "function") { + return curr(); + } + + if (currType === "number") { + return curr; + } + + console.warn(`Expected type: ${currType}`); + return 0; +} diff --git a/src/Netscript/WorkerScript.ts b/src/Netscript/WorkerScript.ts new file mode 100644 index 000000000..556348679 --- /dev/null +++ b/src/Netscript/WorkerScript.ts @@ -0,0 +1,176 @@ +/** + * The worker agent for running a script instance. Each running script instance + * has its own underlying WorkerScript object. + * + * Note that these objects are not saved and re-loaded when the game is refreshed. + * Instead, whenever the game is opened, WorkerScripts are re-created from + * RunningScript objects + */ +import { Environment } from "./Environment"; +import { RamCostConstants } from "./RamCostGenerator"; + +import { RunningScript } from "../Script/RunningScript"; +import { Script } from "../Script/Script"; +import { AllServers } from "../Server/AllServers"; +import { BaseServer } from "../Server/BaseServer"; +import { IMap } from "../types"; + +export class WorkerScript { + /** + * Script's arguments + */ + args: any[]; + + /** + * Copy of the script's code + */ + code: string = ""; + + /** + * Holds the timeoutID (numeric value) for whenever this script is blocked by a + * timed Netscript function. i.e. Holds the return value of setTimeout() + */ + delay: number | null = null; + + /** + * Stores names of all functions that have logging disabled + */ + disableLogs: IMap = {}; + + /** + * Used for dynamic RAM calculation. Stores names of all functions that have + * already been checked by this script. + * TODO: Could probably just combine this with loadedFns? + */ + dynamicLoadedFns: IMap = {}; + + /** + * Tracks dynamic RAM usage + */ + dynamicRamUsage: number = RamCostConstants.ScriptBaseRamCost; + + /** + * Netscript Environment for this script + */ + env: Environment; + + /** + * Status message in case of script error. Currently unused I think + */ + errorMessage: string = ""; + + /** + * Used for static RAM calculation. Stores names of all functions that have + * already been checked by this script + */ + loadedFns: IMap = {}; + + /** + * Filename of script + */ + name: string; + + /** + * Script's output/return value. Currently not used or implemented + */ + output: string = ""; + + /** + * Script's RAM usage. Equivalent to underlying script's RAM usage + */ + ramUsage: number = 0; + + /** + * Whether or not this workerScript is currently running + */ + running: boolean = false; + + /** + * Reference to underlying RunningScript object + */ + scriptRef: RunningScript; + + /** + * IP Address on which this script is running + */ + serverIp: string; + + constructor(runningScriptObj: RunningScript, nsFuncsGenerator?: (ws: WorkerScript) => object) { + this.name = runningScriptObj.filename; + this.serverIp = runningScriptObj.server; + + // Get the underlying script's code + const server = AllServers[this.serverIp]; + if (server == null) { + throw new Error(`WorkerScript constructed with invalid server ip: ${this.serverIp}`); + } + let found = false; + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === this.name) { + found = true; + this.code = server.scripts[i].code; + } + } + if (!found) { + throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`); + } + + this.env = new Environment(null); + if (typeof nsFuncsGenerator === "function") { + this.env.vars = nsFuncsGenerator(this); + } + this.env.set("args", runningScriptObj.args.slice()); + + this.scriptRef = runningScriptObj; + this.args = runningScriptObj.args.slice(); + } + + /** + * Returns the Server on which this script is running + */ + getServer() { + return AllServers[this.serverIp]; + } + + /** + * Returns the Script object for the underlying script. + * Returns null if it cannot be found (which would be a bug) + */ + getScript(): Script | null { + let server = this.getServer(); + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === this.name) { + return server.scripts[i]; + } + } + + console.error("Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong"); + return null; + } + + /** + * Returns the script with the specified filename on the specified server, + * or null if it cannot be found + */ + getScriptOnServer(fn: string, server: BaseServer): Script | null { + if (server == null) { + server = this.getServer(); + } + + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === fn) { + return server.scripts[i]; + } + } + + return null; + } + + shouldLog(fn: string): boolean { + return (this.disableLogs.ALL == null && this.disableLogs[fn] == null); + } + + log(txt: string): void { + this.scriptRef.log(txt); + } +} diff --git a/src/NetscriptEnvironment.js b/src/NetscriptEnvironment.js deleted file mode 100644 index 2c01abcfa..000000000 --- a/src/NetscriptEnvironment.js +++ /dev/null @@ -1,64 +0,0 @@ -import { NetscriptFunctions } from "./NetscriptFunctions"; -import { NetscriptPort } from "./NetscriptPort"; - -/* Environment - * NetScript program environment - */ -function Environment(workerScript,parent) { - if (parent){ - //Create a copy of parent's variables - //this.vars = parent.vars; - this.vars = Object.assign({}, parent.vars); - } else { - this.vars = NetscriptFunctions(workerScript); - } - this.parent = parent; - this.stopFlag = false; -} -Environment.prototype = { - //Create a "subscope", which is a new new "sub-environment" - //The subscope is linked to this through its parent variable - extend: function() { - return new Environment(null, this); - }, - - //Finds the scope where the variable with the given name is defined - lookup: function(name) { - var scope = this; - while (scope) { - if (Object.prototype.hasOwnProperty.call(scope.vars, name)) { - return scope; - } - scope = scope.parent; - } - return null; - }, - - //Get the current value of a variable - get: function(name) { - if (name in this.vars) { - return this.vars[name]; - } - throw new Error("Undefined variable " + name); - }, - - //Sets the value of a variable in any scope - set: function(name, value) { - var scope = this.lookup(name); - - //If scope has a value, then this variable is already set in a higher scope, so - //set is there. Otherwise, create a new variable in the local scope - if (scope !== null) { - return scope.vars[name] = value; - } else { - return this.vars[name] = value; - } - }, - - //Creates (or overwrites) a variable in the current scope - def: function(name, value) { - return this.vars[name] = value; - } -}; - -export {Environment}; diff --git a/src/NetscriptEvaluator.js b/src/NetscriptEvaluator.js index 300050e85..2d6806302 100644 --- a/src/NetscriptEvaluator.js +++ b/src/NetscriptEvaluator.js @@ -1,132 +1,12 @@ -import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; -import { CONSTANTS } from "./Constants"; -import { Player } from "./Player"; -import { Environment } from "./NetscriptEnvironment"; -import { WorkerScript, addWorkerScript } from "./NetscriptWorker"; -import { Server } from "./Server/Server"; +import { WorkerScript } from "./Netscript/WorkerScript"; import { getServer } from "./Server/ServerHelpers"; -import { Settings } from "./Settings/Settings"; -import { RunningScript } from "./Script/RunningScript"; -import { Script } from "./Script/Script"; -import { findRunningScript } from "./Script/ScriptHelpers"; import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { parse, Node } from "../utils/acorn"; -import { arrayToString } from "../utils/helpers/arrayToString"; import { isValidIPAddress } from "../utils/helpers/isValidIPAddress"; import { isString } from "../utils/helpers/isString"; -export function evaluateImport(exp, workerScript, checkingRam=false) { - //When its checking RAM, it exports an array of nodes for each imported function - var ramCheckRes = []; - - var env = workerScript.env; - if (env.stopFlag) { - if (checkingRam) {return ramCheckRes;} - return Promise.reject(workerScript); - } - - //Get source script and name of all functions to import - var scriptName = exp.source.value; - var namespace, namespaceObj, allFns = false, fnNames = []; - if (exp.specifiers.length === 1 && exp.specifiers[0].type === "ImportNamespaceSpecifier") { - allFns = true; - namespace = exp.specifiers[0].local.name; - } else { - for (var i = 0; i < exp.specifiers.length; ++i) { - fnNames.push(exp.specifiers[i].local.name); - } - } - - //Get the code - var server = getServer(workerScript.serverIp), code = ""; - if (server == null) { - if (checkingRam) {return ramCheckRes;} - return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to identify server. This is a bug please report to dev", exp)); - } - for (var i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === scriptName) { - code = server.scripts[i].code; - break; - } - } - if (code === "") { - if (checkingRam) {return ramCheckRes;} - return Promise.reject(makeRuntimeRejectMsg(workerScript, "Could not find script " + scriptName + " to import", exp)); - } - - //Create the AST - try { - var ast = parse(code, {sourceType:"module"}); - } catch(e) { - console.log("Failed to parse import script"); - if (checkingRam) {return ramCheckRes;} - return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to import functions from " + scriptName + - " This is most likely due to a syntax error in the imported script", exp)); - } - - if (allFns) { - //A namespace is implemented as a JS obj - env.set(namespace, {}); - namespaceObj = env.get(namespace); - } - - //Search through the AST for all imported functions - var queue = [ast]; - while (queue.length != 0) { - var node = queue.shift(); - switch (node.type) { - case "BlockStatement": - case "Program": - for (var i = 0; i < node.body.length; ++i) { - if (node.body[i] instanceof Node) { - queue.push(node.body[i]); - } - } - break; - case "FunctionDeclaration": - if (node.id && node.id.name) { - if (allFns) { - //Import all functions under this namespace - if (checkingRam) { - ramCheckRes.push(node); - } else { - namespaceObj[node.id.name] = node; - } - } else { - //Only import specified functions - if (fnNames.includes(node.id.name)) { - if (checkingRam) { - ramCheckRes.push(node); - } else { - env.set(node.id.name, node); - } - - } - } - } else { - if (checkingRam) {return ramCheckRes;} - return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration in imported script " + scriptName, exp)); - } - break; - default: - break; - } - - for (var prop in node) { - if (node.hasOwnProperty(prop)) { - if (node[prop] instanceof Node) { - queue.push(node[prop]); - } - } - } - } - if (!checkingRam) {workerScript.scriptRef.log("Imported functions from " + scriptName);} - if (checkingRam) {return ramCheckRes;} - return Promise.resolve(true); -} - export function killNetscriptDelay(workerScript) { if (workerScript instanceof WorkerScript) { if (workerScript.delay) { @@ -155,60 +35,6 @@ export function makeRuntimeRejectMsg(workerScript, msg, exp=null) { return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg + lineNum; } -//Run a script from inside a script using run() command -export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) { - //Check if the script is already running - let runningScriptObj = findRunningScript(scriptname, args, server); - if (runningScriptObj != null) { - workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname); - return Promise.resolve(false); - } - - //'null/undefined' arguments are not allowed - for (let i = 0; i < args.length; ++i) { - if (args[i] == null) { - workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument"); - return Promise.resolve(false); - } - } - - //Check if the script exists and if it does run it - for (var i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename == scriptname) { - //Check for admin rights and that there is enough RAM availble to run - var script = server.scripts[i]; - var ramUsage = script.ramUsage; - threads = Math.round(Number(threads)); //Convert to number and round - if (threads === 0) { return Promise.resolve(false); } - ramUsage = ramUsage * threads; - var ramAvailable = server.maxRam - server.ramUsed; - - if (server.hasAdminRights == false) { - workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!"); - return Promise.resolve(false); - } else if (ramUsage > ramAvailable){ - workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!"); - return Promise.resolve(false); - } else { - //Able to run script - if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) { - workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`); - } - let runningScriptObj = new RunningScript(script, args); - runningScriptObj.threads = threads; - addWorkerScript(runningScriptObj, server); - - // Push onto runningScripts. - // This has to come after addWorkerScript() because that fn updates RAM usage - server.runScript(runningScriptObj, Player); - return Promise.resolve(true); - } - } - } - workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname); - return Promise.resolve(false); -} - export function getErrorLineNumber(exp, workerScript) { var code = workerScript.scriptRef.codeCode(); diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 3b1b4a1d1..b056394f1 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -1,6 +1,8 @@ const sprintf = require("sprintf-js").sprintf; const vsprintf = require("sprintf-js").vsprintf; +import { getRamCost } from "./Netscript/RamCostGenerator"; + import { updateActiveScriptsItems } from "./ActiveScriptsUI"; import { Augmentation } from "./Augmentation/Augmentation"; import { Augmentations } from "./Augmentation/Augmentations"; @@ -40,14 +42,19 @@ import { import { getCostOfNextHacknetNode, getCostOfNextHacknetServer, - purchaseHacknet, hasHacknetServers, - purchaseHashUpgrade + purchaseHacknet, + purchaseLevelUpgrade, + purchaseRamUpgrade, + purchaseCoreUpgrade, + purchaseCacheUpgrade, + purchaseHashUpgrade, + updateHashManagerCapacity, } from "./Hacknet/HacknetHelpers"; +import { HacknetServer } from "./Hacknet/HacknetServer"; import { CityName } from "./Locations/data/CityNames"; import { LocationName } from "./Locations/data/LocationNames"; -import { HacknetServer } from "./Hacknet/HacknetServer"; import { Message } from "./Message/Message"; import { Messages } from "./Message/MessageHelpers"; import { inMission } from "./Missions"; @@ -56,14 +63,19 @@ import { Programs } from "./Programs/Programs"; import { Script } from "./Script/Script"; import { findRunningScript } from "./Script/ScriptHelpers"; import { isScriptFilename } from "./Script/ScriptHelpersTS"; -import { AllServers, AddToAllServers } from "./Server/AllServers"; +import { + AllServers, + AddToAllServers, + createUniqueRandomIp, +} from "./Server/AllServers"; import { Server } from "./Server/Server"; import { GetServerByHostname, getServer, getServerOnNetwork, numCycleForGrowth, - processSingleServerGrowth + processSingleServerGrowth, + safetlyCreateUniqueServer, } from "./Server/ServerHelpers"; import { getPurchaseServerCost, @@ -72,17 +84,20 @@ import { } from "./Server/ServerPurchases"; import { Settings } from "./Settings/Settings"; import { SpecialServerIps } from "./Server/SpecialServerIps"; -import { Stock } from "./StockMarket/Stock"; +import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { - StockMarket, - SymbolToStockMap, - initSymbolToStockMap, buyStock, sellStock, shortStock, sellShort, +} from "./StockMarket/BuyingAndSelling"; +import { Stock } from "./StockMarket/Stock"; +import { + StockMarket, + SymbolToStockMap, placeOrder, - cancelOrder + cancelOrder, + displayStockMarketContent, } from "./StockMarket/StockMarket"; import { getBuyTransactionCost, @@ -95,7 +110,6 @@ import { getStockmarket4SDataCost, getStockMarket4STixApiCost } from "./StockMarket/StockMarketCosts"; -import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { TextFile, getTextFile, createTextFile } from "./TextFile"; import { @@ -105,15 +119,14 @@ import { } from "./NetscriptBladeburner"; import * as nsGang from "./NetscriptGang"; import { - WorkerScript, workerScripts, killWorkerScript, - NetscriptPorts + NetscriptPorts, + runScriptFromScript, } from "./NetscriptWorker"; import { makeRuntimeRejectMsg, netscriptDelay, - runScriptFromScript } from "./NetscriptEvaluator"; import { NetscriptPort } from "./NetscriptPort"; import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum"; @@ -128,7 +141,6 @@ import { is2DArray } from "./utils/helpers/is2DArray"; import { dialogBoxCreate } from "../utils/DialogBox"; import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo"; import { arrayToString } from "../utils/helpers/arrayToString"; -import { createRandomIp } from "../utils/IPAddress"; import { formatNumber, isHTML } from "../utils/StringHelperFunctions"; import { isString } from "../utils/helpers/isString"; @@ -136,18 +148,7 @@ import { createElement } from "../utils/uiHelpers/createElement"; import { createPopup } from "../utils/uiHelpers/createPopup"; import { removeElementById } from "../utils/uiHelpers/removeElementById"; -let hasCorporationSF = false; // Source-File 3 -let hasSingularitySF = false; // Source-File 4 -let hasAISF = false; // Source-File 5 -let hasBladeburnerSF = false; // Source-File 6 -let hasBladeburner2079SF = false; // Source-File 7 -let hasWallStreetSF = false; // Source-File 8 -let hasBn11SF = false; // Source-File 11 - -let singularitySFLvl = 1; -let wallStreetSFLvl = 1; - -var possibleLogs = { +const possibleLogs = { ALL: true, scan: true, hack: true, @@ -218,28 +219,10 @@ var possibleLogs = { setTerritoryWarfare: true, } -// Used to check and set flags for every Source File, despite the name of the function -function initSingularitySFFlags() { - for (var i = 0; i < Player.sourceFiles.length; ++i) { - if (Player.sourceFiles[i].n === 3) {hasCorporationSF = true;} - if (Player.sourceFiles[i].n === 4) { - hasSingularitySF = true; - singularitySFLvl = Player.sourceFiles[i].lvl; - } - if (Player.sourceFiles[i].n === 5) {hasAISF = true;} - if (Player.sourceFiles[i].n === 6) {hasBladeburnerSF = true;} - if (Player.sourceFiles[i].n === 7) {hasBladeburner2079SF = true;} - if (Player.sourceFiles[i].n === 8) { - hasWallStreetSF = true; - wallStreetSFLvl = Player.sourceFiles[i].lvl; - } - if (Player.sourceFiles[i].n === 11) {hasBn11SF = true;} - } -} function NetscriptFunctions(workerScript) { const updateDynamicRam = function(fnName, ramCost) { - if (workerScript.dynamicLoadedFns[fnName]) {return;} + if (workerScript.dynamicLoadedFns[fnName]) { return; } workerScript.dynamicLoadedFns[fnName] = true; let threads = workerScript.scriptRef.threads; @@ -259,15 +242,6 @@ function NetscriptFunctions(workerScript) { } }; - const updateStaticRam = function(fnName, ramCost) { - if (workerScript.loadedFns[fnName]) { - return 0; - } else { - workerScript.loadedFns[fnName] = true; - return ramCost; - } - }; - /** * Gets the Server for a specific hostname/ip, throwing an error * if the server doesn't exist. @@ -388,36 +362,36 @@ function NetscriptFunctions(workerScript) { }, upgradeLevel : function(i, n) { const node = getHacknetNode(i); - return node.purchaseLevelUpgrade(n, Player); + return purchaseLevelUpgrade(node, n); }, upgradeRam : function(i, n) { const node = getHacknetNode(i); - return node.purchaseRamUpgrade(n, Player); + return purchaseRamUpgrade(node, n); }, upgradeCore : function(i, n) { const node = getHacknetNode(i); - return node.purchaseCoreUpgrade(n, Player); + return purchaseCoreUpgrade(node, n); }, upgradeCache : function(i, n) { if (!hasHacknetServers()) { return false; } const node = getHacknetNode(i); - const res = node.purchaseCacheUpgrade(n, Player); + const res = purchaseCacheUpgrade(node, n); if (res) { - Player.hashManager.updateCapacity(Player); + updateHashManagerCapacity(); } return res; }, getLevelUpgradeCost : function(i, n) { const node = getHacknetNode(i); - return node.calculateLevelUpgradeCost(n, Player); + return node.calculateLevelUpgradeCost(n, Player.hacknet_node_level_cost_mult); }, getRamUpgradeCost : function(i, n) { const node = getHacknetNode(i); - return node.calculateRamUpgradeCost(n, Player); + return node.calculateRamUpgradeCost(n, Player.hacknet_node_ram_cost_mult); }, getCoreUpgradeCost : function(i, n) { const node = getHacknetNode(i); - return node.calculateCoreUpgradeCost(n, Player); + return node.calculateCoreUpgradeCost(n, Player.hacknet_node_core_cost_mult); }, getCacheUpgradeCost : function(i, n) { if (!hasHacknetServers()) { return Infinity; } @@ -440,11 +414,8 @@ function NetscriptFunctions(workerScript) { }, sprintf : sprintf, vsprintf: vsprintf, - scan : function(ip=workerScript.serverIp, hostnames=true){ - if (workerScript.checkingRam) { - return updateStaticRam("scan", CONSTANTS.ScriptScanRamCost); - } - updateDynamicRam("scan", CONSTANTS.ScriptScanRamCost); + scan : function(ip=workerScript.serverIp, hostnames=true) { + updateDynamicRam("scan", getRamCost("scan")); var server = getServer(ip); if (server == null) { throw makeRuntimeRejectMsg(workerScript, 'Invalid IP or hostname passed into scan() command'); @@ -468,10 +439,7 @@ function NetscriptFunctions(workerScript) { return out; }, hack : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("hack", CONSTANTS.ScriptHackRamCost); - } - updateDynamicRam("hack", CONSTANTS.ScriptHackRamCost); + updateDynamicRam("hack", getRamCost("hack")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Hack() call has incorrect number of arguments. Takes 1 argument"); } @@ -546,10 +514,7 @@ function NetscriptFunctions(workerScript) { }); }, hackAnalyzeThreads : function(ip, hackAmount) { - if (workerScript.checkingRam) { - return updateStaticRam("hackAnalyzeThreads", CONSTANTS.ScriptHackAnalyzeRamCost); - } - updateDynamicRam("hackAnalyzeThreads", CONSTANTS.ScriptHackAnalyzeRamCost); + updateDynamicRam("hackAnalyzeThreads", getRamCost("hackAnalyzeThreads")); // Check argument validity const server = safeGetServer(ip, 'hackAnalyzeThreads'); @@ -566,27 +531,20 @@ function NetscriptFunctions(workerScript) { return hackAmount / Math.floor(server.moneyAvailable * percentHacked); }, hackAnalyzePercent : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("hackAnalyzePercent", CONSTANTS.ScriptHackAnalyzeRamCost); - } - updateDynamicRam("hackAnalyzePercent", CONSTANTS.ScriptHackAnalyzeRamCost); + updateDynamicRam("hackAnalyzePercent", getRamCost("hackAnalyzePercent")); const server = safeGetServer(ip, 'hackAnalyzePercent'); return calculatePercentMoneyHacked(server) * 100; }, hackChance : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("hackChance", CONSTANTS.ScriptHackAnalyzeRamCost); - } - updateDynamicRam("hackChance", CONSTANTS.ScriptHackAnalyzeRamCost); + updateDynamicRam("hackChance", getRamCost("hackChance")); const server = safeGetServer(ip, 'hackChance'); return calculateHackingChance(server); }, sleep : function(time){ - if (workerScript.checkingRam) {return 0;} if (time === undefined) { throw makeRuntimeRejectMsg(workerScript, "sleep() call has incorrect number of arguments. Takes 1 argument"); } @@ -598,10 +556,7 @@ function NetscriptFunctions(workerScript) { }); }, grow : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("grow", CONSTANTS.ScriptGrowRamCost); - } - updateDynamicRam("grow", CONSTANTS.ScriptGrowRamCost); + updateDynamicRam("grow", getRamCost("grow")); var threads = workerScript.scriptRef.threads; if (isNaN(threads) || threads < 1) {threads = 1;} if (ip === undefined) { @@ -646,10 +601,7 @@ function NetscriptFunctions(workerScript) { }); }, growthAnalyze : function(ip, growth) { - if (workerScript.checkingRam) { - return updateStaticRam("growthAnalyze", CONSTANTS.ScriptGrowthAnalyzeRamCost); - } - updateDynamicRam("growthAnalyze", CONSTANTS.ScriptGrowthAnalyzeRamCost); + updateDynamicRam("growthAnalyze", getRamCost("growthAnalyze")); // Check argument validity const server = safeGetServer(ip, 'growthAnalyze'); @@ -660,10 +612,7 @@ function NetscriptFunctions(workerScript) { return numCycleForGrowth(server, Number(growth), Player); }, weaken : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("weaken", CONSTANTS.ScriptWeakenRamCost); - } - updateDynamicRam("weaken", CONSTANTS.ScriptWeakenRamCost); + updateDynamicRam("weaken", getRamCost("weaken")); var threads = workerScript.scriptRef.threads; if (isNaN(threads) || threads < 1) {threads = 1;} if (ip === undefined) { @@ -701,28 +650,24 @@ function NetscriptFunctions(workerScript) { return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads); }); }, - print : function(args){ - if (workerScript.checkingRam) {return 0;} + print: function(args){ if (args === undefined) { throw makeRuntimeRejectMsg(workerScript, "print() call has incorrect number of arguments. Takes 1 argument"); } workerScript.scriptRef.log(args.toString()); }, - tprint : function(args) { - if (workerScript.checkingRam) {return 0;} + tprint: function(args) { if (args === undefined || args == null) { throw makeRuntimeRejectMsg(workerScript, "tprint() call has incorrect number of arguments. Takes 1 argument"); } var x = args.toString(); post(workerScript.scriptRef.filename + ": " + args.toString()); }, - clearLog : function() { - if (workerScript.checkingRam) {return 0;} + clearLog: function() { workerScript.scriptRef.clearLog(); }, - disableLog : function(fn) { - if (workerScript.checkingRam) {return 0;} - if(possibleLogs[fn]===undefined) { + disableLog: function(fn) { + if (possibleLogs[fn]===undefined) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument to disableLog: "+fn); } workerScript.disableLogs[fn] = true; @@ -730,9 +675,8 @@ function NetscriptFunctions(workerScript) { workerScript.scriptRef.log("Disabled logging for " + fn); } }, - enableLog : function(fn) { - if (workerScript.checkingRam) {return 0;} - if(possibleLogs[fn]===undefined) { + enableLog: function(fn) { + if (possibleLogs[fn]===undefined) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument to enableLog: "+fn); } delete workerScript.disableLogs[fn]; @@ -741,15 +685,12 @@ function NetscriptFunctions(workerScript) { } }, isLogEnabled : function(fn) { - if (workerScript.checkingRam) {return 0;} if (possibleLogs[fn] === undefined) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument to isLogEnabled: " + fn); } return workerScript.disableLogs[fn] ? false : true; }, - getScriptLogs : function(fn, ip) { - if (workerScript.checkingRam) {return 0;} - + getScriptLogs: function(fn, ip) { if (fn != null && typeof fn === 'string') { // Get Logs of another script if (ip == null) { ip = workerScript.serverIp; } @@ -773,11 +714,8 @@ function NetscriptFunctions(workerScript) { return workerScript.scriptRef.logs.slice(); }, - nuke : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("nuke", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("nuke", CONSTANTS.ScriptPortProgramRamCost); + nuke: function(ip){ + updateDynamicRam("nuke", getRamCost("nuke")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -804,11 +742,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - brutessh : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("brutessh", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("brutessh", CONSTANTS.ScriptPortProgramRamCost); + brutessh: function(ip){ + updateDynamicRam("brutessh", getRamCost("brutessh")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -834,11 +769,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - ftpcrack : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("ftpcrack", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("ftpcrack", CONSTANTS.ScriptPortProgramRamCost); + ftpcrack: function(ip) { + updateDynamicRam("ftpcrack", getRamCost("ftpcrack")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -863,11 +795,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - relaysmtp : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("relaysmtp", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("relaysmtp", CONSTANTS.ScriptPortProgramRamCost); + relaysmtp: function(ip) { + updateDynamicRam("relaysmtp", getRamCost("relaysmtp")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -892,11 +821,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - httpworm : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("httpworm", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("httpworm", CONSTANTS.ScriptPortProgramRamCost); + httpworm: function(ip) { + updateDynamicRam("httpworm", getRamCost("httpworm")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -921,11 +847,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - sqlinject : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("sqlinject", CONSTANTS.ScriptPortProgramRamCost); - } - updateDynamicRam("sqlinject", CONSTANTS.ScriptPortProgramRamCost); + sqlinject: function(ip) { + updateDynamicRam("sqlinject", getRamCost("sqlinject")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "Program call has incorrect number of arguments. Takes 1 argument"); } @@ -950,11 +873,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - run : function(scriptname,threads = 1){ - if (workerScript.checkingRam) { - return updateStaticRam("run", CONSTANTS.ScriptRunRamCost); - } - updateDynamicRam("run", CONSTANTS.ScriptRunRamCost); + run: function(scriptname,threads = 1) { + updateDynamicRam("run", getRamCost("run")); if (scriptname === undefined) { throw makeRuntimeRejectMsg(workerScript, "run() call has incorrect number of arguments. Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); } @@ -972,11 +892,8 @@ function NetscriptFunctions(workerScript) { return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads); }, - exec : function(scriptname,ip,threads = 1) { - if (workerScript.checkingRam) { - return updateStaticRam("exec", CONSTANTS.ScriptExecRamCost); - } - updateDynamicRam("exec", CONSTANTS.ScriptExecRamCost); + exec: function(scriptname, ip, threads = 1) { + updateDynamicRam("exec", getRamCost("exec")); if (scriptname === undefined || ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "exec() call has incorrect number of arguments. Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); } @@ -993,11 +910,8 @@ function NetscriptFunctions(workerScript) { } return runScriptFromScript(server, scriptname, argsForNewScript, workerScript, threads); }, - spawn : function(scriptname, threads) { - if (workerScript.checkingRam) { - return updateStaticRam("spawn", CONSTANTS.ScriptSpawnRamCost); - } - updateDynamicRam("spawn", CONSTANTS.ScriptSpawnRamCost); + spawn: function(scriptname, threads) { + updateDynamicRam("spawn", getRamCost("spawn")); if (scriptname == null || threads == null) { throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()"); } @@ -1024,11 +938,8 @@ function NetscriptFunctions(workerScript) { } NetscriptFunctions(workerScript).exit(); }, - kill : function(filename, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("kill", CONSTANTS.ScriptKillRamCost); - } - updateDynamicRam("kill", CONSTANTS.ScriptKillRamCost); + kill: function(filename, ip) { + updateDynamicRam("kill", getRamCost("kill")); if (filename === undefined || ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)"); } @@ -1059,11 +970,8 @@ function NetscriptFunctions(workerScript) { return false; } }, - killall : function(ip=workerScript.serverIp){ - if (workerScript.checkingRam) { - return updateStaticRam("killall", CONSTANTS.ScriptKillRamCost); - } - updateDynamicRam("killall", CONSTANTS.ScriptKillRamCost); + killall: function(ip=workerScript.serverIp) { + updateDynamicRam("killall", getRamCost("killall")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "killall() call has incorrect number of arguments. Takes 1 argument"); } @@ -1082,7 +990,6 @@ function NetscriptFunctions(workerScript) { return scriptsRunning; }, exit : function() { - if (workerScript.checkingRam) {return 0;} var server = getServer(workerScript.serverIp); if (server == null) { throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in exit(). This is a bug please contact game dev"); @@ -1093,11 +1000,8 @@ function NetscriptFunctions(workerScript) { workerScript.scriptRef.log("Exit failed(). This is a bug please contact game developer"); } }, - scp : function(scriptname, ip1, ip2) { - if (workerScript.checkingRam) { - return updateStaticRam("scp", CONSTANTS.ScriptScpRamCost); - } - updateDynamicRam("scp", CONSTANTS.ScriptScpRamCost); + scp: function(scriptname, ip1, ip2) { + updateDynamicRam("scp", getRamCost("scp")); if (arguments.length !== 2 && arguments.length !== 3) { throw makeRuntimeRejectMsg(workerScript, "ERROR: scp() call has incorrect number of arguments. Takes 2 or 3 arguments"); } @@ -1252,11 +1156,8 @@ function NetscriptFunctions(workerScript) { } return true; }, - ls : function(ip, grep) { - if (workerScript.checkingRam) { - return updateStaticRam("ls", CONSTANTS.ScriptScanRamCost); - } - updateDynamicRam("ls", CONSTANTS.ScriptScanRamCost); + ls: function(ip, grep) { + updateDynamicRam("ls", getRamCost("ls")); if (ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "ls() failed because of invalid arguments. Usage: ls(ip/hostname, [grep filter])"); } @@ -1334,11 +1235,8 @@ function NetscriptFunctions(workerScript) { allFiles.sort(); return allFiles; }, - ps : function(ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("ps", CONSTANTS.ScriptScanRamCost); - } - updateDynamicRam("ps", CONSTANTS.ScriptScanRamCost); + ps: function(ip=workerScript.serverIp) { + updateDynamicRam("ps", getRamCost("ps")); var server = getServer(ip); if (server == null){ workerScript.scriptRef.log("ps() failed. Invalid IP or hostname passed in: " + ip); @@ -1351,11 +1249,8 @@ function NetscriptFunctions(workerScript) { } return processes; }, - hasRootAccess : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("hasRootAccess", CONSTANTS.ScriptHasRootAccessRamCost); - } - updateDynamicRam("hasRootAccess", CONSTANTS.ScriptHasRootAccessRamCost); + hasRootAccess: function(ip) { + updateDynamicRam("hasRootAccess", getRamCost("hasRootAccess")); if (ip===undefined){ throw makeRuntimeRejectMsg(workerScript, "hasRootAccess() call has incorrect number of arguments. Takes 1 argument"); } @@ -1366,44 +1261,32 @@ function NetscriptFunctions(workerScript) { } return server.hasAdminRights; }, - getIp : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getIp", CONSTANTS.ScriptGetHostnameRamCost); - } - updateDynamicRam("getIp", CONSTANTS.ScriptGetHostnameRamCost); + getIp: function() { + updateDynamicRam("getIp", getRamCost("getIp")); var scriptServer = getServer(workerScript.serverIp); if (scriptServer == null) { throw makeRuntimeRejectMsg(workerScript, "Could not find server. This is a bug in the game. Report to game dev"); } return scriptServer.ip; }, - getHostname : function(){ - if (workerScript.checkingRam) { - return updateStaticRam("getHostname", CONSTANTS.ScriptGetHostnameRamCost); - } - updateDynamicRam("getHostname", CONSTANTS.ScriptGetHostnameRamCost); + getHostname: function() { + updateDynamicRam("getHostname", getRamCost("getHostname")); var scriptServer = getServer(workerScript.serverIp); if (scriptServer == null) { throw makeRuntimeRejectMsg(workerScript, "Could not find server. This is a bug in the game. Report to game dev"); } return scriptServer.hostname; }, - getHackingLevel : function(){ - if (workerScript.checkingRam) { - return updateStaticRam("getHackingLevel", CONSTANTS.ScriptGetHackingLevelRamCost); - } - updateDynamicRam("getHackingLevel", CONSTANTS.ScriptGetHackingLevelRamCost); + getHackingLevel: function() { + updateDynamicRam("getHackingLevel", getRamCost("getHackingLevel")); Player.updateSkillLevels(); if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getHackingLevel == null) { workerScript.scriptRef.log("getHackingLevel() returned " + Player.hacking_skill); } return Player.hacking_skill; }, - getHackingMultipliers : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getHackingMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); - } - updateDynamicRam("getHackingMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); + getHackingMultipliers: function() { + updateDynamicRam("getHackingMultipliers", getRamCost("getHackingMultipliers")); return { chance: Player.hacking_chance_mult, speed: Player.hacking_speed_mult, @@ -1411,11 +1294,8 @@ function NetscriptFunctions(workerScript) { growth: Player.hacking_grow_mult, }; }, - getHacknetMultipliers : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getHacknetMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); - } - updateDynamicRam("getHacknetMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); + getHacknetMultipliers: function() { + updateDynamicRam("getHacknetMultipliers", getRamCost("getHacknetMultipliers")); return { production: Player.hacknet_node_money_mult, purchaseCost: Player.hacknet_node_purchase_cost_mult, @@ -1425,21 +1305,15 @@ function NetscriptFunctions(workerScript) { }; }, getBitNodeMultipliers: function() { - if (workerScript.checkingRam) { - return updateStaticRam("getBitNodeMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); - } - updateDynamicRam("getBitNodeMultipliers", CONSTANTS.ScriptGetMultipliersRamCost); - if (!hasAISF) { + updateDynamicRam("getBitNodeMultipliers", getRamCost("getBitNodeMultipliers")); + if (SourceFileFlags[5] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getBitNodeMultipliers(). It requires Source-File 5 to run."); } let copy = Object.assign({}, BitNodeMultipliers); return copy; }, - getServerMoneyAvailable : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerMoneyAvailable", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerMoneyAvailable", CONSTANTS.ScriptGetServerRamCost); + getServerMoneyAvailable: function(ip) { + updateDynamicRam("getServerMoneyAvailable", getRamCost("getServerMoneyAvailable")); const server = safeGetServer(ip, "getServerMoneyAvailable"); if (failOnHacknetServer(server, "getServerMoneyAvailable")) { return 0; } if (server.hostname == "home") { @@ -1454,11 +1328,8 @@ function NetscriptFunctions(workerScript) { } return server.moneyAvailable; }, - getServerSecurityLevel : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("getServerSecurityLevel", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerSecurityLevel", CONSTANTS.ScriptGetServerRamCost); + getServerSecurityLevel: function(ip) { + updateDynamicRam("getServerSecurityLevel", getRamCost("getServerSecurityLevel")); const server = safeGetServer(ip, "getServerSecurityLevel"); if (failOnHacknetServer(server, "getServerSecurityLevel")) { return 1; } if (workerScript.shouldLog("getServerSecurityLevel")) { @@ -1466,11 +1337,8 @@ function NetscriptFunctions(workerScript) { } return server.hackDifficulty; }, - getServerBaseSecurityLevel : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("getServerBaseSecurityLevel", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerBaseSecurityLevel", CONSTANTS.ScriptGetServerRamCost); + getServerBaseSecurityLevel: function(ip) { + updateDynamicRam("getServerBaseSecurityLevel", getRamCost("getServerBaseSecurityLevel")); const server = safeGetServer(ip, "getServerBaseSecurityLevel"); if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { return 1; } if (workerScript.shouldLog("getServerBaseSecurityLevel")) { @@ -1478,11 +1346,8 @@ function NetscriptFunctions(workerScript) { } return server.baseDifficulty; }, - getServerMinSecurityLevel : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerMinSecurityLevel", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerMinSecurityLevel", CONSTANTS.ScriptGetServerRamCost); + getServerMinSecurityLevel: function(ip) { + updateDynamicRam("getServerMinSecurityLevel", getRamCost("getServerMinSecurityLevel")); const server = safeGetServer(ip, "getServerMinSecurityLevel"); if (failOnHacknetServer(server, "getServerMinSecurityLevel")) { return 1; } if (workerScript.shouldLog("getServerMinSecurityLevel")) { @@ -1490,11 +1355,8 @@ function NetscriptFunctions(workerScript) { } return server.minDifficulty; }, - getServerRequiredHackingLevel : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerRequiredHackingLevel", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerRequiredHackingLevel", CONSTANTS.ScriptGetServerRamCost); + getServerRequiredHackingLevel: function(ip) { + updateDynamicRam("getServerRequiredHackingLevel", getRamCost("getServerRequiredHackingLevel")); const server = safeGetServer(ip, "getServerRequiredHackingLevel"); if (failOnHacknetServer(server, "getServerRequiredHackingLevel")) { return 1; } if (workerScript.shouldLog("getServerRequiredHackingLevel")) { @@ -1502,11 +1364,8 @@ function NetscriptFunctions(workerScript) { } return server.requiredHackingSkill; }, - getServerMaxMoney : function(ip){ - if (workerScript.checkingRam) { - return updateStaticRam("getServerMaxMoney", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerMaxMoney", CONSTANTS.ScriptGetServerRamCost); + getServerMaxMoney: function(ip) { + updateDynamicRam("getServerMaxMoney", getRamCost("getServerMaxMoney")); const server = safeGetServer(ip, "getServerMaxMoney"); if (failOnHacknetServer(server, "getServerMaxMoney")) { return 0; } if (workerScript.shouldLog("getServerMaxMoney")) { @@ -1514,11 +1373,8 @@ function NetscriptFunctions(workerScript) { } return server.moneyMax; }, - getServerGrowth : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerGrowth", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerGrowth", CONSTANTS.ScriptGetServerRamCost); + getServerGrowth: function(ip) { + updateDynamicRam("getServerGrowth", getRamCost("getServerGrowth")); const server = safeGetServer(ip, "getServerGrowth"); if (failOnHacknetServer(server, "getServerGrowth")) { return 1; } if (workerScript.shouldLog("getServerGrowth")) { @@ -1526,11 +1382,8 @@ function NetscriptFunctions(workerScript) { } return server.serverGrowth; }, - getServerNumPortsRequired : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerNumPortsRequired", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerNumPortsRequired", CONSTANTS.ScriptGetServerRamCost); + getServerNumPortsRequired: function(ip) { + updateDynamicRam("getServerNumPortsRequired", getRamCost("getServerNumPortsRequired")); const server = safeGetServer(ip, "getServerNumPortsRequired"); if (failOnHacknetServer(server, "getServerNumPortsRequired")) { return 5; } if (workerScript.shouldLog("getServerNumPortsRequired")) { @@ -1538,29 +1391,20 @@ function NetscriptFunctions(workerScript) { } return server.numOpenPortsRequired; }, - getServerRam : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getServerRam", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("getServerRam", CONSTANTS.ScriptGetServerRamCost); + getServerRam: function(ip) { + updateDynamicRam("getServerRam", getRamCost("getServerRam")); const server = safeGetServer(ip, "getServerRam"); if (workerScript.shouldLog("getServerRam")) { workerScript.log("getServerRam() returned [" + formatNumber(server.maxRam, 2) + "GB, " + formatNumber(server.ramUsed, 2) + "GB]"); } return [server.maxRam, server.ramUsed]; }, - serverExists : function(ip) { - if (workerScript.checkingRam) { - return updateStaticRam("serverExists", CONSTANTS.ScriptGetServerRamCost); - } - updateDynamicRam("serverExists", CONSTANTS.ScriptGetServerRamCost); + serverExists: function(ip) { + updateDynamicRam("serverExists", getRamCost("serverExists")); return (getServer(ip) !== null); }, - fileExists : function(filename,ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("fileExists", CONSTANTS.ScriptFileExistsRamCost); - } - updateDynamicRam("fileExists", CONSTANTS.ScriptFileExistsRamCost); + fileExists: function(filename,ip=workerScript.serverIp) { + updateDynamicRam("fileExists", getRamCost("fileExists")); if (filename === undefined) { throw makeRuntimeRejectMsg(workerScript, "fileExists() call has incorrect number of arguments. Usage: fileExists(scriptname, [server])"); } @@ -1591,11 +1435,8 @@ function NetscriptFunctions(workerScript) { } return false; }, - isRunning : function(filename,ip) { - if (workerScript.checkingRam) { - return updateStaticRam("isRunning", CONSTANTS.ScriptIsRunningRamCost); - } - updateDynamicRam("isRunning", CONSTANTS.ScriptIsRunningRamCost); + isRunning: function(filename,ip) { + updateDynamicRam("isRunning", getRamCost("isRunning")); if (filename === undefined || ip === undefined) { throw makeRuntimeRejectMsg(workerScript, "isRunning() call has incorrect number of arguments. Usage: isRunning(scriptname, server, [arg1], [arg2]...)"); } @@ -1610,49 +1451,34 @@ function NetscriptFunctions(workerScript) { } return (findRunningScript(filename, argsForTargetScript, server) != null); }, - getStockSymbols : function(){ - if (workerScript.checkingRam) { - return updateStaticRam("getStockSymbols", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockSymbols", CONSTANTS.ScriptGetStockRamCost); + getStockSymbols: function() { + updateDynamicRam("getStockSymbols", getRamCost("getStockSymbols")); checkTixApiAccess("getStockSymbols"); return Object.values(StockSymbols); }, - getStockPrice : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockPrice", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockPrice", CONSTANTS.ScriptGetStockRamCost); + getStockPrice: function(symbol) { + updateDynamicRam("getStockPrice", getRamCost("getStockPrice")); checkTixApiAccess("getStockPrice"); const stock = getStockFromSymbol(symbol, "getStockPrice"); return stock.price; }, - getStockAskPrice : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockAskPrice", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockAskPrice", CONSTANTS.ScriptGetStockRamCost); + getStockAskPrice: function(symbol) { + updateDynamicRam("getStockAskPrice", getRamCost("getStockAskPrice")); checkTixApiAccess("getStockAskPrice"); const stock = getStockFromSymbol(symbol, "getStockAskPrice"); return stock.getAskPrice(); }, - getStockBidPrice : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockBidPrice", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockBidPrice", CONSTANTS.ScriptGetStockRamCost); + getStockBidPrice: function(symbol) { + updateDynamicRam("getStockBidPrice", getRamCost("getStockBidPrice")); checkTixApiAccess("getStockBidPrice"); const stock = getStockFromSymbol(symbol, "getStockBidPrice"); return stock.getBidPrice(); }, - getStockPosition : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockPosition", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockPosition", CONSTANTS.ScriptGetStockRamCost); + getStockPosition: function(symbol) { + updateDynamicRam("getStockPosition", getRamCost("getStockPosition")); checkTixApiAccess("getStockPosition"); var stock = SymbolToStockMap[symbol]; if (stock == null) { @@ -1660,21 +1486,15 @@ function NetscriptFunctions(workerScript) { } return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; }, - getStockMaxShares : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockMaxShares", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockMaxShares", CONSTANTS.ScriptGetStockRamCost); + getStockMaxShares: function(symbol) { + updateDynamicRam("getStockMaxShares", getRamCost("getStockMaxShares")); checkTixApiAccess("getStockMaxShares"); const stock = getStockFromSymbol(symbol, "getStockMaxShares"); return stock.maxShares; }, - getStockPurchaseCost : function(symbol, shares, posType) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockPurchaseCost", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockPurchaseCost", CONSTANTS.ScriptGetStockRamCost); + getStockPurchaseCost: function(symbol, shares, posType) { + updateDynamicRam("getStockPurchaseCost", getRamCost("getStockPurchaseCost")); checkTixApiAccess("getStockPurchaseCost"); const stock = getStockFromSymbol(symbol, "getStockPurchaseCost"); shares = Math.round(shares); @@ -1694,11 +1514,8 @@ function NetscriptFunctions(workerScript) { return res; }, - getStockSaleGain : function(symbol, shares, posType) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockSaleGain", CONSTANTS.ScriptGetStockRamCost); - } - updateDynamicRam("getStockSaleGain", CONSTANTS.ScriptGetStockRamCost); + getStockSaleGain: function(symbol, shares, posType) { + updateDynamicRam("getStockSaleGain", getRamCost("getStockSaleGain")); checkTixApiAccess("getStockSaleGain"); const stock = getStockFromSymbol(symbol, "getStockSaleGain"); shares = Math.round(shares); @@ -1718,68 +1535,53 @@ function NetscriptFunctions(workerScript) { return res; }, - buyStock : function(symbol, shares) { - if (workerScript.checkingRam) { - return updateStaticRam("buyStock", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("buyStock", CONSTANTS.ScriptBuySellStockRamCost); + buyStock: function(symbol, shares) { + updateDynamicRam("buyStock", getRamCost("buyStock")); checkTixApiAccess("buyStock"); const stock = getStockFromSymbol(symbol, "buyStock"); - const res = buyStock(stock, shares, workerScript); + const res = buyStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent }); return res ? stock.price : 0; }, - sellStock : function(symbol, shares) { - if (workerScript.checkingRam) { - return updateStaticRam("sellStock", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("sellStock", CONSTANTS.ScriptBuySellStockRamCost); + sellStock: function(symbol, shares) { + updateDynamicRam("sellStock", getRamCost("sellStock")); checkTixApiAccess("sellStock"); const stock = getStockFromSymbol(symbol, "sellStock"); - const res = sellStock(stock, shares, workerScript); + const res = sellStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent }); return res ? stock.price : 0; }, - shortStock : function(symbol, shares) { - if (workerScript.checkingRam) { - return updateStaticRam("shortStock", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("shortStock", CONSTANTS.ScriptBuySellStockRamCost); + shortStock: function(symbol, shares) { + updateDynamicRam("shortStock", getRamCost("shortStock")); checkTixApiAccess("shortStock"); if (Player.bitNodeN !== 8) { - if (!(hasWallStreetSF && wallStreetSFLvl >= 2)) { + if (SourceFileFlags[8] <= 1) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use shortStock(). You must either be in BitNode-8 or you must have Level 2 of Source-File 8"); } } const stock = getStockFromSymbol(symbol, "shortStock"); - const res = shortStock(stock, shares, workerScript); + const res = shortStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent }); return res ? stock.price : 0; }, - sellShort : function(symbol, shares) { - if (workerScript.checkingRam) { - return updateStaticRam("sellShort", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("sellShort", CONSTANTS.ScriptBuySellStockRamCost); + sellShort: function(symbol, shares) { + updateDynamicRam("sellShort", getRamCost("sellShort")); checkTixApiAccess("sellShort"); if (Player.bitNodeN !== 8) { - if (!(hasWallStreetSF && wallStreetSFLvl >= 2)) { + if (SourceFileFlags[8] <= 1) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use sellShort(). You must either be in BitNode-8 or you must have Level 2 of Source-File 8"); } } const stock = getStockFromSymbol(symbol, "sellShort"); - const res = sellShort(stock, shares, workerScript); + const res = sellShort(stock, shares, workerScript, { rerenderFn: displayStockMarketContent }); return res ? stock.price : 0; }, - placeOrder : function(symbol, shares, price, type, pos) { - if (workerScript.checkingRam) { - return updateStaticRam("placeOrder", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("placeOrder", CONSTANTS.ScriptBuySellStockRamCost); + placeOrder: function(symbol, shares, price, type, pos) { + updateDynamicRam("placeOrder", getRamCost("placeOrder")); checkTixApiAccess("placeOrder"); if (Player.bitNodeN !== 8) { - if (!(hasWallStreetSF && wallStreetSFLvl >= 3)) { + if (SourceFileFlags[8] <= 2) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use placeOrder(). You must either be in BitNode-8 or have Level 3 of Source-File 8"); } } @@ -1810,14 +1612,11 @@ function NetscriptFunctions(workerScript) { return placeOrder(stock, shares, price, orderType, orderPos, workerScript); }, - cancelOrder : function(symbol, shares, price, type, pos) { - if (workerScript.checkingRam) { - return updateStaticRam("cancelOrder", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("cancelOrder", CONSTANTS.ScriptBuySellStockRamCost); + cancelOrder: function(symbol, shares, price, type, pos) { + updateDynamicRam("cancelOrder", getRamCost("cancelOrder")); checkTixApiAccess("cancelOrder"); if (Player.bitNodeN !== 8) { - if (!(hasWallStreetSF && wallStreetSFLvl >= 3)) { + if (SourceFileFlags[8] <= 2) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use cancelOrder(). You must either be in BitNode-8 or have Level 3 of Source-File 8"); } } @@ -1856,14 +1655,11 @@ function NetscriptFunctions(workerScript) { }; return cancelOrder(params, workerScript); }, - getOrders : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getOrders", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("getOrders", CONSTANTS.ScriptBuySellStockRamCost); + getOrders: function() { + updateDynamicRam("getOrders", getRamCost("getOrders")); checkTixApiAccess("getOrders"); if (Player.bitNodeN !== 8) { - if (!(hasWallStreetSF && wallStreetSFLvl >= 3)) { + if (SourceFileFlags[8] <= 2) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use getOrders(). You must either be in BitNode-8 or have Level 3 of Source-File 8"); } } @@ -1888,11 +1684,8 @@ function NetscriptFunctions(workerScript) { return orders; }, - getStockVolatility : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockVolatility", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("getStockVolatility", CONSTANTS.ScriptBuySellStockRamCost); + getStockVolatility: function(symbol) { + updateDynamicRam("getStockVolatility", getRamCost("getStockVolatility")); if (!Player.has4SDataTixApi) { throw makeRuntimeRejectMsg(workerScript, "You don't have 4S Market Data TIX API Access! Cannot use getStockVolatility()"); } @@ -1900,11 +1693,8 @@ function NetscriptFunctions(workerScript) { return stock.mv / 100; // Convert from percentage to decimal }, - getStockForecast : function(symbol) { - if (workerScript.checkingRam) { - return updateStaticRam("getStockForecast", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("getStockForecast", CONSTANTS.ScriptBuySellStockRamCost); + getStockForecast: function(symbol) { + updateDynamicRam("getStockForecast", getRamCost("getStockForecast")); if (!Player.has4SDataTixApi) { throw makeRuntimeRejectMsg(workerScript, "You don't have 4S Market Data TIX API Access! Cannot use getStockForecast()"); } @@ -1914,11 +1704,8 @@ function NetscriptFunctions(workerScript) { stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag; return forecast / 100; // Convert from percentage to decimal }, - purchase4SMarketData : function() { - if (workerScript.checkingRam) { - return updateStaticRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost); + purchase4SMarketData: function() { + updateDynamicRam("purchase4SMarketData", getRamCost("purchase4SMarketData")); checkTixApiAccess("purchase4SMarketData"); if (Player.has4SData) { @@ -1943,10 +1730,7 @@ function NetscriptFunctions(workerScript) { return true; }, purchase4SMarketDataTixApi : function() { - if (workerScript.checkingRam) { - return updateStaticRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost); - } - updateDynamicRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost); + updateDynamicRam("purchase4SMarketDataTixApi", getRamCost("purchase4SMarketDataTixApi")); checkTixApiAccess("purchase4SMarketDataTixApi"); if (Player.has4SDataTixApi) { @@ -1971,26 +1755,17 @@ function NetscriptFunctions(workerScript) { return true; }, getPurchasedServerLimit : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit); - } - updateDynamicRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit); + updateDynamicRam("getPurchasedServerLimit", getRamCost("getPurchasedServerLimit")); return getPurchaseServerLimit(); }, getPurchasedServerMaxRam: function() { - if (workerScript.checkingRam) { - return updateStaticRam("getPurchasedServerMaxRam", CONSTANTS.ScriptGetPurchasedServerMaxRam); - } - updateDynamicRam("getPurchasedServerMaxRam", CONSTANTS.ScriptGetPurchasedServerMaxRam); + updateDynamicRam("getPurchasedServerMaxRam", getRamCost("getPurchasedServerMaxRam")); return getPurchaseServerMaxRam(); }, getPurchasedServerCost: function(ram) { - if (workerScript.checkingRam) { - return updateStaticRam("getPurchasedServerCost", CONSTANTS.ScriptGetPurchaseServerRamCost); - } - updateDynamicRam("getPurchasedServerCost", CONSTANTS.ScriptGetPurchaseServerRamCost); + updateDynamicRam("getPurchasedServerCost", getRamCost("getPurchasedServerCost")); const cost = getPurchaseServerCost(ram); if (cost === Infinity) { @@ -2000,11 +1775,8 @@ function NetscriptFunctions(workerScript) { return cost; }, - purchaseServer : function(hostname, ram) { - if (workerScript.checkingRam) { - return updateStaticRam("purchaseServer", CONSTANTS.ScriptPurchaseServerRamCost); - } - updateDynamicRam("purchaseServer", CONSTANTS.ScriptPurchaseServerRamCost); + purchaseServer: function(hostname, ram) { + updateDynamicRam("purchaseServer", getRamCost("purchaseServer")); var hostnameStr = String(hostname); hostnameStr = hostnameStr.replace(/\s+/g, ''); if (hostnameStr == "") { @@ -2027,8 +1799,8 @@ function NetscriptFunctions(workerScript) { workerScript.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2)); return ""; } - var newServ = new Server({ - ip: createRandomIp(), + var newServ = safetlyCreateUniqueServer({ + ip: createUniqueRandomIp(), hostname: hostnameStr, organizationName: "", isConnectedTo: false, @@ -2048,11 +1820,8 @@ function NetscriptFunctions(workerScript) { } return newServ.hostname; }, - deleteServer : function(hostname) { - if (workerScript.checkingRam) { - return updateStaticRam("deleteServer", CONSTANTS.ScriptPurchaseServerRamCost); - } - updateDynamicRam("deleteServer", CONSTANTS.ScriptPurchaseServerRamCost); + deleteServer: function(hostname) { + updateDynamicRam("deleteServer", getRamCost("deleteServer")); var hostnameStr = String(hostname); hostnameStr = hostnameStr.replace(/\s\s+/g, ''); var server = GetServerByHostname(hostnameStr); @@ -2123,11 +1892,8 @@ function NetscriptFunctions(workerScript) { "as a purchased server. This is likely a bug please contact game dev"); return false; }, - getPurchasedServers : function(hostname=true) { - if (workerScript.checkingRam) { - return updateStaticRam("getPurchasedServers", CONSTANTS.ScriptPurchaseServerRamCost); - } - updateDynamicRam("getPurchasedServers", CONSTANTS.ScriptPurchaseServerRamCost); + getPurchasedServers: function(hostname=true) { + updateDynamicRam("getPurchasedServers", getRamCost("getPurchasedServers")); var res = []; Player.purchasedServers.forEach(function(ip) { if (hostname) { @@ -2142,11 +1908,8 @@ function NetscriptFunctions(workerScript) { }); return res; }, - write : function(port, data="", mode="a") { - if (workerScript.checkingRam) { - return updateStaticRam("write", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("write", CONSTANTS.ScriptReadWriteRamCost); + write: function(port, data="", mode="a") { + updateDynamicRam("write", getRamCost("write")); if (!isNaN(port)) { // Write to port // Port 1-10 port = Math.round(port); @@ -2193,11 +1956,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for write: " + port); } }, - tryWrite : function(port, data="") { - if (workerScript.checkingRam) { - return updateStaticRam("tryWrite", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("tryWrite", CONSTANTS.ScriptReadWriteRamCost); + tryWrite: function(port, data="") { + updateDynamicRam("tryWrite", getRamCost("tryWrite")); if (!isNaN(port)) { port = Math.round(port); if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { @@ -2212,11 +1972,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for tryWrite: " + port); } }, - read : function(port) { - if (workerScript.checkingRam) { - return updateStaticRam("read", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("read", CONSTANTS.ScriptReadWriteRamCost); + read: function(port) { + updateDynamicRam("read", getRamCost("read")); if (!isNaN(port)) { // Read from port // Port 1-10 port = Math.round(port); @@ -2254,11 +2011,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for read(): " + port); } }, - peek : function(port) { - if (workerScript.checkingRam) { - return updateStaticRam("peek", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("peek", CONSTANTS.ScriptReadWriteRamCost); + peek: function(port) { + updateDynamicRam("peek", getRamCost("peek")); if (isNaN(port)) { throw makeRuntimeRejectMsg(workerScript, "ERROR: peek() called with invalid argument. Must be a port number between 1 and " + CONSTANTS.NumNetscriptPorts); } @@ -2272,11 +2026,8 @@ function NetscriptFunctions(workerScript) { } return port.peek(); }, - clear : function(port) { - if (workerScript.checkingRam) { - return updateStaticRam("clear", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("clear", CONSTANTS.ScriptReadWriteRamCost); + clear: function(port) { + updateDynamicRam("clear", getRamCost("clear")); if (!isNaN(port)) { // Clear port port = Math.round(port); if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { @@ -2302,11 +2053,8 @@ function NetscriptFunctions(workerScript) { } return 0; }, - getPortHandle : function(port) { - if (workerScript.checkingRam) { - return updateStaticRam("getPortHandle", CONSTANTS.ScriptReadWriteRamCost * 10); - } - updateDynamicRam("getPortHandle", CONSTANTS.ScriptReadWriteRamCost * 10); + getPortHandle: function(port) { + updateDynamicRam("getPortHandle", getRamCost("getPortHandle")); if (isNaN(port)) { throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid argument passed into getPortHandle(). Must be an integer between 1 and " + CONSTANTS.NumNetscriptPorts); } @@ -2320,11 +2068,8 @@ function NetscriptFunctions(workerScript) { } return port; }, - rm : function(fn, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("rm", CONSTANTS.ScriptReadWriteRamCost); - } - updateDynamicRam("rm", CONSTANTS.ScriptReadWriteRamCost); + rm: function(fn, ip) { + updateDynamicRam("rm", getRamCost("rm")); if (ip == null || ip === "") { ip = workerScript.serverIp; @@ -2338,11 +2083,8 @@ function NetscriptFunctions(workerScript) { return status.res; }, - scriptRunning : function(scriptname, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("scriptRunning", CONSTANTS.ScriptArbScriptRamCost); - } - updateDynamicRam("scriptRunning", CONSTANTS.ScriptArbScriptRamCost); + scriptRunning: function(scriptname, ip) { + updateDynamicRam("scriptRunning", getRamCost("scriptRunning")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("scriptRunning() failed. Invalid IP or hostname passed in: " + ip); @@ -2355,11 +2097,8 @@ function NetscriptFunctions(workerScript) { } return false; }, - scriptKill : function(scriptname, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("scriptKill", CONSTANTS.ScriptArbScriptRamCost); - } - updateDynamicRam("scriptKill", CONSTANTS.ScriptArbScriptRamCost); + scriptKill: function(scriptname, ip) { + updateDynamicRam("scriptKill", getRamCost("scriptKill")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("scriptKill() failed. Invalid IP or hostname passed in: " + ip); @@ -2374,15 +2113,11 @@ function NetscriptFunctions(workerScript) { } return suc; }, - getScriptName : function() { - if (workerScript.checkingRam) {return 0;} + getScriptName: function() { return workerScript.name; }, - getScriptRam : function (scriptname, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("getScriptRam", CONSTANTS.ScriptGetScriptRamCost); - } - updateDynamicRam("getScriptRam", CONSTANTS.ScriptGetScriptRamCost); + getScriptRam: function(scriptname, ip=workerScript.serverIp) { + updateDynamicRam("getScriptRam", getRamCost("getScriptRam")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getScriptRam() failed. Invalid IP or hostname passed in: " + ip); @@ -2395,11 +2130,8 @@ function NetscriptFunctions(workerScript) { } return 0; }, - getHackTime : function(ip, hack, int) { - if (workerScript.checkingRam) { - return updateStaticRam("getHackTime", CONSTANTS.ScriptGetHackTimeRamCost); - } - updateDynamicRam("getHackTime", CONSTANTS.ScriptGetHackTimeRamCost); + getHackTime: function(ip, hack, int) { + updateDynamicRam("getHackTime", getRamCost("getHackTime")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getHackTime() failed. Invalid IP or hostname passed in: " + ip); @@ -2407,11 +2139,8 @@ function NetscriptFunctions(workerScript) { } return calculateHackingTime(server, hack, int); // Returns seconds }, - getGrowTime : function(ip, hack, int) { - if (workerScript.checkingRam) { - return updateStaticRam("getGrowTime", CONSTANTS.ScriptGetHackTimeRamCost); - } - updateDynamicRam("getGrowTime", CONSTANTS.ScriptGetHackTimeRamCost); + getGrowTime: function(ip, hack, int) { + updateDynamicRam("getGrowTime", getRamCost("getGrowTime")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getGrowTime() failed. Invalid IP or hostname passed in: " + ip); @@ -2419,11 +2148,8 @@ function NetscriptFunctions(workerScript) { } return calculateGrowTime(server, hack, int); // Returns seconds }, - getWeakenTime : function(ip, hack, int) { - if (workerScript.checkingRam) { - return updateStaticRam("getWeakenTime", CONSTANTS.ScriptGetHackTimeRamCost); - } - updateDynamicRam("getWeakenTime", CONSTANTS.ScriptGetHackTimeRamCost); + getWeakenTime: function(ip, hack, int) { + updateDynamicRam("getWeakenTime", getRamCost("getWeakenTime")); var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getWeakenTime() failed. Invalid IP or hostname passed in: " + ip); @@ -2431,11 +2157,8 @@ function NetscriptFunctions(workerScript) { } return calculateWeakenTime(server, hack, int); // Returns seconds }, - getScriptIncome : function(scriptname, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getScriptIncome", CONSTANTS.ScriptGetScriptRamCost); - } - updateDynamicRam("getScriptIncome", CONSTANTS.ScriptGetScriptRamCost); + getScriptIncome: function(scriptname, ip) { + updateDynamicRam("getScriptIncome", getRamCost("getScriptIncome")); if (arguments.length === 0) { // Get total script income var res = []; @@ -2461,11 +2184,8 @@ function NetscriptFunctions(workerScript) { return runningScriptObj.onlineMoneyMade / runningScriptObj.onlineRunningTime; } }, - getScriptExpGain : function(scriptname, ip) { - if (workerScript.checkingRam) { - return updateStaticRam("getScriptExpGain", CONSTANTS.ScriptGetScriptRamCost); - } - updateDynamicRam("getScriptExpGain", CONSTANTS.ScriptGetScriptRamCost); + getScriptExpGain: function(scriptname, ip) { + updateDynamicRam("getScriptExpGain", getRamCost("getScriptExpGain")); if (arguments.length === 0) { var total = 0; for (var i = 0; i < workerScripts.length; ++i) { @@ -2491,23 +2211,18 @@ function NetscriptFunctions(workerScript) { return runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime; } }, - nFormat : function(n, format) { - if (workerScript.checkingRam) { return 0; } + nFormat: function(n, format) { if (isNaN(n) || isNaN(parseFloat(n)) || typeof format !== "string") { return ""; } return numeralWrapper.format(parseFloat(n), format); }, - getTimeSinceLastAug : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getTimeSinceLastAug", CONSTANTS.ScriptGetHackTimeRamCost); - } - updateDynamicRam("getTimeSinceLastAug", CONSTANTS.ScriptGetHackTimeRamCost); + getTimeSinceLastAug: function() { + updateDynamicRam("getTimeSinceLastAug", getRamCost("getTimeSinceLastAug")); return Player.playtimeSinceLastAug; }, prompt : function(txt) { - if (workerScript.checkingRam) {return 0;} if (!isString(txt)) {txt = String(txt);} // The id for this popup will consist of the first 20 characters of the prompt string.. @@ -2537,8 +2252,7 @@ function NetscriptFunctions(workerScript) { createPopup(popupId, [textElement, yesBtn, noBtn]); }); }, - wget : async function(url, target, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { return 0; } + wget: async function(url, target, ip=workerScript.serverIp) { if (!isScriptFilename(target) && !target.endsWith(".txt")) { workerSript.log(`ERROR: wget() failed because of an invalid target file: ${target}. Target file must be a script or text file`); return Promise.resolve(false); @@ -2569,23 +2283,15 @@ function NetscriptFunctions(workerScript) { }); }, getFavorToDonate: function() { - if (workerScript.checkingRam) { - return updateStaticRam("getFavorToDonate", CONSTANTS.ScriptGetFavorToDonate); - } - updateDynamicRam("getFavorToDonate", CONSTANTS.ScriptGetFavorToDonate); + updateDynamicRam("getFavorToDonate", getRamCost("getFavorToDonate")); return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); }, /* Singularity Functions */ - universityCourse : function(universityName, className) { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("universityCourse", ramCost); - } - updateDynamicRam("universityCourse", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + universityCourse: function(universityName, className) { + updateDynamicRam("universityCourse", getRamCost("universityCourse")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run universityCourse(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -2666,15 +2372,10 @@ function NetscriptFunctions(workerScript) { return true; }, - gymWorkout : function(gymName, stat) { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("gymWorkout", ramCost); - } - updateDynamicRam("gymWorkout", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + gymWorkout: function(gymName, stat) { + updateDynamicRam("gymWorkout", getRamCost("gymWorkout")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run gymWorkout(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -2768,15 +2469,10 @@ function NetscriptFunctions(workerScript) { return true; }, - travelToCity(cityname) { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("travelToCity", ramCost); - } - updateDynamicRam("travelToCity", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + travelToCity: function(cityname) { + updateDynamicRam("travelToCity", getRamCost("travelToCity")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run travelToCity(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -2805,15 +2501,10 @@ function NetscriptFunctions(workerScript) { } }, - purchaseTor() { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("purchaseTor", ramCost); - } - updateDynamicRam("purchaseTor", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + purchaseTor: function() { + updateDynamicRam("purchaseTor", getRamCost("purchaseTor")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run purchaseTor(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -2830,8 +2521,8 @@ function NetscriptFunctions(workerScript) { } Player.loseMoney(CONSTANTS.TorRouterCost); - var darkweb = new Server({ - ip:createRandomIp(), hostname:"darkweb", organizationName:"", + var darkweb = safetlyCreateUniqueServer({ + ip: createUniqueRandomIp(), hostname:"darkweb", organizationName:"", isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1 }); AddToAllServers(darkweb); @@ -2845,15 +2536,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - purchaseProgram(programName) { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("purchaseProgram", ramCost); - } - updateDynamicRam("purchaseProgram", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + purchaseProgram: function(programName) { + updateDynamicRam("purchaseProgram", getRamCost("purchaseProgram")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run purchaseProgram(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -2897,15 +2583,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - getStats : function() { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getStats", ramCost); - } - updateDynamicRam("getStats", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + getStats: function() { + updateDynamicRam("getStats", getRamCost("getStats")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getStats(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return {}; } @@ -2921,15 +2602,10 @@ function NetscriptFunctions(workerScript) { intelligence: Player.intelligence } }, - getCharacterInformation : function() { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getCharacterInformation", ramCost); - } - updateDynamicRam("getCharacterInformation", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + getCharacterInformation: function() { + updateDynamicRam("getCharacterInformation", getRamCost("getCharacterInformation")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getCharacterInformation(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return {}; } @@ -2972,30 +2648,20 @@ function NetscriptFunctions(workerScript) { workMoneyGain: Player.workMoneyGained, }; }, - isBusy : function() { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("isBusy", ramCost); - } - updateDynamicRam("isBusy", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + isBusy: function() { + updateDynamicRam("isBusy", getRamCost("isBusy")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run isBusy(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return; } } return Player.isWorking; }, - stopAction : function() { - var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 2; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("stopAction", ramCost); - } - updateDynamicRam("stopAction", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 1)) { + stopAction: function() { + updateDynamicRam("stopAction", getRamCost("stopAction")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 0) { throw makeRuntimeRejectMsg(workerScript, "Cannot run stopAction(). It is a Singularity Function and requires SourceFile-4 (level 1) to run."); return false; } @@ -3009,15 +2675,10 @@ function NetscriptFunctions(workerScript) { } return false; }, - upgradeHomeRam : function() { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("upgradeHomeRam", ramCost); - } - updateDynamicRam("upgradeHomeRam", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + upgradeHomeRam: function() { + updateDynamicRam("upgradeHomeRam", getRamCost("upgradeHomeRam")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run upgradeHomeRam(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3045,15 +2706,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - getUpgradeHomeRamCost : function() { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 2; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getUpgradeHomeRamCost", ramCost); - } - updateDynamicRam("getUpgradeHomeRamCost", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getUpgradeHomeRamCost: function() { + updateDynamicRam("getUpgradeHomeRamCost", getRamCost("getUpgradeHomeRamCost")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getUpgradeHomeRamCost(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3061,15 +2717,10 @@ function NetscriptFunctions(workerScript) { return Player.getUpgradeHomeRamCost(); }, - workForCompany : function(companyName) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("workForCompany", ramCost); - } - updateDynamicRam("workForCompany", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + workForCompany: function(companyName) { + updateDynamicRam("workForCompany", getRamCost("workForCompany")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run workForCompany(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3123,15 +2774,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - applyToCompany : function(companyName, field) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("applyToCompany", ramCost); - } - updateDynamicRam("applyToCompany", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + applyToCompany: function(companyName, field) { + updateDynamicRam("applyToCompany", getRamCost("applyToCompany")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run applyToCompany(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3204,15 +2850,10 @@ function NetscriptFunctions(workerScript) { } return res; }, - getCompanyRep : function(companyName) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 2; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getCompanyRep", ramCost); - } - updateDynamicRam("getCompanyRep", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getCompanyRep: function(companyName) { + updateDynamicRam("getCompanyRep", getRamCost("getCompanyRep")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getCompanyRep(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3225,15 +2866,10 @@ function NetscriptFunctions(workerScript) { } return company.playerReputation; }, - getCompanyFavor : function(companyName) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getCompanyFavor", ramCost); - } - updateDynamicRam("getCompanyFavor", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getCompanyFavor: function(companyName) { + updateDynamicRam("getCompanyFavor", getRamCost("getCompanyFavor")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getCompanyFavor(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3246,15 +2882,10 @@ function NetscriptFunctions(workerScript) { } return company.favor; }, - getCompanyFavorGain : function(companyName) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getCompanyFavorGain", ramCost); - } - updateDynamicRam("getCompanyFavorGain", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getCompanyFavorGain: function(companyName) { + updateDynamicRam("getCompanyFavorGain", getRamCost("getCompanyFavorGain")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getCompanyFavorGain(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return -1; } @@ -3267,15 +2898,10 @@ function NetscriptFunctions(workerScript) { } return company.getFavorGain()[0]; }, - checkFactionInvitations : function() { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("checkFactionInvitations", ramCost); - } - updateDynamicRam("checkFactionInvitations", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + checkFactionInvitations: function() { + updateDynamicRam("checkFactionInvitations", getRamCost("checkFactionInvitations")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run checkFactionInvitations(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3283,15 +2909,10 @@ function NetscriptFunctions(workerScript) { // Make a copy of Player.factionInvitations return Player.factionInvitations.slice(); }, - joinFaction : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("joinFaction", ramCost); - } - updateDynamicRam("joinFaction", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + joinFaction: function(name) { + updateDynamicRam("joinFaction", getRamCost("joinFaction")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run joinFaction(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3322,15 +2943,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - workForFaction : function(name, type) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("workForFaction", ramCost); - } - updateDynamicRam("workForFaction", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + workForFaction: function(name, type) { + updateDynamicRam("workForFaction", getRamCost("workForFaction")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run workForFaction(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return false; } @@ -3422,15 +3038,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - getFactionRep : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getFactionRep", ramCost); - } - updateDynamicRam("getFactionRep", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getFactionRep: function(name) { + updateDynamicRam("getFactionRep", getRamCost("getFactionRep")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getFactionRep(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return -1; } @@ -3443,15 +3054,10 @@ function NetscriptFunctions(workerScript) { return Factions[name].playerReputation; }, - getFactionFavor : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getFactionFavor", ramCost); - } - updateDynamicRam("getFactionFavor", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + getFactionFavor: function(name) { + updateDynamicRam("getFactionFavor", getRamCost("getFactionFavor")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getFactionFavor(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return -1; } @@ -3465,14 +3071,9 @@ function NetscriptFunctions(workerScript) { return Factions[name].favor; }, getFactionFavorGain: function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getFactionFavorGain", ramCost); - } - updateDynamicRam("getFactionFavorGain", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 2)) { + updateDynamicRam("getFactionFavorGain", getRamCost("getFactionFavorGain")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 1) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getFactionFavorGain(). It is a Singularity Function and requires SourceFile-4 (level 2) to run."); return -1; } @@ -3485,15 +3086,10 @@ function NetscriptFunctions(workerScript) { return Factions[name].getFavorGain()[0]; }, - donateToFaction : function(name, amt) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("donateToFaction", ramCost); - } - updateDynamicRam("donateToFaction", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + donateToFaction: function(name, amt) { + updateDynamicRam("donateToFaction", getRamCost("donateToFaction")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run donateToFaction(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return; } @@ -3524,15 +3120,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - createProgram : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("createProgram", ramCost); - } - updateDynamicRam("createProgram", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + createProgram: function(name) { + updateDynamicRam("createProgram", getRamCost("createProgram")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run createProgram(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return false; } @@ -3578,15 +3169,10 @@ function NetscriptFunctions(workerScript) { } return true; }, - commitCrime : function(crimeRoughName) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("commitCrime", ramCost); - } - updateDynamicRam("commitCrime", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + commitCrime: function(crimeRoughName) { + updateDynamicRam("commitCrime", getRamCost("commitCrime")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run commitCrime(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return; } @@ -3614,15 +3200,10 @@ function NetscriptFunctions(workerScript) { } return crime.commit(Player, 1, {workerscript: workerScript}); }, - getCrimeChance : function(crimeRoughName) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getCrimeChance", ramCost); - } - updateDynamicRam("getCrimeChance", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getCrimeChance: function(crimeRoughName) { + updateDynamicRam("getCrimeChance", getRamCost("getCrimeChance")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getCrimeChance(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return; } @@ -3635,15 +3216,10 @@ function NetscriptFunctions(workerScript) { return crime.successRate(Player); }, - getOwnedAugmentations : function(purchased=false) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getOwnedAugmentations", ramCost); - } - updateDynamicRam("getOwnedAugmentations", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getOwnedAugmentations: function(purchased=false) { + updateDynamicRam("getOwnedAugmentations", getRamCost("getOwnedAugmentations")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getOwnedAugmentations(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return []; } @@ -3659,15 +3235,10 @@ function NetscriptFunctions(workerScript) { } return res; }, - getOwnedSourceFiles : function() { - let ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getOwnedSourceFiles", ramCost); - } - updateDynamicRam("getOwnedSourceFiles", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getOwnedSourceFiles: function() { + updateDynamicRam("getOwnedSourceFiles", getRamCost("getOwnedSourceFiles")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getOwnedSourceFiles(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return []; } @@ -3678,15 +3249,10 @@ function NetscriptFunctions(workerScript) { } return res; }, - getAugmentationsFromFaction : function(facname) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getAugmentationsFromFaction", ramCost); - } - updateDynamicRam("getAugmentationsFromFaction", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getAugmentationsFromFaction: function(facname) { + updateDynamicRam("getAugmentationsFromFaction", getRamCost("getAugmentationsFromFaction")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getAugmentationsFromFaction(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return []; } @@ -3713,15 +3279,10 @@ function NetscriptFunctions(workerScript) { return fac.augmentations.slice(); }, - getAugmentationPrereq : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getAugmentationPrereq", ramCost); - } - updateDynamicRam("getAugmentationPrereq", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getAugmentationPrereq: function(name) { + updateDynamicRam("getAugmentationPrereq", getRamCost("getAugmentationPrereq")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getAugmentationPrereq(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return false; } @@ -3735,15 +3296,10 @@ function NetscriptFunctions(workerScript) { var aug = Augmentations[name]; return aug.prereqs.slice(); }, - getAugmentationCost : function(name) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("getAugmentationCost", ramCost); - } - updateDynamicRam("getAugmentationCost", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + getAugmentationCost: function(name) { + updateDynamicRam("getAugmentationCost", getRamCost("getAugmentationCost")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run getAugmentationCost(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return false; } @@ -3757,15 +3313,10 @@ function NetscriptFunctions(workerScript) { var aug = Augmentations[name]; return [aug.baseRepRequirement, aug.baseCost]; }, - purchaseAugmentation : function(faction, name) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("purchaseAugmentation", ramCost); - } - updateDynamicRam("purchaseAugmentation", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + purchaseAugmentation: function(faction, name) { + updateDynamicRam("purchaseAugmentation", getRamCost("purchaseAugmentation")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run purchaseAugmentation(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return false; } @@ -3830,15 +3381,10 @@ function NetscriptFunctions(workerScript) { return false; } }, - installAugmentations : function(cbScript) { - var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; - if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} - if (workerScript.checkingRam) { - return updateStaticRam("installAugmentations", ramCost); - } - updateDynamicRam("installAugmentations", ramCost); - if (Player.bitNodeN != 4) { - if (!(hasSingularitySF && singularitySFLvl >= 3)) { + installAugmentations: function(cbScript) { + updateDynamicRam("installAugmentations", getRamCost("installAugmentations")); + if (Player.bitNodeN !== 4) { + if (SourceFileFlags[4] <= 2) { throw makeRuntimeRejectMsg(workerScript, "Cannot run installAugmentations(). It is a Singularity Function and requires SourceFile-4 (level 3) to run."); return false; } @@ -3855,12 +3401,9 @@ function NetscriptFunctions(workerScript) { }, // Gang API - gang : { - getMemberNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getMemberNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); - } - updateDynamicRam("getMemberNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); + gang: { + getMemberNames: function() { + updateDynamicRam("getMemberNames", getRamCost("gang", "getMemberNames")); nsGang.checkGangApiAccess(workerScript, "getMemberNames"); try { @@ -3873,11 +3416,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getMemberNames", e)); } }, - getGangInformation : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getGangInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("getGangInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); + getGangInformation: function() { + updateDynamicRam("getGangInformation", getRamCost("gang", "getGangInformation")); nsGang.checkGangApiAccess(workerScript, "getGangInformation"); try { @@ -3898,11 +3438,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getGangInformation", e)); } }, - getOtherGangInformation : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getOtherGangInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("getOtherGangInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); + getOtherGangInformation: function() { + updateDynamicRam("getOtherGangInformation", getRamCost("gang", "getOtherGangInformation")); nsGang.checkGangApiAccess(workerScript, "getOtherGangInformation"); try { @@ -3911,11 +3448,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getOtherGangInformation", e)); } }, - getMemberInformation : function(name) { - if (workerScript.checkingRam) { - return updateStaticRam("getMemberInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("getMemberInformation", CONSTANTS.ScriptGangApiBaseRamCost / 2); + getMemberInformation: function(name) { + updateDynamicRam("getMemberInformation", getRamCost("gang", "getMemberInformation")); nsGang.checkGangApiAccess(workerScript, "getMemberInformation"); try { @@ -3953,11 +3487,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getMemberInformation", e)); } }, - canRecruitMember : function() { - if (workerScript.checkingRam) { - return updateStaticRam("canRecruitMember", CONSTANTS.ScriptGangApiBaseRamCost / 4); - } - updateDynamicRam("canRecruitMember", CONSTANTS.ScriptGangApiBaseRamCost / 4); + canRecruitMember: function() { + updateDynamicRam("canRecruitMember", getRamCost("gang", "canRecruitMember")); nsGang.checkGangApiAccess(workerScript, "canRecruitMember"); try { @@ -3966,11 +3497,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("canRecruitMember", e)); } }, - recruitMember : function(name) { - if (workerScript.checkingRam) { - return updateStaticRam("recruitMember", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("recruitMember", CONSTANTS.ScriptGangApiBaseRamCost / 2); + recruitMember: function(name) { + updateDynamicRam("recruitMember", getRamCost("gang", "recruitMember")); nsGang.checkGangApiAccess(workerScript, "recruitMember"); try { @@ -3988,11 +3516,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("recruitMember", e)); } }, - getTaskNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getTaskNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); - } - updateDynamicRam("getTaskNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); + getTaskNames: function() { + updateDynamicRam("getTaskNames", getRamCost("gang", "getTaskNames")); nsGang.checkGangApiAccess(workerScript, "getTaskNames"); try { @@ -4003,11 +3528,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getTaskNames", e)); } }, - setMemberTask : function(memberName, taskName) { - if (workerScript.checkingRam) { - return updateStaticRam("setMemberTask", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("setMemberTask", CONSTANTS.ScriptGangApiBaseRamCost / 2); + setMemberTask: function(memberName, taskName) { + updateDynamicRam("setMemberTask", getRamCost("gang", "setMemberTask")); nsGang.checkGangApiAccess(workerScript, "setMemberTask"); try { @@ -4032,11 +3554,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("setMemberTask", e)); } }, - getEquipmentNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getEquipmentNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); - } - updateDynamicRam("getEquipmentNames", CONSTANTS.ScriptGangApiBaseRamCost / 4); + getEquipmentNames: function() { + updateDynamicRam("getEquipmentNames", getRamCost("gang", "getEquipmentNames")); nsGang.checkGangApiAccess(workerScript, "getEquipmentNames"); try { @@ -4045,11 +3564,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getEquipmentNames", e)); } }, - getEquipmentCost : function(equipName) { - if (workerScript.checkingRam) { - return updateStaticRam("getEquipmentCost", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("getEquipmentCost", CONSTANTS.ScriptGangApiBaseRamCost / 2); + getEquipmentCost: function(equipName) { + updateDynamicRam("getEquipmentCost", getRamCost("gang", "getEquipmentCost")); nsGang.checkGangApiAccess(workerScript, "getEquipmentCost"); try { @@ -4058,11 +3574,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getEquipmentCost", e)); } }, - getEquipmentType : function(equipName) { - if (workerScript.checkingRam) { - return updateStaticRam("getEquipmentType", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("getEquipmentType", CONSTANTS.ScriptGangApiBaseRamCost / 2); + getEquipmentType: function(equipName) { + updateDynamicRam("getEquipmentType", getRamCost("gang", "getEquipmentType")); nsGang.checkGangApiAccess(workerScript, "getEquipmentType"); try { @@ -4071,11 +3584,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getEquipmentType", e)); } }, - purchaseEquipment : function(memberName, equipName) { - if (workerScript.checkingRam) { - return updateStaticRam("purchaseEquipment", CONSTANTS.ScriptGangApiBaseRamCost); - } - updateDynamicRam("purchaseEquipment", CONSTANTS.ScriptGangApiBaseRamCost); + purchaseEquipment: function(memberName, equipName) { + updateDynamicRam("purchaseEquipment", getRamCost("gang", "purchaseEquipment")); nsGang.checkGangApiAccess(workerScript, "purchaseEquipment"); try { @@ -4100,11 +3610,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("purchaseEquipment", e)); } }, - ascendMember : function(name) { - if (workerScript.checkingRam) { - return updateStaticRam("ascendMember", CONSTANTS.ScriptGangApiBaseRamCost); - } - updateDynamicRam("ascendMember", CONSTANTS.ScriptGangApiBaseRamCost); + ascendMember: function(name) { + updateDynamicRam("ascendMember", getRamCost("gang", "ascendMember")); nsGang.checkGangApiAccess(workerScript, "ascendMember"); try { @@ -4120,11 +3627,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("ascendMember", e)); } }, - setTerritoryWarfare : function(engage) { - if (workerScript.checkingRam) { - return updateStaticRam("setTerritoryWarfare", CONSTANTS.ScriptGangApiBaseRamCost / 2); - } - updateDynamicRam("setTerritoryWarfare", CONSTANTS.ScriptGangApiBaseRamCost / 2); + setTerritoryWarfare: function(engage) { + updateDynamicRam("setTerritoryWarfare", getRamCost("gang", "setTerritoryWarfare")); nsGang.checkGangApiAccess(workerScript, "setTerritoryWarfare"); try { @@ -4143,11 +3647,8 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("setTerritoryWarfare", e)); } }, - getChanceToWinClash : function(otherGang) { - if (workerScript.checkingRam) { - return updateStaticRam("getChanceToWinClash", CONSTANTS.ScriptGangApiBaseRamCost); - } - updateDynamicRam("getChanceToWinClash", CONSTANTS.ScriptGangApiBaseRamCost); + getChanceToWinClash: function(otherGang) { + updateDynamicRam("getChanceToWinClash", getRamCost("gang", "getChanceToWinClash")); nsGang.checkGangApiAccess(workerScript, "getChanceToWinClash"); try { @@ -4164,8 +3665,7 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getChanceToWinClash", e)); } }, - getBonusTime : function() { - if (workerScript.checkingRam) { return 0; } + getBonusTime: function() { nsGang.checkGangApiAccess(workerScript, "getBonusTime"); try { @@ -4177,46 +3677,34 @@ function NetscriptFunctions(workerScript) { }, // end gang namespace // Bladeburner API - bladeburner : { - getContractNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getContractNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - } - updateDynamicRam("getContractNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + bladeburner: { + getContractNames: function() { + updateDynamicRam("getContractNames", getRamCost("bladeburner", "getContractNames")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getContractNamesNetscriptFn(); } throw makeRuntimeRejectMsg(workerScript, "getContractNames() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getOperationNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getOperationNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - } - updateDynamicRam("getOperationNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getOperationNames: function() { + updateDynamicRam("getOperationNames", getRamCost("bladeburner", "getOperationNames")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getOperationNamesNetscriptFn(); } throw makeRuntimeRejectMsg(workerScript, "getOperationNames() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getBlackOpNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getBlackOpNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - } - updateDynamicRam("getBlackOpNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getBlackOpNames: function() { + updateDynamicRam("getBlackOpNames", getRamCost("bladeburner", "getBlackOpNames")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getBlackOpNamesNetscriptFn(); } throw makeRuntimeRejectMsg(workerScript, "getBlackOpNames() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getBlackOpRank : function(name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getBlackOpRank", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 2); - } - updateDynamicRam("getBlackOpRank", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 2); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getBlackOpRank: function(name="") { + updateDynamicRam("getBlackOpRank", getRamCost("bladeburner", "getBlackOpRank")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { const actionId = Player.bladeburner.getActionIdFromTypeAndName('blackops', name) if (!actionId) { return -1; @@ -4230,34 +3718,25 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getBlackOpRank() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getGeneralActionNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getGeneralActionNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - } - updateDynamicRam("getGeneralActionNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getGeneralActionNames: function() { + updateDynamicRam("getGeneralActionNames", getRamCost("bladeburner", "getGeneralActionNames")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getGeneralActionNamesNetscriptFn(); } throw makeRuntimeRejectMsg(workerScript, "getGeneralActionNames() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getSkillNames : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getSkillNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - } - updateDynamicRam("getSkillNames", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 10); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getSkillNames: function() { + updateDynamicRam("getSkillNames", getRamCost("bladeburner", "getSkillNames")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getSkillNamesNetscriptFn(); } throw makeRuntimeRejectMsg(workerScript, "getSkillNames() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - startAction : function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("startAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("startAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + startAction: function(type="", name="") { + updateDynamicRam("startAction", getRamCost("bladeburner", "startAction")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.startActionNetscriptFn(type, name, workerScript); } catch(e) { @@ -4267,34 +3746,25 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "startAction() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - stopBladeburnerAction : function() { - if (workerScript.checkingRam) { - return updateStaticRam("stopBladeburnerAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 2); - } - updateDynamicRam("stopBladeburnerAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 2); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + stopBladeburnerAction: function() { + updateDynamicRam("stopBladeburnerAction", getRamCost("bladeburner", "stopBladeburnerAction")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.resetAction(); } throw makeRuntimeRejectMsg(workerScript, "stopBladeburnerAction() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getCurrentAction : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getCurrentAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 4); - } - updateDynamicRam("getCurrentAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 4); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getCurrentAction: function() { + updateDynamicRam("getCurrentAction", getRamCost("bladeburner", "getCurrentAction")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.getTypeAndNameFromActionId(Player.bladeburner.action); } throw makeRuntimeRejectMsg(workerScript, "getCurrentAction() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getActionTime : function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionTime", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionTime", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getActionTime: function(type="", name="") { + updateDynamicRam("getActionTime", getRamCost("bladeburner", "getActionTime")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getActionTimeNetscriptFn(type, name, workerScript); } catch(e) { @@ -4304,12 +3774,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getActionTime() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getActionEstimatedSuccessChance : function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionEstimatedSuccessChance", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionEstimatedSuccessChance", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getActionEstimatedSuccessChance: function(type="", name="") { + updateDynamicRam("getActionEstimatedSuccessChance", getRamCost("bladeburner", "getActionEstimatedSuccessChance")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getActionEstimatedSuccessChanceNetscriptFn(type, name, workerScript); } catch(e) { @@ -4320,10 +3787,7 @@ function NetscriptFunctions(workerScript) { "at the Bladeburner division or because you do not have Source-File 7"); }, getActionRepGain: function(type="", name="", level) { - if (workerScript.checkingRam) { - return updateStaticRam("getActionRepGain", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionRepGain", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("getActionRepGain", getRamCost("bladeburner", "getActionRepGain")); checkBladeburnerAccess(workerScript, "getActionRepGain"); try { @@ -4350,12 +3814,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, unknownBladeburnerExceptionMessage("getActionAutolevel", err)); } }, - getActionCountRemaining : function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionCountRemaining", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionCountRemaining", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getActionCountRemaining: function(type="", name="") { + updateDynamicRam("getActionCountRemaining", getRamCost("bladeburner", "getActionCountRemaining")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); } catch(e) { @@ -4366,10 +3827,7 @@ function NetscriptFunctions(workerScript) { "at the Bladeburner division or because you do not have Source-File 7"); }, getActionMaxLevel: function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionMaxLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionMaxLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("getActionMaxLevel", getRamCost("bladeburner", "getActionMaxLevel")); checkBladeburnerAccess(workerScript, "getActionMaxLevel"); try { @@ -4390,10 +3848,7 @@ function NetscriptFunctions(workerScript) { } }, getActionCurrentLevel: function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionCurrentLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionCurrentLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("getActionCurrentLevel", getRamCost("bladeburner", "getActionCurrentLevel")); checkBladeburnerAccess(workerScript, "getActionCurrentLevel"); try { @@ -4414,10 +3869,7 @@ function NetscriptFunctions(workerScript) { } }, getActionAutolevel: function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getActionAutolevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getActionAutolevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("getActionAutolevel", getRamCost("bladeburner", "getActionAutolevel")); checkBladeburnerAccess(workerScript, "getActionAutolevel"); try { @@ -4438,10 +3890,7 @@ function NetscriptFunctions(workerScript) { } }, setActionAutolevel: function(type="", name="", autoLevel=true) { - if (workerScript.checkingRam) { - return updateStaticRam("setActionAutolevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("setActionAutolevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("setActionAutolevel", getRamCost("bladeburner", "setActionAutolevel")); checkBladeburnerAccess(workerScript, "setActionAutolevel"); try { @@ -4462,10 +3911,7 @@ function NetscriptFunctions(workerScript) { } }, setActionLevel: function(type="", name="", level=1) { - if (workerScript.checkingRam) { - return updateStaticRam("setActionLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("setActionLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); + updateDynamicRam("setActionLevel", getRamCost("bladeburner", "setActionLevel")); checkBladeburnerAccess(workerScript, "setActionLevel"); try { @@ -4493,34 +3939,25 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, unknownBladeburnerExceptionMessage("setActionLevel", err)); } }, - getRank : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getRank", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getRank", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getRank: function() { + updateDynamicRam("getRank", getRamCost("bladeburner", "getRank")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.rank; } throw makeRuntimeRejectMsg(workerScript, "getRank() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getSkillPoints : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getSkillPoints", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getSkillPoints", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getSkillPoints: function() { + updateDynamicRam("getSkillPoints", getRamCost("bladeburner", "getSkillPoints")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.skillPoints; } throw makeRuntimeRejectMsg(workerScript, "getSkillPoints() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getSkillLevel : function(skillName="") { - if (workerScript.checkingRam) { - return updateStaticRam("getSkillLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getSkillLevel", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getSkillLevel: function(skillName="") { + updateDynamicRam("getSkillLevel", getRamCost("bladeburner", "getSkillLevel")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); } catch(e) { @@ -4530,12 +3967,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getSkillLevel() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getSkillUpgradeCost : function(skillName="") { - if (workerScript.checkingRam) { - return updateStaticRam("getSkillUpgradeCost", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getSkillUpgradeCost", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getSkillUpgradeCost: function(skillName="") { + updateDynamicRam("getSkillUpgradeCost", getRamCost("bladeburner", "getSkillUpgradeCost")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); } catch(e) { @@ -4545,12 +3979,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getSkillUpgradeCost() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - upgradeSkill : function(skillName) { - if (workerScript.checkingRam) { - return updateStaticRam("upgradeSkill", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("upgradeSkill", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + upgradeSkill: function(skillName) { + updateDynamicRam("upgradeSkill", getRamCost("bladeburner", "upgradeSkill")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); } catch(e) { @@ -4560,12 +3991,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "upgradeSkill() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getTeamSize : function(type="", name="") { - if (workerScript.checkingRam) { - return updateStaticRam("getTeamSize", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getTeamSize", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getTeamSize: function(type="", name="") { + updateDynamicRam("getTeamSize", getRamCost("bladeburner", "getTeamSize")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); } catch(e) { @@ -4575,12 +4003,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getTeamSize() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - setTeamSize : function(type="", name="", size) { - if (workerScript.checkingRam) { - return updateStaticRam("setTeamSize", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("setTeamSize", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + setTeamSize: function(type="", name="", size) { + updateDynamicRam("setTeamSize",getRamCost("bladeburner", "setTeamSize")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); } catch(e) { @@ -4590,12 +4015,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "setTeamSize() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getCityEstimatedPopulation : function(cityName) { - if (workerScript.checkingRam) { - return updateStaticRam("getCityEstimatedPopulation", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getCityEstimatedPopulation", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getCityEstimatedPopulation: function(cityName) { + updateDynamicRam("getCityEstimatedPopulation", getRamCost("bladeburner", "getCityEstimatedPopulation")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getCityEstimatedPopulationNetscriptFn(cityName, workerScript); } catch(e) { @@ -4605,12 +4027,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getCityEstimatedPopulation() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getCityEstimatedCommunities : function(cityName) { - if (workerScript.checkingRam) { - return updateStaticRam("getCityEstimatedCommunities", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getCityEstimatedCommunities", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getCityEstimatedCommunities: function(cityName) { + updateDynamicRam("getCityEstimatedCommunities", getRamCost("bladeburner", "getCityEstimatedCommunities")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getCityEstimatedCommunitiesNetscriptFn(cityName, workerScript); } catch(e) { @@ -4620,12 +4039,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getCityEstimatedCommunities() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getCityChaos : function(cityName) { - if (workerScript.checkingRam) { - return updateStaticRam("getCityChaos", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getCityChaos", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getCityChaos: function(cityName) { + updateDynamicRam("getCityChaos", getRamCost("bladeburner", "getCityChaos")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.getCityChaosNetscriptFn(cityName, workerScript); } catch(e) { @@ -4635,12 +4051,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getCityChaos() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getCity : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getCity: function() { + updateDynamicRam("getCity", getRamCost("bladeburner", "getCity")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.city; } catch(e) { @@ -4650,12 +4063,9 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "getCity() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - switchCity : function(cityName) { - if (workerScript.checkingRam) { - return updateStaticRam("switchCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("switchCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + switchCity: function(cityName) { + updateDynamicRam("switchCity", getRamCost("bladeburner", "switchCity")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { try { return Player.bladeburner.switchCityNetscriptFn(cityName, workerScript); } catch(e) { @@ -4665,34 +4075,25 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "switchCity() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getStamina : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getStamina", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("getStamina", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getStamina: function() { + updateDynamicRam("getStamina", getRamCost("bladeburner", "getStamina")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return [Player.bladeburner.stamina, Player.bladeburner.maxStamina]; } throw makeRuntimeRejectMsg(workerScript, "getStamina() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - joinBladeburnerFaction : function() { - if (workerScript.checkingRam) { - return updateStaticRam("joinBladeburnerFaction", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("joinBladeburnerFaction", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) { + joinBladeburnerFaction: function() { + updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction")); + if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Player.bladeburner.joinBladeburnerFactionNetscriptFn(workerScript); } throw makeRuntimeRejectMsg(workerScript, "joinBladeburnerFaction() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - joinBladeburnerDivision : function() { - if (workerScript.checkingRam) { - return updateStaticRam("joinBladeburnerDivision", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - } - updateDynamicRam("joinBladeburnerDivision", CONSTANTS.ScriptBladeburnerApiBaseRamCost); - if ((Player.bitNodeN === 7 || hasBladeburner2079SF)) { + joinBladeburnerDivision: function() { + 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 @@ -4715,21 +4116,19 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeRejectMsg(workerScript, "joinBladeburnerDivision() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); }, - getBonusTime : function() { - if (workerScript.checkingRam) {return 0;} - if ((Player.bitNodeN === 7 || hasBladeburner2079SF)) { + getBonusTime: function() { + if ((Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) { return Math.round(Player.bladeburner.storedCycles / 5); } throw makeRuntimeRejectMsg(workerScript, "getBonusTime() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + "at the Bladeburner division or because you do not have Source-File 7"); } }, // End Bladeburner - codingcontract : { - attempt : function(answer, fn, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("attempt", CONSTANTS.ScriptCodingContractBaseRamCost); - } - updateDynamicRam("attempt", CONSTANTS.ScriptCodingContractBaseRamCost); + + // Coding Contract API + codingcontract: { + attempt: function(answer, fn, ip=workerScript.serverIp) { + updateDynamicRam("attempt", getRamCost("codingcontract", "attempt")); const contract = getCodingContract(fn, ip); if (contract == null) { workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`); @@ -4766,11 +4165,8 @@ function NetscriptFunctions(workerScript) { return false; } }, - getContractType : function(fn, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("getContractType", CONSTANTS.ScriptCodingContractBaseRamCost / 2); - } - updateDynamicRam("getContractType", CONSTANTS.ScriptCodingContractBaseRamCost / 2); + getContractType: function(fn, ip=workerScript.serverIp) { + updateDynamicRam("getContractType", getRamCost("codingcontract", "getContractType")); let contract = getCodingContract(fn, ip); if (contract == null) { workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`); @@ -4778,11 +4174,8 @@ function NetscriptFunctions(workerScript) { } return contract.getType(); }, - getData : function(fn, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2); - } - updateDynamicRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2); + getData: function(fn, ip=workerScript.serverIp) { + updateDynamicRam("getData", getRamCost("codingcontract", "getData")); let contract = getCodingContract(fn, ip); if (contract == null) { workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`); @@ -4805,11 +4198,8 @@ function NetscriptFunctions(workerScript) { return data; } }, - getDescription : function(fn, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("getDescription", CONSTANTS.ScriptCodingContractBaseRamCost / 2); - } - updateDynamicRam("getDescription", CONSTANTS.ScriptCodingContractBaseRamCost / 2); + getDescription: function(fn, ip=workerScript.serverIp) { + updateDynamicRam("getDescription", getRamCost("codingcontract", "getDescription")); var contract = getCodingContract(fn, ip); if (contract == null) { workerScript.log(`ERROR: codingcontract.getDescription() failed because it could find the specified contract ${fn} on server ${ip}`); @@ -4817,11 +4207,8 @@ function NetscriptFunctions(workerScript) { } return contract.getDescription(); }, - getNumTriesRemaining : function(fn, ip=workerScript.serverIp) { - if (workerScript.checkingRam) { - return updateStaticRam("getNumTriesRemaining", CONSTANTS.ScriptCodingContractBaseRamCost / 2); - } - updateDynamicRam("getNumTriesRemaining", CONSTANTS.ScriptCodingContractBaseRamCost / 2); + getNumTriesRemaining: function(fn, ip=workerScript.serverIp) { + updateDynamicRam("getNumTriesRemaining", getRamCost("codingcontract", "getNumTriesRemaining")); var contract = getCodingContract(fn, ip); if (contract == null) { workerScript.log(`ERROR: codingcontract.getNumTriesRemaining() failed because it could find the specified contract ${fn} on server ${ip}`); @@ -4830,25 +4217,21 @@ function NetscriptFunctions(workerScript) { return contract.getMaxNumTries() - contract.tries; }, }, // End coding contracts - sleeve : { - getNumSleeves : function() { - if (workerScript.checkingRam) { - return updateStaticRam("getNumSleeves", CONSTANTS.ScriptSleeveBaseRamCost); - } + + // Duplicate Sleeve API + sleeve: { + getNumSleeves: function() { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getNumSleeves() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("getNumSleeves", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("getNumSleeves", getRamCost("sleeve", "getNumSleeves")); return Player.sleeves.length; }, - setToShockRecovery : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("setToShockRecovery", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToShockRecovery: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToShockRecovery() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToShockRecovery", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToShockRecovery", getRamCost("sleeve", "setToShockRecovery")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToShockRecovery(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4856,14 +4239,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].shockRecovery(Player); }, - setToSynchronize : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("setToSynchronize", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToSynchronize: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToSynchronize() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToSynchronize", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToSynchronize", getRamCost("sleeve", "setToSynchronize")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToSynchronize(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4871,14 +4251,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].synchronize(Player); }, - setToCommitCrime : function(sleeveNumber=0, crimeName="") { - if (workerScript.checkingRam) { - return updateStaticRam("setToCommitCrime", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToCommitCrime: function(sleeveNumber=0, crimeName="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToCommitCrime() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToCommitCrime", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToCommitCrime", getRamCost("sleeve", "setToCommitCrime")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToCommitCrime(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4886,14 +4263,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].commitCrime(Player, crimeName); }, - setToUniversityCourse : function(sleeveNumber=0, universityName="", className="") { - if (workerScript.checkingRam) { - return updateStaticRam("setToUniversityCourse", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToUniversityCourse: function(sleeveNumber=0, universityName="", className="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToUniversityCourse() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToUniversityCourse", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToUniversityCourse", getRamCost("sleeve", "setToUniversityCourse")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToUniversityCourse(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4901,14 +4275,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].takeUniversityCourse(Player, universityName, className); }, - travel : function(sleeveNumber=0, cityName="") { - if (workerScript.checkingRam) { - return updateStaticRam("travel", CONSTANTS.ScriptSleeveBaseRamCost); - } + travel: function(sleeveNumber=0, cityName="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "travel() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("travel", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("travel", getRamCost("sleeve", "travel")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.travel(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4916,14 +4287,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].travel(Player, cityName); }, - setToCompanyWork : function(sleeveNumber=0, companyName="") { - if (workerScript.checkingRam) { - return updateStaticRam("setToCompanyWork", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToCompanyWork: function(sleeveNumber=0, companyName="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToCompanyWork() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToCompanyWork", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToCompanyWork", getRamCost("sleeve", "setToCompanyWork")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToCompanyWork(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4941,14 +4309,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].workForCompany(Player, companyName); }, - setToFactionWork : function(sleeveNumber=0, factionName="", workType="") { - if (workerScript.checkingRam) { - return updateStaticRam("setToFactionWork", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToFactionWork: function(sleeveNumber=0, factionName="", workType="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToFactionWork() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToFactionWork", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToFactionWork", getRamCost("sleeve", "setToFactionWork")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToFactionWork(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4966,14 +4331,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].workForFaction(Player, factionName, workType); }, - setToGymWorkout : function(sleeveNumber=0, gymName="", stat="") { - if (workerScript.checkingRam) { - return updateStaticRam("setToGymWorkout", CONSTANTS.ScriptSleeveBaseRamCost); - } + setToGymWorkout: function(sleeveNumber=0, gymName="", stat="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "setToGymWorkout() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("setToGymWorkout", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("setToGymWorkout", getRamCost("sleeve", "setToGymWorkout")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.setToGymWorkout(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -4981,14 +4343,11 @@ function NetscriptFunctions(workerScript) { return Player.sleeves[sleeveNumber].workoutAtGym(Player, gymName, stat); }, - getSleeveStats : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("workoutAtGym", CONSTANTS.ScriptSleeveBaseRamCost); - } + getSleeveStats: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getStats() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("workoutAtGym", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("workoutAtGym", getRamCost("sleeve", "getSleeveStats")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.workoutAtGym(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -5006,14 +4365,11 @@ function NetscriptFunctions(workerScript) { charisma: sl.charisma, }; }, - getTask : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("getTask", CONSTANTS.ScriptSleeveBaseRamCost); - } + getTask: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getTask() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("getTask", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("getTask", getRamCost("sleeve", "getTask")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.getTask(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -5028,14 +4384,11 @@ function NetscriptFunctions(workerScript) { factionWorkType: FactionWorkType[sl.factionWorkType], }; }, - getInformation : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("getInformation", CONSTANTS.ScriptSleeveBaseRamCost); - } + getInformation: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getInformation() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("getInformation", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("getInformation", getRamCost("sleeve", "getInformation")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.getInformation(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -5099,14 +4452,11 @@ function NetscriptFunctions(workerScript) { workRepGain: sl.getRepGain(Player), } }, - getSleeveAugmentations : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("getSleeveAugmentations", CONSTANTS.ScriptSleeveBaseRamCost); - } + getSleeveAugmentations: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getSleeveAugmentations() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("getSleeveAugmentations", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("getSleeveAugmentations", getRamCost("sleeve", "getSleeveAugmentations")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.getSleeveAugmentations(${sleeveNumber}) failed because it is an invalid sleeve number.`); return []; @@ -5118,14 +4468,11 @@ function NetscriptFunctions(workerScript) { } return augs; }, - getSleevePurchasableAugs : function(sleeveNumber=0) { - if (workerScript.checkingRam) { - return updateStaticRam("getSleevePurchasableAugs", CONSTANTS.ScriptSleeveBaseRamCost); - } + getSleevePurchasableAugs: function(sleeveNumber=0) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getSleevePurchasableAugs() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("getSleevePurchasableAugs", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("getSleevePurchasableAugs", getRamCost("sleeve", "getSleevePurchasableAugs")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.getSleevePurchasableAugs(${sleeveNumber}) failed because it is an invalid sleeve number.`); return []; @@ -5143,14 +4490,11 @@ function NetscriptFunctions(workerScript) { return augs; }, - purchaseSleeveAug : function(sleeveNumber=0, augName="") { - if (workerScript.checkingRam) { - return updateStaticRam("purchaseSleeveAug", CONSTANTS.ScriptSleeveBaseRamCost); - } + purchaseSleeveAug: function(sleeveNumber=0, augName="") { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "purchaseSleeveAug() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("purchaseSleeveAug", CONSTANTS.ScriptSleeveBaseRamCost); + updateDynamicRam("purchaseSleeveAug", getRamCost("sleeve", "purchaseSleeveAug")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.purchaseSleeveAug(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; @@ -5166,14 +4510,11 @@ function NetscriptFunctions(workerScript) { }, // End sleeve heart: { // Easter egg function - break : function() { - if (workerScript.checkingRam) { return 0; } - + break: function() { return Player.karma; } } } // End return } // End NetscriptFunction() -export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF, - hasWallStreetSF, wallStreetSFLvl, hasCorporationSF, hasAISF, hasBladeburnerSF}; +export { NetscriptFunctions }; diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js index 7dcba4852..1f55d600f 100644 --- a/src/NetscriptWorker.js +++ b/src/NetscriptWorker.js @@ -1,3 +1,9 @@ +/** + * Functions for handling WorkerScripts, which are the underlying mechanism + * that allows for scripts to run + */ +import { WorkerScript } from "./Netscript/WorkerScript"; + import { addActiveScriptsItem, deleteActiveScriptsItem, @@ -6,9 +12,7 @@ import { import { CONSTANTS } from "./Constants"; import { Engine } from "./engine"; import { Interpreter } from "./JSInterpreter"; -import { Environment } from "./NetscriptEnvironment"; import { - evaluate, isScriptErrorMessage, makeRuntimeRejectMsg, killNetscriptDelay @@ -16,6 +20,11 @@ import { import { NetscriptFunctions } from "./NetscriptFunctions"; import { executeJSScript } from "./NetscriptJSEvaluator"; import { NetscriptPort } from "./NetscriptPort"; +import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers"; +import { + findRunningScript, + scriptCalculateOfflineProduction, +} from "./Script/ScriptHelpers"; import { AllServers } from "./Server/AllServers"; import { Settings } from "./Settings/Settings"; import { setTimeoutRef } from "./utils/SetTimeoutRef"; @@ -29,79 +38,17 @@ import { arrayToString } from "../utils/helpers/arrayToString"; import { roundToTwo } from "../utils/helpers/roundToTwo"; import { isString } from "../utils/StringHelperFunctions"; - const walk = require("acorn/dist/walk"); -function WorkerScript(runningScriptObj) { - this.name = runningScriptObj.filename; - this.running = false; - this.serverIp = runningScriptObj.server; - this.code = runningScriptObj.getCode(); - this.env = new Environment(this); - this.env.set("args", runningScriptObj.args.slice()); - this.output = ""; - this.ramUsage = 0; - this.scriptRef = runningScriptObj; - this.errorMessage = ""; - this.args = runningScriptObj.args.slice(); - this.delay = null; - this.fnWorker = null; //Workerscript for a function call - this.checkingRam = false; - this.loadedFns = {}; //Stores names of fns that are "loaded" by this script, thus using RAM. Used for static RAM evaluation - this.disableLogs = {}; //Stores names of fns that should have logs disabled - - //Properties used for dynamic RAM evaluation - this.dynamicRamUsage = CONSTANTS.ScriptBaseRamCost; - this.dynamicLoadedFns = {}; -} - -//Returns the server on which the workerScript is running -WorkerScript.prototype.getServer = function() { - return AllServers[this.serverIp]; -} - -//Returns the Script object for the underlying script -WorkerScript.prototype.getScript = function() { - let server = this.getServer(); - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === this.name) { - return server.scripts[i]; - } - } - console.log("ERROR: Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong"); - return null; -} - -//Returns the Script object for the specified script -WorkerScript.prototype.getScriptOnServer = function(fn, server) { - if (server == null) { - server = this.getServer(); - } - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === fn) { - return server.scripts[i]; - } - } - return null; -} - -WorkerScript.prototype.shouldLog = function(fn) { - return (this.disableLogs.ALL == null && this.disableLogs[fn] == null); -} - -WorkerScript.prototype.log = function(txt) { - this.scriptRef.log(txt); -} - //Array containing all scripts that are running across all servers, to easily run them all -let workerScripts = []; +export const workerScripts = []; -var NetscriptPorts = []; +export const NetscriptPorts = []; for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) { NetscriptPorts.push(new NetscriptPort()); } -function prestigeWorkerScripts() { +export function prestigeWorkerScripts() { for (var i = 0; i < workerScripts.length; ++i) { deleteActiveScriptsItem(workerScripts[i]); workerScripts[i].env.stopFlag = true; @@ -457,7 +404,7 @@ function processNetscript1Imports(code, workerScript) { } //Loop through workerScripts and run every script that is not currently running -function runScriptsLoop() { +export function runScriptsLoop() { var scriptDeleted = false; //Delete any scripts that finished or have been killed. Loop backwards bc removing items screws up indexing @@ -563,30 +510,28 @@ function runScriptsLoop() { setTimeoutRef(runScriptsLoop, 6000); } -//Queues a script to be killed by settings its stop flag to true. Then, the code will reject -//all of its promises recursively, and when it does so it will no longer be running. -//The runScriptsLoop() will then delete the script from worker scripts -function killWorkerScript(runningScriptObj, serverIp) { +/** + * Queues a script to be killed by setting its stop flag to true. This + * kills and timed/blocking Netscript functions (like hack(), sleep(), etc.) and + * prevents any further execution of Netscript functions. + * The runScriptsLoop() handles the actual deletion of the WorkerScript + */ +export function killWorkerScript(runningScriptObj, serverIp) { for (var i = 0; i < workerScripts.length; i++) { if (workerScripts[i].name == runningScriptObj.filename && workerScripts[i].serverIp == serverIp && compareArrays(workerScripts[i].args, runningScriptObj.args)) { workerScripts[i].env.stopFlag = true; killNetscriptDelay(workerScripts[i]); - //Recursively kill all functions - var curr = workerScripts[i]; - while (curr.fnWorker) { - curr.fnWorker.env.stopFlag = true; - killNetscriptDelay(curr.fnWorker); - curr = curr.fnWorker; - } return true; } } return false; } -//Queues a script to be run -function addWorkerScript(runningScriptObj, server) { +/** + * Given a RunningScript object, queues that script to be run + */ +export function addWorkerScript(runningScriptObj, server) { var filename = runningScriptObj.filename; //Update server's ram usage @@ -596,7 +541,7 @@ function addWorkerScript(runningScriptObj, server) { } else { runningScriptObj.threads = 1; } - var ramUsage = roundToTwo(runningScriptObj.getRamUsage() * threads); + var ramUsage = roundToTwo(getRamUsageFromRunningScript(runningScriptObj) * threads); var ramAvailable = server.maxRam - server.ramUsed; if (ramUsage > ramAvailable) { dialogBoxCreate("Not enough RAM to run script " + runningScriptObj.filename + " with args " + @@ -608,7 +553,7 @@ function addWorkerScript(runningScriptObj, server) { server.ramUsed = roundToTwo(server.ramUsed + ramUsage); //Create the WorkerScript - var s = new WorkerScript(runningScriptObj); + var s = new WorkerScript(runningScriptObj, NetscriptFunctions); s.ramUsage = ramUsage; //Add the WorkerScript to the Active Scripts list @@ -619,14 +564,105 @@ function addWorkerScript(runningScriptObj, server) { return; } -//Updates the online running time stat of all running scripts -function updateOnlineScriptTimes(numCycles = 1) { +/** + * Updates the online running time stat of all running scripts + */ +export function updateOnlineScriptTimes(numCycles = 1) { var time = (numCycles * Engine._idleSpeed) / 1000; //seconds for (var i = 0; i < workerScripts.length; ++i) { workerScripts[i].scriptRef.onlineRunningTime += time; } } -export {WorkerScript, workerScripts, NetscriptPorts, runScriptsLoop, - killWorkerScript, addWorkerScript, updateOnlineScriptTimes, - prestigeWorkerScripts}; +/** + * Called when the game is loaded. Loads all running scripts (from all servers) + * into worker scripts so that they will start running + */ +export function loadAllRunningScripts() { + var total = 0; + let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1); + if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); } + for (const property in AllServers) { + if (AllServers.hasOwnProperty(property)) { + const server = AllServers[property]; + + // Reset each server's RAM usage to 0 + server.ramUsed = 0; + + // Reset modules on all scripts + for (let i = 0; i < server.scripts.length; ++i) { + server.scripts[i].module = ""; + } + + if (skipScriptLoad) { + // Start game with no scripts + server.runningScripts.length = 0; + } else { + for (let j = 0; j < server.runningScripts.length; ++j) { + addWorkerScript(server.runningScripts[j], server); + + // Offline production + total += scriptCalculateOfflineProduction(server.runningScripts[j]); + } + } + } + } + + return total; +} + +/** + * Run a script from inside another script (run(), exec(), spawn(), etc.) + */ +export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) { + //Check if the script is already running + let runningScriptObj = findRunningScript(scriptname, args, server); + if (runningScriptObj != null) { + workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname); + return Promise.resolve(false); + } + + //'null/undefined' arguments are not allowed + for (let i = 0; i < args.length; ++i) { + if (args[i] == null) { + workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument"); + return Promise.resolve(false); + } + } + + //Check if the script exists and if it does run it + for (var i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename == scriptname) { + //Check for admin rights and that there is enough RAM availble to run + var script = server.scripts[i]; + var ramUsage = script.ramUsage; + threads = Math.round(Number(threads)); //Convert to number and round + if (threads === 0) { return Promise.resolve(false); } + ramUsage = ramUsage * threads; + var ramAvailable = server.maxRam - server.ramUsed; + + if (server.hasAdminRights == false) { + workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!"); + return Promise.resolve(false); + } else if (ramUsage > ramAvailable){ + workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!"); + return Promise.resolve(false); + } else { + //Able to run script + if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) { + workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`); + } + let runningScriptObj = new RunningScript(script, args); + runningScriptObj.threads = threads; + addWorkerScript(runningScriptObj, server); + + // Push onto runningScripts. + // This has to come after addWorkerScript() because that fn updates RAM usage + server.runScript(runningScriptObj, Player.hacknet_node_money_mult); + return Promise.resolve(true); + } + } + } + workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname); + return Promise.resolve(false); +} diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index bcd14088a..be95132c8 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -51,6 +51,7 @@ export interface IPlayer { purchasedServers: any[]; queuedAugmentations: IPlayerOwnedAugmentation[]; resleeves: Resleeve[]; + scriptProdSinceLastAug: number; sleeves: Sleeve[]; sleevesFromCovenant: number; sourceFiles: IPlayerOwnedSourceFile[]; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.js b/src/PersonObjects/Player/PlayerObjectGeneralMethods.js index e5728f6ff..6b310e58f 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.js +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.js @@ -1,60 +1,69 @@ -import { Augmentations } from "../../Augmentation/Augmentations"; -import { applyAugmentation } from "../../Augmentation/AugmentationHelpers"; -import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; -import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; -import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; -import { Bladeburner } from "../../Bladeburner"; -import { CodingContractRewardType } from "../../CodingContracts"; -import { Company } from "../../Company/Company"; -import { Companies } from "../../Company/Companies"; -import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPosition"; -import { getJobRequirementText } from "../../Company/GetJobRequirementText"; -import { CompanyPositions } from "../../Company/CompanyPositions"; -import * as posNames from "../../Company/data/companypositionnames"; -import {CONSTANTS} from "../../Constants"; -import { Corporation } from "../../Corporation/Corporation"; -import { Programs } from "../../Programs/Programs"; -import { determineCrimeSuccess } from "../../Crime/CrimeHelpers"; -import { Crimes } from "../../Crime/Crimes"; -import {Engine} from "../../engine"; -import { Faction } from "../../Faction/Faction"; -import { Factions } from "../../Faction/Factions"; -import { displayFactionContent } from "../../Faction/FactionHelpers"; -import { resetGangs } from "../../Gang"; -import { hasHacknetServers } from "../../Hacknet/HacknetHelpers"; -import { HashManager } from "../../Hacknet/HashManager"; -import { Cities } from "../../Locations/Cities"; -import { Locations } from "../../Locations/Locations"; -import { CityName } from "../../Locations/data/CityNames"; -import { LocationName } from "../../Locations/data/LocationNames"; -import {hasBn11SF, hasWallStreetSF,hasAISF} from "../../NetscriptFunctions"; -import { Sleeve } from "../../PersonObjects/Sleeve/Sleeve"; -import { AllServers, - AddToAllServers } from "../../Server/AllServers"; -import { Server } from "../../Server/Server"; -import {Settings} from "../../Settings/Settings"; -import {SpecialServerIps, SpecialServerNames} from "../../Server/SpecialServerIps"; -import {SourceFiles, applySourceFile} from "../../SourceFile"; -import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; +import { Augmentations } from "../../Augmentation/Augmentations"; +import { applyAugmentation } from "../../Augmentation/AugmentationHelpers"; +import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; +import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; +import { Bladeburner } from "../../Bladeburner"; +import { CodingContractRewardType } from "../../CodingContracts"; +import { Company } from "../../Company/Company"; +import { Companies } from "../../Company/Companies"; +import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPosition"; +import { getJobRequirementText } from "../../Company/GetJobRequirementText"; +import { CompanyPositions } from "../../Company/CompanyPositions"; +import * as posNames from "../../Company/data/companypositionnames"; +import {CONSTANTS} from "../../Constants"; +import { Corporation } from "../../Corporation/Corporation"; +import { Programs } from "../../Programs/Programs"; +import { determineCrimeSuccess } from "../../Crime/CrimeHelpers"; +import { Crimes } from "../../Crime/Crimes"; +import { Engine } from "../../engine"; +import { Faction } from "../../Faction/Faction"; +import { Factions } from "../../Faction/Factions"; +import { displayFactionContent } from "../../Faction/FactionHelpers"; +import { resetGangs } from "../../Gang"; +import { hasHacknetServers } from "../../Hacknet/HacknetHelpers"; +import { HashManager } from "../../Hacknet/HashManager"; +import { Cities } from "../../Locations/Cities"; +import { Locations } from "../../Locations/Locations"; +import { CityName } from "../../Locations/data/CityNames"; +import { LocationName } from "../../Locations/data/LocationNames"; +import { Sleeve } from "../../PersonObjects/Sleeve/Sleeve"; +import { + AllServers, + AddToAllServers, + createUniqueRandomIp, +} from "../../Server/AllServers"; +import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers"; +import { Settings } from "../../Settings/Settings"; +import { SpecialServerIps, SpecialServerNames } from "../../Server/SpecialServerIps"; +import { SourceFiles, applySourceFile } from "../../SourceFile"; +import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; -import Decimal from "decimal.js"; +import Decimal from "decimal.js"; -import {numeralWrapper} from "../../ui/numeralFormat"; -import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; -import {dialogBoxCreate} from "../../../utils/DialogBox"; -import {clearEventListeners} from "../../../utils/uiHelpers/clearEventListeners"; -import {createRandomIp} from "../../../utils/IPAddress"; -import {Reviver, Generic_toJSON, - Generic_fromJSON} from "../../../utils/JSONReviver"; -import {convertTimeMsToTimeElapsedString} from "../../../utils/StringHelperFunctions"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners"; +import { + Reviver, + Generic_toJSON, + Generic_fromJSON, +} from "../../../utils/JSONReviver"; +import {convertTimeMsToTimeElapsedString} from "../../../utils/StringHelperFunctions"; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; export function init() { /* Initialize Player's home computer */ - var t_homeComp = new Server({ - ip:createRandomIp(), hostname:"home", organizationName:"Home PC", - isConnectedTo:true, adminRights:true, purchasedByPlayer:true, maxRam:8 + var t_homeComp = safetlyCreateUniqueServer({ + adminRights: true, + hostname: "home", + ip: createUniqueRandomIp(), + isConnectedTo: true, + maxRam: 8, + organizationName: "Home PC", + purchasedByPlayer: true, }); this.homeComputer = t_homeComp.ip; this.currentServer = t_homeComp.ip; @@ -150,7 +159,7 @@ export function prestigeAugmentation() { this.moneySourceA.reset(); this.hacknetNodes.length = 0; - this.hashManager.prestige(this); + this.hashManager.prestige(); // Re-calculate skills and reset HP this.updateSkillLevels(); @@ -240,7 +249,7 @@ export function prestigeSourceFile() { this.lastUpdate = new Date().getTime(); this.hacknetNodes.length = 0; - this.hashManager.prestige(this); + this.hashManager.prestige(); // Gang this.gang = null; @@ -454,7 +463,7 @@ export function gainIntelligenceExp(exp) { if (isNaN(exp)) { console.log("ERROR: NaN passed into Player.gainIntelligenceExp()"); return; } - if (hasAISF || this.intelligence > 0) { + if (SourceFileFlags[5] > 0 || this.intelligence > 0) { this.intelligence_exp += exp; } } @@ -943,7 +952,7 @@ export function getWorkMoneyGain() { // If player has SF-11, calculate salary multiplier from favor let bn11Mult = 1; const company = Companies[this.companyName]; - if (hasBn11SF) { bn11Mult = 1 + (company.favor / 100); } + if (SourceFileFlags[11] > 0) { bn11Mult = 1 + (company.favor / 100); } // Get base salary const companyPositionName = this.jobs[this.companyName]; diff --git a/src/PersonObjects/Player/PlayerObjectServerMethods.ts b/src/PersonObjects/Player/PlayerObjectServerMethods.ts index 3b4410da1..84ec0a79f 100644 --- a/src/PersonObjects/Player/PlayerObjectServerMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectServerMethods.ts @@ -1,15 +1,18 @@ /** * Server and HacknetServer-related methods for the Player class (PlayerObject) */ -import { IPlayer } from "../IPlayer"; +import { IPlayer } from "../IPlayer"; -import { CONSTANTS } from "../../Constants"; +import { CONSTANTS } from "../../Constants"; -import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; -import { HacknetServer } from "../../Hacknet/HacknetServer"; -import { AddToAllServers, - AllServers } from "../../Server/AllServers"; -import { SpecialServerIps } from "../../Server/SpecialServerIps"; +import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; +import { HacknetServer } from "../../Hacknet/HacknetServer"; +import { + AddToAllServers, + AllServers, + createUniqueRandomIp, +} from "../../Server/AllServers"; +import { SpecialServerIps } from "../../Server/SpecialServerIps"; export function hasTorRouter(this: IPlayer) { return SpecialServerIps.hasOwnProperty("Darkweb Server"); @@ -41,7 +44,8 @@ export function createHacknetServer(this: IPlayer): HacknetServer { const server = new HacknetServer({ adminRights: true, hostname: name, - player: this, + ip: createUniqueRandomIp(), + // player: this, }); this.hacknetNodes.push(server.ip); diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 0bfdf20e9..5a3a4df38 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -14,8 +14,6 @@ import { Person, createTaskTracker } from "../Person"; import { Augmentation } from "../../Augmentation/Augmentation"; -import { Augmentations } from "../../Augmentation/Augmentations"; -import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; diff --git a/src/Player.d.ts b/src/Player.d.ts new file mode 100644 index 000000000..a4e5b3f7a --- /dev/null +++ b/src/Player.d.ts @@ -0,0 +1,3 @@ +import { IPlayer } from "./PersonObjects/IPlayer"; + +export declare let Player: IPlayer; diff --git a/src/Prestige.js b/src/Prestige.js index 2bea2d0de..f04885eb9 100755 --- a/src/Prestige.js +++ b/src/Prestige.js @@ -16,14 +16,10 @@ import { Faction } from "./Faction/Faction"; import { Factions, initFactions } from "./Faction/Factions"; import { joinFaction } from "./Faction/FactionHelpers"; import { deleteGangDisplayContent } from "./Gang"; +import { updateHashManagerCapacity } from "./Hacknet/HacknetHelpers"; import { Message } from "./Message/Message"; import { initMessages, Messages } from "./Message/MessageHelpers"; -import { initSingularitySFFlags, hasWallStreetSF } from "./NetscriptFunctions"; -import { - WorkerScript, - workerScripts, - prestigeWorkerScripts -} from "./NetscriptWorker"; +import { prestigeWorkerScripts } from "./NetscriptWorker"; import { Player } from "./Player"; import { @@ -152,7 +148,7 @@ function prestigeAugmentation() { // BitNode 8: Ghost of Wall Street if (Player.bitNodeN === 8) {Player.money = new Decimal(BitNode8StartingMoney);} - if (Player.bitNodeN === 8 || hasWallStreetSF) { + if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) { Player.hasWseAccount = true; Player.hasTixApiAccess = true; } @@ -257,9 +253,6 @@ function prestigeSourceFile() { Terminal.resetTerminalInput(); Engine.loadTerminalContent(); - // Reinitialize Bit Node flags - initSingularitySFFlags(); - // BitNode 3: Corporatocracy if (Player.bitNodeN === 3) { homeComp.messages.push("corporation-management-handbook.lit"); @@ -313,7 +306,7 @@ function prestigeSourceFile() { // BitNode 8: Ghost of Wall Street if (Player.bitNodeN === 8) {Player.money = new Decimal(BitNode8StartingMoney);} - if (Player.bitNodeN === 8 || hasWallStreetSF) { + if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) { Player.hasWseAccount = true; Player.hasTixApiAccess = true; } @@ -345,7 +338,7 @@ function prestigeSourceFile() { hserver.cache = 5; hserver.updateHashRate(Player); hserver.updateHashCapacity(); - Player.hashManager.updateCapacity(Player); + updateHashManagerCapacity(); } // Refresh Main Menu (the 'World' menu, specifically) diff --git a/src/Script/RamCalculations.js b/src/Script/RamCalculations.js index ac4c1a2ef..77d02797e 100644 --- a/src/Script/RamCalculations.js +++ b/src/Script/RamCalculations.js @@ -1,11 +1,8 @@ // Calculate a script's RAM usage const walk = require("acorn/dist/walk"); // Importing this doesn't work for some reason. -import { CONSTANTS } from "../Constants"; -import {evaluateImport} from "../NetscriptEvaluator"; -import { WorkerScript } from "../NetscriptWorker"; -import { Player } from "../Player"; -import {parse, Node} from "../../utils/acorn"; +import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator"; +import { parse, Node } from "../../utils/acorn"; // These special strings are used to reference the presence of a given logical // construct within a user script. @@ -90,7 +87,7 @@ async function parseOnlyRamCalculate(server, code, workerScript) { // Finally, walk the reference map and generate a ram cost. The initial set of keys to scan // are those that start with __SPECIAL_INITIAL_MODULE__. - let ram = CONSTANTS.ScriptBaseRamCost; + let ram = RamCostConstants.ScriptBaseRamCost; const unresolvedRefs = Object.keys(dependencyMap).filter(s => s.startsWith(initialModule)); const resolvedRefs = new Set(); while (unresolvedRefs.length > 0) { @@ -98,13 +95,13 @@ async function parseOnlyRamCalculate(server, code, workerScript) { // Check if this is one of the special keys, and add the appropriate ram cost if so. if (ref === "hacknet" && !resolvedRefs.has("hacknet")) { - ram += CONSTANTS.ScriptHacknetNodesRamCost; + ram += RamCostConstants.ScriptHacknetNodesRamCost; } if (ref === "document" && !resolvedRefs.has("document")) { - ram += CONSTANTS.ScriptDomRamCost; + ram += RamCostConstants.ScriptDomRamCost; } if (ref === "window" && !resolvedRefs.has("window")) { - ram += CONSTANTS.ScriptDomRamCost; + ram += RamCostConstants.ScriptDomRamCost; } resolvedRefs.add(ref); @@ -124,9 +121,8 @@ async function parseOnlyRamCalculate(server, code, workerScript) { } } - // Check if this ident is a function in the workerscript env. If it is, then we need to - // get its RAM cost. We do this by calling it, which works because the running script - // is in checkingRam mode. + // Check if this identifier is a function in the workerscript env. + // If it is, then we need to get its RAM cost. // // TODO it would be simpler to just reference a dictionary. try { @@ -152,8 +148,15 @@ async function parseOnlyRamCalculate(server, code, workerScript) { } } - //Special logic for namespaces (Bladeburner, CodingCOntract) - var func; + // Only count each function once + if (workerScript.loadedFns[ref]) { + continue; + } else { + workerScript.loadedFns[ref] = true; + } + + // This accounts for namespaces (Bladeburner, CodingCOntract) + let func; if (ref in workerScript.env.vars.bladeburner) { func = workerScript.env.vars.bladeburner[ref]; } else if (ref in workerScript.env.vars.codingcontract) { @@ -163,7 +166,7 @@ async function parseOnlyRamCalculate(server, code, workerScript) { } else if (ref in workerScript.env.vars.sleeve) { func = workerScript.env.vars.sleeve[ref]; } else { - func = workerScript.env.get(ref); + func = workerScript.env.vars[ref]; } ram += applyFuncRam(func); } catch (error) {continue;} @@ -309,103 +312,23 @@ function parseOnlyCalculateDeps(code, currentModule) { } export async function calculateRamUsage(codeCopy) { - //Create a temporary/mock WorkerScript and an AST from the code - var currServ = Player.getCurrentServer(); - var workerScript = new WorkerScript({ - filename:"foo", - scriptRef: {code:""}, - args:[], - getCode: function() { return ""; } - }); - workerScript.checkingRam = true; //Netscript functions will return RAM usage - workerScript.serverIp = currServ.ip; + // We don't need a real WorkerScript for this. Just an object that keeps + // track of whatever's needed for RAM calculations + const workerScript = { + loadedFns: {}, + serverIp: currServ.ip, + env: { + vars: RamCosts, + } + } try { return await parseOnlyRamCalculate(currServ, codeCopy, workerScript); } catch (e) { - console.log("Failed to parse ram using new method. Falling back.", e); + console.error(`Failed to parse script for RAM calculations:`); + console.error(e); + return -1; } - // Try the old way. - - try { - var ast = parse(codeCopy, {sourceType:"module"}); - } catch(e) { - return -1; - } - - //Search through AST, scanning for any 'Identifier' nodes for functions, or While/For/If nodes - var queue = [], ramUsage = CONSTANTS.ScriptBaseRamCost; - var whileUsed = false, forUsed = false, ifUsed = false; - queue.push(ast); - while (queue.length != 0) { - var exp = queue.shift(); - switch (exp.type) { - case "ImportDeclaration": - //Gets an array of all imported functions as AST expressions - //and pushes them on the queue. - var res = evaluateImport(exp, workerScript, true); - for (var i = 0; i < res.length; ++i) { - queue.push(res[i]); - } - break; - case "BlockStatement": - case "Program": - for (var i = 0; i < exp.body.length; ++i) { - if (exp.body[i] instanceof Node) { - queue.push(exp.body[i]); - } - } - break; - case "WhileStatement": - if (!whileUsed) { - ramUsage += CONSTANTS.ScriptWhileRamCost; - whileUsed = true; - } - break; - case "ForStatement": - if (!forUsed) { - ramUsage += CONSTANTS.ScriptForRamCost; - forUsed = true; - } - break; - case "IfStatement": - if (!ifUsed) { - ramUsage += CONSTANTS.ScriptIfRamCost; - ifUsed = true; - } - break; - case "Identifier": - if (exp.name in workerScript.env.vars) { - var func = workerScript.env.get(exp.name); - if (typeof func === "function") { - try { - var res = func.apply(null, []); - if (typeof res === "number") { - ramUsage += res; - } - } catch(e) { - console.log("ERROR applying function: " + e); - } - } - } - break; - default: - break; - } - - for (var prop in exp) { - if (exp.hasOwnProperty(prop)) { - if (exp[prop] instanceof Node) { - queue.push(exp[prop]); - } - } - } - } - - //Special case: hacknetnodes array - if (codeCopy.includes("hacknet")) { - ramUsage += CONSTANTS.ScriptHacknetNodesRamCost; - } - return ramUsage; + return -1; } diff --git a/src/Script/RunningScript.ts b/src/Script/RunningScript.ts index 9da52d47b..be26ddba8 100644 --- a/src/Script/RunningScript.ts +++ b/src/Script/RunningScript.ts @@ -1,16 +1,17 @@ // Class representing a Script instance that is actively running. // A Script can have multiple active instances -import { Script } from "./Script"; -import { FconfSettings } from "../Fconf/FconfSettings"; -import { AllServers } from "../Server/AllServers"; -import { Settings } from "../Settings/Settings"; -import { IMap } from "../types"; -import { post } from "../ui/postToTerminal"; +import { Script } from "./Script"; +import { FconfSettings } from "../Fconf/FconfSettings"; +import { Settings } from "../Settings/Settings"; +import { IMap } from "../types"; +import { post } from "../ui/postToTerminal"; -import { Generic_fromJSON, - Generic_toJSON, - Reviver } from "../../utils/JSONReviver"; -import { getTimestamp } from "../../utils/helpers/getTimestamp"; +import { + Generic_fromJSON, + Generic_toJSON, + Reviver +} from "../../utils/JSONReviver"; +import { getTimestamp } from "../../utils/helpers/getTimestamp"; export class RunningScript { // Initializes a RunningScript Object from a JSON save state @@ -73,35 +74,6 @@ export class RunningScript { this.ramUsage = script.ramUsage; } - getCode(): string { - const server = AllServers[this.server]; - if (server == null) { return ""; } - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === this.filename) { - return server.scripts[i].code; - } - } - - return ""; - } - - getRamUsage(): number { - if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value - - const server = AllServers[this.server]; - if (server == null) { return 0; } - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === this.filename) { - // Cache the ram usage for the next call - this.ramUsage = server.scripts[i].ramUsage; - return this.ramUsage; - } - } - - - return 0; - } - log(txt: string): void { if (this.logs.length > Settings.MaxLogCapacity) { //Delete first element and add new log entry to the end. diff --git a/src/Script/RunningScriptHelpers.ts b/src/Script/RunningScriptHelpers.ts new file mode 100644 index 000000000..8d63a0752 --- /dev/null +++ b/src/Script/RunningScriptHelpers.ts @@ -0,0 +1,20 @@ +import { AllServers } from "../Server/AllServers"; +import { RunningScript } from "./RunningScript"; + +export function getRamUsageFromRunningScript(script: RunningScript): number { + if (script.ramUsage != null && script.ramUsage > 0) { + return script.ramUsage; // Use cached value + } + + const server = AllServers[script.server]; + if (server == null) { return 0; } + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === script.filename) { + // Cache the ram usage for the next call + script.ramUsage = server.scripts[i].ramUsage; + return script.ramUsage; + } + } + + return 0; +} diff --git a/src/Script/Script.ts b/src/Script/Script.ts index 92f854f40..bc1f3659b 100644 --- a/src/Script/Script.ts +++ b/src/Script/Script.ts @@ -2,14 +2,14 @@ // This does NOT represent a script that is actively running and // being evaluated. See RunningScript for that import { calculateRamUsage } from "./RamCalculations"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { Page, - routing } from "../ui/navigationTracking"; +import { Page, routing } from "../ui/navigationTracking"; import { setTimeoutRef } from "../utils/SetTimeoutRef"; -import { Generic_fromJSON, - Generic_toJSON, - Reviver } from "../../utils/JSONReviver"; +import { + Generic_fromJSON, + Generic_toJSON, + Reviver +} from "../../utils/JSONReviver"; import { roundToTwo } from "../../utils/helpers/roundToTwo"; export class Script { @@ -64,7 +64,7 @@ export class Script { } // Save a script FROM THE SCRIPT EDITOR - saveScript(code: string, p: IPlayer): void { + saveScript(code: string, currServ: string): void { if (routing.isOn(Page.ScriptEditor)) { //Update code and filename this.code = code.replace(/^\s+|\s+$/g, ''); @@ -77,7 +77,7 @@ export class Script { this.filename = filenameElem!.value; // Server - this.server = p.currentServer; + this.server = currServ; //Calculate/update ram usage, execution time, etc. this.updateRamUsage(); diff --git a/src/Script/ScriptHelpers.js b/src/Script/ScriptHelpers.js index ae0f19f92..916dd03f7 100644 --- a/src/Script/ScriptHelpers.js +++ b/src/Script/ScriptHelpers.js @@ -1,33 +1,38 @@ -import { Script } from "./Script"; +import { Script } from "./Script"; -import { calculateRamUsage } from "./RamCalculations"; -import { isScriptFilename } from "./ScriptHelpersTS"; +import { calculateRamUsage } from "./RamCalculations"; +import { isScriptFilename } from "./ScriptHelpersTS"; -import {CONSTANTS} from "../Constants"; -import {Engine} from "../engine"; -import { parseFconfSettings } from "../Fconf/Fconf"; -import { FconfSettings } from "../Fconf/FconfSettings"; -import {iTutorialSteps, iTutorialNextStep, - ITutorial} from "../InteractiveTutorial"; -import { addWorkerScript } from "../NetscriptWorker"; -import { Player } from "../Player"; -import { AceEditor } from "../ScriptEditor/Ace"; -import { CodeMirrorEditor } from "../ScriptEditor/CodeMirror"; -import { AllServers } from "../Server/AllServers"; -import { processSingleServerGrowth } from "../Server/ServerHelpers"; -import { Settings } from "../Settings/Settings"; -import { EditorSetting } from "../Settings/SettingEnums"; -import { isValidFilePath } from "../Terminal/DirectoryHelpers"; -import {TextFile} from "../TextFile"; +import {CONSTANTS} from "../Constants"; +import {Engine} from "../engine"; +import { parseFconfSettings } from "../Fconf/Fconf"; +import { FconfSettings } from "../Fconf/FconfSettings"; +import { + iTutorialSteps, + iTutorialNextStep, + ITutorial +} from "../InteractiveTutorial"; +import { Player } from "../Player"; +import { AceEditor } from "../ScriptEditor/Ace"; +import { CodeMirrorEditor } from "../ScriptEditor/CodeMirror"; +import { AllServers } from "../Server/AllServers"; +import { processSingleServerGrowth } from "../Server/ServerHelpers"; +import { Settings } from "../Settings/Settings"; +import { EditorSetting } from "../Settings/SettingEnums"; +import { isValidFilePath } from "../Terminal/DirectoryHelpers"; +import { TextFile } from "../TextFile"; -import {Page, routing} from "../ui/navigationTracking"; -import {numeralWrapper} from "../ui/numeralFormat"; +import { Page, routing } from "../ui/navigationTracking"; +import { numeralWrapper } from "../ui/numeralFormat"; -import {dialogBoxCreate} from "../../utils/DialogBox"; -import {Reviver, Generic_toJSON, - Generic_fromJSON} from "../../utils/JSONReviver"; -import {compareArrays} from "../../utils/helpers/compareArrays"; -import {createElement} from "../../utils/uiHelpers/createElement"; +import { dialogBoxCreate } from "../../utils/DialogBox"; +import { + Reviver, + Generic_toJSON, + Generic_fromJSON +} from "../../utils/JSONReviver"; +import { compareArrays } from "../../utils/helpers/compareArrays"; +import { createElement } from "../../utils/uiHelpers/createElement"; var scriptEditorRamCheck = null, scriptEditorRamText = null; export function scriptEditorInit() { @@ -127,20 +132,22 @@ export function scriptEditorInit() { editorSelector.onchange = () => { const opt = editorSelector.value; switch (opt) { - case EditorSetting.Ace: + case EditorSetting.Ace: { const codeMirrorCode = CodeMirrorEditor.getCode(); const codeMirrorFn = CodeMirrorEditor.getFilename(); AceEditor.create(); CodeMirrorEditor.setInvisible(); AceEditor.openScript(codeMirrorFn, codeMirrorCode); break; - case EditorSetting.CodeMirror: + } + case EditorSetting.CodeMirror: { const aceCode = AceEditor.getCode(); const aceFn = AceEditor.getFilename(); CodeMirrorEditor.create(); AceEditor.setInvisible(); CodeMirrorEditor.openScript(aceFn, aceCode); break; + } default: console.error(`Unrecognized Editor Setting: ${opt}`); return; @@ -229,7 +236,7 @@ function saveAndCloseScriptEditor() { let s = Player.getCurrentServer(); for (var i = 0; i < s.scripts.length; i++) { if (filename == s.scripts[i].filename) { - s.scripts[i].saveScript(getCurrentEditor().getCode(), Player); + s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer); Engine.loadTerminalContent(); return iTutorialNextStep(); } @@ -237,7 +244,7 @@ function saveAndCloseScriptEditor() { //If the current script does NOT exist, create a new one let script = new Script(); - script.saveScript(getCurrentEditor().getCode(), Player); + script.saveScript(getCurrentEditor().getCode(), Player.currentServer); s.scripts.push(script); return iTutorialNextStep(); @@ -265,7 +272,7 @@ function saveAndCloseScriptEditor() { //If the current script already exists on the server, overwrite it for (var i = 0; i < s.scripts.length; i++) { if (filename == s.scripts[i].filename) { - s.scripts[i].saveScript(getCurrentEditor().getCode(), Player); + s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer); Engine.loadTerminalContent(); return; } @@ -273,7 +280,7 @@ function saveAndCloseScriptEditor() { //If the current script does NOT exist, create a new one const script = new Script(); - script.saveScript(getCurrentEditor().getCode(), Player); + script.saveScript(getCurrentEditor().getCode(), Player.currentServer); s.scripts.push(script); } else if (filename.endsWith(".txt")) { for (var i = 0; i < s.textFiles.length; ++i) { @@ -293,42 +300,7 @@ function saveAndCloseScriptEditor() { Engine.loadTerminalContent(); } -//Called when the game is loaded. Loads all running scripts (from all servers) -//into worker scripts so that they will start running -export function loadAllRunningScripts() { - var total = 0; - let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1); - if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); } - for (var property in AllServers) { - if (AllServers.hasOwnProperty(property)) { - var server = AllServers[property]; - - //Reset each server's RAM usage to 0 - server.ramUsed = 0; - - //Reset modules on all scripts - for (var i = 0; i < server.scripts.length; ++i) { - server.scripts[i].module = ""; - } - - if (skipScriptLoad) { - //Start game with no scripts - server.runningScripts.length = 0; - } else { - for (var j = 0; j < server.runningScripts.length; ++j) { - addWorkerScript(server.runningScripts[j], server); - - //Offline production - total += scriptCalculateOfflineProduction(server.runningScripts[j]); - } - } - } - } - - return total; -} - -function scriptCalculateOfflineProduction(runningScriptObj) { +export function scriptCalculateOfflineProduction(runningScriptObj) { //The Player object stores the last update time from when we were online var thisUpdate = new Date().getTime(); var lastUpdate = Player.lastUpdate; diff --git a/src/Server/AllServers.ts b/src/Server/AllServers.ts index 84b17bad0..c91dce857 100644 --- a/src/Server/AllServers.ts +++ b/src/Server/AllServers.ts @@ -5,25 +5,42 @@ import { serverMetadata } from "./data/servers"; import { HacknetServer } from "../Hacknet/HacknetServer"; import { IMap } from "../types"; -import { createRandomIp, - ipExists } from "../../utils/IPAddress"; +import { createRandomIp } from "../../utils/IPAddress"; import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { Reviver } from "../../utils/JSONReviver"; -// Map of all Servers that exist in the game -// Key (string) = IP -// Value = Server object +/** + * Map of all Servers that exist in the game + * Key (string) = IP + * Value = Server object + */ export let AllServers: IMap = {}; +export function ipExists(ip: string) { + return (AllServers[ip] != null); +} + +export function createUniqueRandomIp(): string { + const ip = createRandomIp(); + + // If the Ip already exists, recurse to create a new one + if (ipExists(ip)) { + return createRandomIp(); + } + + return ip; +} + // Saftely add a Server to the AllServers map export function AddToAllServers(server: Server | HacknetServer): void { var serverIp = server.ip; if (ipExists(serverIp)) { - console.log("IP of server that's being added: " + serverIp); - console.log("Hostname of the server thats being added: " + server.hostname); - console.log("The server that already has this IP is: " + AllServers[serverIp].hostname); + console.warn(`IP of server that's being added: ${serverIp}`); + console.warn(`Hostname of the server thats being added: ${server.hostname}`); + console.warn(`The server that already has this IP is: ${AllServers[serverIp].hostname}`); throw new Error("Error: Trying to add a server with an existing IP"); } + AllServers[serverIp] = server; } @@ -71,7 +88,7 @@ export function initForeignServers(homeComputer: Server) { for (const metadata of serverMetadata) { const serverParams: IServerParams = { hostname: metadata.hostname, - ip: createRandomIp(), + ip: createUniqueRandomIp(), numOpenPortsRequired: metadata.numOpenPortsRequired, organizationName: metadata.organizationName }; diff --git a/src/Server/Server.ts b/src/Server/Server.ts index 8b32b06d1..e5b995714 100644 --- a/src/Server/Server.ts +++ b/src/Server/Server.ts @@ -1,9 +1,6 @@ // Class representing a single hackable Server import { BaseServer } from "./BaseServer"; -// TODO This import is a circular import. Try to fix it in the future -import { GetServerByHostname } from "./ServerHelpers"; - import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { createRandomString } from "../utils/helpers/createRandomString"; @@ -12,7 +9,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; -interface IConstructorParams { +export interface IConstructorParams { adminRights?: boolean; hackDifficulty?: number; hostname: string; @@ -77,17 +74,6 @@ export class Server extends BaseServer { this.hostname = createRandomString(10); } - // Validate hostname by ensuring there are no repeats - if (GetServerByHostname(this.hostname) != null) { - // Use a for loop to ensure that we don't get suck in an infinite loop somehow - let hostname: string = this.hostname; - for (let i = 0; i < 200; ++i) { - hostname = `${this.hostname}-${i}`; - if (GetServerByHostname(hostname) == null) { break; } - } - this.hostname = hostname; - } - this.purchasedByPlayer = params.purchasedByPlayer != null ? params.purchasedByPlayer : false; //RAM, CPU speed and Scripts diff --git a/src/Server/ServerHelpers.ts b/src/Server/ServerHelpers.ts index e6ad5df67..f91a2f8fc 100644 --- a/src/Server/ServerHelpers.ts +++ b/src/Server/ServerHelpers.ts @@ -1,13 +1,39 @@ -import { AllServers } from "./AllServers"; -import { Server } from "./Server"; +import { + AllServers, + createUniqueRandomIp, + ipExists, +} from "./AllServers"; +import { Server, IConstructorParams } from "./Server"; -import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { CONSTANTS } from "../Constants"; -import { HacknetServer } from "../Hacknet/HacknetServer"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { Programs } from "../Programs/Programs"; +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; +import { CONSTANTS } from "../Constants"; +import { HacknetServer } from "../Hacknet/HacknetServer"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Programs } from "../Programs/Programs"; -import {isValidIPAddress} from "../../utils/helpers/isValidIPAddress"; +import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress"; + +/** + * Constructs a new server, while also ensuring that the new server + * does not have a duplicate hostname/ip. + */ +export function safetlyCreateUniqueServer(params: IConstructorParams): Server { + if (params.ip != null && ipExists(params.ip)) { + params.ip = createUniqueRandomIp(); + } + + if (GetServerByHostname(params.hostname) != null) { + // Use a for loop to ensure that we don't get suck in an infinite loop somehow + let hostname: string = params.hostname; + for (let i = 0; i < 200; ++i) { + hostname = `${params.hostname}-${i}`; + if (GetServerByHostname(hostname) == null) { break; } + } + params.hostname = hostname; + } + + return new Server(params); +} // Returns the number of cycles needed to grow the specified server by the // specified amount. 'growth' parameter is in decimal form, not percentage diff --git a/src/Server/ServerPurchases.ts b/src/Server/ServerPurchases.ts index 401f8efc5..4cf47549a 100644 --- a/src/Server/ServerPurchases.ts +++ b/src/Server/ServerPurchases.ts @@ -2,15 +2,19 @@ * Implements functions for purchasing servers or purchasing more RAM for * the home computer */ -import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { CONSTANTS } from "../Constants"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { AddToAllServers } from "../Server/AllServers"; -import { Server } from "../Server/Server"; -import { dialogBoxCreate } from "../../utils/DialogBox"; -import { createRandomIp } from "../../utils/IPAddress"; -import { yesNoTxtInpBoxGetInput } from "../../utils/YesNoBox"; -import { isPowerOfTwo } from "../../utils/helpers/isPowerOfTwo"; +import { + AddToAllServers, + createUniqueRandomIp, +} from "./AllServers"; +import { safetlyCreateUniqueServer } from "./ServerHelpers"; + +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; +import { CONSTANTS } from "../Constants"; +import { IPlayer } from "../PersonObjects/IPlayer"; + +import { dialogBoxCreate } from "../../utils/DialogBox"; +import { yesNoTxtInpBoxGetInput } from "../../utils/YesNoBox"; +import { isPowerOfTwo } from "../../utils/helpers/isPowerOfTwo"; // Returns the cost of purchasing a server with the given RAM // Returns Infinity for invalid 'ram' arguments @@ -66,17 +70,22 @@ export function purchaseServer(ram: number, p: IPlayer) { return; } - //Create server - var newServ = new Server({ - ip:createRandomIp(), hostname:hostname, organizationName:"", - isConnectedTo:false, adminRights:true, purchasedByPlayer:true, maxRam:ram + // Create server + const newServ = safetlyCreateUniqueServer({ + adminRights: true, + hostname: hostname, + ip: createUniqueRandomIp(), + isConnectedTo: false, + maxRam:ram, + organizationName: "", + purchasedByPlayer: true, }); AddToAllServers(newServ); - //Add to Player's purchasedServers array + // Add to Player's purchasedServers array p.purchasedServers.push(newServ.ip); - //Connect new server to home computer + // Connect new server to home computer var homeComputer = p.getHomeComputer(); homeComputer.serversOnNetwork.push(newServ.ip); newServ.serversOnNetwork.push(homeComputer.ip); diff --git a/src/SourceFile/SourceFileFlags.ts b/src/SourceFile/SourceFileFlags.ts index 3b9a6a796..db6fa701e 100644 --- a/src/SourceFile/SourceFileFlags.ts +++ b/src/SourceFile/SourceFileFlags.ts @@ -4,7 +4,7 @@ import { CONSTANTS } from "../Constants"; import { IPlayer } from "../PersonObjects/IPlayer"; -export const SourceFileFlags: number[] = Array(CONSTANTS.TotalNumBitNodes + 1); // Skip 0 +export const SourceFileFlags: number[] = Array(CONSTANTS.TotalNumBitNodes + 1); // Skip index 0 export function updateSourceFileFlags(p: IPlayer) { for (let i = 0; i < SourceFileFlags.length; ++i) { diff --git a/src/StockMarket/BuyingAndSelling.ts b/src/StockMarket/BuyingAndSelling.ts new file mode 100644 index 000000000..52e3f9d0f --- /dev/null +++ b/src/StockMarket/BuyingAndSelling.ts @@ -0,0 +1,296 @@ +/** + * Functions for buying/selling stocks. There are four functions total, two for + * long positions and two for short positions. + */ +import { Stock } from "./Stock"; +import { + getBuyTransactionCost, + getSellTransactionGain, + processBuyTransactionPriceMovement, + processSellTransactionPriceMovement +} from "./StockMarketHelpers"; + +import { PositionTypes } from "./data/PositionTypes"; + +import { CONSTANTS } from "../Constants"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { Player } from "../Player"; + +import { numeralWrapper } from "../ui/numeralFormat"; + +import { dialogBoxCreate } from "../../utils/DialogBox"; + +/** +* Each function takes an optional config object as its last argument +*/ +interface IOptions { + rerenderFn?: () => void; + suppressDialog?: boolean; +} + +/** + * Attempt to buy a stock in the long position + * @param {Stock} stock - Stock to buy + * @param {number} shares - Number of shares to buy + * @param {WorkerScript} workerScript - If this is being called through Netscript + * @param opts - Optional configuration for this function's behavior. See top of file + * @returns {boolean} - true if successful, false otherwise + */ +export function buyStock(stock: Stock, shares: number, workerScript: WorkerScript | null=null, opts: IOptions={}): boolean { + const tixApi = (workerScript instanceof WorkerScript); + + // Validate arguments + shares = Math.round(shares); + if (shares == 0 || shares < 0) { return false; } + if (stock == null || isNaN(shares)) { + if (tixApi) { + workerScript!.log(`ERROR: buyStock() failed due to invalid arguments`); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer"); + } + + return false; + } + + // Does player have enough money? + const totalPrice = getBuyTransactionCost(stock, shares, PositionTypes.Long); + if (totalPrice == null) { return false; } + if (Player.money.lt(totalPrice)) { + if (tixApi) { + workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this potiion. You need ${numeralWrapper.formatMoney(totalPrice)}`); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); + } + + return false; + } + + // Would this purchase exceed the maximum number of shares? + if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { + if (tixApi) { + workerScript!.log(`ERROR: buyStock() failed because purchasing this many shares would exceed ${stock.symbol}'s maximum number of shares`); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${numeralWrapper.formatBigNumber(stock.maxShares)} shares.`); + } + + return false; + } + + const origTotal = stock.playerShares * stock.playerAvgPx; + Player.loseMoney(totalPrice); + const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; + stock.playerShares = Math.round(stock.playerShares + shares); + stock.playerAvgPx = newTotal / stock.playerShares; + processBuyTransactionPriceMovement(stock, shares, PositionTypes.Long); + if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") { + opts.rerenderFn(); + } + + const resultTxt = `Bought ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} for ${numeralWrapper.formatMoney(totalPrice)}. ` + + `Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.` + if (tixApi) { + if (workerScript!.shouldLog("buyStock")) { workerScript!.log(resultTxt); } + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate(resultTxt); + } + + return true; +} + +/** + * Attempt to sell a stock in the long position + * @param {Stock} stock - Stock to sell + * @param {number} shares - Number of shares to sell + * @param {WorkerScript} workerScript - If this is being called through Netscript + * @param opts - Optional configuration for this function's behavior. See top of file + * returns {boolean} - true if successfully sells given number of shares OR MAX owned, false otherwise + */ +export function sellStock(stock: Stock, shares: number, workerScript: WorkerScript | null=null, opts: IOptions={}): boolean { + const tixApi = (workerScript instanceof WorkerScript); + + // Sanitize/Validate arguments + if (stock == null || shares < 0 || isNaN(shares)) { + if (tixApi) { + workerScript!.log(`ERROR: sellStock() failed due to invalid arguments`); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate("Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer"); + } + + return false; + } + shares = Math.round(shares); + if (shares > stock.playerShares) {shares = stock.playerShares;} + if (shares === 0) {return false;} + + const gains = getSellTransactionGain(stock, shares, PositionTypes.Long); + if (gains == null) { return false; } + let netProfit = gains - (stock.playerAvgPx * shares); + if (isNaN(netProfit)) { netProfit = 0; } + Player.gainMoney(gains); + Player.recordMoneySource(netProfit, "stock"); + if (tixApi) { + workerScript!.scriptRef.onlineMoneyMade += netProfit; + Player.scriptProdSinceLastAug += netProfit; + } + + stock.playerShares = Math.round(stock.playerShares - shares); + if (stock.playerShares === 0) { + stock.playerAvgPx = 0; + } + + processSellTransactionPriceMovement(stock, shares, PositionTypes.Long); + + if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") { + opts.rerenderFn(); + } + + const resultTxt = `Sold ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` + + `After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`; + if (tixApi) { + if (workerScript!.shouldLog("sellStock")) { workerScript!.log(resultTxt); } + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate(resultTxt); + } + + return true; +} + +/** + * Attempt to buy a stock in the short position + * @param {Stock} stock - Stock to sell + * @param {number} shares - Number of shares to short + * @param {WorkerScript} workerScript - If this is being called through Netscript + * @param opts - Optional configuration for this function's behavior. See top of file + * @returns {boolean} - true if successful, false otherwise + */ +export function shortStock(stock: Stock, shares: number, workerScript: WorkerScript | null=null, opts: IOptions={}): boolean { + const tixApi = (workerScript instanceof WorkerScript); + + // Validate arguments + shares = Math.round(shares); + if (shares === 0 || shares < 0) { return false; } + if (stock == null || isNaN(shares)) { + if (tixApi) { + workerScript!.log("ERROR: shortStock() failed because of invalid arguments."); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate("Failed to initiate a short position in a stock. This is probably " + + "due to an invalid quantity. Otherwise, this may be a bug, so contact developer"); + } + return false; + } + + // Does the player have enough money? + const totalPrice = getBuyTransactionCost(stock, shares, PositionTypes.Short); + if (totalPrice == null) { return false; } + if (Player.money.lt(totalPrice)) { + if (tixApi) { + workerScript!.log("ERROR: shortStock() failed because you do not have enough " + + "money to purchase this short position. You need " + + numeralWrapper.formatMoney(totalPrice)); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate("You do not have enough money to purchase this short position. You need " + + numeralWrapper.formatMoney(totalPrice)); + } + + return false; + } + + // Would this purchase exceed the maximum number of shares? + if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { + if (tixApi) { + workerScript!.log(`ERROR: shortStock() failed because purchasing this many short shares would exceed ${stock.symbol}'s maximum number of shares.`); + } else if (opts.suppressDialog != null && !opts.suppressDialog) { + dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${stock.maxShares} shares.`); + } + + return false; + } + + const origTotal = stock.playerShortShares * stock.playerAvgShortPx; + Player.loseMoney(totalPrice); + const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; + stock.playerShortShares = Math.round(stock.playerShortShares + shares); + stock.playerAvgShortPx = newTotal / stock.playerShortShares; + processBuyTransactionPriceMovement(stock, shares, PositionTypes.Short); + + if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") { + opts.rerenderFn(); + } + + const resultTxt = `Bought a short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} ` + + `for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} ` + + `in commission fees.`; + if (tixApi) { + if (workerScript!.shouldLog("shortStock")) { workerScript!.log(resultTxt); } + } else if (!opts.suppressDialog) { + dialogBoxCreate(resultTxt); + } + + return true; +} + +/** + * Attempt to sell a stock in the short position + * @param {Stock} stock - Stock to sell + * @param {number} shares - Number of shares to sell + * @param {WorkerScript} workerScript - If this is being called through Netscript + * @param opts - Optional configuration for this function's behavior. See top of file + * @returns {boolean} true if successfully sells given amount OR max owned, false otherwise + */ +export function sellShort(stock: Stock, shares: number, workerScript: WorkerScript | null=null, opts: IOptions={}): boolean { + const tixApi = (workerScript instanceof WorkerScript); + + if (stock == null || isNaN(shares) || shares < 0) { + if (tixApi) { + workerScript!.log("ERROR: sellShort() failed because of invalid arguments."); + } else if (!opts.suppressDialog) { + dialogBoxCreate("Failed to sell a short position in a stock. This is probably " + + "due to an invalid quantity. Otherwise, this may be a bug, so contact developer"); + } + + return false; + } + shares = Math.round(shares); + if (shares > stock.playerShortShares) {shares = stock.playerShortShares;} + if (shares === 0) {return false;} + + const origCost = shares * stock.playerAvgShortPx; + const totalGain = getSellTransactionGain(stock, shares, PositionTypes.Short); + if (totalGain == null || isNaN(totalGain) || origCost == null) { + if (tixApi) { + workerScript!.log(`Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`); + } else if (!opts.suppressDialog) { + dialogBoxCreate(`Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`); + } + + return false; + } + let profit = totalGain - origCost; + if (isNaN(profit)) { profit = 0; } + Player.gainMoney(totalGain); + Player.recordMoneySource(profit, "stock"); + if (tixApi) { + workerScript!.scriptRef.onlineMoneyMade += profit; + Player.scriptProdSinceLastAug += profit; + } + + stock.playerShortShares = Math.round(stock.playerShortShares - shares); + if (stock.playerShortShares === 0) { + stock.playerAvgShortPx = 0; + } + processSellTransactionPriceMovement(stock, shares, PositionTypes.Short); + + if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") { + opts.rerenderFn(); + } + + const resultTxt = `Sold your short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` + + `After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`; + if (tixApi) { + if (workerScript!.shouldLog("sellShort")) { workerScript!.log(resultTxt); } + } else if (!opts.suppressDialog) { + dialogBoxCreate(resultTxt); + } + + return true; +} diff --git a/src/StockMarket/Order.ts b/src/StockMarket/Order.ts index dc65be16c..098cea14e 100644 --- a/src/StockMarket/Order.ts +++ b/src/StockMarket/Order.ts @@ -22,11 +22,26 @@ export class Order { readonly pos: PositionTypes; readonly price: number; - readonly shares: number; + shares: number; readonly stock: Stock; readonly type: OrderTypes; constructor(stk: Stock = new Stock(), shares: number=0, price: number=0, typ: OrderTypes=OrderTypes.LimitBuy, pos: PositionTypes=PositionTypes.Long) { + // Validate arguments + let invalidArgs: boolean = false; + if (typeof shares !== "number" || typeof price !== "number") { + invalidArgs = true; + } + if (isNaN(shares) || isNaN(price)) { + invalidArgs = true; + } + if (!(stk instanceof Stock)) { + invalidArgs = true; + } + if (invalidArgs) { + throw new Error(`Invalid constructor paramters for Order`); + } + this.stock = stk; this.shares = shares; this.price = price; diff --git a/src/StockMarket/OrderProcessing.ts b/src/StockMarket/OrderProcessing.ts new file mode 100644 index 000000000..13d65d2a0 --- /dev/null +++ b/src/StockMarket/OrderProcessing.ts @@ -0,0 +1,252 @@ +/** + * Helper functions for determine whether Limit and Stop orders should + * be executed (and executing them) + */ +import { + buyStock, + sellStock, + shortStock, + sellShort, +} from "./BuyingAndSelling"; +import { IOrderBook } from "./IOrderBook"; +import { IStockMarket } from "./IStockMarket"; +import { Order } from "./Order"; +import { Stock } from "./Stock"; + +import { OrderTypes } from "./data/OrderTypes"; +import { PositionTypes } from "./data/PositionTypes"; + +import { numeralWrapper } from "../ui/numeralFormat"; + +import { dialogBoxCreate } from "../../utils/DialogBox"; + +interface IProcessOrderRefs { + rerenderFn: () => void; + stockMarket: IStockMarket; +} + +/** + * Search for all orders of a specific type and execute them if appropriate + * @param {Stock} stock - Stock for which orders should be processed + * @param {OrderTypes} orderType - Type of order to check (Limit/Stop buy/sell) + * @param {PositionTypes} posType - Long or short + * @param {IProcessOrderRefs} refs - References to objects/functions that are required for this function + */ +export function processOrders(stock: Stock, orderType: OrderTypes, posType: PositionTypes, refs: IProcessOrderRefs): void { + let orderBook = refs.stockMarket["Orders"]; + if (orderBook == null) { + const orders: IOrderBook = {}; + for (const name in refs.stockMarket) { + const stock = refs.stockMarket[name]; + if (!(stock instanceof Stock)) { continue; } + orders[stock.symbol] = []; + } + refs.stockMarket["Orders"] = orders; + return; // Newly created, so no orders to process + } + let stockOrders = orderBook[stock.symbol]; + if (stockOrders == null || !(stockOrders.constructor === Array)) { + console.error(`Invalid Order book for ${stock.symbol} in processOrders()`); + stockOrders = []; + return; + } + + for (const order of stockOrders) { + if (order.type === orderType && order.pos === posType) { + switch (order.type) { + case OrderTypes.LimitBuy: + if (order.pos === PositionTypes.Long && stock.price <= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } + break; + case OrderTypes.LimitSell: + if (order.pos === PositionTypes.Long && stock.price >= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } + break; + case OrderTypes.StopBuy: + if (order.pos === PositionTypes.Long && stock.price >= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } + break; + case OrderTypes.StopSell: + if (order.pos === PositionTypes.Long && stock.price <= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { + executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + } + break; + default: + console.warn(`Invalid order type: ${order.type}`); + return; + } + } + } +} + +/** + * Execute a Stop or Limit Order. + * @param {Order} order - Order being executed + * @param {IStockMarket} stockMarket - Reference to StockMarket object + */ +function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => void) { + const stock = order.stock; + const orderBook = stockMarket["Orders"]; + const stockOrders = orderBook[stock.symbol]; + const isLimit = (order.type === OrderTypes.LimitBuy || order.type === OrderTypes.LimitSell); + let limitShares = 0; + + // When orders are executed, the buying and selling functions shouldn't + // emit popup dialog boxes. This options object configures the functions for that + const opts = { + rerenderFn: rerenderFn, + suppressDialog: true + } + + let res = true; + let isBuy = false; + console.log("Executing the following order:"); + console.log(order); + switch (order.type) { + case OrderTypes.LimitBuy: { + isBuy = true; + + // We only execute limit orders until the price fails to match the order condition + const isLong = (order.pos === PositionTypes.Long); + const firstShares = Math.min(order.shares, stock.shareTxUntilMovement); + + // First transaction to trigger movement + if (isLong ? buyStock(stock, firstShares, null, opts) : shortStock(stock, firstShares, null, opts)) { + limitShares = firstShares; + } else { + break; + } + + let remainingShares = order.shares - firstShares; + let remainingIterations = Math.ceil(remainingShares / stock.shareTxForMovement); + for (let i = 0; i < remainingIterations; ++i) { + if (isLong && stock.price > order.price) { + break; + } else if (!isLong && stock.price < order.price) { + break; + } + + const shares = Math.min(remainingShares, stock.shareTxForMovement); + if (isLong ? buyStock(stock, shares, null, opts) : shortStock(stock, shares, null, opts)) { + limitShares += shares; + remainingShares -= shares; + } else { + break; + } + } + } + case OrderTypes.StopBuy: { + isBuy = true; + if (order.pos === PositionTypes.Long) { + res = buyStock(stock, order.shares, null, opts) && res; + } else if (order.pos === PositionTypes.Short) { + res = shortStock(stock, order.shares, null, opts) && res; + } + break; + } + case OrderTypes.LimitSell: { + // We only execute limit orders until the price fails to match the order condition + const isLong = (order.pos === PositionTypes.Long); + const firstShares = Math.min(order.shares, stock.shareTxUntilMovement); + + // First transaction to trigger movement + if (isLong ? sellStock(stock, firstShares, null, opts) : sellShort(stock, firstShares, null, opts)) { + limitShares = firstShares; + } else { + break; + } + + let remainingShares = order.shares - firstShares; + let remainingIterations = Math.ceil(remainingShares / stock.shareTxForMovement); + for (let i = 0; i < remainingIterations; ++i) { + if (isLong && stock.price < order.price) { + break; + } else if (!isLong && stock.price > order.price) { + break; + } + + const shares = Math.min(remainingShares, stock.shareTxForMovement); + if (isLong ? sellStock(stock, shares, null, opts) : sellShort(stock, shares, null, opts)) { + limitShares += shares; + remainingShares -= shares; + } else { + break; + } + } + } + case OrderTypes.StopSell: { + if (order.pos === PositionTypes.Long) { + res = sellStock(stock, order.shares, null, opts) && res; + } else if (order.pos === PositionTypes.Short) { + res = sellShort(stock, order.shares, null, opts) && res; + } + break; + } + default: + console.warn(`Invalid order type: ${order.type}`); + return; + } + + // Position type, for logging/message purposes + const pos = order.pos === PositionTypes.Long ? "Long" : "Short"; + + if (res) { + if (isLimit) { + + } else { + + for (let i = 0; i < stockOrders.length; ++i) { + if (order == stockOrders[i]) { + + } + } + } + + for (let i = 0; i < stockOrders.length; ++i) { + if (order == stockOrders[i]) { + if (isLimit) { + // Limit orders might only transact a certain # of shares, so we have the adjust the order qty. + stockOrders[i].shares -= limitShares; + if (stockOrders[i].shares <= 0) { + stockOrders.splice(i, 1); + dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + + `(${Math.round(limitShares)} shares`); + } else { + dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was partially filled ` + + `(${Math.round(limitShares)} shares transacted, ${stockOrders[i].shares} shares remaining`); + } + } else { + // Stop orders will transact everything, so they can be removed completely + stockOrders.splice(i, 1); + dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + + `(${Math.round(order.shares)} shares transacted)`); + } + + rerenderFn(); + return; + } + } + + console.error("Could not find the following Order in Order Book: "); + console.error(order); + } else { + if (isBuy) { + dialogBoxCreate(`Failed to execute ${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + + `This is most likely because you do not have enough money or the order would exceed the stock's maximum number of shares`); + } else { + dialogBoxCreate(`Failed to execute ${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + + `This is most likely a bug, please report to game developer with details.`); + } + } +} diff --git a/src/StockMarket/StockMarket.jsx b/src/StockMarket/StockMarket.jsx index 390f691e4..e50b9b20c 100644 --- a/src/StockMarket/StockMarket.jsx +++ b/src/StockMarket/StockMarket.jsx @@ -1,4 +1,11 @@ +import { + buyStock, + sellStock, + shortStock, + sellShort, +} from "./BuyingAndSelling"; import { Order } from "./Order"; +import { processOrders } from "./OrderProcessing"; import { Stock } from "./Stock"; import { getBuyTransactionCost, @@ -17,7 +24,7 @@ import { StockSymbols } from "./data/StockSymbols"; import { StockMarketRoot } from "./ui/Root"; import { CONSTANTS } from "../Constants"; -import { WorkerScript } from "../NetscriptWorker"; +import { WorkerScript } from "../Netscript/WorkerScript"; import { Player } from "../Player"; import { Page, routing } from ".././ui/navigationTracking"; @@ -29,10 +36,12 @@ import { Reviver } from "../../utils/JSONReviver"; import React from "react"; import ReactDOM from "react-dom"; +export let StockMarket = {}; // Maps full stock name -> Stock object +export let SymbolToStockMap = {}; // Maps symbol -> Stock object + export function placeOrder(stock, shares, price, type, position, workerScript=null) { - var tixApi = (workerScript instanceof WorkerScript); - var order = new Order(stock, shares, price, type, position); - if (isNaN(shares) || isNaN(price)) { + const tixApi = (workerScript instanceof WorkerScript); + if (typeof shares !== "number" || typeof price !== "number") { if (tixApi) { workerScript.scriptRef.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument"); } else { @@ -40,21 +49,27 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu } return false; } + + const order = new Order(stock, shares, price, type, position); if (StockMarket["Orders"] == null) { const orders = {}; for (const name in StockMarket) { - if (StockMarket.hasOwnProperty(name)) { - const stk = StockMarket[name]; - if (!(stk instanceof Stock)) { continue; } - orders[stk.symbol] = []; - } + const stk = StockMarket[name]; + if (!(stk instanceof Stock)) { continue; } + orders[stk.symbol] = []; } StockMarket["Orders"] = orders; } StockMarket["Orders"][stock.symbol].push(order); - //Process to see if it should be executed immediately - processOrders(order.stock, order.type, order.pos); + + // Process to see if it should be executed immediately + const processOrderRefs = { + rerenderFn: displayStockMarketContent, + stockMarket: StockMarket, + } + processOrders(order.stock, order.type, order.pos, processOrderRefs); displayStockMarketContent(); + return true; } @@ -79,7 +94,7 @@ export function cancelOrder(params, workerScript=null) { // Order properties are passed in. Need to look for the order var stockOrders = StockMarket["Orders"][params.stock.symbol]; var orderTxt = params.stock.symbol + " - " + params.shares + " @ " + - numeralWrapper.format(params.price, '$0.000a'); + numeralWrapper.formatMoney(params.price); for (var i = 0; i < stockOrders.length; ++i) { var order = stockOrders[i]; if (params.shares === order.shares && @@ -102,50 +117,6 @@ export function cancelOrder(params, workerScript=null) { return false; } -function executeOrder(order) { - var stock = order.stock; - var orderBook = StockMarket["Orders"]; - var stockOrders = orderBook[stock.symbol]; - var res = true; - console.log("Executing the following order:"); - console.log(order); - switch (order.type) { - case OrderTypes.LimitBuy: - case OrderTypes.StopBuy: - if (order.pos === PositionTypes.Long) { - res = buyStock(order.stock, order.shares) && res; - } else if (order.pos === PositionTypes.Short) { - res = shortStock(order.stock, order.shares) && res; - } - break; - case OrderTypes.LimitSell: - case OrderTypes.StopSell: - if (order.pos === PositionTypes.Long) { - res = sellStock(order.stock, order.shares) && res; - } else if (order.pos === PositionTypes.Short) { - res = sellShort(order.stock, order.shares) && res; - } - break; - } - if (res) { - // Remove order from order book - for (var i = 0; i < stockOrders.length; ++i) { - if (order == stockOrders[i]) { - stockOrders.splice(i, 1); - displayStockMarketContent(); - return; - } - } - console.log("ERROR: Could not find the following Order in Order Book: "); - console.log(order); - } else { - console.log("Order failed to execute"); - } -} - -export let StockMarket = {}; // Maps full stock name -> Stock object -export let SymbolToStockMap = {}; // Maps symbol -> Stock object - export function loadStockMarket(saveString) { if (saveString === "") { StockMarket = {}; @@ -172,11 +143,9 @@ export function initStockMarket() { const orders = {}; for (const name in StockMarket) { - if (StockMarket.hasOwnProperty(name)) { - const stock = StockMarket[name]; - if (!(stock instanceof Stock)) { continue; } - orders[stock.symbol] = []; - } + const stock = StockMarket[name]; + if (!(stock instanceof Stock)) { continue; } + orders[stock.symbol] = []; } StockMarket["Orders"] = orders; @@ -211,252 +180,6 @@ export function stockMarketCycle() { } } -/** - * Attempt to buy a stock in the long position - * @param {Stock} stock - Stock to buy - * @param {number} shares - Number of shares to buy - * @param {WorkerScript} workerScript - If this is being called through Netscript - * @returns {boolean} - true if successful, false otherwise - */ -export function buyStock(stock, shares, workerScript=null) { - const tixApi = (workerScript instanceof WorkerScript); - - // Validate arguments - shares = Math.round(shares); - if (shares == 0 || shares < 0) { return false; } - if (stock == null || isNaN(shares)) { - if (tixApi) { - workerScript.log(`ERROR: buyStock() failed due to invalid arguments`); - } else { - dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer"); - } - - return false; - } - - // Does player have enough money? - const totalPrice = getBuyTransactionCost(stock, shares, PositionTypes.Long); - if (Player.money.lt(totalPrice)) { - if (tixApi) { - workerScript.log(`ERROR: buyStock() failed because you do not have enough money to purchase this potiion. You need ${numeralWrapper.formatMoney(totalPrice)}`); - } else { - dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); - } - - return false; - } - - // Would this purchase exceed the maximum number of shares? - if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { - if (tixApi) { - workerScript.log(`ERROR: buyStock() failed because purchasing this many shares would exceed ${stock.symbol}'s maximum number of shares`); - } else { - dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${numeralWrapper.formatBigNumber(stock.maxShares)} shares.`); - } - - return false; - } - - const origTotal = stock.playerShares * stock.playerAvgPx; - Player.loseMoney(totalPrice); - const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; - stock.playerShares = Math.round(stock.playerShares + shares); - stock.playerAvgPx = newTotal / stock.playerShares; - processBuyTransactionPriceMovement(stock, shares, PositionTypes.Long); - displayStockMarketContent(); - - const resultTxt = `Bought ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} for ${numeralWrapper.formatMoney(totalPrice)}. ` + - `Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.` - if (tixApi) { - if (workerScript.shouldLog("buyStock")) { workerScript.log(resultTxt); } - } else { - dialogBoxCreate(resultTxt); - } - - return true; -} - -/** - * Attempt to sell a stock in the long position - * @param {Stock} stock - Stock to sell - * @param {number} shares - Number of shares to sell - * @param {WorkerScript} workerScript - If this is being called through Netscript - * returns {boolean} - true if successfully sells given number of shares OR MAX owned, false otherwise - */ -export function sellStock(stock, shares, workerScript=null) { - const tixApi = (workerScript instanceof WorkerScript); - - // Sanitize/Validate arguments - if (stock == null || shares < 0 || isNaN(shares)) { - if (tixApi) { - workerScript.log(`ERROR: sellStock() failed due to invalid arguments`); - } else { - dialogBoxCreate("Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer"); - } - - return false; - } - shares = Math.round(shares); - if (shares > stock.playerShares) {shares = stock.playerShares;} - if (shares === 0) {return false;} - - const gains = getSellTransactionGain(stock, shares, PositionTypes.Long); - let netProfit = gains - (stock.playerAvgPx * shares); - if (isNaN(netProfit)) { netProfit = 0; } - Player.gainMoney(gains); - Player.recordMoneySource(netProfit, "stock"); - if (tixApi) { - workerScript.scriptRef.onlineMoneyMade += netProfit; - Player.scriptProdSinceLastAug += netProfit; - } - - stock.playerShares = Math.round(stock.playerShares - shares); - if (stock.playerShares === 0) { - stock.playerAvgPx = 0; - } - - processSellTransactionPriceMovement(stock, shares, PositionTypes.Long); - displayStockMarketContent(); - const resultTxt = `Sold ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` + - `After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`; - if (tixApi) { - if (workerScript.shouldLog("sellStock")) { workerScript.log(resultTxt); } - } else { - dialogBoxCreate(resultTxt); - } - - return true; -} - -/** - * Attempt to buy a stock in the short position - * @param {Stock} stock - Stock to sell - * @param {number} shares - Number of shares to short - * @param {WorkerScript} workerScript - If this is being called through Netscript - * @returns {boolean} - true if successful, false otherwise - */ -export function shortStock(stock, shares, workerScript=null) { - const tixApi = (workerScript instanceof WorkerScript); - - // Validate arguments - shares = Math.round(shares); - if (shares === 0 || shares < 0) { return false; } - if (stock == null || isNaN(shares)) { - if (tixApi) { - workerScript.log("ERROR: shortStock() failed because of invalid arguments."); - } else { - dialogBoxCreate("Failed to initiate a short position in a stock. This is probably " + - "due to an invalid quantity. Otherwise, this may be a bug, so contact developer"); - } - return false; - } - - // Does the player have enough money? - const totalPrice = getBuyTransactionCost(stock, shares, PositionTypes.Short); - if (Player.money.lt(totalPrice)) { - if (tixApi) { - workerScript.log("ERROR: shortStock() failed because you do not have enough " + - "money to purchase this short position. You need " + - numeralWrapper.formatMoney(totalPrice)); - } else { - dialogBoxCreate("You do not have enough money to purchase this short position. You need " + - numeralWrapper.formatMoney(totalPrice)); - } - - return false; - } - - // Would this purchase exceed the maximum number of shares? - if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { - if (tixApi) { - workerScript.log(`ERROR: shortStock() failed because purchasing this many short shares would exceed ${stock.symbol}'s maximum number of shares.`); - } else { - dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${stock.maxShares} shares.`); - } - - return false; - } - - const origTotal = stock.playerShortShares * stock.playerAvgShortPx; - Player.loseMoney(totalPrice); - const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; - stock.playerShortShares = Math.round(stock.playerShortShares + shares); - stock.playerAvgShortPx = newTotal / stock.playerShortShares; - processBuyTransactionPriceMovement(stock, shares, PositionTypes.Short); - displayStockMarketContent(); - const resultTxt = `Bought a short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} ` + - `for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} ` + - `in commission fees.`; - if (tixApi) { - if (workerScript.shouldLog("shortStock")) { workerScript.log(resultTxt); } - } else { - dialogBoxCreate(resultTxt); - } - - return true; -} - -/** - * Attempt to sell a stock in the short position - * @param {Stock} stock - Stock to sell - * @param {number} shares - Number of shares to sell - * @param {WorkerScript} workerScript - If this is being called through Netscript - * @returns {boolean} true if successfully sells given amount OR max owned, false otherwise - */ -export function sellShort(stock, shares, workerScript=null) { - const tixApi = (workerScript instanceof WorkerScript); - - if (stock == null || isNaN(shares) || shares < 0) { - if (tixApi) { - workerScript.log("ERROR: sellShort() failed because of invalid arguments."); - } else { - dialogBoxCreate("Failed to sell a short position in a stock. This is probably " + - "due to an invalid quantity. Otherwise, this may be a bug, so contact developer"); - } - - return false; - } - shares = Math.round(shares); - if (shares > stock.playerShortShares) {shares = stock.playerShortShares;} - if (shares === 0) {return false;} - - const origCost = shares * stock.playerAvgShortPx; - const totalGain = getSellTransactionGain(stock, shares, PositionTypes.Short); - if (totalGain == null || isNaN(totalGain) || origCost == null) { - if (tixApi) { - workerScript.log(`Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`); - } else { - dialogBoxCreate(`Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`); - } - - return false; - } - let profit = totalGain - origCost; - if (isNaN(profit)) { profit = 0; } - Player.gainMoney(totalGain); - Player.recordMoneySource(profit, "stock"); - if (tixApi) { - workerScript.scriptRef.onlineMoneyMade += profit; - Player.scriptProdSinceLastAug += profit; - } - - stock.playerShortShares = Math.round(stock.playerShortShares - shares); - if (stock.playerShortShares === 0) { - stock.playerAvgShortPx = 0; - } - processSellTransactionPriceMovement(stock, shares, PositionTypes.Short); - displayStockMarketContent(); - const resultTxt = `Sold your short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` + - `After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`; - if (tixApi) { - if (workerScript.shouldLog("sellShort")) { workerScript.log(resultTxt); } - } else { - dialogBoxCreate(resultTxt); - } - - return true; -} - // Stock prices updated every 6 seconds const msPerStockUpdate = 6e3; const cyclesPerStockUpdate = msPerStockUpdate / CONSTANTS.MilliPerCycle; @@ -494,18 +217,22 @@ export function processStockPrices(numCycles=1) { if (isNaN(chc)) { chc = 0.5; } const c = Math.random(); + const processOrderRefs = { + rerenderFn: displayStockMarketContent, + stockMarket: StockMarket, + } if (c < chc) { stock.price *= (1 + av); - processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Short); - processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long); - processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long); - processOrders(stock, OrderTypes.StopSell, PositionTypes.Short); + processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Short, processOrderRefs); + processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long, processOrderRefs); + processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long, processOrderRefs); + processOrders(stock, OrderTypes.StopSell, PositionTypes.Short, processOrderRefs); } else { stock.price /= (1 + av); - processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Long); - processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short); - processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short); - processOrders(stock, OrderTypes.StopSell, PositionTypes.Long); + processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Long, processOrderRefs); + processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short, processOrderRefs); + processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short, processOrderRefs); + processOrders(stock, OrderTypes.StopSell, PositionTypes.Long, processOrderRefs); } let otlkMagChange = stock.otlkMag * av; @@ -530,67 +257,6 @@ export function processStockPrices(numCycles=1) { displayStockMarketContent(); } -// Checks and triggers any orders for the specified stock -function processOrders(stock, orderType, posType) { - var orderBook = StockMarket["Orders"]; - if (orderBook == null) { - var orders = {}; - for (var name in StockMarket) { - if (StockMarket.hasOwnProperty(name)) { - var stock = StockMarket[name]; - if (!(stock instanceof Stock)) {continue;} - orders[stock.symbol] = []; - } - } - StockMarket["Orders"] = orders; - return; //Newly created, so no orders to process - } - var stockOrders = orderBook[stock.symbol]; - if (stockOrders == null || !(stockOrders.constructor === Array)) { - console.log("ERROR: Invalid Order book for " + stock.symbol + " in processOrders()"); - stockOrders = []; - return; - } - for (var i = 0; i < stockOrders.length; ++i) { - var order = stockOrders[i]; - if (order.type === orderType && order.pos === posType) { - switch(order.type) { - case OrderTypes.LimitBuy: - if (order.pos === PositionTypes.Long && stock.price <= order.price) { - executeOrder/*66*/(order); - } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { - executeOrder/*66*/(order); - } - break; - case OrderTypes.LimitSell: - if (order.pos === PositionTypes.Long && stock.price >= order.price) { - executeOrder/*66*/(order); - } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { - executeOrder/*66*/(order); - } - break; - case OrderTypes.StopBuy: - if (order.pos === PositionTypes.Long && stock.price >= order.price) { - executeOrder/*66*/(order); - } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { - executeOrder/*66*/(order); - } - break; - case OrderTypes.StopSell: - if (order.pos === PositionTypes.Long && stock.price <= order.price) { - executeOrder/*66*/(order); - } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { - executeOrder/*66*/(order); - } - break; - default: - console.log("Invalid order type: " + order.type); - return; - } - } - } -} - let stockMarketContainer = null; function setStockMarketContainer() { stockMarketContainer = document.getElementById("stock-market-container"); @@ -599,7 +265,6 @@ function setStockMarketContainer() { document.addEventListener("DOMContentLoaded", setStockMarketContainer); - function initStockMarketFnForReact() { initStockMarket(); initSymbolToStockMap(); diff --git a/src/StockMarket/StockMarketHelpers.ts b/src/StockMarket/StockMarketHelpers.ts index 1c4c28d8f..2d296e401 100644 --- a/src/StockMarket/StockMarketHelpers.ts +++ b/src/StockMarket/StockMarketHelpers.ts @@ -3,7 +3,7 @@ import { PositionTypes } from "./data/PositionTypes"; import { CONSTANTS } from "../Constants"; // Amount by which a stock's forecast changes during each price movement -const forecastChangePerPriceMovement = 0.4; +export const forecastChangePerPriceMovement = 0.4; /** * Given a stock, calculates the amount by which the stock price is multiplied @@ -103,16 +103,6 @@ export function processBuyTransactionPriceMovement(stock: Stock, shares: number, const isLong = (posType === PositionTypes.Long); - // If the number of shares doesn't trigger a price movement, just return - if (shares <= stock.shareTxUntilMovement) { - stock.shareTxUntilMovement -= shares; - return; - } - - // Calculate how many iterations of price changes we need to account for - let remainingShares = shares - stock.shareTxUntilMovement; - let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); - let currPrice = stock.price; function processPriceMovement() { if (isLong) { @@ -122,15 +112,38 @@ export function processBuyTransactionPriceMovement(stock: Stock, shares: number, } } + // No price/forecast movement + if (shares <= stock.shareTxUntilMovement) { + stock.shareTxUntilMovement -= shares; + if (stock.shareTxUntilMovement <= 0) { + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + stock.price = currPrice; + stock.otlkMag -= (forecastChangePerPriceMovement); + } + + return; + } + + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + for (let i = 1; i < numIterations; ++i) { processPriceMovement(); } - stock.price = currPrice; stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement); + if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) { + // The shareTxUntilMovement ended up at 0 at the end of the "processing" + ++numIterations; + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + } + stock.price = currPrice; // Forecast always decreases in magnitude - const forecastChange = Math.min(5, forecastChangePerPriceMovement * numIterations); + const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1)); stock.otlkMag -= forecastChange; } @@ -214,18 +227,8 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number const isLong = (posType === PositionTypes.Long); - if (shares <= stock.shareTxUntilMovement) { - stock.shareTxUntilMovement -= shares; - return; - } - - // Calculate how many iterations of price changes we need to accoutn for - let remainingShares = shares - stock.shareTxUntilMovement; - let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); - let currPrice = stock.price; - for (let i = 1; i < numIterations; ++i) { - // Price movement + function processPriceMovement() { if (isLong) { currPrice *= calculateDecreasingPriceMovement(stock)!; } else { @@ -233,11 +236,37 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number } } - stock.price = currPrice; + // No price/forecast movement + if (shares <= stock.shareTxUntilMovement) { + stock.shareTxUntilMovement -= shares; + if (stock.shareTxUntilMovement <= 0) { + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + stock.price = currPrice; + stock.otlkMag -= (forecastChangePerPriceMovement); + } + + return; + } + + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + + for (let i = 1; i < numIterations; ++i) { + processPriceMovement(); + } + stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement); + if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) { + ++numIterations; + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + } + stock.price = currPrice; // Forecast always decreases in magnitude - const forecastChange = Math.min(5, forecastChangePerPriceMovement * numIterations); + const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1)); stock.otlkMag -= forecastChange; } diff --git a/src/StockMarket/ui/StockTicker.tsx b/src/StockMarket/ui/StockTicker.tsx index d21e5a424..db018745d 100644 --- a/src/StockMarket/ui/StockTicker.tsx +++ b/src/StockMarket/ui/StockTicker.tsx @@ -18,7 +18,6 @@ import { import { OrderTypes } from "../data/OrderTypes"; import { PositionTypes } from "../data/PositionTypes"; -import { CONSTANTS } from "../../Constants"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { numeralWrapper } from "../../ui/numeralFormat"; @@ -58,6 +57,7 @@ type IState = { orderType: SelectorOrderType; position: PositionTypes; qty: string; + rerenderFlag: boolean; } export class StockTicker extends React.Component { @@ -68,6 +68,7 @@ export class StockTicker extends React.Component { orderType: SelectorOrderType.Market, position: PositionTypes.Long, qty: "", + rerenderFlag: false, } this.getBuyTransactionCostText = this.getBuyTransactionCostText.bind(this); @@ -80,6 +81,7 @@ export class StockTicker extends React.Component { this.handleQuantityChange = this.handleQuantityChange.bind(this); this.handleSellButtonClick = this.handleSellButtonClick.bind(this); this.handleSellAllButtonClick = this.handleSellAllButtonClick.bind(this); + this.rerender = this.rerender.bind(this); } createPlaceOrderPopupBox(yesTxt: string, popupTxt: string, yesBtnCb: (price: number) => void) { @@ -172,6 +174,7 @@ export class StockTicker extends React.Component { } else { this.props.buyStockLong(this.props.stock, shares); } + this.rerender(); break; } case SelectorOrderType.Limit: { @@ -213,30 +216,13 @@ export class StockTicker extends React.Component { } else { this.props.buyStockLong(stock, maxShares); } + this.rerender(); break; } - case SelectorOrderType.Limit: { - this.createPlaceOrderPopupBox( - "Place Buy Limit Order", - "Enter the price for your Limit Order", - (price: number) => { - this.props.placeOrder(stock, maxShares, price, OrderTypes.LimitBuy, this.state.position); - } - ); + default: { + dialogBoxCreate(`ERROR: 'Buy Max' only works for Market Orders`); break; } - case SelectorOrderType.Stop: { - this.createPlaceOrderPopupBox( - "Place Buy Stop Order", - "Enter the price for your Stop Order", - (price: number) => { - this.props.placeOrder(stock, maxShares, price, OrderTypes.StopBuy, this.state.position); - } - ) - break; - } - default: - break; } } @@ -311,6 +297,7 @@ export class StockTicker extends React.Component { } else { this.props.sellStockLong(this.props.stock, shares); } + this.rerender(); break; } case SelectorOrderType.Limit: { @@ -348,6 +335,7 @@ export class StockTicker extends React.Component { } else { this.props.sellStockLong(stock, stock.playerShares); } + this.rerender(); break; } default: { @@ -367,6 +355,14 @@ export class StockTicker extends React.Component { return (this.props.p.bitNodeN === 8 || (SourceFileFlags[8] >= 2)); } + rerender(): void { + this.setState((prevState) => { + return { + rerenderFlag: !prevState.rerenderFlag, + } + }); + } + render() { // Determine if the player's intended transaction will cause a price movement let causesMovement: boolean = false; diff --git a/src/StockMarket/ui/StockTickerHeaderText.tsx b/src/StockMarket/ui/StockTickerHeaderText.tsx index 9c0b3ef6f..1791d8be0 100644 --- a/src/StockMarket/ui/StockTickerHeaderText.tsx +++ b/src/StockMarket/ui/StockTickerHeaderText.tsx @@ -18,7 +18,6 @@ type IProps = { export function StockTickerHeaderText(props: IProps): React.ReactElement { const stock = props.stock; - const p = props.p; const stockPriceFormat = numeralWrapper.formatMoney(stock.price); diff --git a/src/StockMarket/ui/StockTickerOrder.tsx b/src/StockMarket/ui/StockTickerOrder.tsx index ff87e7eb0..1114b5e90 100644 --- a/src/StockMarket/ui/StockTickerOrder.tsx +++ b/src/StockMarket/ui/StockTickerOrder.tsx @@ -28,7 +28,7 @@ export class StockTickerOrder extends React.Component { const order = this.props.order; const posTxt = order.pos === PositionTypes.Long ? "Long Position" : "Short Position"; - const txt = `${order.type} - ${posTxt} - ${order.shares} @ ${numeralWrapper.formatMoney(order.price)}` + const txt = `${order.type} - ${posTxt} - ${numeralWrapper.formatBigNumber(order.shares)} @ ${numeralWrapper.formatMoney(order.price)}` return (
  • diff --git a/src/Terminal.js b/src/Terminal.js index 800eee287..728d96892 100644 --- a/src/Terminal.js +++ b/src/Terminal.js @@ -57,6 +57,7 @@ import { killWorkerScript, addWorkerScript } from "./NetscriptWorker"; import { Player } from "./Player"; import { hackWorldDaemon } from "./RedPill"; import { RunningScript } from "./Script/RunningScript"; +import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers"; import { findRunningScript } from "./Script/ScriptHelpers"; import { isScriptFilename } from "./Script/ScriptHelpersTS"; import { AllServers } from "./Server/AllServers"; @@ -1447,7 +1448,7 @@ let Terminal = { let spacesThread = Array(numSpacesThread+1).join(" "); // Calculate and transform RAM usage - let ramUsage = numeralWrapper.format(script.getRamUsage() * script.threads, '0.00') + " GB"; + let ramUsage = numeralWrapper.format(getRamUsageFromRunningScript(script) * script.threads, '0.00') + " GB"; var entry = [script.filename, spacesScript, script.threads, spacesThread, ramUsage]; post(entry.join("")); @@ -2268,7 +2269,7 @@ let Terminal = { // This has to come after addWorkerScript() because that fn // updates the RAM usage. This kinda sucks, address if possible - server.runScript(runningScriptObj, Player); + server.runScript(runningScriptObj, Player.hacknet_node_money_mult); return; } } diff --git a/src/engine.jsx b/src/engine.jsx index 4bd7a63b5..5870457b9 100644 --- a/src/engine.jsx +++ b/src/engine.jsx @@ -49,11 +49,10 @@ import { LocationRoot } from "./Locations/ui/Root"; import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers"; import { inMission, currMission } from "./Missions"; import { - initSingularitySFFlags, - hasSingularitySF, - hasCorporationSF -} from "./NetscriptFunctions"; -import { updateOnlineScriptTimes, runScriptsLoop } from "./NetscriptWorker"; + loadAllRunningScripts, + runScriptsLoop, + updateOnlineScriptTimes, +} from "./NetscriptWorker"; import { Player } from "./Player"; import { prestigeAugmentation, prestigeSourceFile } from "./Prestige"; import { Programs } from "./Programs/Programs"; @@ -66,7 +65,6 @@ import { redPillFlag, hackWorldDaemon } from "./RedPill"; import { saveObject, loadGame } from "./SaveObject"; import { getCurrentEditor, - loadAllRunningScripts, scriptEditorInit, updateScriptEditorContent } from "./Script/ScriptHelpers"; @@ -1087,7 +1085,6 @@ const Engine = { initSymbolToStockMap(); } initLiterature(); - initSingularitySFFlags(); updateSourceFileFlags(Player); // Calculate the number of cycles have elapsed while offline @@ -1213,7 +1210,6 @@ const Engine = { initAugmentations(); initMessages(); initLiterature(); - initSingularitySFFlags(); // Open main menu accordions for new game const hackingHdr = document.getElementById("hacking-menu-header"); diff --git a/tests/StockMarketTests.js b/tests/StockMarketTests.js index d53c90526..59270fdd9 100644 --- a/tests/StockMarketTests.js +++ b/tests/StockMarketTests.js @@ -1,14 +1,28 @@ import { CONSTANTS } from "../src/Constants"; -import { Stock } from "../src/StockMarket/Stock"; +import { Order } from "../src/StockMarket/Order"; +//import { processOrders } from "../src/StockMarket/OrderProcessing"; +// import { Stock } from "../src/StockMarket/Stock"; +import { + deleteStockMarket, + initStockMarket, + initSymbolToStockMap, + loadStockMarket, + StockMarket, + SymbolToStockMap, +} from "../src/StockMarket/StockMarket"; +/* import { calculateIncreasingPriceMovement, calculateDecreasingPriceMovement, + forecastChangePerPriceMovement, getBuyTransactionCost, getSellTransactionGain, processBuyTransactionPriceMovement, processSellTransactionPriceMovement, } from "../src/StockMarket/StockMarketHelpers"; -import { PositionTypes } from "../src/StockMarket/data/PositionTypes"; +*/ +// import { OrderTypes } from "../src/StockMarket/data/OrderTypes" +// import { PositionTypes } from "../src/StockMarket/data/PositionTypes"; const assert = chai.assert; const expect = chai.expect; @@ -144,6 +158,67 @@ describe("Stock Market Tests", function() { }); }); + describe("StockMarket object", function() { + describe("Initialization", function() { + // Keeps track of initialized stocks. Contains their symbols + const stocks = []; + + before(function() { + expect(initStockMarket).to.not.throw(); + expect(initSymbolToStockMap).to.not.throw(); + }); + + it("should have Stock objects", function() { + for (const prop in StockMarket) { + const stock = StockMarket[prop]; + if (stock instanceof Stock) { + stocks.push(stock.symbol); + } + } + + // We'll just check that there are some stocks + expect(stocks.length).to.be.at.least(1); + }); + + it("should have an order book in the 'Orders' property", function() { + expect(StockMarket).to.have.property("Orders"); + + const orderbook = StockMarket["Orders"]; + for (const symbol of stocks) { + const ordersForStock = orderbook[symbol]; + expect(ordersForStock).to.be.an("array"); + expect(ordersForStock.length).to.equal(0); + } + }); + + it("should have properties for managing game cycles", function() { + expect(StockMarket).to.have.property("storedCycles"); + expect(StockMarket).to.have.property("lastUpdate"); + }); + }); + + // Because 'StockMarket' is a global object, the effects of initialization from + // the block above should still stand + describe("Deletion", function() { + it("should set StockMarket to be an empty object", function() { + expect(StockMarket).to.be.an("object").that.is.not.empty; + deleteStockMarket(); + expect(StockMarket).to.be.an("object").that.is.empty; + }); + }); + + // Reset stock market for each test + beforeEach(function() { + deleteStockMarket(); + initStockMarket(); + initSymbolToStockMap(); + }); + + it("should properly initialize", function() { + + }); + }); + describe("Transaction Cost Calculator Functions", function() { describe("getBuyTransactionCost()", function() { it("should fail on invalid 'stock' argument", function() { @@ -279,6 +354,31 @@ describe("Stock Market Tests", function() { }); describe("Price Movement Processor Functions", function() { + // N = 1 is the original price + function getNthPriceIncreasing(origPrice, n) { + let price = origPrice; + for (let i = 1; i < n; ++i) { + price *= calculateIncreasingPriceMovement(stock); + } + + return price; + } + + // N = 1 is the original price + function getNthPriceDecreasing(origPrice, n) { + let price = origPrice; + for (let i = 1; i < n; ++i) { + price *= calculateDecreasingPriceMovement(stock); + } + + return price; + } + + // N = 1 is the original forecast + function getNthForecast(origForecast, n) { + return origForecast - forecastChangePerPriceMovement * (n - 1); + } + describe("processBuyTransactionPriceMovement()", function() { const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2); const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares; @@ -305,53 +405,107 @@ describe("Stock Market Tests", function() { expect(stock.shareTxUntilMovement).to.equal(oldTracker); }); - it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() { + it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() { const oldPrice = stock.price; + const oldForecast = stock.otlkMag; processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long); expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); - it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() { + it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() { const oldPrice = stock.price; + const oldForecast = stock.otlkMag; processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short); expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); it("should properly evaluate LONG transactions that trigger price movements", function() { const oldPrice = stock.price; - function getNthPrice(n) { - let price = oldPrice; - for (let i = 1; i < n; ++i) { - price *= calculateIncreasingPriceMovement(stock); - } - - return price; - } + const oldForecast = stock.otlkMag; processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long); - expect(stock.price).to.equal(getNthPrice(4)); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); it("should properly evaluate SHORT transactions that trigger price movements", function() { const oldPrice = stock.price; - function getNthPrice(n) { - let price = oldPrice; - for (let i = 1; i < n; ++i) { - price *= calculateDecreasingPriceMovement(stock); - } - - return price; - } + const oldForecast = stock.otlkMag; processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short); - expect(stock.price).to.equal(getNthPrice(4)); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); + + it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long); + processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short); + processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); }); describe("processSellTransactionPriceMovement()", function() { @@ -380,53 +534,137 @@ describe("Stock Market Tests", function() { expect(stock.shareTxUntilMovement).to.equal(oldTracker); }); - it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() { + it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() { const oldPrice = stock.price; + const oldForecast = stock.otlkMag; processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long); expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); - it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() { + it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() { const oldPrice = stock.price; + const oldForecast = stock.otlkMag; processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short); expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); it("should properly evaluate LONG transactions that trigger price movements", function() { const oldPrice = stock.price; - function getNthPrice(n) { - let price = oldPrice; - for (let i = 1; i < n; ++i) { - price *= calculateDecreasingPriceMovement(stock); - } - - return price; - } + const oldForecast = stock.otlkMag; processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long); - expect(stock.price).to.equal(getNthPrice(4)); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); it("should properly evaluate SHORT transactions that trigger price movements", function() { const oldPrice = stock.price; - function getNthPrice(n) { - let price = oldPrice; - for (let i = 1; i < n; ++i) { - price *= calculateIncreasingPriceMovement(stock); - } - - return price; - } + const oldForecast = stock.otlkMag; processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short); - expect(stock.price).to.equal(getNthPrice(4)); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); }); + + it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long); + processSellTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short); + processSellTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); }); }); + + describe("Order Class", function() { + it("should throw on invalid arguments", function() { + function invalid1() { + return new Order({}, 1, 1, OrderTypes.LimitBuy, PositionTypes.Long); + } + function invalid2() { + return new Order(new Stock(), "z", 0, OrderTypes.LimitBuy, PositionTypes.Short); + } + function invalid3() { + return new Order(new Stock(), 1, {}, OrderTypes.LimitBuy, PositionTypes.Short); + } + function invalid4() { + return new Order(new Stock(), 1, NaN, OrderTypes.LimitBuy, PositionTypes.Short); + } + function invalid5() { + return new Order(new Stock(), NaN, 0, OrderTypes.LimitBuy, PositionTypes.Short); + } + + expect(invalid1).to.throw(); + expect(invalid2).to.throw(); + expect(invalid3).to.throw(); + expect(invalid4).to.throw(); + expect(invalid5).to.throw(); + }); + }); + + describe("Order Processing", function() { + + }); }); diff --git a/utils/IPAddress.ts b/utils/IPAddress.ts index 948b055eb..241239fff 100644 --- a/utils/IPAddress.ts +++ b/utils/IPAddress.ts @@ -1,25 +1,14 @@ -import { AllServers } from "../src/Server/AllServers"; import { getRandomByte } from "./helpers/getRandomByte"; -/* Functions to deal with manipulating IP addresses*/ - -//Generate a random IP address -//Will not return an IP address that already exists in the AllServers array +/** + * Generate a random IP address + * Does not check to see if the IP already exists in the game + */ export function createRandomIp(): string { const ip: string = getRandomByte(99) + '.' + getRandomByte(9) + '.' + getRandomByte(9) + '.' + getRandomByte(9); - // If the Ip already exists, recurse to create a new one - if (ipExists(ip)) { - return createRandomIp(); - } - return ip; } - -// Returns true if the IP already exists in one of the game's servers -export function ipExists(ip: string) { - return (AllServers[ip] != null); -}