Merge pull request #154 from danielyxie/dev

Dev v0.32.0
This commit is contained in:
danielyxie 2017-10-25 14:51:14 -05:00 committed by GitHub
commit 5d37227ea0
21 changed files with 4649 additions and 3152 deletions

@ -596,28 +596,31 @@ div.faction-clear {
margin: 10px;
}
.stock-market-qty-input {
border: 1px solid white;
color: var(--my-font-color);
.stock-market-input {
display: inline-block;
padding: 4px;
margin: 4px;
background-color:black;
}
.stock-market-buy-sell-button {
color: #aaa;
font-size: 16px;
font-weight: bold;
padding: 2px;
margin: 6px;
margin: 2px;
background-color: black;
border: 1px solid white;
color: var(--my-font-color);
}
.stock-market-buy-sell-button:hover,
.stock-market-buy-sell-button:focus {
.stock-market-position-text {
color:white;
display:inline-block;
}
.stock-market-order-list {
overflow-y:auto;
max-height: 100px;
}
.stock-market-order-cancel-btn {
background-color: black;
border: 1px solid white;
color: var(--my-font-color);
text-decoration: none;
cursor: pointer;
margin: 2px;
padding: 0px;
}
/* Gang */

@ -445,7 +445,8 @@ a:link, a:visited {
margin-left: 5%;
display: none;
background-color: #555;
overflow:auto;
overflow-y:auto;
overflow-x:none;
}
.accordion-panel div,

6201
dist/bundle.js vendored

File diff suppressed because it is too large Load Diff

@ -781,9 +781,8 @@
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 - COMING SOON</a>
<a id="stock-market-investopedia" class='a-link-button'>Investopedia</a>
<p id="stock-market-commission"> </p>
<ul id="stock-market-list" style="list-style:none;">

@ -77,7 +77,21 @@ function initBitNodes() {
"Level 3: 7%");
BitNodes["BitNode6"] = new BitNode(6, "Do Androids Dream?", "COMING SOON"); //Build androids for automation
BitNodes["BitNode7"] = new BitNode(7, "Waste Runner", "COMING SOON"); //Postapocalyptic wasteland + blade runner
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "COMING SOON"); //Trading only viable strategy
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
"In this BitNode:<br><br>" +
"You start with $100 million<br>" +
"The only way to earn money is by trading on the stock market<br>" +
"You start with a WSE membership and access to the TIX API<br>" +
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
"You can immediately donate to factions to gain reputation<br><br>" +
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanent access to WSE and TIX API<br>" +
"Level 2: Ability to short stocks in other BitNodes<br>" +
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"This Source-File also increases your hacking growth multipliers by: " +
"<br>Level 1: 8%<br>Level 2: 12%<br>Level 3: 14%");
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic
BitNodes["BitNode10"] = new BitNode(10, "MegaCorp", "COMING SOON"); //Not sure yet
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
@ -148,6 +162,7 @@ let BitNodeMultipliers = {
FactionWorkRepGain: 1,
FactionPassiveRepGain: 1,
RepToDonateToFaction: 1,
AugmentationRepCost: 1,
AugmentationMoneyCost: 1,
@ -203,6 +218,15 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5;
break;
case 8: //Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0;
BitNodeMultipliers.ManualHackMoney = 0;
BitNodeMultipliers.CompanyWorkMoney = 0;
BitNodeMultipliers.CrimeMoney = 0;
BitNodeMultipliers.HacknetNodeMoney = 0;
BitNodeMultipliers.InfiltrationMoney = 0;
BitNodeMultipliers.RepToDonateToFaction = 0
break;
case 11: //The Big Crash
BitNodeMultipliers.ServerMaxMoney = 0.1;
BitNodeMultipliers.ServerStartingMoney = 0.1;

@ -1,8 +1,156 @@
/* CompanyManagement.js */
/*
Company made up of
Products
For certain industries, players can created their own custom products
Essentially, these are just things you give a certain name to.
Products have certain properties that affect how well they sell. These properties
are just numbers. For each Industry, only some of these properties are applicable
(e.g. Performance isnt applicable for food industry)
Demand: Determined by industry. For most industries this will slowly decrease over time, meaning
that you must create new and better products to remain successful. The speed at which this
decreases over time is dependent on industry
Competition: Determined by industry
Markup : Determined by Industry
Quality:
Performance:
Durability:
Reliability:
Aesthetics:
Features:
Location: Only valid for 'building' products like Restaurants, Hospitals, etc.
Scientific Research affects the properties of products
Materials:
To create Products, you need materials. There are different tiers of Materials
Materials have several properties that determine how profitable they can be:
Quality:
Demand:
Competition:
Markup: How much price markup a material can have before theres a significant dropoff in how much its bought
Materials Types:
1st tier:
Water - High Stable Demand, Medium competition, low markup
Energy - Suuuuuuper high stable demand, High competition, low markup
2nd Tier:
Food - High Stable Demand, Lots of competition, medium markup
Plants - Initially high but volatile demand. Decent competition, low markup
Metal - Very high stable demand, lots of competition, low markup
3rd Tier:
Hardware - Very high stable demand, lots of competition, med markup
Chemicals - High stable demand, good amount of competition, med markup
Real estate - Initially high but volatile demand. Decent competition, med markup. Tied to a certain city
4th tier:
Drugs - High stable demand, lots of competition, medium markup
Robots - Very high stable demand, looots of competition, high markup
AI Cores - Very high stable demand, looooots of competition, veeery high markup
5th tier:
Scientific Research
Industries:
- Some Industries let you create your own custom "Products", while others just produce Materials
- Each Industry has different characteristics for things
- List of Industries:
Energy - Requires hardware, real estate
Produces Energy
Can Use Hardware/AI Cores to increase production
More real estate = more production with very little dimishing returns
Production increased by scientific research
High starting cost
Utilities - Requires hardware, Real Estate
Produces Water
Can use Hardware, Robotics, and AI Cores to increase production
More real estate = more production with medium diminishing returns
Production increased by scientific research
High starting cost
Agriculture - Requires Water and Energy
Produces food and plants
Can use Hardware/Robotics/AI Cores to increase production
Production increased by scientific research
More real estate = more production with very little diminishing returns
Medium starting cost
Fishing - Requires energy
Produces lots of food
Can use Hardware/Robotics/AI Cores to increase production
Production increased by scientific research
More real estate = slightly more production with very high diminishing returns
Medium starting cost (higher than agriculture)
Mining - Requires Energy
Produces Metal
Can use hardware/Robotics/AI Cores to increase production
Production increased by scientific research
More real estate = more production with medium diminishing returns
High starting cost
Food - Create your own "restaurant" products
Restaurants require food, water, energy, and real estate
Restaurants in general are high stable demand, but lots of competition, and medium markup
Low starting cost
Production increase from real estate diminishes greatly in city. e.g. making many restaurants
in one city has high diminishing returns, but making a few in every city is good
Tobacco - Create your own tobacco products
Requires plants, water, and real estate
High volatile demand, but not much competition. Low markup
Low starting cost
Product quality significantly affected by scientific research
Chemical - Create your own chemical products.
Requires plants, energy, water, and real estate
High stable demand, high competition, low markup
Medium starting cost
Advertising does very little
Product quality significantly affected by scientific research
Pharmaceutical - Create your own drug products
Requires chemicals, energy, water, and real estate
Very high stable demand. High competition, very high markup
High starting cost
Requires constant creation of new and better products to be successful
Product quality significantly affected by scientific research
Computer - Creates 'Hardware' material
Requires metal, energy, real estate
Can use Robotics/AI Cores to increase production
More real estate = more production with high diminishing returns
Production significantly affected by scientific research
High starting cost
Robotics - Create 'Robots' material and create your own 'Robot' products
Requires hardware, energy, and real estate
Production can be improved by using AI Cores
Extremely high stable demand, medium competition, high markup
Extremely high starting cost
Product quality significantly affected by scientific research
more real estate = more production with medium diminishing returns for 'Robot' materials
Software - Create 'AI Cores' material and create your own software products
Requires hardware, energy, real estate
Very high stable demand, high competition, low markup
Low starting cost
Product quality slightly affected by scientific research
Healthcare - Open your own hospitals (each is its own product).
Requires real estate, robots, AI Cores, energy, water
Extremely high stable demand, semi-high competition, super high markup
Extremely high starting cost
Production increase from real estate diminishes greatly in city. e.g. making many hospitals
in one city has high diminishing returns, but making a few in every city is goodIn
Real Estate - Create 'Real Estate'.
Requires metal, energy, water, hardware
Can use Hardware/Robotics/AI Cores to increase production
Production slightly affected by scientific research
Heavily affected by advertising
Biotechnology -
Entertainment -
Finance -
Mass Media -
Telecommunications -
Employees:
Has morale and energy that must be managed to maintain productivity
Stats:
Intelligence, Charisma, Experience, Creativity, Efficiency
Assigned to different positions. The productivity at each position is determined by
stats. I.e. each employe should be assigned to positions based on stats to optimize production
Position
Operations -
Engineer -
Business -
Accounting -
Management -
Research and Development -
*/

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.31.0",
Version: "0.32.0",
//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
@ -440,18 +440,18 @@ let CONSTANTS = {
"<u><h1> Functions </h1></u><br>" +
"You can NOT define you own functions in Netscript (yet), but there are several built in functions that " +
"you may use: <br><br> " +
"<i>hack(hostname/ip)</i><br>Core function that is used to try and hack servers to steal money and gain hacking experience. The argument passed in must be a string with " +
"<i><u>hack(hostname/ip)</u></i><br>Core function that is used to try and hack servers to steal money and gain hacking experience. The argument passed in must be a string with " +
"either the IP or hostname of the server you want to hack. The runtime for this command depends on your hacking level and the target server's security level. " +
" A script can hack a server from anywhere. It does not need to be running on the same server to hack that server. " +
"For example, you can create a script that hacks the 'foodnstuff' server and run that script on any server in the game. A successful hack() on " +
"a server will raise that server's security level by 0.002. Returns true if the hack is successful and " +
"false otherwise. <br>" +
"Examples: hack('foodnstuff'); or hack('148.192.0.12');<br><br>" +
"<i>sleep(n, log=true)</i><br>Suspends the script for n milliseconds. The second argument is an optional boolean that indicates " +
"<i><u>sleep(n, log=true)</u></i><br>Suspends the script for n milliseconds. The second argument is an optional boolean that indicates " +
"whether or not the function should log the sleep action. If this argument is true, then calling this function will write " +
"'Sleeping for N milliseconds' to the script's logs. If it's false, then this function will not log anything. " +
"If this argument is not specified then it will be true by default. <br>Example: sleep(5000);<br><br>" +
"<i>grow(hostname/ip)</i><br>Use your hacking skills to increase the amount of money available on a server. The argument passed in " +
"<i><u>grow(hostname/ip)</u></i><br>Use your hacking skills to increase the amount of money available on a server. The argument passed in " +
"must be a string with either the IP or hostname of the target server. The runtime for this command depends on your hacking level and the target server's security level. " +
"When grow() completes, the money available on a target server will be increased by a certain, fixed percentage. This percentage " +
"is determined by the server's growth rate and varies between servers. Generally, higher-level servers have higher growth rates. <br><br> " +
@ -460,28 +460,28 @@ let CONSTANTS = {
"It also raises the security level of the target server by 0.004. " +
"Returns the number by which the money on the server was multiplied for the growth. " +
"Works offline at a slower rate. <br> Example: grow('foodnstuff');<br><br>" +
"<i>weaken(hostname/ip)</i><br>Use your hacking skills to attack a server's security, lowering the server's security level. The argument passed " +
"<i><u>weaken(hostname/ip)</u></i><br>Use your hacking skills to attack a server's security, lowering the server's security level. The argument passed " +
"in must be a string with either the IP or hostname of the target server. The runtime for this command depends on your " +
"hacking level and the target server's security level. This function lowers the security level of the target server by " +
"0.05.<br><br> Like hack() and grow(), weaken() can be called on " +
"any server, regardless of where the script is running. This command requires root access to the target server, but " +
"there is no required hacking level to run the command. Returns " +
"0.1. Works offline at a slower rate<br> Example: weaken('foodnstuff');<br><br>" +
"<i>print(x)</i><br>Prints a value or a variable to the scripts logs (which can be viewed with the 'tail [script]' terminal command ). <br><br>" +
"<i>tprint(x)</i><br>Prints a value or a variable to the Terminal<br><br>" +
"<i>clearLog()</i><br>Clears the script's logs. <br><br>" +
"<i>scan(hostname/ip, [hostnames=true])</i><br>Returns an array containing the hostnames or IPs of all servers that are one node away from the specified server. " +
"<i><u>print(x)</u></i><br>Prints a value or a variable to the scripts logs (which can be viewed with the 'tail [script]' terminal command ). <br><br>" +
"<i><u>tprint(x)</u></i><br>Prints a value or a variable to the Terminal<br><br>" +
"<i><u>clearLog()</u></i><br>Clears the script's logs. <br><br>" +
"<i><u>scan(hostname/ip, [hostnames=true])</u></i><br>Returns an array containing the hostnames or IPs of all servers that are one node away from the specified server. " +
"The argument must be a string containing the IP or hostname of the target server. The second argument is a boolean that specifies whether " +
"the hostnames or IPs of the scanned servers should be output. If it is true then hostnames will be returned, and if false then IP addresses will. " +
"This second argument is optional and, if ommitted, the function will output " +
"the hostnames of the scanned servers. The hostnames/IPs in the returned array are strings.<br><br>" +
"<i>nuke(hostname/ip)</i><br>Run NUKE.exe on the target server. NUKE.exe must exist on your home computer.<br> Example: nuke('foodnstuff'); <br><br>" +
"<i>brutessh(hostname/ip)</i><br>Run BruteSSH.exe on the target server. BruteSSH.exe must exist on your home computer.<br> Example: brutessh('foodnstuff');<br><br>" +
"<i>ftpcrack(hostname/ip)</i><br>Run FTPCrack.exe on the target server. FTPCrack.exe must exist on your home computer.<br> Example: ftpcrack('foodnstuff');<br><br>" +
"<i>relaysmtp(hostname/ip)</i><br>Run relaySMTP.exe on the target server. relaySMTP.exe must exist on your home computer.<br> Example: relaysmtp('foodnstuff');<br><br>" +
"<i>httpworm(hostname/ip)</i><br>Run HTTPWorm.exe on the target server. HTTPWorm.exe must exist on your home computer.<br> Example: httpworm('foodnstuff');<br><br>" +
"<i>sqlinject(hostname/ip)</i><br>Run SQLInject.exe on the target server. SQLInject.exe must exist on your home computer.<br> Example: sqlinject('foodnstuff');<br><br>" +
"<i>run(script, [numThreads], [args...])</i> <br> Run a script as a separate process. The first argument that is passed in is the name of the script as a string. This function can only " +
"<i><u>nuke(hostname/ip)</u></i><br>Run NUKE.exe on the target server. NUKE.exe must exist on your home computer.<br> Example: nuke('foodnstuff'); <br><br>" +
"<i><u>brutessh(hostname/ip)</u></i><br>Run BruteSSH.exe on the target server. BruteSSH.exe must exist on your home computer.<br> Example: brutessh('foodnstuff');<br><br>" +
"<i><u>ftpcrack(hostname/ip)</u></i><br>Run FTPCrack.exe on the target server. FTPCrack.exe must exist on your home computer.<br> Example: ftpcrack('foodnstuff');<br><br>" +
"<i><u>relaysmtp(hostname/ip)</u></i><br>Run relaySMTP.exe on the target server. relaySMTP.exe must exist on your home computer.<br> Example: relaysmtp('foodnstuff');<br><br>" +
"<i><u>httpworm(hostname/ip)</u></i><br>Run HTTPWorm.exe on the target server. HTTPWorm.exe must exist on your home computer.<br> Example: httpworm('foodnstuff');<br><br>" +
"<i><u>sqlinject(hostname/ip)</u></i><br>Run SQLInject.exe on the target server. SQLInject.exe must exist on your home computer.<br> Example: sqlinject('foodnstuff');<br><br>" +
"<i><u>run(script, [numThreads], [args...])</u></i> <br> Run a script as a separate process. The first argument that is passed in is the name of the script as a string. This function can only " +
"be used to run scripts located on the current server (the server running the script that calls this function). The second argument " +
"is optional, and it specifies how many threads to run the script with. This argument must be a number greater than 0. If it is omitted, then the script will be run single-threaded. Any additional arguments will specify " +
"arguments to pass into the new script that is being run. If arguments are specified for the new script, then the second argument numThreads argument must be filled in with a value.<br><br>" +
@ -493,7 +493,7 @@ let CONSTANTS = {
"run('foo.script', 5);<br><br>" +
"The following example will run 'foo.script' single-threaded, and will pass the string 'foodnstuff' into the script as an argument:<br><br>" +
"run('foo.script', 1, 'foodnstuff');<br><br>" +
"<i>exec(script, hostname/ip, [numThreads], [args...])</i><br>Run a script as a separate process on another server. The first argument is the name of the script as a string. The " +
"<i><u>exec(script, hostname/ip, [numThreads], [args...])</u></i><br>Run a script as a separate process on another server. The first argument is the name of the script as a string. The " +
"second argument is a string with the hostname or IP of the 'target server' on which to run the script. The specified script must exist on the target server. " +
"The third argument is optional, and it specifies how many threads to run the script with. If it is omitted, then the script will be run single-threaded. " +
"This argument must be a number that is greater than 0. Any additional arguments will specify arguments to pass into the new script that is being run. If " +
@ -507,7 +507,7 @@ let CONSTANTS = {
"The following example will try to run the script 'foo.script' on the 'foodnstuff' server with 5 threads. It will also pass the number 1 and the string 'test' in as arguments " +
"to the script.<br><br>" +
"exec('foo.script', 'foodnstuff', 5, 1, 'test');<br><br>" +
"<i>kill(script, hostname/ip, [args...])</i><br> Kills the script on the target server specified by the script's name and arguments. Remember that " +
"<i><u>kill(script, hostname/ip, [args...])</u></i><br> Kills the script on the target server specified by the script's name and arguments. Remember that " +
"scripts are uniquely identified by both their name and arguments. For example, if 'foo.script' is run with the argument 1, then this is not the " +
"same as 'foo.script' run with the argument 2, even though they have the same code. <br><br>" +
"The first argument must be a string with the name of the script. The name is case-sensitive. " +
@ -522,9 +522,9 @@ let CONSTANTS = {
"kill('foo.script', getHostname());<br><br>" +
"If you are trying to kill a script named 'foo.script' on the current server that was ran with the arguments 1 and 'foodnstuff', use this:<br><br>" +
"kill('foo.script', getHostname(), 1, 'foodnstuff');<br><br>" +
"<i>killall(hostname/ip)</i><br> Kills all running scripts on the specified server. This function takes a single argument which " +
"<i><u>killall(hostname/ip)</u></i><br> Kills all running scripts on the specified server. This function takes a single argument which " +
"must be a string containing the hostname or IP of the target server. This function will always return true. <br><br>" +
"<i>scp(script, [source], destination)</i><br>Copies a script or literature (.lit) file to another server. The first argument is a string with " +
"<i><u>scp(script, [source], destination)</u></i><br>Copies a script or literature (.lit) file to another server. The first argument is a string with " +
"the filename of the script or literature file " +
"to be copied, or an array of filenames to be copied. The next two arguments are strings containing the hostname/IPs of the source and target server. " +
"The source refers to the server from which the script/literature file will be copied, while the destination " +
@ -534,15 +534,15 @@ let CONSTANTS = {
"will return if at least one of the files in the array is successfully copied over.<br><br>" +
"Example: scp('hack-template.script', 'foodnstuff'); //Copies hack-template.script from the current server to foodnstuff<br>" +
"Example: scp('foo.lit', 'helios', 'home'); //Copies foo.lit from the helios server to the home computer<br><br>" +
"<i>ls(hostname/ip)</i><br>Returns an array containing the names of all files on the specified server. The argument must be a " +
"<i><u>ls(hostname/ip)</u></i><br>Returns an array containing the names of all files on the specified server. The argument must be a " +
"string with the hostname or IP of the target server.<br><br>" +
"<i>hasRootAccess(hostname/ip)</i><br> Returns a boolean (true or false) indicating whether or not the Player has root access to a server. " +
"<i><u>hasRootAccess(hostname/ip)</u></i><br> Returns a boolean (true or false) indicating whether or not the Player has root access to a server. " +
"The argument passed in must be a string with either the hostname or IP of the target server.<br> " +
"Example:<br>if (hasRootAccess('foodnstuff') == false) {<br>&nbsp;&nbsp;&nbsp;&nbsp;nuke('foodnstuff');<br>}<br><br>" +
"<i>getIp()</i><br>Returns a string with the IP Address of the server that the script is running on <br><br>" +
"<i>getHostname()</i><br>Returns a string with the hostname of the server that the script is running on<br><br>" +
"<i>getHackingLevel()</i><br>Returns the Player's current hacking level.<br><br> " +
"<i>getHackingMultipliers()</i><br>Returns an object containing the Player's hacking related multipliers. " +
"<i><u>getIp()</u></i><br>Returns a string with the IP Address of the server that the script is running on <br><br>" +
"<i><u>getHostname()</u></i><br>Returns a string with the hostname of the server that the script is running on<br><br>" +
"<i><u>getHackingLevel()</u></i><br>Returns the Player's current hacking level.<br><br> " +
"<i><u>getHackingMultipliers()</u></i><br>Returns an object containing the Player's hacking related multipliers. " +
"These multipliers are returned in integer forms, not percentages (e.g. 1.5 instead of 150%). " +
"The object has the following structure:<br><br>" +
"{<br>" +
@ -554,7 +554,7 @@ let CONSTANTS = {
"mults = getHackingMultipliers();<br>" +
"print(mults.chance);<br>" +
"print(mults.growth);<br><br>" +
"<i>getBitNodeMultipliers()</i><br>Returns an object containing the current BitNode multipliers. " +
"<i><u>getBitNodeMultipliers()</u></i><br>Returns an object containing the current BitNode multipliers. " +
"This function requires Source-File 5 in order to run. The multipliers are returned in integer forms, not percentages " +
"(e.g. 1.5 instead of 150%). The multipliers represent the difference between the current BitNode and the " +
"original BitNode (BitNode-1). For example, if the 'CrimeMoney' multiplier has a value of 0.1 then that means " +
@ -584,33 +584,33 @@ let CONSTANTS = {
"mults = getBitNodeMultipliers();<br>" +
"print(mults.ServerMaxMoney);<br>" +
"print(mults.HackExpGain);<br><br>" +
"<i>getServerMoneyAvailable(hostname/ip)</i><br> Returns the amount of money available on a server. The argument passed in must be a string with either the " +
"<i><u>getServerMoneyAvailable(hostname/ip)</u></i><br> Returns the amount of money available on a server. The argument passed in must be a string with either the " +
"hostname or IP of the target server.<br> Example: getServerMoneyAvailable('foodnstuff');<br><br>" +
"<i>getServerMaxMoney(hostname/ip)</i><br>Returns the maximum amount of money that can be available on a server. The argument passed in must be a string with " +
"<i><u>getServerMaxMoney(hostname/ip)</u></i><br>Returns the maximum amount of money that can be available on a server. The argument passed in must be a string with " +
"the hostname or IP of the target server.<br>Example: getServerMaxMoney('foodnstuff');<br><br>" +
"<i>getServerGrowth(hostname/ip)</i><br>Returns the server's intrinsic 'growth parameter'. This growth parameter is a number " +
"<i><u>getServerGrowth(hostname/ip)</u></i><br>Returns the server's intrinsic 'growth parameter'. This growth parameter is a number " +
"between 1 and 100 that represents how quickly the server's money grows. This parameter affects the percentage by which this server's " +
"money is increased when using the grow() function. A higher growth parameter will result in a higher percentage from grow().<br><br>" +
"The argument passed in must be a string with the hostname or IP of the target server.<br><br>" +
"<i>getServerSecurityLevel(hostname/ip)</i><br>Returns the security level of a server. The argument passed in must be a string with either the " +
"<i><u>getServerSecurityLevel(hostname/ip)</u></i><br>Returns the security level of a server. The argument passed in must be a string with either the " +
"hostname or IP of the target server. A server's security is denoted by a number, typically between 1 and 100.<br><br>" +
"<i>getServerBaseSecurityLevel(hostname/ip)</i><br>Returns the base security level of a server. This is the security level that the server starts out with. " +
"<i><u>getServerBaseSecurityLevel(hostname/ip)</u></i><br>Returns the base security level of a server. This is the security level that the server starts out with. " +
"This is different than getServerSecurityLevel() because getServerSecurityLevel() returns the current security level of a server, which can constantly change " +
"due to hack(), grow(), and weaken() calls on that server. The base security level will stay the same until you reset by installing an Augmentation. <br><br>" +
"The argument passed in must be a string with either the hostname or IP of the target server. A server's base security is denoted by a number, typically between 1 and 100. " +
"<br><br>" +
"<i>getServerMinSecurityLevel(hostname/ip)</i>Returns the minimum security level of a server. The argument passed in must be a string with " +
"<i><u>getServerMinSecurityLevel(hostname/ip)</u></i>Returns the minimum security level of a server. The argument passed in must be a string with " +
"either the hostname or IP of the target server.<br><br>" +
"<i>getServerRequiredHackingLevel(hostname/ip)</i><br>Returns the required hacking level of a server. The argument passed in must be a string with either the " +
"<i><u>getServerRequiredHackingLevel(hostname/ip)</u></i><br>Returns the required hacking level of a server. The argument passed in must be a string with either the " +
"hostname or IP or the target server.<br><br>" +
"<i>getServerNumPortsRequired(hostname/ip)</i><br>Returns the number of open ports required to successfully run NUKE.exe on a server. The argument " +
"<i><u>getServerNumPortsRequired(hostname/ip)</u></i><br>Returns the number of open ports required to successfully run NUKE.exe on a server. The argument " +
"passed in must be a string with either the hostname or IP of the target server.<br><br>" +
"<i>getServerRam(hostname/ip)</i><br>Returns an array with two elements that gives information about the target server's RAM. The first " +
"<i><u>getServerRam(hostname/ip)</u></i><br>Returns an array with two elements that gives information about the target server's RAM. The first " +
"element in the array is the amount of RAM that the server has (in GB). The second element in the array is the amount of RAM that " +
"is currently being used on the server.<br><br>" +
"<i>serverExists(hostname/ip)</i><br>Returns a boolean denoting whether or not the specified server exists. The argument " +
"<i><u>serverExists(hostname/ip)</u></i><br>Returns a boolean denoting whether or not the specified server exists. The argument " +
"must be a string with the hostname or IP of the target server.<br><br>" +
"<i>fileExists(filename, [hostname/ip])</i><br> Returns a boolean (true or false) indicating whether the specified file exists on a server. " +
"<i><u>fileExists(filename, [hostname/ip])</u></i><br> Returns a boolean (true or false) indicating whether the specified file exists on a server. " +
"The first argument must be a string with the name of the file. A file can either be a script, program, or literature file. A script name is case-sensitive, but a " +
"program/literature file is not. For example, fileExists('brutessh.exe') will work fine, even though the actual program is named BruteSSH.exe. <br><br> " +
"The second argument is a string with the hostname or IP of the server on which to search for the program. This second argument is optional. " +
@ -619,7 +619,7 @@ let CONSTANTS = {
"Example: fileExists('ftpcrack.exe');<br><br>" +
"The first example above will return true if the script named 'foo.script' exists on the 'foodnstuff' server, and false otherwise. The second example above will " +
"return true if the current server (the server on which this function runs) contains the FTPCrack.exe program, and false otherwise. <br><br>" +
"<i>isRunning(filename, hostname/ip, [args...])</i><br> Returns a boolean (true or false) indicating whether the specified script is running on a server. " +
"<i><u>isRunning(filename, hostname/ip, [args...])</u></i><br> Returns a boolean (true or false) indicating whether the specified script is running on a server. " +
"Remember that a script is uniquely identified by both its name and its arguments. <br><br>" +
"The first argument must be a string with the name of the script. The script name is case sensitive. The second argument is a string with the " +
"hostname or IP of the target server. Any additional arguments passed to the function will specify the arguments passed into the target script. " +
@ -631,45 +631,45 @@ let CONSTANTS = {
"example above will return true if there is a script named 'foo.script' with no arguments running on the current server, and false otherwise. " +
"The third example above will return true if there is a script named 'foo.script' with the arguments 1, 5, and 'test' running on the 'joesguns' server, and " +
"false otherwise.<br><br>" +
"<i>getNextHacknetNodeCost()</i><br>Returns the cost of purchasing a new Hacknet Node<br><br>" +
"<i>purchaseHacknetNode()</i><br>Purchases a new Hacknet Node. Returns a number with the index of the Hacknet Node. This index is equivalent to the number " +
"<i><u>getNextHacknetNodeCost()</u></i><br>Returns the cost of purchasing a new Hacknet Node<br><br>" +
"<i><u>purchaseHacknetNode()</u></i><br>Purchases a new Hacknet Node. Returns a number with the index of the Hacknet Node. This index is equivalent to the number " +
"at the end of the Hacknet Node's name (e.g The Hacknet Node named 'hacknet-node-4' will have an index of 4). If the player cannot afford to purchase " +
"a new Hacknet Node then the function will return false. Does NOT work offline<br><br>" +
"<i>purchaseServer(hostname, ram)</i><br> Purchases a server with the specified hostname and amount of RAM. The first argument can be any data type, " +
"<i><u>purchaseServer(hostname, ram)</u></i><br> Purchases a server with the specified hostname and amount of RAM. The first argument can be any data type, " +
"but it will be converted to a string using Javascript's String function. Anything that resolves to an empty string will cause the function to fail. " +
"The second argument specified the amount of RAM (in GB) for the server. This argument must resolve to a numeric and it must be a power of 2 " +
"(2, 4, 8, etc...). <br><br>" +
"This function returns the hostname of the newly purchased server as a string. If the function fails to purchase a server, then it will return " +
"an empty string. The function will fail if the arguments passed in are invalid or if the player does not have enough money to purchase the specified server.<br><br>" +
"<i>deleteServer(hostname)</i><br>Deletes one of the servers you've purchased with the specified hostname. The function will fail if " +
"<i><u>deleteServer(hostname)</u></i><br>Deletes one of the servers you've purchased with the specified hostname. The function will fail if " +
"there are any scripts running on the specified server. Returns true if successful and false otherwise<br><br>" +
"<i>getPurchasedServers([hostname=true])</i><br>Returns an array with either the hostname or IPs of all of the servers you " +
"<i><u>getPurchasedServers([hostname=true])</u></i><br>Returns an array with either the hostname or IPs of all of the servers you " +
"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>round(n)</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>write(port, data)</i><br>Writes data to a port. The first argument must be a number between 1 and 10 that specifies the port. The second " +
"<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>read(port)</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. " +
"<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>scriptRunning(scriptname, hostname/ip)</i><br>Returns a boolean indicating whether any instance of the specified script is running " +
"<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>" +
"The first argument must be a string with the name of the script. The script name is case sensitive. The second argument is " +
"a string with the hostname or IP of the target server. Both arguments are required.<br><br>" +
"<i>scriptKill(scriptname, hostname/ip)</i><br>Kills all scripts with the specified filename that are running on the server specified by the " +
"<i><u>scriptKill(scriptname, hostname/ip)</u></i><br>Kills all scripts with the specified filename that are running on the server specified by the " +
"hostname/ip, regardless of arguments. Returns true if one or more scripts were successfully killed, and false if there were none. <br><br>" +
"The first argument must be a string with the name of the script. The script name is case sensitive. The second argument is " +
"a string with the hostname or IP of the target server. Both arguments are required.<br><br>" +
"<i>getScriptRam(scriptname, hostname/ip)</i><br>Returns the amount of RAM required to run the specified script on the " +
"<i><u>getScriptRam(scriptname, hostname/ip)</u></i><br>Returns the amount of RAM required to run the specified script on the " +
"target server. The first argument must be a string with the name of the script. The script name is case sensitive. " +
"The second argument is a string with the hostname or IP of the server where that script is. Both arguments are required.<br><br>" +
"<i>getHackTime(hostname/ip)</i><br>Returns the amount of time in seconds it takes to execute the hack() Netscript function " +
"<i><u>getHackTime(hostname/ip)</u></i><br>Returns the amount of time in seconds it takes to execute the hack() Netscript function " +
"on the server specified by the hostname/ip. The argument must be a string with the hostname/ip of the target server.<br><br>" +
"<i>getGrowTime(hostname/ip)</i><br>Returns the amount of time in seconds it takes to execute the grow() Netscript function " +
"<i><u>getGrowTime(hostname/ip)</u></i><br>Returns the amount of time in seconds it takes to execute the grow() Netscript function " +
"on the server specified by the hostname/ip. The argument must be a string with the hostname/ip of the target server.<br><br>" +
"<i>getWeakenTime(hostname/ip)</i><br>Returns the amount of time in seconds it takes to execute the weaken() Netscript function " +
"<i><u>getWeakenTime(hostname/ip)</u></i><br>Returns the amount of time in seconds it takes to execute the weaken() Netscript function " +
"on the server specified by the hostname/ip. The argument must be a string with the hostname/ip of the target server.<br><br>" +
"<i>getScriptIncome([scriptname], [hostname/ip], [args...])</i><br>" +
"<i><u>getScriptIncome([scriptname], [hostname/ip], [args...])</u></i><br>" +
"Returns the amount of income the specified script generates while online (when the game is open, does not apply for " +
"offline income). This function can also be called with no arguments. If called with no arguments, then this function " +
"will return an array of two values. The first value is the total income ($/sec) of all of your active scripts (currently running). " +
@ -681,7 +681,7 @@ let CONSTANTS = {
"The second argument must be a string with the hostname/IP of the target server. If the first argument is specified " +
"then the second argument must be specified as well. Any additional arguments passed to the function will specify " +
"the arguments passed into the target script.<br><br>" +
"<i>getScriptExpGain([scriptname], [hostname/ip], [args...])</i><br>" +
"<i><u>getScriptExpGain([scriptname], [hostname/ip], [args...])</u></i><br>" +
"Returns the amount of hacking experience the specified script generates while online (when the game is open, does not apply for " +
"offline experience gains). This function can also return the total experience gain rate of all of your active scripts by running the function " +
"with no arguments.<br><br>" +
@ -692,26 +692,28 @@ let CONSTANTS = {
"The second argument must be a string with the hostname/IP of the target server. If the first argument is specified " +
"then the second argument must be specified as well. Any additional arguments passed to the function will specify " +
"the arguments passed into the target script.<br><br>" +
"<i>getTimeSinceLastAug()</i><br>" +
"<i><u>getTimeSinceLastAug()</u></i><br>" +
"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>" +
"<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>hacknetnodes</i><br> A special variable. This is an array that maps to the Player's Hacknet Nodes. The Hacknet Nodes are accessed through " +
"<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 " +
"indexes. These indexes correspond to the number at the end of the name of the Hacknet Node. For example, the first Hacknet Node you purchase " +
"will have the same 'hacknet-node-0' and can be accessed with hacknetnodes[0]. The fourth Hacknet Node you purchase will have the name " +
"'hacknet-node-3' and can be accessed with hacknetnodes[3]. <br><br>" +
"<i>hacknetnodes.length</i><br>Returns the number of Hacknet Nodes that the player owns<br><br>" +
"<i>hacknetnodes[i].level</i><br>Returns the level of the corresponding Hacknet Node<br><br>" +
"<i>hacknetnodes[i].ram</i><br>Returns the amount of RAM on the corresponding Hacknet Node<br><br>" +
"<i>hacknetnodes[i].cores</i><br>Returns the number of cores on the corresponding Hacknet Node<br><br>" +
"<i>hacknetnodes[i].totalMoneyGenerated</i><br>Returns the total amount of money that the corresponding Hacknet Node has earned<br><br>" +
"<i>hacknetnodes[i].onlineTimeSeconds</i><br>Returns the total amount of time that the corresponding Hacknet Node has existed<br><br>" +
"<i>hacknetnodes[i].moneyGainRatePerSecond</i><br>Returns the income ($ / sec) that the corresponding Hacknet Node earns<br><br>" +
"<i>hacknetnodes[i].upgradeLevel(n)</i><br>Tries to upgrade the level of the corresponding Hacknet Node n times. The argument n must be a " +
"<i><u>hacknetnodes.length</u></i><br>Returns the number of Hacknet Nodes that the player owns<br><br>" +
"<i><u>hacknetnodes[i].level</u></i><br>Returns the level of the corresponding Hacknet Node<br><br>" +
"<i><u>hacknetnodes[i].ram</u></i><br>Returns the amount of RAM on the corresponding Hacknet Node<br><br>" +
"<i><u>hacknetnodes[i].cores</u></i><br>Returns the number of cores on the corresponding Hacknet Node<br><br>" +
"<i><u>hacknetnodes[i].totalMoneyGenerated</u></i><br>Returns the total amount of money that the corresponding Hacknet Node has earned<br><br>" +
"<i><u>hacknetnodes[i].onlineTimeSeconds</u></i><br>Returns the total amount of time that the corresponding Hacknet Node has existed<br><br>" +
"<i><u>hacknetnodes[i].moneyGainRatePerSecond</u></i><br>Returns the income ($ / sec) that the corresponding Hacknet Node earns<br><br>" +
"<i><u>hacknetnodes[i].upgradeLevel(n)</u></i><br>Tries to upgrade the level of the corresponding Hacknet Node n times. The argument n must be a " +
"positive integer. Returns true if the Hacknet Node's level is successfully upgraded n times or up to the max level (200), and false otherwise.<br><br>" +
"<i>hacknetnodes[i].upgradeRam()</i><br>Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the " +
"<i><u>hacknetnodes[i].upgradeRam()</u></i><br>Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the " +
"RAM is successfully upgraded, and false otherwise. <br><br>" +
"<i>hacknetnodes[i].upgradeCore()</i><br>Attempts to purchase an additional core for the corresponding Hacknet Node. Returns true if the " +
"<i><u>hacknetnodes[i].upgradeCore()</u></i><br>Attempts to purchase an additional core for the corresponding Hacknet Node. Returns true if the " +
"additional core is successfully purchase, and false otherwise. <br><br>" +
"Example: The following is an example of one way a script can be used to automate the purchasing and upgrading of Hacknet Nodes. " +
"This script purchases new Hacknet Nodes until the player has four. Then, it iteratively upgrades each of those four Hacknet Nodes " +
@ -738,25 +740,53 @@ let CONSTANTS = {
"&nbsp;&nbsp;&nbsp;&nbsp;}<br>" +
"}<br><br>" +
"<u><h1>Trade Information eXchange (TIX) API</h1></u><br>" +
"<i>getStockPrice(sym)</i><br>Returns the price of a stock. The argument passed in must be the stock's symbol (NOT THE COMPANY NAME!). The symbol " +
"<i><u>getStockPrice(sym)</u></i><br>Returns the price of a stock. The argument passed in must be the stock's symbol (NOT THE COMPANY NAME!). The symbol " +
"is a sequence of two to four capital letters. The symbol argument must be a string. <br><br>" +
"Example: getStockPrice('FSIG');<br><br>" +
"<i>getStockPosition(sym)</i><br>Returns an array of two elements that represents the player's position in a stock. The first element " +
"<i><u>getStockPosition(sym)</u></i><br>Returns an array of two elements that represents the player's position in a stock. The first element " +
"in the array is the number of shares the player owns of the specified stock. The second element in the array is the average price of the player's " +
"shares. Both elements are numbers. The argument passed in must be the stock's symbol, which is a sequence of two to four capital letters.<br><br>" +
"Example: <br><br>pos = getStockPosition('ECP');<br>shares = pos[0];<br>avgPx = pos[1];<br><br>"+
"<i>buyStock(sym, shares)</i><br>Attempts to purchase shares of a stock. The first argument must be a string with the stock's symbol. The second argument " +
"<i><u>buyStock(sym, shares)</u></i><br>Attempts to purchase shares of a stock using a Market Order. The first argument must be a string with the stock's symbol. The second argument " +
"must be the number of shares to purchase.<br><br>" +
"If the player does not have enough money to purchase specified number of shares, then no shares will be purchased (it will not purchase the most you can afford). " +
"Remember that every transaction on the stock exchange costs a certain commission fee.<br><br>" +
"The function will return true if it successfully purchases the specified number of shares of stock, and false otherwise.<br><br>" +
"<i>sellStock(sym, shares)</i><br>Attempts to sell shares of a stock. The first argument must be a string with the stock's symbol. The second argument " +
"<i><u>sellStock(sym, shares)</u></i><br>Attempts to sell shares of a stock using a Market Order. The first argument must be a string with the stock's symbol. The second argument " +
"must be the number of shares to sell.<br><br>" +
"If the specified number of shares in the function exceeds the amount that the player actually owns, then this function will sell all owned shares. " +
"Remember that every transaction on the stock exchange costs a certain commission fee.<br><br>" +
"The net profit made from selling stocks with this function is reflected in the script's statistics. This net profit is calculated as: <br><br>" +
"shares * (sell price - average price of purchased shares)<br><br>" +
"This function will return true if the shares of stock are successfully sold and false otherwise.<br><br>" +
"<i><u>shortStock(sym, shares)</u></i><br>" +
"Attempts to purchase a short position of a stock using a Market Order. The first argument must be a string with the stock's symbol. The second argument " +
"must be the number of shares to purchase.<br><br>" +
"In order to use this function the player must be in BitNode-8 or must have Level 2 of Source-File 8.<br><br>" +
"If the player does not have enough money to purchase the specified number of shares, then no shares will be purchased. Remember that every " +
"every transaction on the stock exchange costs a certain commission fee.<br><br>" +
"Returns true if it successfully shorts the stock with the specified number of shares, and false otherwise.<br><br>" +
"<i><u>sellShort(sym, shares)</u></i><br>" +
"Attempts to sell a short position of a stock using a Market Order. The first argument must be a string with the stock's symbol. The second argument must be the " +
"number of shares to sell.<br><br>" +
"In order to use this function the player must be in BitNode-8 or must have Level 2 of Source-File 8.<br><br>" +
"If the specified number of shares exceeds the amount that the player actually owns, then this function will sell all owned shares. " +
"Remember that every transaction on the stock exchange costs a certain commission fee.<br><br>" +
"This function returns true if it successfully sells any number of shares, and false otherwise.<br><br>" +
"<i><u>placeOrder(sym, shares, price, type, pos)</u></i><br>" +
"Places an order on the stock market. This function only works for Limit and Stop Orders. Use the buyStock/sellStock/shortStock/sellShort functions " +
"to place Market Orders. In order to use this function the player must be in BitNode-8 or must have Level 3 of Source-File 8.<br><br>" +
"The 'sym' argument must be a string with the symbol of the stock. The 'shares' and 'price' arguments " +
"specify the number of shares and the execution price for the order. They must be numeric.<br><br>" +
"The 'type' argument is a string that specifies the type of order. It must specify either 'limit' or 'stop', and must " +
"also specify 'buy' or 'sell'. This argument is NOT case-sensitive. Here are four examples that will work: <br><br>" +
"limitbuy, limitsell, stopbuy, stopsell<br><br>" +
"The last argument, 'pos', is a string that specifies whether the order is a 'Long' or 'Short' position. The values 'L' and " +
"'S' can also be used. This argument is NOT case-sensitive.<br><br>" +
"Returns true if the order is successfully placed, and false otherwise.<br><br>" +
"<i><u>cancelOrder(sym, shares, price, type, pos)</u></i><br>" +
"Cancels an oustanding order on the stock market. In order to use this function the player must be in BitNode-8 or must have " +
"Level 3 of Source-File 8. This function uses the same arguments as placeOrder()<br><br>" +
"<u><h1>While loops </h1></u><br>" +
"A while loop is a control flow statement that repeatedly executes code as long as a condition is met. <br><br> " +
"<i>while (<i>[cond]</i>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;<i>[code]</i><br>}</i><br><br>" +
@ -800,7 +830,7 @@ let CONSTANTS = {
"then you will be able to access all of the Singularity Functions.<br><br>" +
"Note that Singularity Functions require a lot of RAM outside of BitNode-4 (their RAM costs are multiplied by " +
"10 if you are not in BitNode-4).<br><br>" +
"<i>universityCourse(universityName, courseName)</i><br>" +
"<i><u>universityCourse(universityName, courseName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically set you to start taking a course at a university. If you are already " +
"in the middle of some 'working' action (such as working at a company, for a faction, or on a program), " +
@ -814,7 +844,7 @@ let CONSTANTS = {
"The cost and experience gains for all of these universities and classes are the same as if you were to manually " +
"visit and take these classes.<br><br>" +
"This function will return true if you successfully start taking the course, and false otherwise.<br><br>" +
"<i>gymWorkout(gymName, stat)</i><br>" +
"<i><u>gymWorkout(gymName, stat)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically set you to start working out at a gym to train a particular stat. If you are " +
"already in the middle of some 'working' action (such as working at a company, for a faction, or on a program), then " +
@ -826,18 +856,18 @@ let CONSTANTS = {
"The valid stats are:<br><br>strength OR str<br>defense OR def<br>dexterity OR dex<br>agility OR agi<br><br>" +
"The cost and experience gains for all of these gyms are the same as if you were to manually visit these gyms and train " +
"This function will return true if you successfully start working out at the gym, and false otherwise.<br><br>" +
"<i>travelToCity(cityname)</i><br>" +
"<i><u>travelToCity(cityname)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.<br><br>" +
"This function allows the player to travel to any city. The cost for using this function is the same as the cost for traveling through the Travel Agency.<br><br>" +
"The argument passed into this must be a string with the name of the city to travel to. Note that this argument IS CASE SENSITIVE. The valid cities are:<br><br>" +
"Aevum<br>Chongqing<br>Sector-12<br>New Tokyo<br>Ishima<br>Volhaven<br><br>" +
"This function will return true if you successfully travel to the specified city and false otherwise.<br><br>" +
"<i>purchaseTor()</i><br>" +
"<i><u>purchaseTor()</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.<br><br>" +
"This function allows you to automatically purchase a TOR router. The cost for purchasing a TOR router using this " +
"function is the same as if you were to manually purchase one.<br><br>" +
"This function will return true if it successfully purchase a TOR router and false otherwise.<br><br>" +
"<i>purchaseProgram(programName)</i><br>" +
"<i><u>purchaseProgram(programName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.<br><br>" +
"This function allows you to automatically purchase programs. You MUST have a TOR router in order to use this function.<br><br>" +
"The argument passed in must be a string with the name of the program (including the '.exe' extension). This argument is " +
@ -846,29 +876,29 @@ let CONSTANTS = {
"The cost of purchasing programs using this function is the same as if you were purchasing them through the Dark Web (using " +
"the buy Terminal command).<br><br>" +
"This function will return true if the specified program is purchased, and false otherwise.<br><br>" +
"<i>getStats()</i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"<i><u>getStats()</u></i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"function.<br><br>Returns an object with the Player's stats. The object has the following properties:<br><br>" +
"Player.hacking<br>Player.strength<br>Player.defense<br>Player.dexterity<br>Player.agility<br>Player.charisma<br>Player.intelligence<br><br>" +
"Example: <br><br>" +
"res = getStats();<br>print('My charisma level is: ' + res.charisma);<br><br>" +
"<i>isBusy()</i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"<i><u>isBusy()</u></i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"function.<br><br>Returns a boolean indicating whether or not the player is currently performing an 'action'. " +
"These actions include working for a company/faction, studying at a univeristy, working out at a gym, " +
"creating a program, or committing a crime.<br><br>" +
"<i>upgradeHomeRam()</i><br>" +
"<i><u>upgradeHomeRam()</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will upgrade amount of RAM on the player's home computer. The cost is the same as if you were to do it manually.<br><br>" +
"This function will return true if the player's home computer RAM is successfully upgraded, and false otherwise.<br><br>" +
"<i>getUpgradeHomeRamCost()</i><br>" +
"<i><u>getUpgradeHomeRamCost()</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"Returns the cost of upgrading the player's home computer RAM.<br><br>" +
"<i>workForCompany()</i><br>" +
"<i><u>workForCompany()</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically set you to start working at the company at which you are employed. If you are already " +
"in the middle of some 'working' action (such as working for a faction, training at a gym, or creating a program), then " +
"running this function will automatically cancel that action and give you your earnings.<br><br>" +
"This function will return true if the player starts working, and false otherwise.<br><br>" +
"<i>applyToCompany(companyName, field)</i><br>" +
"<i><u>applyToCompany(companyName, field)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically try to apply to the specified company for a position in the specified field. This " +
"function can also be used to apply for promotions by specifying the company and field you are already employed at.<br><br>" +
@ -879,19 +909,19 @@ let CONSTANTS = {
"security<br>agent<br>employee<br>part-time employee<br>waiter<br>part-time waiter<br><br>" +
"This function will return true if you successfully get a job/promotion, and false otherwise. Note " +
"that if you are trying to use this function to apply for a promotion and you don't get one, it will return false.<br><br>" +
"<i>getCompanyRep(companyName)</i><br>" +
"<i><u>getCompanyRep(companyName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will return the amount of reputation you have at the specified company. If the company passed in as " +
"an argument is invalid, -1 will be returned.<br><br>" +
"The argument passed in must be a string with the name of the company. This argument IS CASE-SENSITIVE.<br><br>" +
"<i>checkFactionInvitations()</i><br>" +
"<i><u>checkFactionInvitations()</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"Returns an array with the name of all Factions you currently have oustanding invitations from.<br><br>" +
"<i>joinFaction(name)</i><br>" +
"<i><u>joinFaction(name)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically accept an invitation from a faction and join it.<br><br>" +
"The argument must be a string with the name of the faction. This name IS CASE-SENSITIVE.<br><br>" +
"<i>workForFaction(factionName, workType)</i><br>" +
"<i><u>workForFaction(factionName, workType)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically set you to start working for the specified Faction. Obviously, you " +
"must be a member of the Faction or else this function will fail. If you are already in the middle of " +
@ -901,11 +931,11 @@ let CONSTANTS = {
"must be a string with the type of work you want to perform for the faction. The valid values for this argument are:<br><br>" +
"<br>hacking/hacking contracts/hackingcontracts<br>field/fieldwork/field work<br>security/securitywork/security work<br><br>" +
"This function will return true if you successfully start working for the specified faction, and false otherwise.<br><br>" +
"<i>getFactionRep(factionName)</i><br>" +
"<i><u>getFactionRep(factionName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function returns the amount of reputation you have for the specified Faction. The argument must be a " +
"string with the name of the Faction. The argument IS CASE-SENSITIVE.<br><br>" +
"<i>createProgram(programName)</i><br>" +
"<i><u>createProgram(programName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically set you to start working on creating the specified program. If you are already in " +
"the middle of some 'working' action (such as working for a company, training at a gym, or taking a course), then " +
@ -916,7 +946,7 @@ let CONSTANTS = {
"BruteSSH.exe: 50<br>FTPCrack.exe: 100<br>relaySMTP.exe: 250<br>HTTPWorm.exe: 500<br>SQLInject.exe: 750<br>" +
"DeepscanV1.exe: 75<br>DeepscanV2.exe: 400<br>ServerProfiler.exe: 75<br>AutoLink.exe: 25<br><br>" +
"This function returns true if you successfully start working on the specified program, and false otherwise.<br><br>" +
"<i>commitCrime(crime)</i><br>" +
"<i><u>commitCrime(crime)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function is used to automatically attempt to commit crimes. If you are already in the middle of some 'working' " +
"action (such as working for a company or training at a gym), then running this function will automatically cancel " +
@ -925,40 +955,41 @@ let CONSTANTS = {
"lenient in terms of what inputs it accepts. Here is a list of valid inputs for all of the crimes:<br><br>" +
"shoplift, rob store, mug, larceny, deal drugs, bond forgery, traffick arms, homicide, grand theft auto, " +
"kidnap, assassinate, heist<br><br> " +
"Crimes committed using this function will have all of their earnings halved (this applies for both money and experience!)<br><br>" +
"This function returns the number of seconds it takes to attempt the specified crime (e.g It takes 60 seconds to attempt " +
"the 'Rob Store' crime, so running commitCrime('rob store') will return 60). Warning: I do not recommend using the time " +
"returned from this function to try and schedule your crime attempts. Instead, I would use the isBusy() Singularity function " +
"to check whether you have finished attempting a crime. This is because although the game sets a certain crime to be X amount of seconds, " +
"there is no guarantee that your browser will follow that time limit.<br><br>" +
"<i>getCrimeChance(crime)</i><br>If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to " +
"<i><u>getCrimeChance(crime)</u></i><br>If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to " +
"use this function.<br><br>" +
"This function returns your chance of success at commiting the specified crime. The chance is returned as a decimal " +
"(i.e. 60% would be returned as 0.6). The argument for this function is a string. It is not case-sensitive and is fairly " +
"lenient in terms of what inputs it accepts. Check the documentation for the commitCrime() Singularity Function to see " +
"examples of valid inputs.<br><br>" +
"<i>getOwnedAugmentations(purchased=false)</i><br>" +
"<i><u>getOwnedAugmentations(purchased=false)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function returns an array of the names of all Augmentations you own as strings. It takes a single optional " +
"boolean argument that specifies whether the returned array should include Augmentations you have purchased " +
"but not yet installed. If it is true, then the returned array will include these Augmentations. By default, " +
"this argument is false.<br><br>" +
"<i>getAugmentationsFromFaction(facName)</i><br>" +
"<i><u>getAugmentationsFromFaction(facName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"Returns an array containing the names (as strings) of all Augmentations that are available from the specified faction. " +
"The argument must be a string with the faction's name. This argument is case-sensitive.<br><br>" +
"<i>getAugmentationCost(augName)</i><br>" +
"<i><u>getAugmentationCost(augName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function returns an array with two elements that gives the cost for the specified Augmentation" +
". The first element in the returned array is the reputation requirement of the Augmentation, and the second element " +
"is the money cost.<br><br>" +
"The argument passed in must be a string with the name of the Augmentation. This argument IS CASE-SENSITIVE. " +
"If an invalid Augmentation name is passed in, this function will return the array [-1, -1].<br><br>" +
"<i>purchaseAugmentation(factionName, augName)</i><br>" +
"<i><u>purchaseAugmentation(factionName, augName)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function will try to purchase the specified Augmentation through the given Faction.<br><br>" +
"The two arguments must be strings specifying the name of the Faction and Augmentation, respectively. These arguments are both CASE-SENSITIVE.<br><br>" +
"This function will return true if the Augmentation is successfully purchased, and false otherwise.<br><br>" +
"<i>installAugmentations(cbScript)</i><br>" +
"<i><u>installAugmentations(cbScript)</u></i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function will automatically install your Augmentations, resetting the game as usual.<br><br>" +
"It will return true if successful, and false otherwise.<br><br>" +
@ -1065,27 +1096,10 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>",
LatestUpdate:
"v0.31.0<br>" +
"-Game now saves to IndexedDb (if your browser supports it). This means you should " +
"no longer have trouble saving the game when your save file gets too big (from running " +
"too many scripts). " +
"The game will still be saved to localStorage as well<br>" +
"-New file type: text files (.txt). You can read or write to text files using the read()/write() Netscript commands. " +
"You can view text files in Terminal using 'cat'. Eventually I will make it so you can edit them in the editor " +
"but that's not available yet. You can also download files to your real computer using the 'download' Terminal command<br>" +
"-Added a new Crime: Bond Forgery. This crime takes 5 minutes to attempt " +
"and gives $4,500,000 if successful. It is meant for mid game.<br>" +
"-Added commitCrime(), getCrimeChance(), isBusy(), and getStats() Singularity Functions.<br>" +
"-Removed getIntelligence() Netscript function<br>" +
"-Added sprintf and vsprintf to Netscript. See <a href='https://github.com/alexei/sprintf.js' target='_blank'>this Github page for details</a><br>" +
"-Increased the amount of money gained from Infiltration by 20%, and the amount of faction reputation by 12%<br>" +
"-Rebalanced BitNode-2 so that Crime and Infiltration are more profitable but hacking is less profitable. Infiltration also gives more faction rep<br>" +
"-Rebalanced BitNode-4 so that hacking is slightly less profitable<br>" +
"-Rebalanced BitNode-5 so that Infiltration is more profitable and gives more faction rep<br>" +
"-Rebalanced BitNode-11 so that Crime and Infiltration are more profitable. Infiltration also gives more faction rep.<br>" +
"-Fixed an annoying issue in Hacking Missions where sometimes you would click a Node but it wouldnt actually get selected<br>" +
"-Made the Hacking Mission gameplay a bit slower by lowering the effect of Scan and reducing Attack damage<br>" +
"-Slightly increased the base reputation gain rate for factions when doing Field Work and Security Work<br>"
"v0.32.0<br>" +
"-Released BitNode-8: Ghost of Wall Street<br>" +
"-Re-designed Stock Market UI<br>" +
"-Minor bugfixes<br>"
}
export {CONSTANTS};

@ -605,7 +605,7 @@ function displayFactionContent(factionName) {
}
if (faction.isMember) {
if (faction.favor >= 150) {
if (faction.favor >= (150 * BitNodeMultipliers.RepToDonateToFaction)) {
donateDiv.style.display = "inline";
} else {
donateDiv.style.display = "none";

@ -1132,7 +1132,7 @@ function setGangMemberClickHandlers() {
//Server panel click handlers
var gangMemberHdrs = document.getElementsByClassName("gang-member-header");
if (gangMemberHdrs == null) {
console.log("ERROR: Could not find Active Scripts server panels");
console.log("ERROR: Could not find Gang Member Headers");
return;
}
for (let i = 0; i < gangMemberHdrs.length; ++i) {
@ -1243,7 +1243,6 @@ function createGangMemberDisplayElement(memberObj) {
taskDescP.style.display = "inline";
taskDescDiv.appendChild(taskDescP);
statsDiv.style.width = "30%";
taskDiv.style.width = "30%";
taskDescDiv.style.width = "30%";

@ -48,6 +48,7 @@ function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff)
this.dexExpGained = 0;
this.agiExpGained = 0;
this.chaExpGained = 0;
this.intExpGained = 0;
}
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
@ -80,6 +81,11 @@ InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
this.chaExpGained += amt;
}
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
if (isNaN(amt)) {return;}
this.intExpGained += amt;
}
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
clearInfiltrationStatusText();
@ -87,9 +93,7 @@ function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
}
function endInfiltration(inst, success) {
if (success) {
infiltrationBoxCreate(inst);
}
if (success) {infiltrationBoxCreate(inst);}
clearEventListeners("infiltration-kill");
clearEventListeners("infiltration-knockout");
@ -588,10 +592,10 @@ let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
//Success: 5%, Failure 10%, -Karma
function attemptInfiltrationKill(inst) {
var chance = getInfiltrationKillChance(inst);
inst.gainStrengthExp(inst.securityLevel / 100) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 100) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 100) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 100) * Player.agility_exp_mult;
inst.gainStrengthExp(inst.securityLevel / 90) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 90) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 90) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 90) * Player.agility_exp_mult;
if (Math.random() <= chance) {
inst.securityLevel *= 1.05;
return [true, 1.05];
@ -614,10 +618,10 @@ function getInfiltrationKillChance(inst) {
//Success: 3%, Failure: 10%
function attemptInfiltrationKnockout(inst) {
var chance = getInfiltrationKnockoutChance(inst);
inst.gainStrengthExp(inst.securityLevel / 100) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 100) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 100) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 100) * Player.agility_exp_mult;
inst.gainStrengthExp(inst.securityLevel / 85) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 85) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 85) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 85) * Player.agility_exp_mult;
if (Math.random() <= chance) {
inst.securityLevel *= 1.03;
return [true, 1.03];
@ -639,9 +643,9 @@ function getInfiltrationKnockoutChance(inst) {
//Success: 0%, Failure: 10%
function attemptInfiltrationStealthKnockout(inst) {
var chance = getInfiltrationStealthKnockoutChance(inst);
inst.gainStrengthExp(inst.securityLevel / 100) * Player.strength_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 75) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 75) * Player.agility_exp_mult;
inst.gainStrengthExp(inst.securityLevel / 90) * Player.strength_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 70) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 70) * Player.agility_exp_mult;
if (Math.random() <= chance) {
return [true, 1];
} else {
@ -663,9 +667,9 @@ function getInfiltrationStealthKnockoutChance(inst) {
//Success: 0%, Failure: 5%, -Karma
function attemptInfiltrationAssassinate(inst) {
var chance = getInfiltrationAssassinateChance(inst);
inst.gainStrengthExp(inst.securityLevel / 100) * Player.strength_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 75) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 75) * Player.agility_exp_mult;
inst.gainStrengthExp(inst.securityLevel / 90) * Player.strength_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 70) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 70) * Player.agility_exp_mult;
if (Math.random() <= chance) {
return [true, 1];
} else {
@ -687,10 +691,10 @@ function getInfiltrationAssassinateChance(inst) {
//Success: 5%, Failure: 10%
function attemptInfiltrationDestroySecurity(inst) {
var chance = getInfiltrationDestroySecurityChance(inst);
inst.gainStrengthExp(inst.securityLevel / 100) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 100) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 100) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 100) * Player.agility_exp_mult;
inst.gainStrengthExp(inst.securityLevel / 90) * Player.strength_exp_mult;
inst.gainDefenseExp(inst.securityLevel / 90) * Player.defense_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 90) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 90) * Player.agility_exp_mult;
if (Math.random() <= chance) {
inst.securityLevel *= 1.05;
return [true, 1.05];
@ -714,7 +718,8 @@ function getInfiltrationDestroySecurityChance(inst) {
//Success: 1%, Failure: 5%
function attemptInfiltrationHack(inst) {
var chance = getInfiltrationHackChance(inst);
inst.gainHackingExp(inst.securityLevel / 75) * Player.hacking_exp_mult;
inst.gainHackingExp(inst.securityLevel / 50) * Player.hacking_exp_mult;
inst.gainIntelligenceExp(inst.securityLevel / 700);
if (Math.random() <= chance) {
inst.securityLevel *= 1.03;
return [true, 1.03];
@ -736,7 +741,7 @@ function getInfiltrationHackChance(inst) {
//Success: 0%, Failure: 8%
function attemptInfiltrationSneak(inst) {
var chance = getInfiltrationSneakChance(inst);
inst.gainAgilityExp(inst.securityLevel / 75) * Player.agility_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 50) * Player.agility_exp_mult;
if (Math.random() <= chance) {
return [true, 1];
} else {
@ -757,7 +762,7 @@ function getInfiltrationSneakChance(inst) {
//Success: 1%, Failure: 3%
function attemptInfiltrationPickLockedDoor(inst) {
var chance = getInfiltrationPickLockedDoorChance(inst);
inst.gainDexterityExp(inst.securityLevel / 75) * Player.dexterity_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 40) * Player.dexterity_exp_mult;
if (Math.random() <= chance) {
inst.securityLevel *= 1.01;
return [true, 1.01];
@ -778,7 +783,7 @@ function getInfiltrationPickLockedDoorChance(inst) {
//Success: 0%, Failure: 15%,
function attemptInfiltrationBribe(inst) {
var chance = getInfiltrationBribeChance(inst);
inst.gainCharismaExp(inst.securityLevel / 50) * Player.charisma_exp_mult;
inst.gainCharismaExp(inst.securityLevel / 10) * Player.charisma_exp_mult;
if (Math.random() <= chance) {
return [true, 1];
} else {
@ -797,8 +802,8 @@ function getInfiltrationBribeChance(inst) {
//Failure: 5%
function attemptInfiltrationEscape(inst) {
var chance = getInfiltrationEscapeChance(inst);
inst.gainAgilityExp(inst.securityLevel / 50) * Player.agility_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 50) * Player.dexterity_exp_mult;
inst.gainAgilityExp(inst.securityLevel / 40) * Player.agility_exp_mult;
inst.gainDexterityExp(inst.securityLevel / 40) * Player.dexterity_exp_mult;
if (Math.random() <= chance) {
return [true, 1];
} else {

@ -41,7 +41,8 @@ import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
sellStock, updateStockPrices, displayStockMarketContent,
updateStockTicker, updateStockPlayerPosition,
Stock} from "./StockMarket.js";
Stock, shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket.js";
import {post} from "./Terminal.js";
import {TextFile, getTextFile, createTextFile} from "./TextFile.js";
@ -59,8 +60,8 @@ import {printArray, powerOfTwo} from "../utils/HelperFunctio
import {createRandomIp} from "../utils/IPAddress.js";
import {formatNumber, isString, isHTML} from "../utils/StringHelperFunctions.js";
var hasSingularitySF = false, hasAISF = false, hasBn11SF = false;
var singularitySFLvl = 1;
var hasSingularitySF=false, hasAISF=false, hasBn11SF=false, hasWallStreetSF=false;
var singularitySFLvl=1, wallStreetSFLvl=1;
//Used to check and set flags for every Source File, despite the name of the function
function initSingularitySFFlags() {
@ -72,6 +73,10 @@ function initSingularitySFFlags() {
if (Player.sourceFiles[i].n === 5) {
hasAISF = true;
}
if (Player.sourceFiles[i].n === 8) {
hasWallStreetSF = true;
wallStreetSFLvl = Player.sourceFiles[i].lvl;
}
if (Player.sourceFiles[i].n === 11) {
hasBn11SF = true;
}
@ -845,7 +850,7 @@ function NetscriptFunctions(workerScript) {
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
}
return [stock.playerShares, stock.playerAvgPx];
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
},
buyStock : function(symbol, shares) {
if (!Player.hasTixApiAccess) {
@ -853,9 +858,9 @@ function NetscriptFunctions(workerScript) {
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into buyStock()");
}
if (stock == null || shares < 0 || isNaN(shares)) {
if (shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to buyStock()");
return false;
}
@ -888,10 +893,9 @@ function NetscriptFunctions(workerScript) {
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into sellStock()");
}
if (stock == null || shares < 0 || isNaN(shares)) {
if (shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to sellStock()");
return false;
}
@ -919,6 +923,121 @@ function NetscriptFunctions(workerScript) {
"$" + formatNumber(gains, 2));
return true;
},
shortStock(symbol, shares) {
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use shortStock()");
}
if (Player.bitNodeN !== 8) {
if (!(hasWallStreetSF && wallStreetSFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use shortStock(). You must either be in BitNode-8 or you must have Level 2 of Source-File 8");
}
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into shortStock()");
}
return shortStock(stock, shares, workerScript);
},
sellShort(symbol, shares) {
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use sellShort()");
}
if (Player.bitNodeN !== 8) {
if (!(hasWallStreetSF && wallStreetSFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use sellShort(). You must either be in BitNode-8 or you must have Level 2 of Source-File 8");
}
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into sellShort()");
}
return sellShort(stock, shares, workerScript);
},
placeOrder(symbol, shares, price, type, pos) {
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use placeOrder()");
}
if (Player.bitNodeN !== 8) {
if (!(hasWallStreetSF && wallStreetSFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use placeOrder(). You must either be in BitNode-8 or have Level 3 of Source-File 8");
}
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into placeOrder()");
}
var orderType, orderPos;
type = type.toLowerCase();
if (type.includes("limit") && type.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (type.includes("limit") && type.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (type.includes("stop") && type.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (type.includes("stop") && type.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid Order Type passed into placeOrder()");
}
pos = pos.toLowerCase();
if (pos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (pos.includes('s')) {
orderPos = PositionTypes.Short;
} else {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid Position Type passed into placeOrder()");
}
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
},
cancelOrder(symbol, shares, price, type, pos) {
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use cancelOrder()");
}
if (Player.bitNodeN !== 8) {
if (!(hasWallStreetSF && wallStreetSFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Cannot use cancelOrder(). You must either be in BitNode-8 or have Level 3 of Source-File 8");
}
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into cancelOrder()");
}
if (isNaN(shares) || isNaN(price)) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid shares or price argument passed into cancelOrder(). Must be numeric");
}
var orderType, orderPos;
type = type.toLowerCase();
if (type.includes("limit") && type.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (type.includes("limit") && type.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (type.includes("stop") && type.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (type.includes("stop") && type.includes("sell")) {
orderType = OrderType.StopSell;
} else {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid Order Type passed into placeOrder()");
}
pos = pos.toLowerCase();
if (pos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (pos.includes('s')) {
orderPos = PositionTypes.Short;
} else {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid Position Type passed into placeOrder()");
}
params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos
};
return cancelOrder(params, workerScript);
},
purchaseServer : function(hostname, ram) {
var hostnameStr = String(hostname);
hostnameStr = hostnameStr.replace(/\s\s+/g, '');
@ -1979,6 +2098,30 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log(txt);
}
//Set Location to slums
switch(Player.city) {
case Locations.Aevum:
Player.location = Locations.AevumSlums;
break;
case Locations.Chongqing:
Player.location = Locations.ChongqingSlums;
break;
case Locations.Sector12:
Player.location = Locations.Sector12Slums;
break;
case Locations.NewTokyo:
Player.location = Locations.NewTokyoSlums;
break;
case Locations.Ishima:
Player.location = Locations.IshimaSlums;
break;
case Locations.Volhaven:
Player.location = Locations.VolhavenSlums;
break;
default:
console.log("Invalid Player.city value");
}
crime = crime.toLowerCase();
if (crime.includes("shoplift")) {
workerScript.scriptRef.log("Attempting to shoplift...");
@ -2190,4 +2333,5 @@ function NetscriptFunctions(workerScript) {
}
}
export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF};
export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF, hasWallStreetSF,
wallStreetSFLvl};

@ -13,7 +13,7 @@ import {Factions, Faction,
displayFactionContent} from "./Faction.js";
import {Gang, resetGangs} from "./Gang.js";
import {Locations} from "./Location.js";
import {hasBn11SF} from "./NetscriptFunctions.js";
import {hasBn11SF, hasWallStreetSF} from "./NetscriptFunctions.js";
import {AllServers, Server, AddToAllServers} from "./Server.js";
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps.js";
import {SourceFiles, applySourceFile} from "./SourceFile.js";
@ -367,6 +367,13 @@ PlayerObject.prototype.prestigeSourceFile = function() {
this.hasWseAccount = false;
this.hasTixApiAccess = false;
//BitNode 8: Ghost of Wall Street
if (this.bitNodeN === 8) {this.money = new Decimal(100000000);}
if (this.bitNodeN === 8 || hasWallStreetSF) {
this.hasWseAccount = true;
this.hasTixApiAccess = true;
}
this.playtimeSinceLastAug = 0;
this.scriptProdSinceLastAug = 0;
}

@ -9,7 +9,7 @@ import {Factions, Faction, initFactions,
joinFaction} from "./Faction.js";
import {Locations} from "./Location.js";
import {initMessages, Messages, Message} from "./Message.js";
import {initSingularitySFFlags} from "./NetscriptFunctions.js";
import {initSingularitySFFlags, hasWallStreetSF}from "./NetscriptFunctions.js";
import {WorkerScript, workerScripts,
prestigeWorkerScripts} from "./NetscriptWorker.js";
import {Player} from "./Player.js";
@ -116,6 +116,13 @@ function prestigeAugmentation() {
}
}
//BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) {Player.money = new Decimal(100000000);}
if (Player.bitNodeN === 8 || hasWallStreetSF) {
Player.hasWseAccount = true;
Player.hasTixApiAccess = true;
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
Terminal.resetTerminalInput();

@ -209,7 +209,7 @@ function loadBitVerse(destroyedBitNodeNum) {
var elemId = "bitnode-" + i.toString();
var elem = clearEventListeners(elemId);
if (elem == null) {return;}
if (i === 1 || i === 2 || i === 4 || i === 5 || i === 11) {
if (i === 1 || i === 2 || i === 4 || i === 5 || i === 8 || i === 11) {
elem.addEventListener("click", function() {
var bitNodeKey = "BitNode" + i;
var bitNode = BitNodes[bitNodeKey];

@ -82,13 +82,15 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
}
request.onsuccess = function(e) {
console.log("Successfully saved game to IndexedDB!");
console.log("Saved game to IndexedDB!");
}
try {
window.localStorage.setItem("bitburnerSave", saveString);
console.log("Saved game to LocalStorage!");
} catch(e) {
if (e.code == 22) {
Engine.createStatusText("Save failed for localStorage! Check console(F12)");
console.log("Failed to save game to localStorage because the size of the save file " +
"is too large. However, the game will still be saved to IndexedDb if your browser " +
"supports it. If you would like to save to localStorage as well, then " +
@ -97,7 +99,6 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
}
}
console.log("Game saved!");
Engine.createStatusText("Game saved!");
}

@ -286,7 +286,11 @@ function calculateRamUsage(codeCopy) {
var scriptGetStockCount = numOccurrences(codeCopy, "getStockPrice(") +
numOccurrences(codeCopy, "getStockPosition(");
var scriptBuySellStockCount = numOccurrences(codeCopy, "buyStock(") +
numOccurrences(codeCopy, "sellStock(");
numOccurrences(codeCopy, "sellStock(") +
numOccurrences(codeCopy, "shortStock(") +
numOccurrences(codeCopy, "sellShort(") +
numOccurrences(codeCopy, "placeOrder(") +
numOccurrences(codeCopy, "cancelOrder(");
var scriptPurchaseServerCount = numOccurrences(codeCopy, "purchaseServer(") +
numOccurrences(codeCopy, "deleteServer(") +
numOccurrences(codeCopy, "getPurchasedServers(");
@ -307,7 +311,8 @@ function calculateRamUsage(codeCopy) {
numOccurrences(codeCopy, "travelToCity(") +
numOccurrences(codeCopy, "purchaseTor(") +
numOccurrences(codeCopy, "purchaseProgram(") +
numOccurrences(codeCopy, "getStats(");
numOccurrences(codeCopy, "getStats(") +
numOccurrences(codeCopy, "isBusy(");
var singFn2Count = numOccurrences(codeCopy, "upgradeHomeRam(") +
numOccurrences(codeCopy, "getUpgradeHomeRamCost(") +
numOccurrences(codeCopy, "workForCompany(") +
@ -319,6 +324,7 @@ function calculateRamUsage(codeCopy) {
numOccurrences(codeCopy, "getFactionRep(");
var singFn3Count = numOccurrences(codeCopy, "createProgram(") +
numOccurrences(codeCopy, "commitCrime(") +
numOccurrences(codeCopy, "getCrimeChance(") +
numOccurrences(codeCopy, "getOwnedAugmentations(") +
numOccurrences(codeCopy, "getAugmentationsFromFaction(") +
numOccurrences(codeCopy, "getAugmentationCost(") +

@ -44,7 +44,12 @@ function initSourceFiles() {
"Level 3: 7%");
SourceFiles["SourceFile6"] = new SourceFile(6);
SourceFiles["SourceFile7"] = new SourceFile(7);
SourceFiles["SourceFile8"] = new SourceFile(8);
SourceFiles["SourceFile8"] = new SourceFile(8, "This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanent access to WSE and TIX API<br>" +
"Level 2: Ability to short stocks in other BitNodes<br>" +
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"This Source-File also increases your hacking growth multipliers by: " +
"<br>Level 1: 8%<br>Level 2: 12%<br>Level 3: 14%");
SourceFiles["SourceFile9"] = new SourceFile(9);
SourceFiles["SourceFile10"] = new SourceFile(10);
SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
@ -131,6 +136,14 @@ function applySourceFile(srcFile) {
Player.hacking_mult *= incMult;
Player.hacking_exp_mult *= incMult;
break;
case 8: //Ghost of Wall Street
var mult = 0;
for (var i = 0; i < srcFile.lvl; ++i) {
mult += (8 / (Math.pow(2, i)));
}
var incMult = 1 + (mult / 100);
Player.hacking_grow_mult *= incMult;
break;
case 11: //The Big Crash
var mult = 0;
for (var i = 0; i < srcFile.lvl; ++i) {

@ -1,6 +1,7 @@
import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {Locations} from "./Location.js";
import {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions.js";
import {WorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js";
@ -10,6 +11,11 @@ import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber} from "../utils/StringHelperFunctions.js";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton,
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxGetInput, yesNoBoxClose,
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox.js";
/* StockMarket.js */
function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
@ -24,6 +30,8 @@ function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
this.mv = mv;
this.b = b;
this.otlkMag = otlkMag;
this.posTxtEl = null;
}
Stock.prototype.toJSON = function() {
@ -36,9 +44,149 @@ Stock.fromJSON = function(value) {
Reviver.constructors.Stock = Stock;
//Order types (long and short for each):
// - Limit Order
// - Stop Order
var OrderTypes = {
LimitBuy: "Limit Buy Order",
LimitSell: "Limit Sell Order",
StopBuy: "Stop Buy Order",
StopSell: "Stop Sell Order"
}
var PositionTypes = {
Long: "L",
Short: "S"
}
function placeOrder(stock, shares, price, type, position, workerScript=null) {
var tixApi = (workerScript instanceof WorkerScript);
var order = new Order(stock, shares, price, type, position);
if (isNaN(shares) || isNaN(price)) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
} else {
dialogBoxCreate("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
}
return false;
}
if (StockMarket["Orders"] === 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;
}
StockMarket["Orders"][stock.symbol].push(order);
//Process to see if it should be executed immediately
processOrders(order.stock, order.type, order.pos);
updateStockOrderList(order.stock);
return true;
}
//Returns true if successfully cancels an order, false otherwise
function cancelOrder(params, workerScript=null) {
var tixApi = (workerScript instanceof WorkerScript);
if (StockMarket["Orders"] === null) {return false;}
if (params.order && params.order instanceof Order) {
var order = params.order;
//An 'Order' object is passed in
var stockOrders = StockMarket["Orders"][order.stock.symbol];
for (var i = 0; i < stockOrders.length; ++i) {
if (order == stockOrders[i]) {
stockOrders.splice(i, 1);
updateStockOrderList(order.stock);
return true;
}
}
return false;
} else if (params.stock && params.shares && params.price && params.type &&
params.pos && params.stock instanceof Stock) {
//Order properties are passed in. Need to look for the order
var stockOrders = StockMarket["Orders"][params.stock.symbol];
var orderTxt = params.stock.symbol + " - " + params.shares + " @ " +
numeral(params.price).format('$0.000a');
for (var i = 0; i < stockOrders.length; ++i) {
var order = stockOrders[i];
if (params.shares === order.shares &&
params.price === order.price &&
params.type === order.type &&
params.pos === order.pos) {
stockOrders.splice(i, 1);
updateStockOrderList(order.stock);
if (tixApi) {
workerScript.scriptRef.log("Successfully cancelled order: " + orderTxt);
}
return true;
}
}
if (tixApi) {
workerScript.scriptRef.log("Failed to cancel order: " + orderTxt);
}
return false;
}
return false;
}
function executeOrder(order) {
var stock = order.stock;
var orderBook = StockMarket["Orders"];
var stockOrders = orderBook[stock.symbol];
var res = true;
console.log("Executing the following order:");
console.log(order);
switch (order.type) {
case OrderTypes.LimitBuy:
case OrderTypes.StopBuy:
if (order.pos === PositionTypes.Long) {
res = buyStock(order.stock, order.shares) && res;
} else if (order.pos === PositionTypes.Short) {
res = shortStock(order.stock, order.shares) && res;
}
break;
case OrderTypes.LimitSell:
case OrderTypes.StopSell:
if (order.pos === PositionTypes.Long) {
res = sellStock(order.stock, order.shares) && res;
} else if (order.pos === PositionTypes.Short) {
res = sellShort(order.stock, order.shares) && res;
}
break;
}
if (res) {
//Remove order from order book
for (var i = 0; i < stockOrders.length; ++i) {
if (order == stockOrders[i]) {
stockOrders.splice(i, 1);
updateStockOrderList(order.stock);
return;
}
}
console.log("ERROR: Could not find the following Order in Order Book: ");
console.log(order);
} else {
console.log("Order failed to execute");
}
}
function Order(stock, shares, price, type, position) {
this.stock = stock;
this.shares = shares;
this.price = price;
this.type = type;
this.pos = position;
}
Order.prototype.toJSON = function() {
return Generic_toJSON("Order", this);
}
Order.fromJSON = function(value) {
return Generic_fromJSON(Order, value.data);
}
Reviver.constructors.Order = Order;
let StockMarket = {} //Full name to stock object
let StockSymbols = {} //Full name to symbol
@ -179,7 +327,7 @@ function initStockMarket() {
StockMarket[watchdog] = watchdogStk;
var lexocorp = Locations.VolhavenLexoCorp;
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], 1.25, true, 3, getRandomInt(5000, 7500));
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], 1.25, true, 6, getRandomInt(5000, 7500));
StockMarket[lexocorp] = lexocorpStk;
var rho = Locations.AevumRhoConstruction;
@ -187,15 +335,15 @@ function initStockMarket() {
StockMarket[rho] = rhoStk;
var alpha = Locations.Sector12AlphaEnterprises;
var alphaStk = new Stock(alpha, StockSymbols[alpha], 1.05, true, 2, getRandomInt(5000, 7500));
var alphaStk = new Stock(alpha, StockSymbols[alpha], 2, true, 10, getRandomInt(5000, 7500));
StockMarket[alpha] = alphaStk;
var syscore = Locations.VolhavenSysCoreSecurities;
var syscoreStk = new Stock(syscore, StockSymbols[syscore], 1.25, true, 0, getRandomInt(4000, 7000))
var syscoreStk = new Stock(syscore, StockSymbols[syscore], 1.25, true, 2, getRandomInt(4000, 7000))
StockMarket[syscore] = syscoreStk;
var computek = Locations.VolhavenCompuTek;
var computekStk = new Stock(computek, StockSymbols[computek], 0.9, true, 0, getRandomInt(2000, 5000));
var computekStk = new Stock(computek, StockSymbols[computek], 0.9, true, 2, getRandomInt(2000, 5000));
StockMarket[computek] = computekStk;
var netlink = Locations.AevumNetLinkTechnologies;
@ -211,15 +359,15 @@ function initStockMarket() {
StockMarket[fns] = fnsStk;
var sigmacosm = "Sigma Cosmetics";
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 0.9, true, 0, getRandomInt(2000, 3000));
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 3, true, 0, getRandomInt(2000, 3000));
StockMarket[sigmacosm] = sigmacosmStk;
var joesguns = "Joes Guns";
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 1, true, 1, getRandomInt(500, 1000));
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 4, true, 1, getRandomInt(500, 1000));
StockMarket[joesguns] = joesgunsStk;
var catalyst = "Catalyst Ventures";
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.25, true, 0, getRandomInt(1000, 1500));
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.6, true, 20, getRandomInt(500, 1000));
StockMarket[catalyst] = catalystStk;
var microdyne = "Microdyne Technologies";
@ -229,6 +377,16 @@ function initStockMarket() {
var titanlabs = "Titan Laboratories";
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], 0.6, true, 11, getRandomInt(15000, 20000));
StockMarket[titanlabs] = titanlabsStk;
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;
}
function initSymbolToStockMap() {
@ -250,8 +408,8 @@ function stockMarketCycle() {
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
var thresh = 0.62;
if (stock.b) {thresh = 0.38;}
var thresh = 0.6;
if (stock.b) {thresh = 0.4;}
if (Math.random() < thresh) {
stock.b = !stock.b;
}
@ -409,6 +567,7 @@ function updateStockPrices() {
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;}
var av = (v * stock.mv) / 100;
if (isNaN(av)) {av = .02;}
@ -424,11 +583,19 @@ function updateStockPrices() {
var c = Math.random();
if (c < chc) {
stock.price *= (1 + av);
processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Short);
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long);
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long);
processOrders(stock, OrderTypes.StopSell, PositionTypes.Short);
if (Engine.currentPage == Engine.Page.StockMarket) {
updateStockTicker(stock, true);
}
} else {
stock.price /= (1 + av);
processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Long);
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short);
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short);
processOrders(stock, OrderTypes.StopSell, PositionTypes.Long);
if (Engine.currentPage == Engine.Page.StockMarket) {
updateStockTicker(stock, false);
}
@ -447,6 +614,68 @@ function updateStockPrices() {
stock.otlkMag *= -1;
stock.b = !stock.b;
}
}
}
}
//Checks and triggers any orders for the specified stock
function processOrders(stock, orderType, posType) {
var orderBook = StockMarket["Orders"];
if (orderBook === null) {
var orders = {};
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;}
orders[stock.symbol] = [];
}
}
StockMarket["Orders"] = orders;
return; //Newly created, so no orders to process
}
var stockOrders = orderBook[stock.symbol];
if (stockOrders === null || !(stockOrders.constructor === Array)) {
console.log("ERROR: Invalid Order book for " + stock.symbol + " in processOrders()");
stockOrders = [];
return;
}
for (var i = 0; i < stockOrders.length; ++i) {
var order = stockOrders[i];
if (order.type === orderType && order.pos === posType) {
switch(order.type) {
case OrderTypes.LimitBuy:
if (order.pos === PositionTypes.Long && stock.price <= order.price) {
executeOrder/*66*/(order);
} else if (order.pos === PositionTypes.Short && stock.price >= order.price) {
executeOrder/*66*/(order);
}
break;
case OrderTypes.LimitSell:
if (order.pos === PositionTypes.Long && stock.price >= order.price) {
executeOrder/*66*/(order);
} else if (order.pos === PositionTypes.Short && stock.price <= order.price) {
executeOrder/*66*/(order);
}
break;
case OrderTypes.StopBuy:
if (order.pos === PositionTypes.Long && stock.price >= order.price) {
executeOrder/*66*/(order);
} else if (order.pos === PositionTypes.Short && stock.price <= order.price) {
executeOrder/*66*/(order);
}
break;
case OrderTypes.StopSell:
if (order.pos === PositionTypes.Long && stock.price <= order.price) {
executeOrder/*66*/(order);
} else if (order.pos === PositionTypes.Short && stock.price >= order.price) {
executeOrder/*66*/(order);
}
break;
default:
console.log("Invalid order type: " + order.type);
return;
}
}
}
}
@ -493,6 +722,51 @@ 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;}
@ -514,138 +788,14 @@ function displayStockMarketContent() {
"This means all your positions are lost, so make sure to sell your stocks before installing " +
"Augmentations!";
var hdrLi = document.createElement("li");
var hdrName = document.createElement("p");
var hdrSym = document.createElement("p");
var hdrPrice = document.createElement("p");
var hdrQty = document.createElement("p");
var hdrBuySell = document.createElement("p")
var hdrAvgPrice = document.createElement("p");
var hdrShares = document.createElement("p");
var hdrReturn = document.createElement("p");
hdrName.style.display = "inline-block";
hdrName.innerText = "Stock Name";
hdrName.style.width = "8%";
hdrSym.style.display = "inline-block";
hdrSym.innerText = "Symbol";
hdrSym.style.width = "4%";
hdrPrice.style.display = "inline-block";
hdrPrice.innerText = "Price";
hdrPrice.style.width = "8%";
hdrQty.style.display = "inline-block";
hdrQty.innerText = "Quantity";
hdrQty.style.width = "3%";
hdrBuySell.style.display = "inline-block";
hdrBuySell.innerText = "Buy/Sell";
hdrBuySell.style.width = "5%";
hdrAvgPrice.style.display = "inline-block";
hdrAvgPrice.innerText = "Avg price of owned shares";
hdrAvgPrice.style.width = "7.5%";
hdrShares.style.display = "inline-block";
hdrShares.innerText = "Shares owned";
hdrShares.style.width = "4%";
hdrReturn.style.display = "inline-block";
hdrReturn.innerText = "Total Return";
hdrReturn.style.width = "6%";
hdrLi.appendChild(hdrName);
hdrLi.appendChild(hdrSym);
hdrLi.appendChild(hdrPrice);
hdrLi.appendChild(hdrQty);
hdrLi.appendChild(hdrBuySell);
hdrLi.appendChild(hdrAvgPrice);
hdrLi.appendChild(hdrShares);
hdrLi.appendChild(hdrReturn);
stockList.appendChild(hdrLi);
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
(function() {
var stock = StockMarket[name];
var li = document.createElement("li");
var stkName = document.createElement("p");
var stkSym = document.createElement("p");
var stkPrice = document.createElement("p");
var qtyInput = document.createElement("input");
var buyButton = document.createElement("span");
var sellButton = document.createElement("span");
var avgPriceTxt = document.createElement("p");
var sharesTxt = document.createElement("p");
var returnTxt = document.createElement("p");
var tickerId = "stock-market-ticker-" + stock.symbol;
stkName.setAttribute("id", tickerId + "-name");
stkSym.setAttribute("id", tickerId + "-sym");
stkPrice.setAttribute("id", tickerId + "-price");
stkName.style.display = "inline-block";
stkName.style.width = "8%";
stkSym.style.display = "inline-block";
stkSym.style.width = "4%";
stkPrice.style.display = "inline-block";
stkPrice.style.width = "9%";
li.setAttribute("display", "inline-block");
qtyInput.setAttribute("type", "text");
qtyInput.setAttribute("id", tickerId + "-qty-input");
qtyInput.setAttribute("class", "stock-market-qty-input");
qtyInput.setAttribute("onkeydown", "return ( event.ctrlKey || event.altKey " +
" || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) " +
" || (95<event.keyCode && event.keyCode<106) " +
" || (event.keyCode==8) || (event.keyCode==9) " +
" || (event.keyCode>34 && event.keyCode<40) " +
" || (event.keyCode==46) )");
qtyInput.style.width = "3%";
qtyInput.style.display = "inline-block";
buyButton.innerHTML = "Buy";
buyButton.setAttribute("class", "stock-market-buy-sell-button");
buyButton.style.width = "3%";
buyButton.style.display = "inline-block";
buyButton.addEventListener("click", function() {
var shares = document.getElementById(tickerId + "-qty-input").value;
shares = Number(shares);
if (isNaN(shares)) {return false;}
buyStock(stock, shares);
});
sellButton.innerHTML = "Sell";
sellButton.setAttribute("class", "stock-market-buy-sell-button");
sellButton.style.width = "3%";
sellButton.style.display = "inline-block";
sellButton.addEventListener("click", function() {
var shares = document.getElementById(tickerId + "-qty-input").value;
shares = Number(shares);
if (isNaN(shares)) {return false;}
sellStock(stock, shares);
});
avgPriceTxt.setAttribute("id", tickerId + "-avgprice");
avgPriceTxt.style.display = "inline-block";
avgPriceTxt.style.width = "8%";
avgPriceTxt.style.color = "white";
sharesTxt.setAttribute("id", tickerId + "-shares");
sharesTxt.style.display = "inline-block";
sharesTxt.style.width = "4%";
sharesTxt.style.color = "white";
returnTxt.setAttribute("id", tickerId + "-return");
returnTxt.style.display = "inline-block";
returnTxt.style.width = "6%";
returnTxt.style.color = "white";
li.appendChild(stkName);
li.appendChild(stkSym);
li.appendChild(stkPrice);
li.appendChild(qtyInput);
li.appendChild(buyButton);
li.appendChild(sellButton);
li.appendChild(avgPriceTxt);
li.appendChild(sharesTxt);
li.appendChild(returnTxt);
stockList.appendChild(li);
}()); //Immediate invocation
}//End if
if (!(stock instanceof Stock)) {continue;} //orders property is an array
createStockTicker(stock);
}
}
setStockTickerClickHandlers(); //Clicking headers opens/closes panels
stockMarketContentCreated = true;
}
@ -653,8 +803,194 @@ function displayStockMarketContent() {
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
updateStockTicker(stock, true);
updateStockTicker(stock, null);
updateStockPlayerPosition(stock);
updateStockOrderList(stock);
}
}
}
}
function createStockTicker(stock) {
if (!(stock instanceof Stock)) {
console.log("Invalid stock in createStockSticker()");
return;
}
var tickerId = "stock-market-ticker-" + stock.symbol;
var li = document.createElement("li"), hdr = document.createElement("button");
hdr.classList.add("accordion-header");
hdr.setAttribute("id", tickerId + "-hdr");
hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + stock.price;
//Div for entire panel
var stockDiv = document.createElement("div");
stockDiv.classList.add("accordion-panel");
/* Create panel DOM */
var qtyInput = document.createElement("input"),
longShortSelect = document.createElement("select"),
orderTypeSelect = document.createElement("select"),
buyButton = document.createElement("span"),
sellButton = document.createElement("span"),
positionTxt = document.createElement("p"),
orderList = document.createElement("ul");
qtyInput.classList.add("stock-market-input");
qtyInput.placeholder = "Quantity (Shares)";
qtyInput.setAttribute("id", tickerId + "-qty-input");
qtyInput.setAttribute("onkeydown", "return ( event.ctrlKey || event.altKey " +
" || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) " +
" || (95<event.keyCode && event.keyCode<106) " +
" || (event.keyCode==8) || (event.keyCode==9) " +
" || (event.keyCode>34 && event.keyCode<40) " +
" || (event.keyCode==46) )");
longShortSelect.classList.add("stock-market-input");
longShortSelect.setAttribute("id", tickerId + "-pos-selector");
var longOpt = document.createElement("option");
longOpt.text = "Long";
longShortSelect.add(longOpt);
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
var shortOpt = document.createElement("option");
shortOpt.text = "Short";
longShortSelect.add(shortOpt);
}
orderTypeSelect.classList.add("stock-market-input");
orderTypeSelect.setAttribute("id", tickerId + "-order-selector");
var marketOpt = document.createElement("option");
marketOpt.text = "Market Order";
orderTypeSelect.add(marketOpt);
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 3)) {
var limitOpt = document.createElement("option");
limitOpt.text = "Limit Order";
orderTypeSelect.add(limitOpt);
var stopOpt = document.createElement("option");
stopOpt.text = "Stop Order";
orderTypeSelect.add(stopOpt);
}
buyButton.classList.add("stock-market-input");
buyButton.classList.add("a-link-button");
buyButton.innerHTML = "Buy";
buyButton.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 shares = Number(document.getElementById(tickerId + "-qty-input").value);
if (isNaN(shares)) {return false;}
switch (ordType) {
case "Market Order":
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;
}
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;
});
sellButton.classList.add("stock-market-input");
sellButton.classList.add("a-link-button");
sellButton.innerHTML = "Sell";
sellButton.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 shares = Number(document.getElementById(tickerId + "-qty-input").value);
if (isNaN(shares)) {return false;}
switch (ordType) {
case "Market Order":
pos === PositionTypes.Long ? sellStock(stock, shares) : sellShort(stock, shares, null);
break;
case "Limit Order":
case "Stop Order":
var yesBtn = yesNoTxtInpBoxGetYesButton(),
noBtn = yesNoTxtInpBoxGetNoButton();
yesBtn.innerText = "Place Sell " + ordType;
noBtn.innerText = "Cancel Order";
yesBtn.addEventListener("click", ()=>{
var price = Number(yesNoTxtInpBoxGetInput()), type;
if (ordType === "Limit Order") {
type = OrderTypes.LimitSell;
} else {
type = OrderTypes.StopSell;
}
yesNoTxtInpBoxClose();
placeOrder(stock, shares, price, type, pos);
});
noBtn.addEventListener("click", ()=>{
yesNoTxtInpBoxClose();
});
yesNoTxtInpBoxCreate("Enter the price for your " + ordType);
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;
orderList.setAttribute("id", tickerId + "-order-list");
orderList.classList.add("stock-market-order-list");
stockDiv.appendChild(qtyInput);
stockDiv.appendChild(longShortSelect);
stockDiv.appendChild(orderTypeSelect);
stockDiv.appendChild(buyButton);
stockDiv.appendChild(sellButton);
stockDiv.appendChild(positionTxt);
stockDiv.appendChild(orderList);
li.appendChild(hdr);
li.appendChild(stockDiv);
document.getElementById("stock-market-list").appendChild(li);
updateStockTicker(stock, true);
updateStockOrderList(stock);
}
function setStockTickerClickHandlers() {
var stockList = document.getElementById("stock-market-list");
var tickerHdrs = stockList.getElementsByClassName("accordion-header");
if (tickerHdrs === null) {
console.log("ERROR: Could not find header elements for stock tickers");
return;
}
for (var i = 0; i < tickerHdrs.length; ++i) {
tickerHdrs[i].onclick = function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
}
}
@ -662,57 +998,132 @@ function displayStockMarketContent() {
//'increase' argument is a boolean indicating whether the price increased or decreased
function updateStockTicker(stock, increase) {
var tickerId = "stock-market-ticker-" + stock.symbol;
let stkName = document.getElementById(tickerId + "-name");
let stkSym = document.getElementById(tickerId + "-sym");
let stkPrice = document.getElementById(tickerId + "-price");
if (stkName == null || stkSym == null || stkPrice == null) {
console.log("ERROR, couldn't find elements with tickerId " + tickerId);
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
if (!(stock instanceof Stock)) {
console.log("Invalid stock in updateStockTicker():");
console.log(stock);
return;
}
stkName.innerText = stock.name;
stkSym.innerText = stock.symbol;
stkPrice.innerText = "$" + formatNumber(stock.price, 2).toString();
var tickerId = "stock-market-ticker-" + stock.symbol;
var hdr = document.getElementById(tickerId + "-hdr");
var returnTxt = document.getElementById(tickerId + "-return");
var totalCost = stock.playerShares * stock.playerAvgPx;
var gains = (stock.price - stock.playerAvgPx) * stock.playerShares;
var percentageGains = gains / totalCost;
if (totalCost > 0) {
returnTxt.innerText = "$" + formatNumber(gains, 2) + " (" +
formatNumber(percentageGains * 100, 2) + "%)";
} else {
returnTxt.innerText = "N/A";
if (hdr === null) {
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) {
increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red";
}
if (increase) {
stkName.style.color = "#66ff33";
stkSym.style.color = "#66ff33";
stkPrice.style.color = "#66ff33";
} else {
stkName.style.color = "red";
stkSym.style.color = "red";
stkPrice.style.color = "red";
if (stock.playerShares > 0 || stock.playerShortShares > 0) {
updateStockPlayerPosition(stock);
}
}
function updateStockPlayerPosition(stock) {
var tickerId = "stock-market-ticker-" + stock.symbol;
var avgPriceTxt = document.getElementById(tickerId + "-avgprice");
var sharesTxt = document.getElementById(tickerId + "-shares");
if (avgPriceTxt == null || sharesTxt == null) {
dialogBoxCreate("Could not find element for player positions for stock " +
stock.symbol + ". This is a bug please contact developer");
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
if (!(stock instanceof Stock)) {
console.log("Invalid stock in updateStockPlayerPosition():");
console.log(stock);
return;
}
avgPriceTxt.innerText = "$" + formatNumber(stock.playerAvgPx, 2);
sharesTxt.innerText = stock.playerShares.toString();
var tickerId = "stock-market-ticker-" + stock.symbol;
if (!(stock.posTxtEl instanceof Element)) {
stock.posTxtEl = document.getElementById(tickerId + "-position-text");
}
if (stock.posTxtEl === null) {
console.log("ERROR: Could not find stock position element for: " + stock.symbol);
return;
}
//Calculate returns
var totalCost = stock.playerShares * stock.playerAvgPx,
gains = (stock.price - stock.playerAvgPx) * stock.playerShares,
percentageGains = gains / totalCost;
if (isNaN(percentageGains)) {percentageGains = 0;}
var shortTotalCost = stock.playerShortShares * stock.playerAvgShortPx,
shortGains = (stock.playerAvgShortPx - stock.price) * stock.playerShortShares,
shortPercentageGains = shortGains/ shortTotalCost;
if (isNaN(shortPercentageGains)) {shortPercentageGains = 0;}
stock.posTxtEl.innerHTML =
"<h1 class='tooltip stock-market-position-text'>Long Position: " +
"<span class='tooltiptext'>Shares in the long position will increase " +
"in value if the price of the corresponding stock increases</span></h1>" +
"<br>Shares: " + formatNumber(stock.playerShares, 0) +
"<br>Average Price: " + numeral(stock.playerAvgPx).format('$0.000a') +
" (Total Cost: " + numeral(totalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(gains).format('$0.000a') +
" (" + formatNumber(percentageGains*100, 2) + "%)<br><br>";
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
stock.posTxtEl.innerHTML +=
"<h1 class='tooltip stock-market-position-text'>Short Position: " +
"<span class='tooltiptext'>Shares in short position will increase " +
"in value if the price of the corresponding stock decreases</span></h1>" +
"<br>Shares: " + formatNumber(stock.playerShortShares, 0) +
"<br>Average Price: " + numeral(stock.playerAvgShortPx).format('$0.000a') +
" (Total Cost: " + numeral(shortTotalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(shortGains).format('$0.000a') +
" (" + formatNumber(shortPercentageGains*100, 2) + "%)" +
"<br><br><h1 class='stock-market-position-text'>Orders: </h1>";
}
}
function updateStockOrderList(stock) {
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
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);
return;
}
var orderBook = StockMarket["Orders"];
if (orderBook === null) {
console.log("ERROR: Could not find order book in stock market");
return;
}
var stockOrders = orderBook[stock.symbol];
if (stockOrders === null) {
console.log("ERROR: Could not find orders for: " + stock.symbol);
return;
}
//Remove everything from list
while (orderList.firstChild) {
orderList.removeChild(orderList.firstChild);
}
for (var i = 0; i < stockOrders.length; ++i) {
(function() {
var order = stockOrders[i];
var li = document.createElement("li");
li.style.padding = "4px";
var posText = (order.pos === PositionTypes.Long ? "Long Position" : "Short Position");
li.style.color = "white";
li.innerText = order.type + " - " + posText + " - " +
order.shares + " @ $" + formatNumber(order.price, 2);
var cancelButton = document.createElement("span");
cancelButton.classList.add("stock-market-order-cancel-btn");
cancelButton.classList.add("a-link-button");
cancelButton.innerHTML = "Cancel Order";
cancelButton.addEventListener("click", function() {
cancelOrder({order: order}, null);
return false;
});
li.appendChild(cancelButton);
orderList.appendChild(li);
}());
}
}
export {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
sellStock, updateStockPrices, displayStockMarketContent,
sellStock, shortStock, sellShort, updateStockPrices, displayStockMarketContent,
updateStockTicker, updateStockPlayerPosition, loadStockMarket,
setStockMarketContentCreated};
setStockMarketContentCreated, placeOrder, cancelOrder, Order, OrderTypes, PositionTypes};

78
src/TextFile.js Normal file

@ -0,0 +1,78 @@
import {Server} from "./Server.js";
import {dialogBoxCreate} from "../utils/DialogBox.js";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js";
function TextFile(fn="", txt="") {
this.fn = fn.endsWith(".txt") ? fn : fn + ".txt";
this.text = String(txt);
}
TextFile.prototype.append = function(txt) {
this.text += String(txt);
}
TextFile.prototype.write = function(txt) {
this.text = String(txt);
}
TextFile.prototype.read = function() {
return this.txt;
}
TextFile.prototype.show = function() {
dialogBoxCreate(this.fn + "<br><br>" + this.text);
}
TextFile.prototype.download = function() {
var filename = this.fn;
var file = new Blob([this.text], {type: 'text/plain'});
if (window.navigator.msSaveOrOpenBlob) {// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = this.fn;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
TextFile.prototype.toJSON = function() {
return Generic_toJSON("TextFile", this);
}
TextFile.fromJSON = function(value) {
return Generic_fromJSON(TextFile, value.data);
}
Reviver.constructors.TextFile = TextFile;
function getTextFile(fn, server) {
if (!fn.endsWith(".txt")) {fn += ".txt";}
for (var i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === fn) {
return server.textFiles[i];
}
}
return null;
}
//Returns the TextFile object that was just created
function createTextFile(fn, txt, server) {
if (getTextFile(fn, server) !== null) {
console.log("ERROR: createTextFile failed because the specified " +
"server already has a text file with the same fn");
return;
}
var file = new TextFile(fn, txt);
server.textFiles.push(file);
return file;
}
export {TextFile, getTextFile, createTextFile};

@ -376,8 +376,8 @@ let Engine = {
loadStockMarketContent: function() {
Engine.hideAllContent();
Engine.Display.stockMarketContent.style.visibility = "visible";
displayStockMarketContent();
Engine.currentPage = Engine.Page.StockMarket;
displayStockMarketContent();
},
loadGangContent: function() {
@ -988,7 +988,6 @@ let Engine = {
}
if (Engine.Counters.sCr <= 0) {
//Assume 4Sig will always indicate state of market
if (Player.hasWseAccount) {
stockMarketCycle();
}

@ -31,6 +31,7 @@ function infiltrationBoxCreate(inst) {
Player.gainDexterityExp(inst.dexExpGained);
Player.gainAgilityExp(inst.agiExpGained);
Player.gainCharismaExp(inst.chaExpGained);
Player.gainIntelligenceExp(inst.intExpGained);
var totalValue = 0;
for (var i = 0; i < inst.secretsStolen.length; ++i) {