From bc6f0da6775b96d8ee2ece8512bb0bea06b928e5 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Thu, 2 Nov 2017 16:47:09 -0500 Subject: [PATCH] Fixed Netscript interpreter memory issues by breaking promise chains in while and for loops. Improved Stock Market UI. Added a button to kill script from log display box. Added confirm() Netscript function --- css/popupboxes.css | 19 +- dist/bundle.js | 540 ++++++++++++++++++++++++++------------ index.html | 9 +- src/Constants.js | 27 +- src/NetscriptEvaluator.js | 176 +++++++------ src/NetscriptFunctions.js | 20 +- src/NetscriptWorker.js | 7 +- src/Player.js | 4 +- src/RedPill.js | 4 + src/SaveObject.js | 4 +- src/Script.js | 2 + src/StockMarket.js | 267 ++++++++++++++----- src/Terminal.js | 2 +- utils/HelperFunctions.js | 11 +- utils/LogBox.js | 15 +- 15 files changed, 753 insertions(+), 354 deletions(-) diff --git a/css/popupboxes.css b/css/popupboxes.css index 50378b171..38bc7f503 100644 --- a/css/popupboxes.css +++ b/css/popupboxes.css @@ -73,8 +73,7 @@ padding: 10px; } -.dialog-box-close-button, -#log-box-close { +.dialog-box-close-button { float: right; color: #aaa; font-size: 20px; @@ -89,13 +88,21 @@ #log-box-close { position: fixed; - right: 26%; + right: 27%; +} + +#log-box-kill-script { + right: 11%; + position: relative; +} + +#log-box-close, #log-box-kill-script { + float:right; + display:inline-block; } .dialog-box-close-button:hover, -.dialog-box-close-button:focus, -#log-box-close:hover, -#log-box-close:focus { +.dialog-box-close-button:focus,{ color: white; text-decoration: none; cursor: pointer; diff --git a/dist/bundle.js b/dist/bundle.js index 7ec09cb3d..8c9db18b7 100644 --- a/dist/bundle.js +++ b/dist/bundle.js @@ -278,8 +278,10 @@ function PlayerObject() { this.totalPlaytime = 0; this.playtimeSinceLastAug = 0; - //Script production since last Aug installation + //Production since last Augmentation installation this.scriptProdSinceLastAug = 0; + this.stockProdSinceLastAug = 0; + this.crimeProdSinceLastAug = 0; }; PlayerObject.prototype.init = function() { @@ -2492,6 +2494,7 @@ function dialogBoxCreate(txt) { /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "f", function() { return printArray; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "e", function() { return powerOfTwo; }); /* unused harmony export clearEventListenersEl */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "g", function() { return removeElementById; }); //General helper functions //Returns the size (number of keys) of an object @@ -2532,6 +2535,14 @@ function clearEventListenersEl(el) { return newElem; } +//Given its id, this function removes an element AND its children +function removeElementById(id) { + var elem = document.getElementById(id); + if (elem == null) {return;} + while(elem.firstChild) {elem.removeChild(elem.firstChild);} + elem.parentNode.removeChild(elem); +} + function getRandomInt(min, max) { if (min > max) {return getRandomInt(max, min);} return Math.floor(Math.random() * (max - min + 1)) + min; @@ -3218,10 +3229,21 @@ let CONSTANTS = { "have purchased. It takes an optional parameter specifying whether the hostname or IP addresses will be returned. If this " + "parameter is not specified, it is true by default and hostnames will be returned

" + "round(n)
Rounds the number n to the nearest integer. If the argument passed in is not a number, then the function will return 0.

" + - "write(port, data)
Writes data to a port. The first argument must be a number between 1 and 10 that specifies the port. The second " + - "argument defines the data to write to the port. If the second argument is not specified then it will write an empty string to the port.

" + - "read(port)
Reads data from a port. The first argument must be a number between 1 and 10 that specifies the port. A port is a serialized queue. " + - "This function will remove the first element from the queue and return it. If the queue is empty, then the string 'NULL PORT DATA' will be returned.

" + + "write(port, data='', mode='a')
This function can be used to either write data to a port or to a text file (.txt).

" + + "If the first argument is a number between 1 and 10, then it specifies a port and this function will write data to a port. If the second " + + "argument is not specified then it will write an empty string to the port. The third argument, mode, is not used when writing data to a port.

" + + "If the first argument is a string, then it specifies the name of a text file (.txt) and this function will write data to a text file. " + + "The second argument defines the data to be written to the text file. If it is not specified then it is an empty string by default. " + + "This third argument, mode, defines how the data will be written to the text file. If mode is set to 'w', then the data is written in 'write' " + + "mode which means that it will overwrite the existing data on the file, or it will create a new file if it does not already exist. Otherwise, " + + "the data will be written in 'append' mode which means that the data will be added at the end of the existing file, or it will create a new file if it " + + "does not already exist. If mode isn't specified then it will be 'a' for 'append' mode by default.

" + + "read(port)
This function is used to read data from a port or from a text file (.txt).

" + + "This function takes a single argument. If this argument is a number between 1 and 10, then it specifies a port and it will read data from " + + "a port. A port is a serialized queue. This function will remove the first element from the queue and return it. If the queue is empty, " + + "then the string 'NULL PORT DATA' will be returned.

" + + "If the first argument is a string, then it specifies the name of a text file and this function will return the data in the " + + "specified text file. If the text file does not exist, an empty string will be returned

" + "scriptRunning(scriptname, hostname/ip)
Returns a boolean indicating whether any instance of the specified script is running " + "on a server, regardless of its arguments. This is different than the isRunning() function because it does not " + "try to identify a specific instance of a running script by its arguments.

