Merge pull request #157 from danielyxie/dev

v0.32.1
This commit is contained in:
danielyxie 2017-11-02 22:14:01 -05:00 committed by GitHub
commit b7c16e69a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 10066 additions and 3303 deletions

@ -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;

11711
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -781,10 +781,12 @@
If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-tix-api" class="a-link-button-inactive"> Buy Trade Information eXchange (TIX) API Access - COMING SOON</a>
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
<a id="stock-market-investopedia" class='a-link-button'>Investopedia</a>
<p id="stock-market-commission"> </p>
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<ul id="stock-market-list" style="list-style:none;">
</ul>
</div>
@ -792,7 +794,8 @@
<!-- Log Box -->
<div id="log-box-container">
<div id="log-box-content">
<span id="log-box-close"> &times; </span>
<span id="log-box-close" class="popup-box-button"> Close </span>
<span id="log-box-kill-script" class="popup-box-button">Kill Script</span>
<p id="log-box-text-header"> </p>
<p id="log-box-text"> </p>
</div>

@ -1,3 +1,6 @@
import {Locations} from "./src/Location.js";
import {getRandomInt} from "../utils/HelperFunctions.js";
/*
Products
For certain industries, players can creat their own custom products
@ -204,3 +207,229 @@ Industries:
will fluctuate based on company performance. Then you can sell whatever
shares you have left on the stock market.
*/
var TOTALSHARES = 1000000000; //Total number of shares you have at your company
function Material(params={}) {
this.name = params.name ? params.name : "";
this.qty = 0; //Quantity
this.qlt = 0; //Quality
this.dmd = 0; //Demand
this.cmp = 0; //Competition
this.mku = 0; //Markup
//How much space it takes in a Warehouse
this.siz = params.size ? params.size : 0;
this.purc = 0; //How much of this material is being purchased per second
this.cost = 0; //$ Cost per material
//Material requirements. An object that maps the name of a material to how much it requires
//to make 1 unit of the product.
this.req = params.req ? params.req : {};
}
var Materials = {
Water: new Material({name: "Water", size: 0.1}),
Energy: new Material
Food: 13,
Plants: 14,
Metal: 15,
Hardware: 16,
Chemicals: 17,
RealEstate: 18,
Drugs: 19,
Robots: 20,
AICores:21,
SciResearch: 22
}
function Product(params={}) {
"use strict"
this.name = params.name ? params.name : 0;
this.dmd = params.demand ? params.demand : 0;
this.cmp = params.competition ? params.competition : 0;
this.mku = params.markup ? params.markup : 0;
this.qlt = params.quality ? params.quality : 0;
this.qty = 0;
this.per = params.performance ? params.performance : 0;
this.dur = params.durability ? params.durability : 0;
this.rel = params.reliability ? params.reliability : 0;
this.aes = params.aesthetics ? params.aesthetics : 0;
this.fea = params.features ? params.features : 0;
this.loc = params.location ? params.location : "";
this.siz = params.size ? params.size : 0; //How much space it takes in the warehouse
//Material requirements. An object that maps the name of a material to how much it requires
//to make 1 unit of the product.
this.req = params.req ? params.req : {};
}
var Industries = {
Energy: 50,
Utilities: 51,
Agriculture: 52,
Fishing: 53,
Mining: 54,
Food: 55,
Tobacco: 56,
Chemical: 57,
Pharmaceutical: 58,
Computer: 59,
Robotics: 60,
Software: 61,
Healthcare: 62,
RealEstate: 63,
}
function Industry(params={}) {
"use strict"
this.offices = { //Maps locations to offices. 0 if no office at that location
Locations.Aevum: 0,
Locations.Chonqing: 0,
Locations.Sector12: 0,
Locations.NewTokyo: 0,
Locations.Ishima: 0,
Locations.Volhaven: 0
};
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
Locations.Aevum: 0,
Locations.Chonqing: 0,
Locations.Sector12: 0,
Locations.NewTokyo: 0,
Locations.Ishima: 0,
Locations.Volhaven: 0
};
this.type = params.type ? params.type : 0;
this.materials = {}; //Contains both materials that are created and required
this.products = {};
this.awareness = 0;
this.popularity = 0;
this.startingCost = 0;
/* The following are factors for how much production/other things are increased by
different factors. The production increase always has diminishing returns,
and they are all reprsented by inverse exponentials.
The number for these properties represent the denominator in the inverse
exponential (aka higher number = more diminishing returns); */
this.reFac = 0; //Real estate Factor
this.sciFac = 0; //Scientific Research Factor
this.hwFac = 0; //Hardware factor
this.robFac = 0; //Robotics Factor
this.aiFac = 0; //AI Cores factor;
this.advFac = 0; //Advertising factor
this.init();
}
Industry.prototype.init = function() {
//Set the unique properties of an industry (how much its affected by real estate/scientific research, etc.)
switch (this.type) {
case Industries.Energy:
break;
case Industries.Utilities:
break;
case Industries.Agriculture:
break;
case Industries.Fishing:
break;
case Industries.Mining:
break;
case Industries.Food:
break;
case Industries.Tobacco:
break;
case Industries.Chemical:
break;
case Industries.Pharmaceutical:
break;
case Industries.Computer:
break;
case Industries.Robotics:
break;
case Industries.Software:
break;
case Industries.Healthcare:
break;
case Industries.RealEstate:
break;
default:
console.log("ERR: Invalid Industry Type passed into Industry.init()");
return;
}
}
var EmployeePositions = {
Operations: 1,
Engineer: 2,
Business: 3,
Accounting: 4,
Management: 5,
RandD: 6
}
function Employee(params={}) {
"use strict"
if (!(this instanceof Employee)) {
return new Employee(params);
}
this.name = params.name ? params.name : "Bobby";
this.mor = params.morale ? params.morale : getRandomInt(50, 100);
this.hap = params.happiness ? params.happiness : getRandomInt(50, 100);
this.ene = params.energy ? params.energy : getRandomInt(50, 100);
this.age = params.age ? params.age : getRandomInt(20, 50);
this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50);
this.cha = params.charisma ? params.charisma : getRandomInt(10, 50);
this.exp = params.experience ? params.experience : getRandomInt(10, 50);
this.cre = params.creativity ? params.creativity : getRandomInt(10, 50);
this.eff = params.efficiency ? params.efficiency : getRandomInt(10, 50);
this.sal = params.salary ? params.salary : getRandomInt(0.1, 5);
this.pro = 0; //Calculated
this.off = params.officeSpace ? params.officeSpace : {};
this.loc = params.officeSpace ? params.officeSpace.loc : "";
this.pos = 0;
}
var OfficeSpaceTiers = {
Basic: 7,
Enhanced: 8,
Luxurious: 9,
Extravagant: 10
}
function OfficeSpace(params={}) {
"use strict"
this.loc = params.loc ? params.loc : "";
this.cost = params.cost ? params.cost : 1;
this.size = params.size ? params.size : 1;
this.comf = params.comfort ? params.comfort : 1;
this.beau = parms.beauty ? params.beauty : 1;
this.tier = OfficeSpaceTiers.Basic;
this.employees = [];
}
function Warehouse(params={}) {
"use strict"
this.loc = params.loc ? params.loc : "";
this.size = params.size ? params.size : 0;
this.materials = {};
this.products = {};
}
function Company() {
"use strict"
this.industries = [];
//Financial stats
this.funds = 0;
this.revenue = 0;
this.expenses = 0;
this.valuation = 0; //Private investory valuation of company before you go public.
this.numShares = TOTALSHARES;
this.sharePrice = 0;
}

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.32.0",
Version: "0.32.1",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -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<br><br>" +
"<i><u>round(n)</u></i><br>Rounds the number n to the nearest integer. If the argument passed in is not a number, then the function will return 0.<br><br>" +
"<i><u>write(port, data)</u></i><br>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.<br><br>" +
"<i><u>read(port)</u></i><br>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. <br><br>" +
"<i><u>write(port, data='', mode='a')</u></i><br>This function can be used to either write data to a port or to a text file (.txt).<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"<i><u>read(port)</u></i><br>This function is used to read data from a port or from a text file (.txt).<br><br>" +
"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.<br><br>" +
"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<br><br>" +
"<i><u>scriptRunning(scriptname, hostname/ip)</u></i><br>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.<br><br>" +
@ -696,6 +707,11 @@ let CONSTANTS = {
"Returns the amount of time in milliseconds that have passed since you last installed Augmentations (or destroyed a BitNode).<br><br>" +
"<i><u>sprintf()/vsprintf()</u></i><br>" +
"<a href='https://github.com/alexei/sprintf.js' target='_blank'>See this link for details</a><br><br>" +
"<i><u>prompt(message)</u></i><br>" +
"Prompts the player with a dialog box with two options: 'Yes' and 'No'. This function will returns true if " +
"the player clicks 'Yes' and false if the player click's 'No'. The script's execution is halted until the " +
"player selects 'Yes' or 'No'. The function takes a single string as an argument which specifies the text " +
"that appears on the dialog box.<br><br>" +
"<u><h1>Hacknet Nodes API</h1></u><br>" +
"Netscript provides the following API for accessing and upgrading your Hacknet Nodes through scripts. This API does NOT work offline.<br><br>" +
"<i><u>hacknetnodes</u></i><br>A special variable. This is an array that maps to the Player's Hacknet Nodes. The Hacknet Nodes are accessed through " +
@ -1096,6 +1112,14 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>",
LatestUpdate:
"v0.32.1<br>" +
"-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)<br>" +
"-Rebalanced stock market (adjusted parameters such as the volatility/trends/starting price of certain stocks)<br>" +
"-Added prompt() Netscript function<br>" +
"-Added 'Buy Max' and 'Sell All' functions to Stock Market UI<br>" +
"-Added 'Portfolio' Mode to Stock Market UI so you can only view stocks you have a position/order in<br>" +
"-Added a button to kill a script from its log display box<br><br>" +
"v0.32.0<br>" +
"-Released BitNode-8: Ghost of Wall Street<br>" +
"-Re-designed Stock Market UI<br>" +

File diff suppressed because it is too large Load Diff

@ -30,6 +30,7 @@ import {getCostOfNextHacknetNode,
purchaseHacknet} from "./HacknetNode.js";
import {Locations} from "./Location.js";
import {Message, Messages} from "./Message.js";
import {inMission} from "./Missions.js";
import {Player} from "./Player.js";
import {Script, findRunningScript, RunningScript} from "./Script.js";
import {Server, getServer, AddToAllServers,
@ -59,6 +60,9 @@ import {dialogBoxCreate} from "../utils/DialogBox.js"
import {printArray, powerOfTwo} from "../utils/HelperFunctions.js";
import {createRandomIp} from "../utils/IPAddress.js";
import {formatNumber, isString, isHTML} from "../utils/StringHelperFunctions.js";
import {yesNoBoxClose, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxCreate,
yesNoBoxOpen} from "../utils/YesNoBox.js";
var hasSingularitySF=false, hasAISF=false, hasBn11SF=false, hasWallStreetSF=false;
var singularitySFLvl=1, wallStreetSFLvl=1;
@ -1346,15 +1350,40 @@ function NetscriptFunctions(workerScript) {
getTimeSinceLastAug : function() {
return Player.playtimeSinceLastAug;
},
prompt : 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 */
universityCourse(universityName, className) {
universityCourse : function(universityName, className) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run universityCourse(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return false;
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (Player.isWorking) {
var txt = Player.singularityStopWork();
workerScript.scriptRef.log(txt);
@ -1423,13 +1452,17 @@ function NetscriptFunctions(workerScript) {
return true;
},
gymWorkout(gymName, stat) {
gymWorkout : function(gymName, stat) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run gymWorkout(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return false;
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (Player.isWorking) {
var txt = Player.singularityStopWork();
workerScript.scriptRef.log(txt);
@ -1757,6 +1790,11 @@ function NetscriptFunctions(workerScript) {
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (Player.companyPosition == "" || !(Player.companyPosition instanceof CompanyPosition)) {
workerScript.scriptRef.log("ERROR: workForCompany() failed because you do not have a job");
return false;
@ -1910,6 +1948,11 @@ function NetscriptFunctions(workerScript) {
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (!factionExists(name)) {
workerScript.scriptRef.log("ERROR: Faction specified in workForFaction() does not exist.");
return false;
@ -2005,7 +2048,10 @@ function NetscriptFunctions(workerScript) {
return false;
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (Player.isWorking) {
var txt = Player.singularityStopWork();
workerScript.scriptRef.log(txt);
@ -2085,14 +2131,17 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("Began creating program: " + name);
return true;
},
commitCrime(crime) {
commitCrime : function(crime) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run commitCrime(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return;
}
}
if (inMission) {
workerScript.scriptRef.log("ERROR: universityCourse() failed because you are in the middle of a mission.");
return;
}
if (Player.isWorking) {
var txt = Player.singularityStopWork();
workerScript.scriptRef.log(txt);

@ -4,7 +4,8 @@ import {addActiveScriptsItem,
import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {Environment} from "./NetscriptEnvironment.js";
import {evaluate, isScriptErrorMessage} from "./NetscriptEvaluator.js";
import {evaluate, isScriptErrorMessage,
killNetscriptDelay} from "./NetscriptEvaluator.js";
import {AllServers} from "./Server.js";
import {Settings} from "./Settings.js";
@ -24,7 +25,8 @@ function WorkerScript(runningScriptObj) {
this.scriptRef = runningScriptObj;
this.errorMessage = "";
this.args = runningScriptObj.args;
this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay())
//this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay())
this.delay = null;
this.fnWorker = null; //Workerscript for a function call
}
@ -110,8 +112,8 @@ function runScriptsLoop() {
w.running = false;
w.env.stopFlag = true;
w.scriptRef.log("Script finished running");
}, function(w) {
//console.log(w);
}).catch(function(w) {
console.log(w);
if (w instanceof Error) {
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
console.log("ERROR: Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
@ -165,10 +167,10 @@ function killWorkerScript(runningScriptObj, serverIp) {
if (workerScripts[i].name == runningScriptObj.filename && workerScripts[i].serverIp == serverIp &&
compareArrays(workerScripts[i].args, runningScriptObj.args)) {
workerScripts[i].env.stopFlag = true;
workerScripts[i].killTrigger();
killNetscriptDelay(workerScripts[i]);
if (workerScripts[i].fnWorker) {
workerScripts[i].fnWorker.env.stopFlag = true;
workerScripts[i].fnWorker.killTrigger();
killNetscriptDelay(workerScripts[i].fnWorker);
}
return true;
}

@ -190,8 +190,11 @@ 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;
this.jobProdSinceLastAug = 0;
};
PlayerObject.prototype.init = function() {

@ -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){

@ -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)");

@ -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() {

@ -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";
@ -247,11 +249,11 @@ function initStockMarket() {
}
var ecorp = Locations.AevumECorp;
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], 0.5, true, 16, getRandomInt(20000, 25000));
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], 0.45, true, 19, getRandomInt(20000, 25000));
StockMarket[ecorp] = ecorpStk;
var megacorp = Locations.Sector12MegaCorp;
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], 0.5, true, 16, getRandomInt(25000, 33000));
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], 0.45, true, 19, getRandomInt(25000, 33000));
StockMarket[megacorp] = megacorpStk;
var blade = Locations.Sector12BladeIndustries;
@ -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.8, true, 1, getRandomInt(500, 1000));
StockMarket[joesguns] = joesgunsStk;
var catalyst = "Catalyst Ventures";
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.6, true, 20, getRandomInt(500, 1000));
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.5, true, 14, getRandomInt(500, 1000));
StockMarket[catalyst] = catalystStk;
var microdyne = "Microdyne Technologies";
@ -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.<br><br>" +
"NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"Here is a summary of how each order works and when they execute:<br><br>" +
"In a LONG Position:<br><br>" +
"A Limit Order to buy will execute if the stock's price <= order's price<br>" +
"A Limit Order to sell will execute if the stock's price >= order's price<br>" +
"A Stop Order to buy will execute if the stock's price >= order's price<br>" +
"A Stop Order to sell will execute if the stock's price <= order's price<br><br>" +
"In a SHORT Position:<br><br>" +
"A Limit Order to buy will execute if the stock's price >= order's price<br>" +
"A Limit Order to sell will execute if the stock's price <= order's price<br>" +
"A Stop Order to buy will execute if the stock's price <= order's price<br>" +
"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.<br><br>" +
"NOTE: Shorting stocks is not available immediately, and must be unlocked later on in the game.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"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.<br><br>" +
"Here is a summary of how each order works and when they execute:<br><br>" +
"In a LONG Position:<br><br>" +
"A Limit Order to buy will execute if the stock's price <= order's price<br>" +
"A Limit Order to sell will execute if the stock's price >= order's price<br>" +
"A Stop Order to buy will execute if the stock's price >= order's price<br>" +
"A Stop Order to sell will execute if the stock's price <= order's price<br><br>" +
"In a SHORT Position:<br><br>" +
"A Limit Order to buy will execute if the stock's price >= order's price<br>" +
"A Limit Order to sell will execute if the stock's price <= order's price<br>" +
"A Stop Order to buy will execute if the stock's price <= order's price<br>" +
"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" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
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" +
"<span class='tooltiptext'>Displays all stocks on the WSE</span>";
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" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
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"),
@ -832,6 +927,8 @@ function createStockTicker(stock) {
orderTypeSelect = document.createElement("select"),
buyButton = document.createElement("span"),
sellButton = document.createElement("span"),
buyMaxButton = document.createElement("span"),
sellAllButton = document.createElement("span"),
positionTxt = document.createElement("p"),
orderList = document.createElement("ul");
@ -952,6 +1049,76 @@ function createStockTicker(stock) {
return false;
});
buyMaxButton.classList.add("stock-market-input");
buyMaxButton.classList.add("a-link-button");
buyMaxButton.innerHTML = "Buy MAX";
buyMaxButton.addEventListener("click", ()=>{
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.toNumber();
switch (ordType) {
case "Market Order":
var shares = Math.floor((money - COMM) / stock.price);
pos === PositionTypes.Long ? buyStock(stock, shares) : shortStock(stock, shares, null);
break;
case "Limit Order":
case "Stop Order":
var yesBtn = yesNoTxtInpBoxGetYesButton(),
noBtn = yesNoTxtInpBoxGetNoButton();
yesBtn.innerText = "Place Buy " + ordType;
noBtn.innerText = "Cancel Order";
yesBtn.addEventListener("click", ()=>{
var price = Number(yesNoTxtInpBoxGetInput()), type;
if (ordType === "Limit Order") {
type = OrderTypes.LimitBuy;
} else {
type = OrderTypes.StopBuy;
}
var shares = Math.floor((money-COMM) / price);
placeOrder(stock, shares, price, type, pos);
yesNoTxtInpBoxClose();
});
noBtn.addEventListener("click", ()=>{
yesNoTxtInpBoxClose();
});
yesNoTxtInpBoxCreate("Enter the price for your " + ordType);
break;
default:
console.log("ERROR: Invalid order type");
break;
}
return false;
});
sellAllButton.classList.add("stock-market-input");
sellAllButton.classList.add("a-link-button");
sellAllButton.innerHTML = "Sell ALL";
sellAllButton.addEventListener("click", ()=>{
var pos = longShortSelect.options[longShortSelect.selectedIndex].text;
pos === "Long" ? pos = PositionTypes.Long : pos = PositionTypes.Short;
var ordType = orderTypeSelect.options[orderTypeSelect.selectedIndex].text;
switch (ordType) {
case "Market Order":
if (pos === PositionTypes.Long) {
var shares = stock.playerShares;
sellStock(stock, shares);
} else {
var shares = stock.playerShortShares;
sellShort(stock, shares, null);
}
break;
case "Limit Order":
case "Stop Order":
dialogBoxCreate("ERROR: 'Sell All' only works for Market Orders")
break;
default:
console.log("ERROR: Invalid order type");
break;
}
return false;
});
positionTxt.setAttribute("id", tickerId + "-position-text");
positionTxt.classList.add("stock-market-position-text");
stock.posTxtEl = positionTxt;
@ -964,6 +1131,8 @@ function createStockTicker(stock) {
stockDiv.appendChild(orderTypeSelect);
stockDiv.appendChild(buyButton);
stockDiv.appendChild(sellButton);
stockDiv.appendChild(buyMaxButton);
stockDiv.appendChild(sellAllButton);
stockDiv.appendChild(positionTxt);
stockDiv.appendChild(orderList);
@ -972,6 +1141,7 @@ function createStockTicker(stock) {
document.getElementById("stock-market-list").appendChild(li);
updateStockTicker(stock, true);
updateStockPlayerPosition(stock);
updateStockOrderList(stock);
}
@ -1008,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";
}
@ -1029,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");
}
@ -1077,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;
}
@ -1092,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);

@ -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));

@ -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};

@ -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) + ":<br><br>";
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 += "<br>";
}
logBoxCurrentScript.logUpd = false;
}
}

@ -22,6 +22,7 @@ function yesNoBoxGetNoButton() {
}
function yesNoBoxCreate(txt) {
if (yesNoBoxOpen) {return false;} //Already open
yesNoBoxOpen = true;
var textElement = document.getElementById("yes-no-box-text");
if (textElement) {
@ -34,6 +35,7 @@ function yesNoBoxCreate(txt) {
} else {
console.log("ERROR: Container not found for YesNoBox");
}
return true;
}
/* Generic Yes-No POp-up Box with Text input */