" + @@ -3667,6 +3689,14 @@ let CONSTANTS = { "World Stock Exchange account and TIX API Access
", LatestUpdate: + "v0.32.1
" + + "-Updated Netscript's 'interpreter/engine' to use the Bluebird promise library instead of native promises. " + + "It should now be faster and more memory-efficient. If this has broken any Netscript features please report it through Github or the subreddit (reddit.com/r/bitburner)
" + + "-Rebalanced stock market (adjusted parameters such as the volatility/trends/starting price of certain stocks)
" + + "-Added confirm() Netscript function
" + + "-Added 'Buy Max' and 'Sell All' functions to Stock Market UI
" + + "-Added 'Portfolio' Mode to Stock Market UI so you can only view stocks you have a position/order in
" + + "-Added a button to kill a script from its log display box
" + "v0.32.0
" + "-Released BitNode-8: Ghost of Wall Street
" + "-Re-designed Stock Market UI
" + @@ -18190,7 +18220,7 @@ function runScriptsLoop() { if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) { try { var ast = Object(__WEBPACK_IMPORTED_MODULE_7__utils_acorn_js__["parse"])(workerScripts[i].code); - console.log(ast); + //console.log(ast); } catch (e) { console.log("Error parsing script: " + workerScripts[i].name); Object(__WEBPACK_IMPORTED_MODULE_8__utils_DialogBox_js__["a" /* dialogBoxCreate */])("Syntax ERROR in " + workerScripts[i].name + ":
" + e); @@ -18267,11 +18297,6 @@ function killWorkerScript(runningScriptObj, serverIp) { workerScripts[i].fnWorker.env.stopFlag = true; Object(__WEBPACK_IMPORTED_MODULE_4__NetscriptEvaluator_js__["c" /* killNetscriptDelay */])(workerScripts[i].fnWorker); } - /*workerScripts[i].killTrigger(); - if (workerScripts[i].fnWorker) { - workerScripts[i].fnWorker.env.stopFlag = true; - workerScripts[i].fnWorker.killTrigger(); - }*/ return true; } } @@ -21339,6 +21364,7 @@ function RunningScript(script, args) { this.server = script.server; //IP Address only this.logs = []; //Script logging. Array of strings, with each element being a log entry + this.logUpd = false; //Stats to display on the Scripts menu, and used to determine offline progress this.offlineRunningTime = 0.01; //Seconds @@ -21362,6 +21388,7 @@ RunningScript.prototype.log = function(txt) { this.logs.shift(); } this.logs.push(txt); + this.logUpd = true; } RunningScript.prototype.displayLog = function() { @@ -22895,7 +22922,25 @@ function NetscriptFunctions(workerScript) { return __WEBPACK_IMPORTED_MODULE_14__Player_js__["a" /* Player */].playtimeSinceLastAug; }, confirm : function(txt) { - + if (__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["e" /* yesNoBoxOpen */]) { + workerScript.scriptRef.log("ERROR: confirm() failed because a pop-up dialog box is already open"); + return false; + } + if (!Object(__WEBPACK_IMPORTED_MODULE_29__utils_StringHelperFunctions_js__["f" /* isString */])(txt)) {txt = String(txt);} + var yesBtn = Object(__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["d" /* yesNoBoxGetYesButton */])(), noBtn = Object(__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["c" /* yesNoBoxGetNoButton */])(); + yesBtn.innerHTML = "Yes"; + noBtn.innerHTML = "No"; + return new Promise(function(resolve, reject) { + yesBtn.addEventListener("click", ()=>{ + Object(__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["a" /* yesNoBoxClose */])(); + resolve(true); + }); + noBtn.addEventListener("click", ()=>{ + Object(__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["a" /* yesNoBoxClose */])(); + resolve(false); + }); + Object(__WEBPACK_IMPORTED_MODULE_30__utils_YesNoBox_js__["b" /* yesNoBoxCreate */])(txt); + }); }, /* Singularity Functions */ @@ -27703,7 +27748,7 @@ let Terminal = { else {rootAccess = "NO";} post("Root Access: " + rootAccess); post("Required hacking skill: " + __WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].getCurrentServer().requiredHackingSkill); - post("Estimated server security level(1-100): " + Object(__WEBPACK_IMPORTED_MODULE_17__utils_StringHelperFunctions_js__["c" /* formatNumber */])(Object(__WEBPACK_IMPORTED_MODULE_18__utils_HelperFunctions_js__["a" /* addOffset */])(__WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].getCurrentServer().hackDifficulty, 5), 3)); + post("Estimated server security level: " + Object(__WEBPACK_IMPORTED_MODULE_17__utils_StringHelperFunctions_js__["c" /* formatNumber */])(Object(__WEBPACK_IMPORTED_MODULE_18__utils_HelperFunctions_js__["a" /* addOffset */])(__WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].getCurrentServer().hackDifficulty, 5), 3)); post("Estimated chance to hack: " + Object(__WEBPACK_IMPORTED_MODULE_17__utils_StringHelperFunctions_js__["c" /* formatNumber */])(Object(__WEBPACK_IMPORTED_MODULE_18__utils_HelperFunctions_js__["a" /* addOffset */])(__WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].calculateHackingChance() * 100, 5), 2) + "%"); post("Estimated time to hack: " + Object(__WEBPACK_IMPORTED_MODULE_17__utils_StringHelperFunctions_js__["c" /* formatNumber */])(Object(__WEBPACK_IMPORTED_MODULE_18__utils_HelperFunctions_js__["a" /* addOffset */])(__WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].calculateHackingTime(), 5), 3) + " seconds"); post("Estimated total money available on server: $" + Object(__WEBPACK_IMPORTED_MODULE_17__utils_StringHelperFunctions_js__["c" /* formatNumber */])(Object(__WEBPACK_IMPORTED_MODULE_18__utils_HelperFunctions_js__["a" /* addOffset */])(__WEBPACK_IMPORTED_MODULE_11__Player_js__["a" /* Player */].getCurrentServer().moneyAvailable, 5), 2)); @@ -36711,7 +36756,7 @@ function initStockMarket() { StockMarket[omnitek] = omnitekStk; var foursigma = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].Sector12FourSigma; - var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], 1.1, true, 18, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(60000, 70000)); + var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], 1.05, true, 18, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(60000, 70000)); StockMarket[foursigma] = foursigmaStk; var kuaigong = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].ChongqingKuaiGongInternational; @@ -36719,7 +36764,7 @@ function initStockMarket() { StockMarket[kuaigong] = kuaigongStk; var fulcrum = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].AevumFulcrumTechnologies; - var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], 1.25, true, 17, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(30000, 35000)); + var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], 1.25, true, 16, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(30000, 35000)); StockMarket[fulcrum] = fulcrumStk; var storm = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].IshimaStormTechnologies; @@ -36779,7 +36824,7 @@ function initStockMarket() { StockMarket[rho] = rhoStk; var alpha = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].Sector12AlphaEnterprises; - var alphaStk = new Stock(alpha, StockSymbols[alpha], 2, true, 10, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(5000, 7500)); + var alphaStk = new Stock(alpha, StockSymbols[alpha], 1.9, true, 10, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(5000, 7500)); StockMarket[alpha] = alphaStk; var syscore = __WEBPACK_IMPORTED_MODULE_2__Location_js__["a" /* Locations */].VolhavenSysCoreSecurities; @@ -36803,15 +36848,15 @@ function initStockMarket() { StockMarket[fns] = fnsStk; var sigmacosm = "Sigma Cosmetics"; - var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 3, true, 0, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(2000, 3000)); + var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 2.8, true, 0, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(2000, 3000)); StockMarket[sigmacosm] = sigmacosmStk; var joesguns = "Joes Guns"; - var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 4, true, 1, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(500, 1000)); + var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 3.7, true, 1, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(500, 1000)); StockMarket[joesguns] = joesgunsStk; var catalyst = "Catalyst Ventures"; - var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.6, true, 15, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(500, 1000)); + var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.5, true, 13, Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["d" /* getRandomInt */])(500, 1000)); StockMarket[catalyst] = catalystStk; var microdyne = "Microdyne Technologies"; @@ -36852,8 +36897,8 @@ function stockMarketCycle() { for (var name in StockMarket) { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; - var thresh = 0.59; - if (stock.b) {thresh = 0.41;} + var thresh = 0.6; + if (stock.b) {thresh = 0.4;} if (Math.random() < thresh) { stock.b = !stock.b; } @@ -36946,9 +36991,7 @@ function shortStock(stock, shares, workerScript=null) { var newTotal = origTotal + totalPrice; stock.playerShortShares += shares; stock.playerAvgShortPx = newTotal / stock.playerShortShares; - if (__WEBPACK_IMPORTED_MODULE_1__engine_js__["Engine"].currentPage === __WEBPACK_IMPORTED_MODULE_1__engine_js__["Engine"].Page.StockMarket) { - updateStockPlayerPosition(stock); - } + updateStockPlayerPosition(stock); if (tixApi) { workerScript.scriptRef.log("Bought a short position of " + Object(__WEBPACK_IMPORTED_MODULE_10__utils_StringHelperFunctions_js__["c" /* formatNumber */])(shares, 0) + " shares of " + stock.symbol + " at " + __WEBPACK_IMPORTED_MODULE_9__utils_numeral_min_js___default()(stock.price).format('($0.000a)') + " per share. Paid " + @@ -36990,9 +37033,7 @@ function sellShort(stock, shares, workerScript=null) { if (stock.playerShortShares === 0) { stock.playerAvgShortPx = 0; } - if (__WEBPACK_IMPORTED_MODULE_1__engine_js__["Engine"].currentPage === __WEBPACK_IMPORTED_MODULE_1__engine_js__["Engine"].Page.StockMarket) { - updateStockPlayerPosition(stock); - } + updateStockPlayerPosition(stock); if (tixApi) { workerScript.scriptRef.log("Sold your short position of " + shares + " shares of " + stock.symbol + " at " + __WEBPACK_IMPORTED_MODULE_9__utils_numeral_min_js___default()(stock.price).format('($0.000a)') + " per share. After commissions, you gained " + @@ -37129,6 +37170,8 @@ function setStockMarketContentCreated(b) { } var stockMarketContentCreated = false; +var stockMarketPortfolioMode = false; +var COMM = __WEBPACK_IMPORTED_MODULE_0__Constants_js__["a" /* CONSTANTS */].StockMarketCommission; function displayStockMarketContent() { if (__WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].hasWseAccount == null) {__WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].hasWseAccount = false;} if (__WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].hasTixApiAccess == null) {__WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].hasTixApiAccess = false;} @@ -37166,51 +37209,6 @@ function displayStockMarketContent() { return false; }); - var investopediaButton = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-investopedia"); - investopediaButton.addEventListener("click", function() { - var txt = "When making a transaction on the stock market, there are two " + - "types of positions: Long and Short. A Long position is the typical " + - "scenario where you buy a stock and earn a profit if the price of that " + - "stock increases. Meanwhile, a Short position is the exact opposite. " + - "In a Short position you purchase shares of a stock and earn a profit " + - "if the price of that stock decreases. This is also called 'shorting' a stock.

" + - "NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.

" + - "There are three different types of orders you can make to buy or sell " + - "stocks on the exchange: Market Order, Limit Order, and Stop Order. " + - "Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked " + - "later on in the game.

" + - "When you place a Market Order to buy or sell a stock, the order executes " + - "immediately at whatever the current price of the stock is. For example " + - "if you choose to short a stock with 5000 shares using a Market Order, " + - "you immediately purchase those 5000 shares in a Short position at whatever " + - "the current market price is for that stock.

" + - "A Limit Order is an order that only executes under certain conditions. " + - "A Limit Order is used to buy or sell a stock at a specified price or better. " + - "For example, lets say you purchased a Long position of 100 shares of some stock " + - "at a price of $10 per share. You can place a Limit Order to sell those 100 shares " + - "at $50 or better. The Limit Order will execute when the price of the stock reaches a " + - "value of $50 or higher.

" + - "A Stop Order is the opposite of a Limit Order. It is used to buy or sell a stock " + - "at a specified price (before the price gets 'worse'). For example, lets say you purchased " + - "a Short position of 100 shares of some stock at a price of $100 per share. " + - "The current price of the stock is $80 (a profit of $20 per share). You can place a " + - "Stop Order to sell the Short position if the stock's price reaches $90 or higher. " + - "This can be used to lock in your profits and limit any losses.

" + - "Here is a summary of how each order works and when they execute:

" + - "In a LONG Position:

" + - "A Limit Order to buy will execute if the stock's price <= order's price
" + - "A Limit Order to sell will execute if the stock's price >= order's price
" + - "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

" + - "In a SHORT Position:

" + - "A Limit Order to buy will execute if the stock's price >= order's price
" + - "A Limit Order to sell will execute if the stock's price <= order's price
" + - "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."; - Object(__WEBPACK_IMPORTED_MODULE_6__utils_DialogBox_js__["a" /* dialogBoxCreate */])(txt); - return false; - }); - var stockList = document.getElementById("stock-market-list"); if (stockList == null) {return;} @@ -37232,6 +37230,84 @@ function displayStockMarketContent() { "This means all your positions are lost, so make sure to sell your stocks before installing " + "Augmentations!"; + var investopediaButton = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-investopedia"); + investopediaButton.addEventListener("click", function() { + var txt = "When making a transaction on the stock market, there are two " + + "types of positions: Long and Short. A Long position is the typical " + + "scenario where you buy a stock and earn a profit if the price of that " + + "stock increases. Meanwhile, a Short position is the exact opposite. " + + "In a Short position you purchase shares of a stock and earn a profit " + + "if the price of that stock decreases. This is also called 'shorting' a stock.

" + + "NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.

" + + "There are three different types of orders you can make to buy or sell " + + "stocks on the exchange: Market Order, Limit Order, and Stop Order. " + + "Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked " + + "later on in the game.

" + + "When you place a Market Order to buy or sell a stock, the order executes " + + "immediately at whatever the current price of the stock is. For example " + + "if you choose to short a stock with 5000 shares using a Market Order, " + + "you immediately purchase those 5000 shares in a Short position at whatever " + + "the current market price is for that stock.

" + + "A Limit Order is an order that only executes under certain conditions. " + + "A Limit Order is used to buy or sell a stock at a specified price or better. " + + "For example, lets say you purchased a Long position of 100 shares of some stock " + + "at a price of $10 per share. You can place a Limit Order to sell those 100 shares " + + "at $50 or better. The Limit Order will execute when the price of the stock reaches a " + + "value of $50 or higher.

" + + "A Stop Order is the opposite of a Limit Order. It is used to buy or sell a stock " + + "at a specified price (before the price gets 'worse'). For example, lets say you purchased " + + "a Short position of 100 shares of some stock at a price of $100 per share. " + + "The current price of the stock is $80 (a profit of $20 per share). You can place a " + + "Stop Order to sell the Short position if the stock's price reaches $90 or higher. " + + "This can be used to lock in your profits and limit any losses.

" + + "Here is a summary of how each order works and when they execute:

" + + "In a LONG Position:

" + + "A Limit Order to buy will execute if the stock's price <= order's price
" + + "A Limit Order to sell will execute if the stock's price >= order's price
" + + "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

" + + "In a SHORT Position:

" + + "A Limit Order to buy will execute if the stock's price >= order's price
" + + "A Limit Order to sell will execute if the stock's price <= order's price
" + + "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."; + Object(__WEBPACK_IMPORTED_MODULE_6__utils_DialogBox_js__["a" /* dialogBoxCreate */])(txt); + return false; + }); + + //Switch to Portfolio Mode Button + var modeBtn = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'Portfolio' Mode" + + "Displays only the stocks for which you have shares or orders"; + modeBtn.addEventListener("click", switchToPortfolioMode); + } + + //Expand/Collapse tickers buttons + var expandBtn = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-expand-tickers"), + collapseBtn = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-collapse-tickers"), + stockList = document.getElementById("stock-market-list"); + if (expandBtn) { + expandBtn.addEventListener("click", ()=>{ + var tickerHdrs = stockList.getElementsByClassName("accordion-header"); + for (var i = 0; i < tickerHdrs.length; ++i) { + if (!tickerHdrs[i].classList.contains("active")) { + tickerHdrs[i].click(); + } + } + }); + } + if (collapseBtn) { + collapseBtn.addEventListener("click",()=>{ + var tickerHdrs = stockList.getElementsByClassName("accordion-header"); + for (var i = 0; i < tickerHdrs.length; ++i) { + if (tickerHdrs[i].classList.contains("active")) { + tickerHdrs[i].click(); + } + } + }); + } + for (var name in StockMarket) { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; @@ -37248,13 +37324,74 @@ function displayStockMarketContent() { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; updateStockTicker(stock, null); - updateStockPlayerPosition(stock); updateStockOrderList(stock); } } } } +//Displays only stocks you have position/order in +function switchToPortfolioMode() { + stockMarketPortfolioMode = true; + var stockList = document.getElementById("stock-market-list"); + if (stockList == null) {return;} + var modeBtn = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'All stocks' Mode" + + "Displays all stocks on the WSE"; + modeBtn.addEventListener("click", switchToDisplayAllMode); + } + while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);} + + //Get Order book (create it if it hasn't been created) + 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; + } + + for (var name in StockMarket) { + if (StockMarket.hasOwnProperty(name)) { + var stock = StockMarket[name]; + if (!(stock instanceof Stock)) {continue;} //orders property is an array + var stockOrders = orderBook[stock.symbol]; + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + stockOrders.length === 0) {continue;} + createStockTicker(stock); + } + } + setStockTickerClickHandlers(); +} + +//Displays all stocks +function switchToDisplayAllMode() { + stockMarketPortfolioMode = false; + var stockList = document.getElementById("stock-market-list"); + if (stockList == null) {return;} + var modeBtn = Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["b" /* clearEventListeners */])("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'Portfolio' Mode" + + "Displays only the stocks for which you have shares or orders"; + modeBtn.addEventListener("click", switchToPortfolioMode); + } + while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);} + for (var name in StockMarket) { + if (StockMarket.hasOwnProperty(name)) { + var stock = StockMarket[name]; + if (!(stock instanceof Stock)) {continue;} //orders property is an array + createStockTicker(stock); + } + } + setStockTickerClickHandlers(); +} + function createStockTicker(stock) { if (!(stock instanceof Stock)) { console.log("Invalid stock in createStockSticker()"); @@ -37269,6 +37406,7 @@ function createStockTicker(stock) { //Div for entire panel var stockDiv = document.createElement("div"); stockDiv.classList.add("accordion-panel"); + stockDiv.setAttribute("id", tickerId + "-panel"); /* Create panel DOM */ var qtyInput = document.createElement("input"), @@ -37405,10 +37543,10 @@ function createStockTicker(stock) { var pos = longShortSelect.options[longShortSelect.selectedIndex].text; pos === "Long" ? pos = PositionTypes.Long : pos = PositionTypes.Short; var ordType = orderTypeSelect.options[orderTypeSelect.selectedIndex].text; - var money = __WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].money.toMoney(); + var money = __WEBPACK_IMPORTED_MODULE_5__Player_js__["a" /* Player */].money.toNumber(); switch (ordType) { case "Market Order": - var shares = Math.floor(money / stock.price); + var shares = Math.floor((money - COMM) / stock.price); pos === PositionTypes.Long ? buyStock(stock, shares) : shortStock(stock, shares, null); break; case "Limit Order": @@ -37424,7 +37562,7 @@ function createStockTicker(stock) { } else { type = OrderTypes.StopBuy; } - var shares = Math.floor(money / price); + var shares = Math.floor((money-COMM) / price); placeOrder(stock, shares, price, type, pos); Object(__WEBPACK_IMPORTED_MODULE_11__utils_YesNoBox_js__["f" /* yesNoTxtInpBoxClose */])(); }); @@ -37490,6 +37628,7 @@ function createStockTicker(stock) { document.getElementById("stock-market-list").appendChild(li); updateStockTicker(stock, true); + updateStockPlayerPosition(stock); updateStockOrderList(stock); } @@ -37526,11 +37665,11 @@ function updateStockTicker(stock, increase) { var hdr = document.getElementById(tickerId + "-hdr"); if (hdr == null) { - console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol); + if (!stockMarketPortfolioMode) {console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol);} return; } hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + Object(__WEBPACK_IMPORTED_MODULE_10__utils_StringHelperFunctions_js__["c" /* formatNumber */])(stock.price, 2); - if (increase !== null) { + if (increase != null) { increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red"; } @@ -37547,6 +37686,25 @@ function updateStockPlayerPosition(stock) { return; } var tickerId = "stock-market-ticker-" + stock.symbol; + + if (stockMarketPortfolioMode) { + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + StockMarket["Orders"] && StockMarket["Orders"][stock.symbol] && + StockMarket["Orders"][stock.symbol].length === 0) { + Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["g" /* removeElementById */])(tickerId + "-hdr"); + Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["g" /* removeElementById */])(tickerId + "-panel"); + return; + } else { + //If the ticker hasn't been created, create it (handles updating) + //If it has been created, continue normally + if (document.getElementById(tickerId + "-hdr") == null) { + createStockTicker(stock); + setStockTickerClickHandlers(); + return; + } + } + } + if (!(stock.posTxtEl instanceof Element)) { stock.posTxtEl = document.getElementById(tickerId + "-position-text"); } @@ -37595,7 +37753,7 @@ function updateStockOrderList(stock) { var tickerId = "stock-market-ticker-" + stock.symbol; var orderList = document.getElementById(tickerId + "-order-list"); if (orderList == null) { - console.log("ERROR: Could not find order list for " + stock.symbol); + if (!stockMarketPortfolioMode) {console.log("ERROR: Could not find order list for " + stock.symbol);} return; } @@ -37610,6 +37768,24 @@ function updateStockOrderList(stock) { return; } + if (stockMarketPortfolioMode) { + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + StockMarket["Orders"] && StockMarket["Orders"][stock.symbol] && + StockMarket["Orders"][stock.symbol].length === 0) { + Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["g" /* removeElementById */])(tickerId + "-hdr"); + Object(__WEBPACK_IMPORTED_MODULE_7__utils_HelperFunctions_js__["g" /* removeElementById */])(tickerId + "-panel"); + return; + } else { + //If the ticker hasn't been created, create it (handles updating) + //If it has been created, continue normally + if (document.getElementById(tickerId + "-hdr") == null) { + createStockTicker(stock); + setStockTickerClickHandlers(); + return; + } + } + } + //Remove everything from list while (orderList.firstChild) { orderList.removeChild(orderList.firstChild); @@ -37652,7 +37828,9 @@ function updateStockOrderList(stock) { /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return logBoxUpdateText; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return logBoxOpened; }); /* unused harmony export logBoxCurrentScript */ -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__HelperFunctions_js__ = __webpack_require__(2); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__src_NetscriptWorker_js__ = __webpack_require__(11); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__HelperFunctions_js__ = __webpack_require__(2); + $(document).keydown(function(event) { @@ -37670,6 +37848,7 @@ function logBoxInit() { logBoxClose(); return false; }); + document.getElementById("log-box-text-header").style.display = "inline-block"; }; document.addEventListener("DOMContentLoaded", logBoxInit, false); @@ -37690,23 +37869,30 @@ function logBoxOpen() { var logBoxOpened = false; var logBoxCurrentScript = null; -//ram argument is in GB function logBoxCreate(script) { logBoxCurrentScript = script; + var killScriptBtn = Object(__WEBPACK_IMPORTED_MODULE_1__HelperFunctions_js__["b" /* clearEventListeners */])("log-box-kill-script"); + killScriptBtn.addEventListener("click", ()=>{ + Object(__WEBPACK_IMPORTED_MODULE_0__src_NetscriptWorker_js__["d" /* killWorkerScript */])(script, script.server); + return false; + }); + document.getElementById('log-box-kill-script').style.display = "inline-block"; logBoxOpen(); document.getElementById("log-box-text-header").innerHTML = - logBoxCurrentScript.filename + " " + Object(__WEBPACK_IMPORTED_MODULE_0__HelperFunctions_js__["f" /* printArray */])(logBoxCurrentScript.args) + ":

"; + logBoxCurrentScript.filename + " " + Object(__WEBPACK_IMPORTED_MODULE_1__HelperFunctions_js__["f" /* printArray */])(logBoxCurrentScript.args) + ":

"; + logBoxCurrentScript.logUpd = true; logBoxUpdateText(); } function logBoxUpdateText() { var txt = document.getElementById("log-box-text"); - if (logBoxCurrentScript && logBoxOpened && txt) { + if (logBoxCurrentScript && logBoxOpened && txt && logBoxCurrentScript.logUpd) { txt.innerHTML = ""; for (var i = 0; i < logBoxCurrentScript.logs.length; ++i) { txt.innerHTML += logBoxCurrentScript.logs[i]; txt.innerHTML += "
"; } + logBoxCurrentScript.logUpd = false; } } @@ -40195,6 +40381,10 @@ function loadBitVerse(destroyedBitNodeNum) { return writeRedPillLine("......................................."); }).then(function() { return writeRedPillLine("Welcome to the Bitverse..."); + }).then(function() { + return writeRedPillLine(" "); + }).then(function() { + return writeRedPillLine("(Enter a new BitNode using the image above)"); }).then(function() { return Promise.resolve(true); }).catch(function(e){ @@ -40356,17 +40546,10 @@ function evaluate(exp, workerScript) { }); return Promise.all(argPromises).then(function(array) { return Promise.resolve(array) - }).catch(function(e) { - return Promise.reject(e); }); break; case "CallExpression": return evaluate(exp.callee, workerScript).then(function(func) { - /* - var argPromises = exp.arguments.map(function(arg) { - return evaluate(arg, workerScript); - }); - Promise.all(argPromises).then(function(args) {*/ return Promise.map(exp.arguments, function(arg) { return evaluate(arg, workerScript); }).then(function(args) { @@ -40430,8 +40613,6 @@ function evaluate(exp, workerScript) { } catch (e) { return Promise.reject(makeRuntimeRejectMsg(workerScript, e)); } - }).catch(function(e) { - return Promise.reject(e); }); } else { try { @@ -40453,11 +40634,7 @@ function evaluate(exp, workerScript) { } } } - }).catch(function(e) { - return Promise.reject(e); }); - }).catch(function(e) { - return Promise.reject(e); }); break; case "MemberExpression": @@ -40478,31 +40655,17 @@ function evaluate(exp, workerScript) { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString())); } } - }).catch(function(e) { - return Promise.reject(e); }); break; case "LogicalExpression": case "BinaryExpression": - return evalBinary(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalBinary(exp, workerScript); break; case "UnaryExpression": - return evalUnary(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalUnary(exp, workerScript); break; case "AssignmentExpression": - return evalAssignment(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalAssignment(exp, workerScript); break; case "UpdateExpression": if (exp.argument.type==="Identifier"){ @@ -40541,8 +40704,6 @@ function evaluate(exp, workerScript) { case "ReturnStatement": return evaluate(exp.argument, workerScript).then(function(res) { return Promise.reject(["RETURNSTATEMENT", res]); - }).catch(function(e) { - return Promise.reject(e); }); break; case "BreakStatement": @@ -40552,19 +40713,15 @@ function evaluate(exp, workerScript) { return Promise.reject("CONTINUESTATEMENT"); break; case "IfStatement": - return evaluateIf(exp, workerScript).then(function(forLoopRes) { - return Promise.resolve(forLoopRes); - }).catch(function(e) { - return Promise.reject(e); - }); + return evaluateIf(exp, workerScript); break; case "SwitchStatement": var lineNum = getErrorLineNumber(exp, workerScript); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript (line " + (lineNum+1) + ")")); break; case "WhileStatement": - return evaluateWhile(exp, workerScript).then(function(forLoopRes) { - return Promise.resolve(forLoopRes); + return evaluateWhile(exp, workerScript).then(function(res) { + return Promise.resolve(res); }).catch(function(e) { if (e == "BREAKSTATEMENT" || (e instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */] && e.errorMessage == "BREAKSTATEMENT")) { @@ -40672,11 +40829,7 @@ function evalBinary(exp, workerScript){ default: return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unsupported operator: " + exp.operator)); } - }, function(e) { - return Promise.reject(e); }); - }, function(e) { - return Promise.reject(e); }); } @@ -40695,8 +40848,6 @@ function evalUnary(exp, workerScript){ } else { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unimplemented unary operator: " + exp.operator)); } - }).catch(function(e) { - return Promise.reject(e); }); } @@ -40723,10 +40874,6 @@ function getArrayElement(exp, workerScript) { return Promise.resolve(indices); } } - }).catch(function(e) { - console.log(e); - console.log("Error getting index in getArrayElement: " + e.toString()); - return Promise.reject(e); }); } @@ -40791,8 +40938,6 @@ function evalAssignment(exp, workerScript) { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString())); } } - }, function(e) { - return Promise.reject(e); }); } @@ -40814,8 +40959,6 @@ function evaluateIf(exp, workerScript, i) { } else { return Promise.resolve("endIf"); } - }, function(e) { - return Promise.reject(e); }); } @@ -40823,6 +40966,40 @@ function evaluateIf(exp, workerScript, i) { function evaluateFor(exp, workerScript) { var env = workerScript.env; if (env.stopFlag) {return Promise.reject(workerScript);} + return new Promise(function(resolve, reject) { + function recurse() { + //Don't return a promise so the promise chain is broken on each recursion (saving memory) + evaluate(exp.test, workerScript).then(function(resCond) { + if (resCond) { + return evaluate(exp.body, workerScript).then(function(res) { + return evaluate(exp.update, workerScript); + }).catch(function(e) { + if (e == "CONTINUESTATEMENT" || + (e instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */] && e.errorMessage == "CONTINUESTATEMENT")) { + //Continue statement, recurse to next iteration + return evaluate(exp.update, workerScript).then(function(resPostloop) { + return evaluateFor(exp, workerScript); + }).then(function(foo) { + return Promise.resolve("endForLoop"); + }).catch(function(e) { + return Promise.reject(e); + }); + } else { + return Promise.reject(e); + } + }).then(recurse, reject).catch(function(e) { + return Promise.reject(e); + }); + } else { + resolve(); + } + }).catch(function(e) { + reject(e); + }); + } + recurse(); + }); + /* return evaluate(exp.test, workerScript).then(function(resCond) { if (resCond) { //Execute code (body), update, and then recurse @@ -40830,7 +41007,7 @@ function evaluateFor(exp, workerScript) { return evaluate(exp.update, workerScript); }).catch(function(e) { if (e == "CONTINUESTATEMENT" || - (e instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */] && e.errorMessage == "CONTINUESTATEMENT")) { + (e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) { //Continue statement, recurse to next iteration return evaluate(exp.update, workerScript).then(function(resPostloop) { return evaluateFor(exp, workerScript); @@ -40846,52 +41023,44 @@ function evaluateFor(exp, workerScript) { return evaluateFor(exp, workerScript); }).then(function(foo) { return Promise.resolve("endForLoop"); - }).catch(function(e) { - return Promise.reject(e); }); } else { return Promise.resolve("endForLoop"); //Doesn't need to resolve to any particular value } - }, function(e) { - return Promise.reject(e); - }); + });*/ } function evaluateWhile(exp, workerScript) { var env = workerScript.env; if (env.stopFlag) {return Promise.reject(workerScript);} - return Promise.delay(__WEBPACK_IMPORTED_MODULE_1__Constants_js__["a" /* CONSTANTS */].CodeInstructionRunTime).then(function() { - return evaluate(exp.test, workerScript); - }).then(function(resCond) { - if (resCond) { - return evaluate(exp.body, workerScript).then(function(resCode) { - return Promise.resolve(resCode); - }).catch(function(e) { - if (e == "CONTINUESTATEMENT" || - (e instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */] && e.errorMessage == "CONTINUESTATEMENT")) { - //Continue statement, recurse - return evaluateWhile(exp, workerScript).then(function(foo) { - return Promise.resolve("endWhileLoop"); - }, function(e) { + return new Promise(function (resolve, reject) { + function recurse() { + //Don't return a promise so the promise chain is broken on each recursion (saving memory) + evaluate(exp.test, workerScript).then(function(resCond) { + if (resCond) { + return evaluate(exp.body, workerScript).catch(function(e) { + if (e == "CONTINUESTATEMENT" || + (e instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */] && e.errorMessage == "CONTINUESTATEMENT")) { + //Continue statement, recurse + return evaluateWhile(exp, workerScript).then(function(foo) { + return Promise.resolve("endWhileLoop"); + }, function(e) { + return Promise.reject(e); + }); + } else { + return Promise.reject(e); + } + }).then(recurse, reject).catch(function(e) { return Promise.reject(e); }); } else { - return Promise.reject(e); + resolve(); } - }).then(function(resCode) { - return evaluateWhile(exp, workerScript).then(function(foo) { - return Promise.resolve("endWhileLoop"); - }, function(e) { - return Promise.reject(e); - }); }).catch(function(e) { - return Promise.reject(e); + reject(e); }); - } else { - return Promise.resolve("endWhileLoop"); //Doesn't need to resolve to any particular value } - }).catch(function(e) { - return Promise.reject(e); + recurse(); }); } @@ -40914,17 +41083,42 @@ function evaluateProg(exp, workerScript, index) { } function killNetscriptDelay(workerScript) { - if (workerScript instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */]) { + /* + if (workerScript instanceof WorkerScript) { if (workerScript.delay) { workerScript.delay.cancel(); } } + */ + if (workerScript instanceof __WEBPACK_IMPORTED_MODULE_4__NetscriptWorker_js__["b" /* WorkerScript */]) { + if (workerScript.delay) { + clearTimeout(workerScript.delay); + workerScript.delayResolve(); + } + } } function netscriptDelay(time, workerScript) { - var delay = Promise.delay(time, true); - workerScript.delay = delay; - return delay; + /* + workerScript.delay = new Promise(function(resolve, reject, onCancel) { + Promise.delay(time).then(function() { + resolve(); + workerScript.delay = null; + }); + onCancel(function() { + console.log("Cancelling and rejecting this Promise"); + reject(workerScript); + }) + }); + return workerScript.delay; + */ + return new Promise(function(resolve, reject) { + workerScript.delay = setTimeout(()=>{ + workerScript.delay = null; + resolve(); + }, time); + workerScript.delayResolve = resolve; + }); } function makeRuntimeRejectMsg(workerScript, msg) { @@ -98882,12 +99076,12 @@ BitburnerSaveObject.prototype.saveGame = function(db) { } request.onsuccess = function(e) { - console.log("Saved game to IndexedDB!"); + //console.log("Saved game to IndexedDB!"); } try { window.localStorage.setItem("bitburnerSave", saveString); - console.log("Saved game to LocalStorage!"); + //console.log("Saved game to LocalStorage!"); } catch(e) { if (e.code == 22) { __WEBPACK_IMPORTED_MODULE_3__engine_js__["Engine"].createStatusText("Save failed for localStorage! Check console(F12)"); diff --git a/index.html b/index.html index ca8496da0..52dc0d425 100644 --- a/index.html +++ b/index.html @@ -781,10 +781,12 @@ If you purchase access to the TIX API, you will retain that access even after you 'reset' by installing Augmentations.

- Buy Trade Information eXchange (TIX) API Access - COMING SOON + Buy Trade Information eXchange (TIX) API Access Investopedia

- + + Expand tickers + Collapse tickers @@ -792,7 +794,8 @@
- × + Close + Kill Script

diff --git a/src/Constants.js b/src/Constants.js index 8a4168223..d0e93dfa1 100644 --- a/src/Constants.js +++ b/src/Constants.js @@ -647,10 +647,21 @@ let CONSTANTS = { "have purchased. It takes an optional parameter specifying whether the hostname or IP addresses will be returned. If this " + "parameter is not specified, it is true by default and hostnames will be returned

" + "round(n)
Rounds the number n to the nearest integer. If the argument passed in is not a number, then the function will return 0.

" + - "write(port, data)
Writes data to a port. The first argument must be a number between 1 and 10 that specifies the port. The second " + - "argument defines the data to write to the port. If the second argument is not specified then it will write an empty string to the port.

" + - "read(port)
Reads data from a port. The first argument must be a number between 1 and 10 that specifies the port. A port is a serialized queue. " + - "This function will remove the first element from the queue and return it. If the queue is empty, then the string 'NULL PORT DATA' will be returned.

" + + "write(port, data='', mode='a')
This function can be used to either write data to a port or to a text file (.txt).

" + + "If the first argument is a number between 1 and 10, then it specifies a port and this function will write data to a port. If the second " + + "argument is not specified then it will write an empty string to the port. The third argument, mode, is not used when writing data to a port.

" + + "If the first argument is a string, then it specifies the name of a text file (.txt) and this function will write data to a text file. " + + "The second argument defines the data to be written to the text file. If it is not specified then it is an empty string by default. " + + "This third argument, mode, defines how the data will be written to the text file. If mode is set to 'w', then the data is written in 'write' " + + "mode which means that it will overwrite the existing data on the file, or it will create a new file if it does not already exist. Otherwise, " + + "the data will be written in 'append' mode which means that the data will be added at the end of the existing file, or it will create a new file if it " + + "does not already exist. If mode isn't specified then it will be 'a' for 'append' mode by default.

" + + "read(port)
This function is used to read data from a port or from a text file (.txt).

" + + "This function takes a single argument. If this argument is a number between 1 and 10, then it specifies a port and it will read data from " + + "a port. A port is a serialized queue. This function will remove the first element from the queue and return it. If the queue is empty, " + + "then the string 'NULL PORT DATA' will be returned.

" + + "If the first argument is a string, then it specifies the name of a text file and this function will return the data in the " + + "specified text file. If the text file does not exist, an empty string will be returned

" + "scriptRunning(scriptname, hostname/ip)
Returns a boolean indicating whether any instance of the specified script is running " + "on a server, regardless of its arguments. This is different than the isRunning() function because it does not " + "try to identify a specific instance of a running script by its arguments.

" + @@ -1096,6 +1107,14 @@ let CONSTANTS = { "World Stock Exchange account and TIX API Access
", LatestUpdate: + "v0.32.1
" + + "-Updated Netscript's 'interpreter/engine' to use the Bluebird promise library instead of native promises. " + + "It should now be faster and more memory-efficient. If this has broken any Netscript features please report it through Github or the subreddit (reddit.com/r/bitburner)
" + + "-Rebalanced stock market (adjusted parameters such as the volatility/trends/starting price of certain stocks)
" + + "-Added confirm() Netscript function
" + + "-Added 'Buy Max' and 'Sell All' functions to Stock Market UI
" + + "-Added 'Portfolio' Mode to Stock Market UI so you can only view stocks you have a position/order in
" + + "-Added a button to kill a script from its log display box
" + "v0.32.0
" + "-Released BitNode-8: Ghost of Wall Street
" + "-Re-designed Stock Market UI
" + diff --git a/src/NetscriptEvaluator.js b/src/NetscriptEvaluator.js index d0cdf372e..aa336473f 100644 --- a/src/NetscriptEvaluator.js +++ b/src/NetscriptEvaluator.js @@ -72,17 +72,10 @@ function evaluate(exp, workerScript) { }); return Promise.all(argPromises).then(function(array) { return Promise.resolve(array) - }).catch(function(e) { - return Promise.reject(e); }); break; case "CallExpression": return evaluate(exp.callee, workerScript).then(function(func) { - /* - var argPromises = exp.arguments.map(function(arg) { - return evaluate(arg, workerScript); - }); - Promise.all(argPromises).then(function(args) {*/ return Promise.map(exp.arguments, function(arg) { return evaluate(arg, workerScript); }).then(function(args) { @@ -146,8 +139,6 @@ function evaluate(exp, workerScript) { } catch (e) { return Promise.reject(makeRuntimeRejectMsg(workerScript, e)); } - }).catch(function(e) { - return Promise.reject(e); }); } else { try { @@ -169,11 +160,7 @@ function evaluate(exp, workerScript) { } } } - }).catch(function(e) { - return Promise.reject(e); }); - }).catch(function(e) { - return Promise.reject(e); }); break; case "MemberExpression": @@ -194,31 +181,17 @@ function evaluate(exp, workerScript) { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString())); } } - }).catch(function(e) { - return Promise.reject(e); }); break; case "LogicalExpression": case "BinaryExpression": - return evalBinary(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalBinary(exp, workerScript); break; case "UnaryExpression": - return evalUnary(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalUnary(exp, workerScript); break; case "AssignmentExpression": - return evalAssignment(exp, workerScript).then(function(res) { - return Promise.resolve(res); - }).catch(function(e) { - return Promise.reject(e); - }); + return evalAssignment(exp, workerScript); break; case "UpdateExpression": if (exp.argument.type==="Identifier"){ @@ -257,8 +230,6 @@ function evaluate(exp, workerScript) { case "ReturnStatement": return evaluate(exp.argument, workerScript).then(function(res) { return Promise.reject(["RETURNSTATEMENT", res]); - }).catch(function(e) { - return Promise.reject(e); }); break; case "BreakStatement": @@ -268,19 +239,15 @@ function evaluate(exp, workerScript) { return Promise.reject("CONTINUESTATEMENT"); break; case "IfStatement": - return evaluateIf(exp, workerScript).then(function(forLoopRes) { - return Promise.resolve(forLoopRes); - }).catch(function(e) { - return Promise.reject(e); - }); + return evaluateIf(exp, workerScript); break; case "SwitchStatement": var lineNum = getErrorLineNumber(exp, workerScript); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript (line " + (lineNum+1) + ")")); break; case "WhileStatement": - return evaluateWhile(exp, workerScript).then(function(forLoopRes) { - return Promise.resolve(forLoopRes); + return evaluateWhile(exp, workerScript).then(function(res) { + return Promise.resolve(res); }).catch(function(e) { if (e == "BREAKSTATEMENT" || (e instanceof WorkerScript && e.errorMessage == "BREAKSTATEMENT")) { @@ -388,11 +355,7 @@ function evalBinary(exp, workerScript){ default: return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unsupported operator: " + exp.operator)); } - }, function(e) { - return Promise.reject(e); }); - }, function(e) { - return Promise.reject(e); }); } @@ -411,8 +374,6 @@ function evalUnary(exp, workerScript){ } else { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unimplemented unary operator: " + exp.operator)); } - }).catch(function(e) { - return Promise.reject(e); }); } @@ -439,10 +400,6 @@ function getArrayElement(exp, workerScript) { return Promise.resolve(indices); } } - }).catch(function(e) { - console.log(e); - console.log("Error getting index in getArrayElement: " + e.toString()); - return Promise.reject(e); }); } @@ -507,8 +464,6 @@ function evalAssignment(exp, workerScript) { return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString())); } } - }, function(e) { - return Promise.reject(e); }); } @@ -530,8 +485,6 @@ function evaluateIf(exp, workerScript, i) { } else { return Promise.resolve("endIf"); } - }, function(e) { - return Promise.reject(e); }); } @@ -539,6 +492,40 @@ function evaluateIf(exp, workerScript, i) { function evaluateFor(exp, workerScript) { var env = workerScript.env; if (env.stopFlag) {return Promise.reject(workerScript);} + return new Promise(function(resolve, reject) { + function recurse() { + //Don't return a promise so the promise chain is broken on each recursion (saving memory) + evaluate(exp.test, workerScript).then(function(resCond) { + if (resCond) { + return evaluate(exp.body, workerScript).then(function(res) { + return evaluate(exp.update, workerScript); + }).catch(function(e) { + if (e == "CONTINUESTATEMENT" || + (e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) { + //Continue statement, recurse to next iteration + return evaluate(exp.update, workerScript).then(function(resPostloop) { + return evaluateFor(exp, workerScript); + }).then(function(foo) { + return Promise.resolve("endForLoop"); + }).catch(function(e) { + return Promise.reject(e); + }); + } else { + return Promise.reject(e); + } + }).then(recurse, reject).catch(function(e) { + return Promise.reject(e); + }); + } else { + resolve(); + } + }).catch(function(e) { + reject(e); + }); + } + recurse(); + }); + /* return evaluate(exp.test, workerScript).then(function(resCond) { if (resCond) { //Execute code (body), update, and then recurse @@ -562,52 +549,44 @@ function evaluateFor(exp, workerScript) { return evaluateFor(exp, workerScript); }).then(function(foo) { return Promise.resolve("endForLoop"); - }).catch(function(e) { - return Promise.reject(e); }); } else { return Promise.resolve("endForLoop"); //Doesn't need to resolve to any particular value } - }, function(e) { - return Promise.reject(e); - }); + });*/ } function evaluateWhile(exp, workerScript) { var env = workerScript.env; if (env.stopFlag) {return Promise.reject(workerScript);} - return Promise.delay(CONSTANTS.CodeInstructionRunTime).then(function() { - return evaluate(exp.test, workerScript); - }).then(function(resCond) { - if (resCond) { - return evaluate(exp.body, workerScript).then(function(resCode) { - return Promise.resolve(resCode); - }).catch(function(e) { - if (e == "CONTINUESTATEMENT" || - (e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) { - //Continue statement, recurse - return evaluateWhile(exp, workerScript).then(function(foo) { - return Promise.resolve("endWhileLoop"); - }, function(e) { + return new Promise(function (resolve, reject) { + function recurse() { + //Don't return a promise so the promise chain is broken on each recursion (saving memory) + evaluate(exp.test, workerScript).then(function(resCond) { + if (resCond) { + return evaluate(exp.body, workerScript).catch(function(e) { + if (e == "CONTINUESTATEMENT" || + (e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) { + //Continue statement, recurse + return evaluateWhile(exp, workerScript).then(function(foo) { + return Promise.resolve("endWhileLoop"); + }, function(e) { + return Promise.reject(e); + }); + } else { + return Promise.reject(e); + } + }).then(recurse, reject).catch(function(e) { return Promise.reject(e); }); } else { - return Promise.reject(e); + resolve(); } - }).then(function(resCode) { - return evaluateWhile(exp, workerScript).then(function(foo) { - return Promise.resolve("endWhileLoop"); - }, function(e) { - return Promise.reject(e); - }); }).catch(function(e) { - return Promise.reject(e); + reject(e); }); - } else { - return Promise.resolve("endWhileLoop"); //Doesn't need to resolve to any particular value } - }).catch(function(e) { - return Promise.reject(e); + recurse(); }); } @@ -630,17 +609,42 @@ function evaluateProg(exp, workerScript, index) { } function killNetscriptDelay(workerScript) { + /* if (workerScript instanceof WorkerScript) { if (workerScript.delay) { workerScript.delay.cancel(); } } + */ + if (workerScript instanceof WorkerScript) { + if (workerScript.delay) { + clearTimeout(workerScript.delay); + workerScript.delayResolve(); + } + } } function netscriptDelay(time, workerScript) { - var delay = Promise.delay(time, true); - workerScript.delay = delay; - return delay; + /* + workerScript.delay = new Promise(function(resolve, reject, onCancel) { + Promise.delay(time).then(function() { + resolve(); + workerScript.delay = null; + }); + onCancel(function() { + console.log("Cancelling and rejecting this Promise"); + reject(workerScript); + }) + }); + return workerScript.delay; + */ + return new Promise(function(resolve, reject) { + workerScript.delay = setTimeout(()=>{ + workerScript.delay = null; + resolve(); + }, time); + workerScript.delayResolve = resolve; + }); } function makeRuntimeRejectMsg(workerScript, msg) { diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index e52dd2a56..0e20a78e2 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -1351,7 +1351,25 @@ function NetscriptFunctions(workerScript) { return Player.playtimeSinceLastAug; }, confirm : function(txt) { - + if (yesNoBoxOpen) { + workerScript.scriptRef.log("ERROR: confirm() failed because a pop-up dialog box is already open"); + return false; + } + if (!isString(txt)) {txt = String(txt);} + var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton(); + yesBtn.innerHTML = "Yes"; + noBtn.innerHTML = "No"; + return new Promise(function(resolve, reject) { + yesBtn.addEventListener("click", ()=>{ + yesNoBoxClose(); + resolve(true); + }); + noBtn.addEventListener("click", ()=>{ + yesNoBoxClose(); + resolve(false); + }); + yesNoBoxCreate(txt); + }); }, /* Singularity Functions */ diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js index d62a6d529..e98410432 100644 --- a/src/NetscriptWorker.js +++ b/src/NetscriptWorker.js @@ -95,7 +95,7 @@ function runScriptsLoop() { if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) { try { var ast = parse(workerScripts[i].code); - console.log(ast); + //console.log(ast); } catch (e) { console.log("Error parsing script: " + workerScripts[i].name); dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":
" + e); @@ -172,11 +172,6 @@ function killWorkerScript(runningScriptObj, serverIp) { workerScripts[i].fnWorker.env.stopFlag = true; killNetscriptDelay(workerScripts[i].fnWorker); } - /*workerScripts[i].killTrigger(); - if (workerScripts[i].fnWorker) { - workerScripts[i].fnWorker.env.stopFlag = true; - workerScripts[i].fnWorker.killTrigger(); - }*/ return true; } } diff --git a/src/Player.js b/src/Player.js index 4e9d5de06..dce83d8b1 100644 --- a/src/Player.js +++ b/src/Player.js @@ -190,8 +190,10 @@ function PlayerObject() { this.totalPlaytime = 0; this.playtimeSinceLastAug = 0; - //Script production since last Aug installation + //Production since last Augmentation installation this.scriptProdSinceLastAug = 0; + this.stockProdSinceLastAug = 0; + this.crimeProdSinceLastAug = 0; }; PlayerObject.prototype.init = function() { diff --git a/src/RedPill.js b/src/RedPill.js index d084d06c3..4d3247370 100644 --- a/src/RedPill.js +++ b/src/RedPill.js @@ -265,6 +265,10 @@ function loadBitVerse(destroyedBitNodeNum) { return writeRedPillLine("......................................."); }).then(function() { return writeRedPillLine("Welcome to the Bitverse..."); + }).then(function() { + return writeRedPillLine(" "); + }).then(function() { + return writeRedPillLine("(Enter a new BitNode using the image above)"); }).then(function() { return Promise.resolve(true); }).catch(function(e){ diff --git a/src/SaveObject.js b/src/SaveObject.js index abfd5145c..b93abb7bc 100644 --- a/src/SaveObject.js +++ b/src/SaveObject.js @@ -82,12 +82,12 @@ BitburnerSaveObject.prototype.saveGame = function(db) { } request.onsuccess = function(e) { - console.log("Saved game to IndexedDB!"); + //console.log("Saved game to IndexedDB!"); } try { window.localStorage.setItem("bitburnerSave", saveString); - console.log("Saved game to LocalStorage!"); + //console.log("Saved game to LocalStorage!"); } catch(e) { if (e.code == 22) { Engine.createStatusText("Save failed for localStorage! Check console(F12)"); diff --git a/src/Script.js b/src/Script.js index 31b9ff757..06e26f499 100644 --- a/src/Script.js +++ b/src/Script.js @@ -528,6 +528,7 @@ function RunningScript(script, args) { this.server = script.server; //IP Address only this.logs = []; //Script logging. Array of strings, with each element being a log entry + this.logUpd = false; //Stats to display on the Scripts menu, and used to determine offline progress this.offlineRunningTime = 0.01; //Seconds @@ -551,6 +552,7 @@ RunningScript.prototype.log = function(txt) { this.logs.shift(); } this.logs.push(txt); + this.logUpd = true; } RunningScript.prototype.displayLog = function() { diff --git a/src/StockMarket.js b/src/StockMarket.js index dc58fd5a1..a974cdb1f 100644 --- a/src/StockMarket.js +++ b/src/StockMarket.js @@ -6,7 +6,9 @@ import {WorkerScript} from "./NetscriptWorker.js"; import {Player} from "./Player.js"; import {dialogBoxCreate} from "../utils/DialogBox.js"; -import {clearEventListeners, getRandomInt} from "../utils/HelperFunctions.js"; +import {clearEventListeners, getRandomInt, + removeElementById, + clearEventListenersEl} from "../utils/HelperFunctions.js"; import {Reviver, Generic_toJSON, Generic_fromJSON} from "../utils/JSONReviver.js"; import numeral from "../utils/numeral.min.js"; @@ -267,7 +269,7 @@ function initStockMarket() { StockMarket[omnitek] = omnitekStk; var foursigma = Locations.Sector12FourSigma; - var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], 1.1, true, 18, getRandomInt(60000, 70000)); + var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], 1.05, true, 18, getRandomInt(60000, 70000)); StockMarket[foursigma] = foursigmaStk; var kuaigong = Locations.ChongqingKuaiGongInternational; @@ -275,7 +277,7 @@ function initStockMarket() { StockMarket[kuaigong] = kuaigongStk; var fulcrum = Locations.AevumFulcrumTechnologies; - var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], 1.25, true, 17, getRandomInt(30000, 35000)); + var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], 1.25, true, 16, getRandomInt(30000, 35000)); StockMarket[fulcrum] = fulcrumStk; var storm = Locations.IshimaStormTechnologies; @@ -335,7 +337,7 @@ function initStockMarket() { StockMarket[rho] = rhoStk; var alpha = Locations.Sector12AlphaEnterprises; - var alphaStk = new Stock(alpha, StockSymbols[alpha], 2, true, 10, getRandomInt(5000, 7500)); + var alphaStk = new Stock(alpha, StockSymbols[alpha], 1.9, true, 10, getRandomInt(5000, 7500)); StockMarket[alpha] = alphaStk; var syscore = Locations.VolhavenSysCoreSecurities; @@ -359,15 +361,15 @@ function initStockMarket() { StockMarket[fns] = fnsStk; var sigmacosm = "Sigma Cosmetics"; - var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 3, true, 0, getRandomInt(2000, 3000)); + var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 2.8, true, 0, getRandomInt(2000, 3000)); StockMarket[sigmacosm] = sigmacosmStk; var joesguns = "Joes Guns"; - var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 4, true, 1, getRandomInt(500, 1000)); + var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 3.7, true, 1, getRandomInt(500, 1000)); StockMarket[joesguns] = joesgunsStk; var catalyst = "Catalyst Ventures"; - var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.6, true, 15, getRandomInt(500, 1000)); + var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.5, true, 13, getRandomInt(500, 1000)); StockMarket[catalyst] = catalystStk; var microdyne = "Microdyne Technologies"; @@ -408,8 +410,8 @@ function stockMarketCycle() { for (var name in StockMarket) { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; - var thresh = 0.59; - if (stock.b) {thresh = 0.41;} + var thresh = 0.6; + if (stock.b) {thresh = 0.4;} if (Math.random() < thresh) { stock.b = !stock.b; } @@ -502,9 +504,7 @@ function shortStock(stock, shares, workerScript=null) { var newTotal = origTotal + totalPrice; stock.playerShortShares += shares; stock.playerAvgShortPx = newTotal / stock.playerShortShares; - if (Engine.currentPage === Engine.Page.StockMarket) { - updateStockPlayerPosition(stock); - } + updateStockPlayerPosition(stock); if (tixApi) { workerScript.scriptRef.log("Bought a short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at " + numeral(stock.price).format('($0.000a)') + " per share. Paid " + @@ -546,9 +546,7 @@ function sellShort(stock, shares, workerScript=null) { if (stock.playerShortShares === 0) { stock.playerAvgShortPx = 0; } - if (Engine.currentPage === Engine.Page.StockMarket) { - updateStockPlayerPosition(stock); - } + updateStockPlayerPosition(stock); if (tixApi) { workerScript.scriptRef.log("Sold your short position of " + shares + " shares of " + stock.symbol + " at " + numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " + @@ -685,6 +683,8 @@ function setStockMarketContentCreated(b) { } var stockMarketContentCreated = false; +var stockMarketPortfolioMode = false; +var COMM = CONSTANTS.StockMarketCommission; function displayStockMarketContent() { if (Player.hasWseAccount == null) {Player.hasWseAccount = false;} if (Player.hasTixApiAccess == null) {Player.hasTixApiAccess = false;} @@ -722,51 +722,6 @@ function displayStockMarketContent() { return false; }); - var investopediaButton = clearEventListeners("stock-market-investopedia"); - investopediaButton.addEventListener("click", function() { - var txt = "When making a transaction on the stock market, there are two " + - "types of positions: Long and Short. A Long position is the typical " + - "scenario where you buy a stock and earn a profit if the price of that " + - "stock increases. Meanwhile, a Short position is the exact opposite. " + - "In a Short position you purchase shares of a stock and earn a profit " + - "if the price of that stock decreases. This is also called 'shorting' a stock.

" + - "NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.

" + - "There are three different types of orders you can make to buy or sell " + - "stocks on the exchange: Market Order, Limit Order, and Stop Order. " + - "Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked " + - "later on in the game.

" + - "When you place a Market Order to buy or sell a stock, the order executes " + - "immediately at whatever the current price of the stock is. For example " + - "if you choose to short a stock with 5000 shares using a Market Order, " + - "you immediately purchase those 5000 shares in a Short position at whatever " + - "the current market price is for that stock.

" + - "A Limit Order is an order that only executes under certain conditions. " + - "A Limit Order is used to buy or sell a stock at a specified price or better. " + - "For example, lets say you purchased a Long position of 100 shares of some stock " + - "at a price of $10 per share. You can place a Limit Order to sell those 100 shares " + - "at $50 or better. The Limit Order will execute when the price of the stock reaches a " + - "value of $50 or higher.

" + - "A Stop Order is the opposite of a Limit Order. It is used to buy or sell a stock " + - "at a specified price (before the price gets 'worse'). For example, lets say you purchased " + - "a Short position of 100 shares of some stock at a price of $100 per share. " + - "The current price of the stock is $80 (a profit of $20 per share). You can place a " + - "Stop Order to sell the Short position if the stock's price reaches $90 or higher. " + - "This can be used to lock in your profits and limit any losses.

" + - "Here is a summary of how each order works and when they execute:

" + - "In a LONG Position:

" + - "A Limit Order to buy will execute if the stock's price <= order's price
" + - "A Limit Order to sell will execute if the stock's price >= order's price
" + - "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

" + - "In a SHORT Position:

" + - "A Limit Order to buy will execute if the stock's price >= order's price
" + - "A Limit Order to sell will execute if the stock's price <= order's price
" + - "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."; - dialogBoxCreate(txt); - return false; - }); - var stockList = document.getElementById("stock-market-list"); if (stockList == null) {return;} @@ -788,6 +743,84 @@ function displayStockMarketContent() { "This means all your positions are lost, so make sure to sell your stocks before installing " + "Augmentations!"; + var investopediaButton = clearEventListeners("stock-market-investopedia"); + investopediaButton.addEventListener("click", function() { + var txt = "When making a transaction on the stock market, there are two " + + "types of positions: Long and Short. A Long position is the typical " + + "scenario where you buy a stock and earn a profit if the price of that " + + "stock increases. Meanwhile, a Short position is the exact opposite. " + + "In a Short position you purchase shares of a stock and earn a profit " + + "if the price of that stock decreases. This is also called 'shorting' a stock.

" + + "NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.

" + + "There are three different types of orders you can make to buy or sell " + + "stocks on the exchange: Market Order, Limit Order, and Stop Order. " + + "Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked " + + "later on in the game.

" + + "When you place a Market Order to buy or sell a stock, the order executes " + + "immediately at whatever the current price of the stock is. For example " + + "if you choose to short a stock with 5000 shares using a Market Order, " + + "you immediately purchase those 5000 shares in a Short position at whatever " + + "the current market price is for that stock.

" + + "A Limit Order is an order that only executes under certain conditions. " + + "A Limit Order is used to buy or sell a stock at a specified price or better. " + + "For example, lets say you purchased a Long position of 100 shares of some stock " + + "at a price of $10 per share. You can place a Limit Order to sell those 100 shares " + + "at $50 or better. The Limit Order will execute when the price of the stock reaches a " + + "value of $50 or higher.

" + + "A Stop Order is the opposite of a Limit Order. It is used to buy or sell a stock " + + "at a specified price (before the price gets 'worse'). For example, lets say you purchased " + + "a Short position of 100 shares of some stock at a price of $100 per share. " + + "The current price of the stock is $80 (a profit of $20 per share). You can place a " + + "Stop Order to sell the Short position if the stock's price reaches $90 or higher. " + + "This can be used to lock in your profits and limit any losses.

" + + "Here is a summary of how each order works and when they execute:

" + + "In a LONG Position:

" + + "A Limit Order to buy will execute if the stock's price <= order's price
" + + "A Limit Order to sell will execute if the stock's price >= order's price
" + + "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

" + + "In a SHORT Position:

" + + "A Limit Order to buy will execute if the stock's price >= order's price
" + + "A Limit Order to sell will execute if the stock's price <= order's price
" + + "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."; + dialogBoxCreate(txt); + return false; + }); + + //Switch to Portfolio Mode Button + var modeBtn = clearEventListeners("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'Portfolio' Mode" + + "Displays only the stocks for which you have shares or orders"; + modeBtn.addEventListener("click", switchToPortfolioMode); + } + + //Expand/Collapse tickers buttons + var expandBtn = clearEventListeners("stock-market-expand-tickers"), + collapseBtn = clearEventListeners("stock-market-collapse-tickers"), + stockList = document.getElementById("stock-market-list"); + if (expandBtn) { + expandBtn.addEventListener("click", ()=>{ + var tickerHdrs = stockList.getElementsByClassName("accordion-header"); + for (var i = 0; i < tickerHdrs.length; ++i) { + if (!tickerHdrs[i].classList.contains("active")) { + tickerHdrs[i].click(); + } + } + }); + } + if (collapseBtn) { + collapseBtn.addEventListener("click",()=>{ + var tickerHdrs = stockList.getElementsByClassName("accordion-header"); + for (var i = 0; i < tickerHdrs.length; ++i) { + if (tickerHdrs[i].classList.contains("active")) { + tickerHdrs[i].click(); + } + } + }); + } + for (var name in StockMarket) { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; @@ -804,13 +837,74 @@ function displayStockMarketContent() { if (StockMarket.hasOwnProperty(name)) { var stock = StockMarket[name]; updateStockTicker(stock, null); - updateStockPlayerPosition(stock); updateStockOrderList(stock); } } } } +//Displays only stocks you have position/order in +function switchToPortfolioMode() { + stockMarketPortfolioMode = true; + var stockList = document.getElementById("stock-market-list"); + if (stockList == null) {return;} + var modeBtn = clearEventListeners("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'All stocks' Mode" + + "Displays all stocks on the WSE"; + modeBtn.addEventListener("click", switchToDisplayAllMode); + } + while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);} + + //Get Order book (create it if it hasn't been created) + 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; + } + + for (var name in StockMarket) { + if (StockMarket.hasOwnProperty(name)) { + var stock = StockMarket[name]; + if (!(stock instanceof Stock)) {continue;} //orders property is an array + var stockOrders = orderBook[stock.symbol]; + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + stockOrders.length === 0) {continue;} + createStockTicker(stock); + } + } + setStockTickerClickHandlers(); +} + +//Displays all stocks +function switchToDisplayAllMode() { + stockMarketPortfolioMode = false; + var stockList = document.getElementById("stock-market-list"); + if (stockList == null) {return;} + var modeBtn = clearEventListeners("stock-market-mode"); + if (modeBtn) { + modeBtn.innerHTML = "Switch to 'Portfolio' Mode" + + "Displays only the stocks for which you have shares or orders"; + modeBtn.addEventListener("click", switchToPortfolioMode); + } + while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);} + for (var name in StockMarket) { + if (StockMarket.hasOwnProperty(name)) { + var stock = StockMarket[name]; + if (!(stock instanceof Stock)) {continue;} //orders property is an array + createStockTicker(stock); + } + } + setStockTickerClickHandlers(); +} + function createStockTicker(stock) { if (!(stock instanceof Stock)) { console.log("Invalid stock in createStockSticker()"); @@ -825,6 +919,7 @@ function createStockTicker(stock) { //Div for entire panel var stockDiv = document.createElement("div"); stockDiv.classList.add("accordion-panel"); + stockDiv.setAttribute("id", tickerId + "-panel"); /* Create panel DOM */ var qtyInput = document.createElement("input"), @@ -961,10 +1056,10 @@ function createStockTicker(stock) { var pos = longShortSelect.options[longShortSelect.selectedIndex].text; pos === "Long" ? pos = PositionTypes.Long : pos = PositionTypes.Short; var ordType = orderTypeSelect.options[orderTypeSelect.selectedIndex].text; - var money = Player.money.toMoney(); + var money = Player.money.toNumber(); switch (ordType) { case "Market Order": - var shares = Math.floor(money / stock.price); + var shares = Math.floor((money - COMM) / stock.price); pos === PositionTypes.Long ? buyStock(stock, shares) : shortStock(stock, shares, null); break; case "Limit Order": @@ -980,7 +1075,7 @@ function createStockTicker(stock) { } else { type = OrderTypes.StopBuy; } - var shares = Math.floor(money / price); + var shares = Math.floor((money-COMM) / price); placeOrder(stock, shares, price, type, pos); yesNoTxtInpBoxClose(); }); @@ -1046,6 +1141,7 @@ function createStockTicker(stock) { document.getElementById("stock-market-list").appendChild(li); updateStockTicker(stock, true); + updateStockPlayerPosition(stock); updateStockOrderList(stock); } @@ -1082,11 +1178,11 @@ function updateStockTicker(stock, increase) { var hdr = document.getElementById(tickerId + "-hdr"); if (hdr == null) { - console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol); + if (!stockMarketPortfolioMode) {console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol);} return; } hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + formatNumber(stock.price, 2); - if (increase !== null) { + if (increase != null) { increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red"; } @@ -1103,6 +1199,25 @@ function updateStockPlayerPosition(stock) { return; } var tickerId = "stock-market-ticker-" + stock.symbol; + + if (stockMarketPortfolioMode) { + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + StockMarket["Orders"] && StockMarket["Orders"][stock.symbol] && + StockMarket["Orders"][stock.symbol].length === 0) { + removeElementById(tickerId + "-hdr"); + removeElementById(tickerId + "-panel"); + return; + } else { + //If the ticker hasn't been created, create it (handles updating) + //If it has been created, continue normally + if (document.getElementById(tickerId + "-hdr") == null) { + createStockTicker(stock); + setStockTickerClickHandlers(); + return; + } + } + } + if (!(stock.posTxtEl instanceof Element)) { stock.posTxtEl = document.getElementById(tickerId + "-position-text"); } @@ -1151,7 +1266,7 @@ function updateStockOrderList(stock) { var tickerId = "stock-market-ticker-" + stock.symbol; var orderList = document.getElementById(tickerId + "-order-list"); if (orderList == null) { - console.log("ERROR: Could not find order list for " + stock.symbol); + if (!stockMarketPortfolioMode) {console.log("ERROR: Could not find order list for " + stock.symbol);} return; } @@ -1166,6 +1281,24 @@ function updateStockOrderList(stock) { return; } + if (stockMarketPortfolioMode) { + if (stock.playerShares === 0 && stock.playerShortShares === 0 && + StockMarket["Orders"] && StockMarket["Orders"][stock.symbol] && + StockMarket["Orders"][stock.symbol].length === 0) { + removeElementById(tickerId + "-hdr"); + removeElementById(tickerId + "-panel"); + return; + } else { + //If the ticker hasn't been created, create it (handles updating) + //If it has been created, continue normally + if (document.getElementById(tickerId + "-hdr") == null) { + createStockTicker(stock); + setStockTickerClickHandlers(); + return; + } + } + } + //Remove everything from list while (orderList.firstChild) { orderList.removeChild(orderList.firstChild); diff --git a/src/Terminal.js b/src/Terminal.js index 37e2da656..e6ecaef8f 100644 --- a/src/Terminal.js +++ b/src/Terminal.js @@ -493,7 +493,7 @@ let Terminal = { else {rootAccess = "NO";} post("Root Access: " + rootAccess); post("Required hacking skill: " + Player.getCurrentServer().requiredHackingSkill); - post("Estimated server security level(1-100): " + formatNumber(addOffset(Player.getCurrentServer().hackDifficulty, 5), 3)); + post("Estimated server security level: " + formatNumber(addOffset(Player.getCurrentServer().hackDifficulty, 5), 3)); post("Estimated chance to hack: " + formatNumber(addOffset(Player.calculateHackingChance() * 100, 5), 2) + "%"); post("Estimated time to hack: " + formatNumber(addOffset(Player.calculateHackingTime(), 5), 3) + " seconds"); post("Estimated total money available on server: $" + formatNumber(addOffset(Player.getCurrentServer().moneyAvailable, 5), 2)); diff --git a/utils/HelperFunctions.js b/utils/HelperFunctions.js index 5fb168ea1..6c443d6fe 100644 --- a/utils/HelperFunctions.js +++ b/utils/HelperFunctions.js @@ -38,6 +38,14 @@ function clearEventListenersEl(el) { return newElem; } +//Given its id, this function removes an element AND its children +function removeElementById(id) { + var elem = document.getElementById(id); + if (elem == null) {return;} + while(elem.firstChild) {elem.removeChild(elem.firstChild);} + elem.parentNode.removeChild(elem); +} + function getRandomInt(min, max) { if (min > max) {return getRandomInt(max, min);} return Math.floor(Math.random() * (max - min + 1)) + min; @@ -67,4 +75,5 @@ function powerOfTwo(n) { } export {sizeOfObject, addOffset, clearEventListeners, getRandomInt, - compareArrays, printArray, powerOfTwo, clearEventListenersEl}; + compareArrays, printArray, powerOfTwo, clearEventListenersEl, + removeElementById}; diff --git a/utils/LogBox.js b/utils/LogBox.js index be6b14e2b..5222afeb3 100644 --- a/utils/LogBox.js +++ b/utils/LogBox.js @@ -1,4 +1,5 @@ -import {printArray} from "./HelperFunctions.js"; +import {killWorkerScript} from "../src/NetscriptWorker.js"; +import {printArray, clearEventListeners} from "./HelperFunctions.js"; $(document).keydown(function(event) { if (logBoxOpened && event.keyCode == 27) { @@ -15,6 +16,7 @@ function logBoxInit() { logBoxClose(); return false; }); + document.getElementById("log-box-text-header").style.display = "inline-block"; }; document.addEventListener("DOMContentLoaded", logBoxInit, false); @@ -35,23 +37,30 @@ function logBoxOpen() { var logBoxOpened = false; var logBoxCurrentScript = null; -//ram argument is in GB function logBoxCreate(script) { logBoxCurrentScript = script; + var killScriptBtn = clearEventListeners("log-box-kill-script"); + killScriptBtn.addEventListener("click", ()=>{ + killWorkerScript(script, script.server); + return false; + }); + document.getElementById('log-box-kill-script').style.display = "inline-block"; logBoxOpen(); document.getElementById("log-box-text-header").innerHTML = logBoxCurrentScript.filename + " " + printArray(logBoxCurrentScript.args) + ":

"; + logBoxCurrentScript.logUpd = true; logBoxUpdateText(); } function logBoxUpdateText() { var txt = document.getElementById("log-box-text"); - if (logBoxCurrentScript && logBoxOpened && txt) { + if (logBoxCurrentScript && logBoxOpened && txt && logBoxCurrentScript.logUpd) { txt.innerHTML = ""; for (var i = 0; i < logBoxCurrentScript.logs.length; ++i) { txt.innerHTML += logBoxCurrentScript.logs[i]; txt.innerHTML += "
"; } + logBoxCurrentScript.logUpd = false; } }