This commit is contained in:
danielyxie 2018-11-06 22:05:55 -06:00
commit 8fbf673719
31 changed files with 1128 additions and 168336 deletions

@ -0,0 +1,89 @@
@import "mixins";
@import "theme";
/**
* Styling for the Character Overview Panel (top-right)
*/
#character-overview-wrapper {
position: relative;
}
#character-overview-container {
display: none;
position: absolute; /* Stay in place */
right: 0;
top: 0;
height: auto; /* Full height */
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: auto;
max-width: 280px;
overflow: auto; /* Enable scroll if needed */
background-color: rgba(57, 54, 54, 0.9); /* Fallback color */
z-index: 1;
}
#character-overview-text {
color: $my-stat-physical;
table {
border-collapse: collapse;
margin: auto;
}
td {
padding: 2px;
vertical-align: middle;
}
}
.character-stat-text {
color: #fff;
background-color: #444;
}
.character-stat-cell {
text-align: right;
}
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px;
}
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px;
}
#character-hp-wrapper { color: $my-stat-hp-color; }
#character-money-wrapper { color: $my-stat-money-color; }
#character-hack-wrapper { color: $my-stat-hack-color; }
#character-cha-wrapper { color: $my-stat-cha-color; }
#character-int-wrapper { color: $my-stat-int-color; }
.character-overview-btn {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #cecece;
display: inline-block;
font-size: $defaultFontSize * 0.875;
font-weight: bold;
height: 25px;
background-color: #000;
padding: 5px 8px;
}
.character-quick-options {
margin-top: 10px;
text-align: center;
}
.character-overview-btn:hover,
.character-overview-btn:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}

@ -296,93 +296,6 @@ a:visited {
width: auto; width: auto;
} }
/* Character Overview */
#character-overview-wrapper {
position: relative;
}
#character-overview-container {
display: none;
position: absolute; /* Stay in place */
right: 0;
top: 0;
height: auto; /* Full height */
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: auto;
max-width: 280px;
overflow: auto; /* Enable scroll if needed */
background-color: rgba(57, 54, 54, 0.9); /* Fallback color */
z-index: 1;
}
#character-overview-text {
color: $my-stat-physical;
table {
border-collapse: collapse;
margin: auto;
}
td {
padding: 2px;
vertical-align: middle;
}
}
.character-stat-text {
color: #fff;
background-color: #444;
}
.character-stat-cell {
text-align: right;
}
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px;
}
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px;
}
#character-hp-wrapper { color: $my-stat-hp-color; }
#character-money-wrapper { color: $my-stat-money-color; }
#character-hack-wrapper { color: $my-stat-hack-color; }
#character-cha-wrapper { color: $my-stat-cha-color; }
#character-int-wrapper { color: $my-stat-int-color; }
#character-overview-save-button,
#character-overview-options-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #cecece;
display: inline-block;
font-size: $defaultFontSize * 0.875;
font-weight: bold;
height: 25px;
background-color: #000;
padding: 5px 8px;
}
.character-quick-options {
margin-top: 10px;
text-align: center;
}
#character-overview-save-button:hover,
#character-overview-save-button:focus,
#character-overview-options-button:hover,
#character-overview-options-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
/* Scan analyze links from AutoLink */ /* Scan analyze links from AutoLink */
.scan-analyze-link { .scan-analyze-link {
cursor: pointer; cursor: pointer;

56961
dist/engine.bundle.js vendored

File diff suppressed because one or more lines are too long

181
dist/engine.css vendored

@ -305,96 +305,6 @@ a:visited {
top: 0; top: 0;
width: auto; } width: auto; }
/* Character Overview */
#character-overview-wrapper {
position: relative; }
#character-overview-container {
display: none;
position: absolute;
/* Stay in place */
right: 0;
top: 0;
height: auto;
/* Full height */
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: auto;
max-width: 280px;
overflow: auto;
/* Enable scroll if needed */
background-color: rgba(57, 54, 54, 0.9);
/* Fallback color */
z-index: 1; }
#character-overview-text {
color: #faffdf; }
#character-overview-text table {
border-collapse: collapse;
margin: auto; }
#character-overview-text td {
padding: 2px;
vertical-align: middle; }
.character-stat-text {
color: #fff;
background-color: #444; }
.character-stat-cell {
text-align: right; }
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px; }
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px; }
#character-hp-wrapper {
color: #dd3434; }
#character-money-wrapper {
color: #ffd700; }
#character-hack-wrapper {
color: #adff2f; }
#character-cha-wrapper {
color: #a671d1; }
#character-int-wrapper {
color: #6495ed; }
#character-overview-save-button,
#character-overview-options-button {
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #cecece;
display: inline-block;
font-size: 14px;
font-weight: bold;
height: 25px;
background-color: #000;
padding: 5px 8px; }
.character-quick-options {
margin-top: 10px;
text-align: center; }
#character-overview-save-button:hover,
#character-overview-save-button:focus,
#character-overview-options-button:hover,
#character-overview-options-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
/* Scan analyze links from AutoLink */ /* Scan analyze links from AutoLink */
.scan-analyze-link { .scan-analyze-link {
cursor: pointer; cursor: pointer;
@ -678,6 +588,97 @@ button {
opacity: 1; opacity: 1;
transition: max-height 0.2s ease-out; } transition: max-height 0.2s ease-out; }
/* COLORS */
/* Attributes */
/**
* Styling for the Character Overview Panel (top-right)
*/
#character-overview-wrapper {
position: relative; }
#character-overview-container {
display: none;
position: absolute;
/* Stay in place */
right: 0;
top: 0;
height: auto;
/* Full height */
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: auto;
max-width: 280px;
overflow: auto;
/* Enable scroll if needed */
background-color: rgba(57, 54, 54, 0.9);
/* Fallback color */
z-index: 1; }
#character-overview-text {
color: #faffdf; }
#character-overview-text table {
border-collapse: collapse;
margin: auto; }
#character-overview-text td {
padding: 2px;
vertical-align: middle; }
.character-stat-text {
color: #fff;
background-color: #444; }
.character-stat-cell {
text-align: right; }
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px; }
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px; }
#character-hp-wrapper {
color: #dd3434; }
#character-money-wrapper {
color: #ffd700; }
#character-hack-wrapper {
color: #adff2f; }
#character-cha-wrapper {
color: #a671d1; }
#character-int-wrapper {
color: #6495ed; }
.character-overview-btn {
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #cecece;
display: inline-block;
font-size: 14px;
font-weight: bold;
height: 25px;
background-color: #000;
padding: 5px 8px; }
.character-quick-options {
margin-top: 10px;
text-align: center; }
.character-overview-btn:hover,
.character-overview-btn:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#terminal-container { #terminal-container {

111046
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,66 @@
Changelog Changelog
========= =========
v0.41.1 - 11/5/2018
-------------------
* IMPORTANT - Netscript Changes:
* purchaseTor() now returns true if you already have a TOR router (it used to return false)
* getPurchasedServerCost() now returns Infinity if the specified RAM is an invalid amount or is greater than the max amount of RAM (2 ^ 20 GB)
* Added purchase4SMarketData() and purchase4SMarketDataTixApi() functions
* getScriptLogs() now takes in optional arguments that let you get the logs of another script
* Stock Market changes:
* Stocks now have "maximum prices". These are hidden from the player
* If a stock reaches its "maximum price", it will most likely drop in value (although it might still rise)
* Each stock has its own, unique maximum price
* Maximum price for each stock are randomly generated and change during each 'reset'
* Stock Market cycles are now accumulated/stored, much like it is for Gangs and Bladeburners
* Accumulated/stored cycles cause stock prices to update up to 50% faster (from every 6 seconds to 4 seconds)
* This means that after coming back from being offline, stock prices will update faster to make up for offline time
* Decreased the Hacking Level multiplier for BitNodes 6 and 7 to 0.4 (from 0.5)
* Bladeburner console history is now saved and persists when switching screens or closing/reopening the game
* In Bladeburner, if your stamina reaches 0 your current action will be cancelled
* b1t_flum3.exe is no longer removed from your home computer upon reset
* Added main menu link for the Stock Market (once you've purchased an account)
* Job main menu link only appears if you actually have a job
* Bug Fix: After installing Augs, the "Portfolio Mode" button on the Stock Market page should be properly reset
* Bug Fix: bladeburner.getActionCountRemaining()'s return value is now rounded down (by Kline-)
v0.41.0 - 10/29/2018
--------------------
* WARNING: In NetscriptJS, defining a function called print() is no longer possible
* Gang Mechanic Changes (BitNode-2):
* Added a Gang Netscript API
* Added new 'ascension' mechanic for Gang Members
* The first three gang members are now 'free' (can be recruited instantly)
* Maximum number of increased Gang Members increased from 20 to 30
* Changed the formula for calculating respect needed to recruit the next gang member
* Added a new category of upgrades for Gang Members: Augmentations
* Non-Augmentation Gang member upgrades are now significantly weaker
* Reputation for your Gang faction can no longer be gained through Infiltration
* Re-worked the territory 'warfare' mechanic so that player can choose when to engage in it
* Gang Members can now be killed during territory 'warfare'
* Changed BitNode-2 Multipliers to make hacking slightly less profitable
* Gang Member Equipment + Upgrades now get cheaper as your gang grows in power and respect
* The effects of Source-File 2 are now slightly more powerful
* RAM Cost of accessing the global document object lowered from 100 GB to 25 GB
* RAM Cost to use Singularity Functions outside of BitNode-4 lowered by 75%. They now only cost twice as much as they do in BitNode-4
* b1t_flum3.exe now takes significantly less time to create
* Crimes commited through Singularity function no longer give half money/exp (there is now no penalty)
* Improved number formatting for Player 'work' actions (including crimes, etc.). These numbers should also adhere to locale settings now (by Kline-)
* The order that Augmentations are listed in (when purchasing from Faction and viewing your Augmentations) is now saved and persists when choosing different orders
* getCharacterInformation() Singularity function now returns multiplier information (from Augmentations/Source Files)
* Bug Fix: Calling print() in NetscriptJS no longer brings up the print dialog
* Bug Fix: Fixed a bug that sometimes caused a blank black screen when destroying/resetting/switching BitNodes
* Bug Fix: Netscript calls that throw errors will now no longer cause the 'concurrent calls' error if they are caught in the script. i.e. try/catch should now work properly in scripts
* Bug Fix: Fixed a bug where sometimes the NeuroFlux Governor Augmentation level would be incorrectly calculated when the game was loaded
* Bug Fix: Fixed a bug where calling the scp() Netscript function with invalid hostname/ips would throw an unclear error message
* Bug Fix: Bladeburner API function getActionCountRemaining() should now work properly for BlackOps
* Bug Fix: Black Ops can no longer be attempted out-of-order or without the required rank via Bladeburner API
* Bug Fix: Dynamic RAM Calculation now properly accounts for number of threads
* RAM cost for basic Netscript functions added to documentation (by CBJamo)
v0.40.5 - 10/09/2018 v0.40.5 - 10/09/2018
-------------------- --------------------
* Added codingcontract.getContractType() Netscript function * Added codingcontract.getContractType() Netscript function

@ -153,17 +153,37 @@ isLogEnabled
getScriptLogs getScriptLogs
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
.. js:function:: getScriptLogs() .. js:function:: getScriptLogs([fn], [hostname/ip=current ip], [args...])
:param string fn: Optional. Filename of script to get logs from.
:param string ip: Optional. IP or hostname of the server that the script is on
:param args...: Arguments to identify which scripts to get logs for
:RAM cost: 0 GB :RAM cost: 0 GB
Returns the script's logs. The logs are returned as an array, where each Returns a script's logs. The logs are returned as an array, where each
line is an element in the array. The most recently logged line is at the line is an element in the array. The most recently logged line is at the
end of the array. end of the array.
Note that there is a maximum number of lines that a script stores in its logs. Note that there is a maximum number of lines that a script stores in its logs.
This is configurable in the game's options. This is configurable in the game's options.
If the function is called with no arguments, it will return the current script's logs.
Otherwise, the `fn`, `hostname/ip,` and `args...` arguments can be used to get the logs
from another script. Remember that scripts are uniquely identified by both
their names and arguments.
Examples::
// Get logs from foo.script on the current server that was run with no args
getScriptLogs("foo.script");
// Get logs from foo.script on the foodnstuff server that was run with no args
getScriptLogs("foo.script", "foodnstuff");
// Get logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
getScriptLogs("foo.script", "foodnstuff", 1, "test");
scan scan
^^^^ ^^^^

@ -2,7 +2,7 @@ Netscript Trade Information eXchange (TIX) API
============================================== ==============================================
The Trade Information eXchange (TIX) is the communications protocol supported by the World Stock Exchange (WSE). The Trade Information eXchange (TIX) is the communications protocol supported by the World Stock Exchange (WSE).
The WESE provides an API that allows you to automatically communicate with the The WSE provides an API that allows you to automatically communicate with the
`Stock Market <http://bitburner.wikia.com/wiki/Stock_Market>`_. This API lets you write code using Netscript `Stock Market <http://bitburner.wikia.com/wiki/Stock_Market>`_. This API lets you write code using Netscript
to build automated trading systems and create your own algorithmic trading strategies. Access to this to build automated trading systems and create your own algorithmic trading strategies. Access to this
TIX API can be purchased by visiting the World Stock Exchange in-game. TIX API can be purchased by visiting the World Stock Exchange in-game.
@ -11,7 +11,7 @@ Access to the TIX API currently costs $5 billion. After you purchase it, you wil
access even after you 'reset' by installing Augmentations access even after you 'reset' by installing Augmentations
getStockSymbols getStockSymbols
------------- ---------------
.. js:function:: getStockSymbols() .. js:function:: getStockSymbols()
@ -214,3 +214,23 @@ getStockForecast
In other words, if this function returned 0.30 for a stock, then this means In other words, if this function returned 0.30 for a stock, then this means
that the stock's price has a 30% chance of increasing and a 70% chance of that the stock's price has a 30% chance of increasing and a 70% chance of
decreasing during the next tick. decreasing during the next tick.
purchase4SMarketData
--------------------
.. js:function:: purchase4SMarketData()
Purchase 4S Market Data Access.
Returns true if you successfully purchased it or if you already have access.
Returns false otherwise.
purchase4SMarketDataTixApi
--------------------------
.. js:function:: purchase4SMarketDataTixApi()
Purchase 4S Market Data TIX API Access.
Returns true if you successfully purchased it or if you already have access.
Returns false otherwise.

@ -500,8 +500,6 @@ commitCrime
This function is used to automatically attempt to commit crimes. If you are already in the middle of some 'working' action 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 that action and give you your earnings. (such as working for a company or training at a gym), then running this function will automatically cancel that action and give you your earnings.
Note that crimes committed using this function will have all of their earnings halved (this applied for both money and experience!)
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, 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). so running *commitCrime('rob store')* will return 60).

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Bitburner - development</title> <title>Bitburner</title>
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
@ -79,6 +79,9 @@
<li id="job-tab" class="mainmenu-accordion-panel"> <li id="job-tab" class="mainmenu-accordion-panel">
<button id="job-menu-link"> Job </button> <button id="job-menu-link"> Job </button>
</li> </li>
<li id="stock-market-tab" class="mainmenu-accordion-panel">
<button id="stock-market-menu-link"> Stock Market </button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel"> <li id="bladeburner-tab" class="mainmenu-accordion-panel">
<button id="bladeburner-menu-link"> Bladeburner </button> <button id="bladeburner-menu-link"> Bladeburner </button>
</li> </li>
@ -109,7 +112,7 @@
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" /> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
</div> </div>
<div id="javascript-editor"></div> <div id="javascript-editor"></div>
@ -159,7 +162,7 @@
<fieldset> <fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" /> <input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset> </fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
@ -170,7 +173,7 @@
<table id="terminal"> <table id="terminal">
<tr id="terminal-input"> <tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$ <td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;" /> <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
</td> </td>
</tr> </tr>
</table> </table>
@ -187,7 +190,7 @@
provides information about each script's production. The scripts are categorized by the hostname of the servers on which provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p> they are running. </p>
<p id="active-scripts-total-prod">Total online production of <p id="active-scripts-total-prod">Total online production of
Active scripts: <span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br /> Active scripts: <span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br/>
Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span> Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span class="money-gold"><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span>)</p> (<span class="money-gold"><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span>)</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;"> <ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;">
@ -201,19 +204,19 @@
The Hacknet is a global, decentralized network of machines. It is used by hackers all around The Hacknet is a global, decentralized network of machines. It is used by hackers all around
the world to anonymously share computing power and perform distributed cyberattacks without the the world to anonymously share computing power and perform distributed cyberattacks without the
fear of being traced. fear of being traced.
<br /><br /> <br/><br/>
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
resources to the Hacknet network. This allows you to take a small percentage of profits resources to the Hacknet network. This allows you to take a small percentage of profits
from hacks performed on the network. Essentially, you are renting out your Node's computing power. from hacks performed on the network. Essentially, you are renting out your Node's computing power.
<br /><br /> <br/><br/>
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
in order to increase its computing power and thereby increase the profit you earn from it. in order to increase its computing power and thereby increase the profit you earn from it.
</p> </p>
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a> <a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
<br /> <br/>
<div id="hacknet-nodes-money-multipliers-div"> <div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money"> <p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br /> <span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br/>
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span> <span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
</p> </p>
<span id="hacknet-nodes-multipliers"> <span id="hacknet-nodes-multipliers">
@ -471,8 +474,7 @@
<!-- Tutorial content --> <!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container"> <div id="tutorial-container" class="generic-menupage-container">
<a id="tutorial-getting-started-link" class="a-link-button" <a id="tutorial-getting-started-link" class="a-link-button" href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a> <a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a>
<a id="tutorial-hacking-link" class="a-link-button"> Hacking </a> <a id="tutorial-hacking-link" class="a-link-button"> Hacking </a>
<a id="tutorial-scripts-link" class="a-link-button"> Scripts </a> <a id="tutorial-scripts-link" class="a-link-button"> Scripts </a>
@ -481,8 +483,7 @@
<a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a> <a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a>
<a id="tutorial-factions-link" class="a-link-button"> Factions </a> <a id="tutorial-factions-link" class="a-link-button"> Factions </a>
<a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a> <a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a>
<a id="tutorial-shortcuts-link" class="a-link-button" <a id="tutorial-shortcuts-link" class="a-link-button" href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-back-button" class="a-link-button"> Back </a> <a id="tutorial-back-button" class="a-link-button"> Back </a>
<p id="tutorial-text"> </p> <p id="tutorial-text"> </p>
@ -573,7 +574,7 @@
<p id="location-slums-description"> <p id="location-slums-description">
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
other shadowy entities. The city's government and police have neglected this area for years... other shadowy entities. The city's government and police have neglected this area for years...
<br /><br /><br /> <br/><br/><br/>
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
successful. Your chance at successfully committing a crime is determined by your stats. successful. Your chance at successfully committing a crime is determined by your stats.
</p> </p>
@ -620,7 +621,7 @@
<div id="stock-market-container" class="generic-menupage-container"> <div id="stock-market-container" class="generic-menupage-container">
<p> <p>
Welcome to the World Stock Exchange (WSE)! <br /><br /> Welcome to the World Stock Exchange (WSE)! <br/><br/>
To begin trading, you must first purchase an account. WSE accounts will persist To begin trading, you must first purchase an account. WSE accounts will persist
after you 'reset' by installing Augmentations. after you 'reset' by installing Augmentations.
@ -633,7 +634,7 @@
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE. TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies. trading strategies.
<br /><br /> <br/><br/>
If you purchase access to the TIX API, you will retain that access even after If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations. you 'reset' by installing Augmentations.
</p> </p>
@ -643,7 +644,7 @@
<p> <p>
Four Sigma's (4S) Market Data Feed provides information about stocks Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies. that will help your trading strategies.
<br /><br /> <br/><br/>
If you purchase access to 4S Market Data and/or the 4S TIX API, you will If you purchase access to 4S Market Data and/or the 4S TIX API, you will
retain that access even after you 'reset' by installing Augmentations. retain that access even after you 'reset' by installing Augmentations.
</p> </p>
@ -661,7 +662,7 @@
<a id="stock-market-mode" class="a-link-button tooltip"></a> <a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a> <a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a> <a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br /><br /> <br/><br/>
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/> <input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a> <a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;"> <ul id="stock-market-list" style="list-style:none;">
@ -691,7 +692,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container"> <div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content"> <div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p> <p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" /> <input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"/>
<button id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </button> <button id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-text-input-box-no" class="popup-box-button"> No </button> <button id="yes-no-text-input-box-no" class="popup-box-button"> No </button>
</div> </div>
@ -703,7 +704,7 @@
<p id="faction-invitation-box-text"> </p> <p id="faction-invitation-box-text"> </p>
<p id="faction-invitation-box-message"> </p> <p id="faction-invitation-box-message"> </p>
<p id="faction-invitation-box-warning"> <p id="faction-invitation-box-warning">
Would you like to join? <br /> <br /> Would you like to join? <br/> <br/>
Warning: Joining this faction may prevent you from joining other factions during this run! Warning: Joining this faction may prevent you from joining other factions during this run!
</p> </p>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button> <button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
@ -716,8 +717,8 @@
<div id="infiltration-box-content" class="popup-box-content"> <div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p> <p id="infiltration-box-text"> </p>
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br /><br /> <button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br/><br/>
<select id="infiltration-faction-select"> </select> <br /> <select id="infiltration-faction-select"> </select> <br/>
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button> <button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
</div> </div>
@ -787,8 +788,8 @@
</table> </table>
</div> </div>
<div class="character-quick-options"> <div class="character-quick-options">
<button id="character-overview-save-button">Save Game</button> <button id="character-overview-save-button" class="character-overview-btn">Save Game</button>
<button id="character-overview-options-button">Options</button> <button id="character-overview-options-button" class="character-overview-btn">Options</button>
</div> </div>
</div> </div>
</div> </div>
@ -803,7 +804,7 @@
<div id="game-options-content" class="game-options-box"> <div id="game-options-content" class="game-options-box">
<button id="game-options-close-button">&times;</button> <button id="game-options-close-button">&times;</button>
<h1> Game Options </h1> <h1> Game Options </h1>
<br /> <br/>
<div id="game-options-left-panel"> <div id="game-options-left-panel">
<!-- Netscript execution time --> <!-- Netscript execution time -->
<fieldset> <fieldset>
@ -815,7 +816,7 @@
</span> </span>
</label> </label>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25" /> <input type="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -829,7 +830,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" /> <input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50"/>
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -843,7 +844,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" /> <input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50"/>
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -855,7 +856,7 @@
</span> </span>
</label> </label>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" /> <input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60"/>
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em> <em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -967,7 +968,7 @@
<a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a> <a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a>
<a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a> <a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a>
<a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a> <a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a>
<input type="file" id="import-game-file-selector" name="file" /> <input type="file" id="import-game-file-selector" name="file"/>
<a id="import-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Import Game </a> <a id="import-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Import Game </a>
<a id="debug-delete-scripts-link" class="a-link-button tooltip" style="display:block;width:46%;"> <a id="debug-delete-scripts-link" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Delete Active Scripts (DEBUG) Delete Active Scripts

@ -62,7 +62,7 @@ let NetscriptFunctions =
"hack|sleep|grow|weaken|print|tprint|scan|nuke|brutessh|ftpcrack|" + //Netscript functions "hack|sleep|grow|weaken|print|tprint|scan|nuke|brutessh|ftpcrack|" + //Netscript functions
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" + "clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" + "relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
"scp|ls|hasRootAccess|" + "scp|ls|ps|hasRootAccess|" +
"getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" + "getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" +
"getHacknetMultipliers|" + "getHacknetMultipliers|" +
"getHostname|getHackingLevel|getServerMoneyAvailable|getServerMaxMoney|" + "getHostname|getHackingLevel|getServerMoneyAvailable|getServerMaxMoney|" +
@ -95,6 +95,7 @@ let NetscriptFunctions =
"getStockPrice|getStockPosition|getStockSymbols|buyStock|sellStock|" + "getStockPrice|getStockPosition|getStockSymbols|buyStock|sellStock|" +
"shortStock|sellShort|" + "shortStock|sellShort|" +
"placeOrder|cancelOrder|getStockVolatility|getStockForecast|" + "placeOrder|cancelOrder|getStockVolatility|getStockForecast|" +
"purchase4SMarketData|purchase4SMarketDataTixApi|" +
// Hacknet Node API // Hacknet Node API
"hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" + "hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" +

@ -265,7 +265,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.CorporationValuation = 0.5; BitNodeMultipliers.CorporationValuation = 0.5;
break; break;
case 6: //Bladeburner case 6: //Bladeburner
BitNodeMultipliers.HackingLevelMultiplier = 0.5; BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.ServerMaxMoney = 0.5; BitNodeMultipliers.ServerMaxMoney = 0.5;
BitNodeMultipliers.ServerStartingMoney = 0.5; BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5; BitNodeMultipliers.ServerStartingSecurity = 1.5;
@ -282,7 +282,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.BladeburnerRank = 0.6; BitNodeMultipliers.BladeburnerRank = 0.6;
BitNodeMultipliers.BladeburnerSkillCost = 2; BitNodeMultipliers.BladeburnerSkillCost = 2;
BitNodeMultipliers.AugmentationMoneyCost = 3; BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.HackingLevelMultiplier = 0.5; BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.ServerMaxMoney = 0.5; BitNodeMultipliers.ServerMaxMoney = 0.5;
BitNodeMultipliers.ServerStartingMoney = 0.5; BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5; BitNodeMultipliers.ServerStartingSecurity = 1.5;

@ -74,7 +74,6 @@ var ContractBaseMoneyGain = 50e3; //Base Money Gained per contract
var ActiveActionCssClass = "bladeburner-active-action"; var ActiveActionCssClass = "bladeburner-active-action";
//Console related stuff //Console related stuff
var consoleHistory = []; //Console command history
var consoleHistoryIndex = 0; var consoleHistoryIndex = 0;
var consoleHelpText = { var consoleHelpText = {
helpList:"Use 'help [command]' to get more information about a particular Bladeburner console command.<br><br>" + helpList:"Use 'help [command]' to get more information about a particular Bladeburner console command.<br><br>" +
@ -153,6 +152,7 @@ $(document).keydown(function(event) {
//} //}
if (!(Player.bladeburner instanceof Bladeburner)) {return;} if (!(Player.bladeburner instanceof Bladeburner)) {return;}
let consoleHistory = Player.bladeburner.consoleHistory;
//NOTE: Keycodes imported from Terminal.js //NOTE: Keycodes imported from Terminal.js
if (event.keyCode === KEY.ENTER) { if (event.keyCode === KEY.ENTER) {
@ -712,6 +712,10 @@ function Bladeburner(params={}) {
this.automateActionLow = 0; this.automateActionLow = 0;
this.automateThreshLow = 0; //Stamina Threshold this.automateThreshLow = 0; //Stamina Threshold
//Console command history
this.consoleHistory = [];
this.consoleLogs = [];
//Initialization //Initialization
initBladeburner(); initBladeburner();
this.initializeDomElementRefs(); this.initializeDomElementRefs();
@ -864,6 +868,12 @@ Bladeburner.prototype.process = function() {
this.resetAction(); this.resetAction();
} }
// If the Player has no Stamina, set action to idle
if (this.stamina <= 0) {
this.log("Your Bladeburner action was cancelled because your stamina hit 0");
this.resetAction();
}
//A 'tick' for this mechanic is one second (= 5 game cycles) //A 'tick' for this mechanic is one second (= 5 game cycles)
if (this.storedCycles >= CyclesPerSecond) { if (this.storedCycles >= CyclesPerSecond) {
var seconds = Math.floor(this.storedCycles / CyclesPerSecond); var seconds = Math.floor(this.storedCycles / CyclesPerSecond);
@ -1345,7 +1355,6 @@ Bladeburner.prototype.completeAction = function() {
Player.gainIntelligenceExp(BaseIntGain); Player.gainIntelligenceExp(BaseIntGain);
Player.gainCharismaExp(charismaExpGain); Player.gainCharismaExp(charismaExpGain);
this.changeRank(0.1 * BitNodeMultipliers.BladeburnerRank); this.changeRank(0.1 * BitNodeMultipliers.BladeburnerRank);
console.log("DEBUG: Field Analysis effectiveness is " + (eff * this.skillMultipliers.successChanceEstimate));
this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate); this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate);
if (this.logging.general) { if (this.logging.general) {
this.log("Field analysis completed. Gained 0.1 rank, " + formatNumber(hackingExpGain, 1) + " hacking exp, and " + formatNumber(charismaExpGain, 1) + " charisma exp"); this.log("Field analysis completed. Gained 0.1 rank, " + formatNumber(hackingExpGain, 1) + " hacking exp, and " + formatNumber(charismaExpGain, 1) + " charisma exp");
@ -1738,8 +1747,15 @@ Bladeburner.prototype.createContent = function() {
document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv); document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv);
this.postToConsole("Bladeburner Console BETA"); if (this.consoleLogs.length === 0) {
this.postToConsole("Type 'help' to see console commands"); this.postToConsole("Bladeburner Console BETA");
this.postToConsole("Type 'help' to see console commands");
} else {
for (let i = 0; i < this.consoleLogs.length; ++i) {
this.postToConsole(this.consoleLogs[i], false);
}
}
DomElems.consoleInput.focus(); DomElems.consoleInput.focus();
} }
@ -2732,12 +2748,22 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
} }
//Bladeburner Console Window //Bladeburner Console Window
Bladeburner.prototype.postToConsole = function(input) { Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
const MaxConsoleEntries = 100;
if (saveToLogs === true) {
this.consoleLogs.push(input);
if (this.consoleLogs.length > MaxConsoleEntries) {
this.consoleLogs.shift();
}
}
if (input == null || DomElems.consoleDiv == null) {return;} if (input == null || DomElems.consoleDiv == null) {return;}
$("#bladeubrner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>'); $("#bladeubrner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>');
if (DomElems.consoleTable.childNodes.length > 200) {
if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
} }
this.updateConsoleScroll(); this.updateConsoleScroll();
} }
@ -2753,6 +2779,8 @@ Bladeburner.prototype.clearConsole = function() {
while (DomElems.consoleTable.childNodes.length > 1) { while (DomElems.consoleTable.childNodes.length > 1) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
} }
this.consoleLogs.length = 0;
} }
Bladeburner.prototype.log = function(input) { Bladeburner.prototype.log = function(input) {
@ -2764,13 +2792,13 @@ Bladeburner.prototype.log = function(input) {
Bladeburner.prototype.executeConsoleCommands = function(commands) { Bladeburner.prototype.executeConsoleCommands = function(commands) {
try { try {
//Console History //Console History
if (consoleHistory[consoleHistory.length-1] != commands) { if (this.consoleHistory[this.consoleHistory.length-1] != commands) {
consoleHistory.push(commands); this.consoleHistory.push(commands);
if (consoleHistory.length > 50) { if (this.consoleHistory.length > 50) {
consoleHistory.splice(0, 1); this.consoleHistory.splice(0, 1);
} }
} }
consoleHistoryIndex = consoleHistory.length; consoleHistoryIndex = this.consoleHistory.length;
var arrayOfCommands = commands.split(";"); var arrayOfCommands = commands.split(";");
for (var i = 0; i < arrayOfCommands.length; ++i) { for (var i = 0; i < arrayOfCommands.length; ++i) {
@ -3469,7 +3497,7 @@ Bladeburner.prototype.getActionCountRemainingNetscriptFn = function(type, name,
switch (actionId.type) { switch (actionId.type) {
case ActionTypes["Contract"]: case ActionTypes["Contract"]:
case ActionTypes["Operation"]: case ActionTypes["Operation"]:
return actionObj.count; return Math.floor( actionObj.count );
case ActionTypes["BlackOp"]: case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]: case ActionTypes["BlackOperation"]:
if (this.blackops[name] != null) { if (this.blackops[name] != null) {

@ -0,0 +1,158 @@
import {CodingContract,
CodingContractRewardType,
CodingContractTypes} from "./CodingContracts";
import {Factions} from "./Faction";
import {Player} from "./Player";
import {GetServerByHostname,
AllServers} from "./Server";
import {getRandomInt} from "../utils/helpers/getRandomInt";
export function generateRandomContract() {
// First select a random problem type
const problemTypes = Object.keys(CodingContractTypes);
let randIndex = getRandomInt(0, problemTypes.length - 1);
let problemType = problemTypes[randIndex];
// Then select a random reward type. 'Money' will always be the last reward type
const reward = getRandomReward();
// Choose random server
const randServer = getRandomServer();
let contractFn = getRandomFilename(randServer, reward);
let contract = new CodingContract(contractFn, problemType, reward);
randServer.addContract(contract);
}
export function generateContract(params) {
// Problem Type
let problemType;
const problemTypes = Object.keys(CodingContractTypes);
if (params.problemType != null && problemTypes.includes(params.problemType)) {
problemType = params.problemType;
} else {
let randIndex = getRandomInt(0, problemTypes.length - 1);
problemType = problemTypes[randIndex];
}
// Reward Type - This is always random for now
const reward = getRandomReward();
// Server
let server;
if (params.server != null) {
server = GetServerByHostname(params.server);
if (server == null) {
server = AllServers[param.server];
}
if (server == null) {
server = getRandomServer();
}
} else {
server = getRandomServer();
}
// Filename
let fn;
if (params.fn != null) {
fn = params.fn;
} else {
fn = getRandomFilename(server, reward);
}
let contract = new CodingContract(fn, problemType, reward);
server.addContract(contract);
}
// Ensures that a contract's reward type is valid
function sanitizeRewardType(rewardType) {
let type = rewardType; // Create copy
const factionsThatAllowHacking = Player.factions.filter((fac) => {
try {
return Factions[fac].getInfo().offerHackingWork;
} catch (e) {
console.error(`Error when trying to filter Hacking Factions for Coding Contract Generation: ${e}`);
return false;
}
});
if (type === CodingContractRewardType.FactionReputation && factionsThatAllowHacking.length === 0) {
type = CodingContractRewardType.CompanyReputation;
}
if (type === CodingContractRewardType.FactionReputationAll && factionsThatAllowHacking.length === 0) {
type = CodingContractRewardType.CompanyReputation;
}
if (type === CodingContractRewardType.CompanyReputation && Player.companyName === "") {
type = CodingContractRewardType.Money;
}
return type;
}
function getRandomReward() {
let reward = {};
reward.type = getRandomInt(0, CodingContractRewardType.Money);
reward.type = sanitizeRewardType(reward.type);
// Add additional information based on the reward type
const factionsThatAllowHacking = Player.factions.filter((fac) => {
try {
return Factions[fac].getInfo().offerHackingWork;
} catch (e) {
console.error(`Error when trying to filter Hacking Factions for Coding Contract Generation: ${e}`);
return false;
}
});
switch (reward.type) {
case CodingContractRewardType.FactionReputation:
// Get a random faction that player is a part of. That
// faction must allow hacking contracts
var numFactions = factionsThatAllowHacking.length;
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction;
break;
case CodingContractRewardType.CompanyReputation:
if (Player.companyName !== "") {
reward.name = Player.companyName;
} else {
reward.type = CodingContractRewardType.Money;
}
break;
default:
break;
}
return reward;
}
function getRandomServer() {
const servers = Object.keys(AllServers);
let randIndex = getRandomInt(0, servers.length - 1);
let randServer = AllServers[servers[randIndex]];
// An infinite loop shouldn't ever happen, but to be safe we'll use
// a for loop with a limited number of tries
for (let i = 0; i < 200; ++i) {
if (randServer.purchasedByPlayer === false) { break; }
randIndex = getRandomInt(0, servers.length - 1);
randServer = AllServers[servers[randIndex]];
}
return randServer;
}
function getRandomFilename(server, reward) {
let contractFn = `contract-${getRandomInt(0, 1e6)}`;
for (let i = 0; i < 1000; ++i) {
if (server.contracts.filter((c) => {return c.fn === contractFn}).length <= 0) { break; }
contractFn = `contract-${getRandomInt(0, 1e6)}`;
}
if (reward.name) { contractFn += `-${reward.name.replace(/\s/g, "")}`; }
return contractFn;
}

@ -1,5 +1,5 @@
let CONSTANTS = { let CONSTANTS = {
Version: "0.40.5", Version: "0.41.1",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience //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 //and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -502,38 +502,30 @@ let CONSTANTS = {
LatestUpdate: LatestUpdate:
` `
v0.41.0 v0.41.1
* WARNING: In NetscriptJS, defining a function called print() is no longer possible * IMPORTANT - Netscript Changes:
* Gang Mechanic Changes (BitNode-2): ** purchaseTor() now returns true if you already have a TOR router (it used to return false)
*** Added a Gang Netscript API ** getPurchasedServerCost() now returns Infinity if the specified RAM is an invalid amount or is greater than the max amount of RAM (2 ^ 20 GB)
*** Added new 'ascension' mechanic for Gang Members ** Added purchase4SMarketData() and purchase4SMarketDataTixApi() functions
*** The first three gang members are now 'free' (can be recruited instantly) ** getScriptLogs() now takes in optional arguments that let you get the logs of another script
*** Maximum number of increased Gang Members increased from 20 to 30
*** Changed the formula for calculating respect needed to recruit the next gang member * Stock Market changes:
*** Added a new category of upgrades for Gang Members: Augmentations ** Stocks now have "maximum prices". These are hidden from the player
*** Non-Augmentation Gang member upgrades are now significantly weaker ** If a stock reaches its "maximum price", it will most likely drop in value (although it might still rise)
*** Reputation for your Gang faction can no longer be gained through Infiltration ** Each stock has its own, unique maximum price
*** Re-worked the territory 'warfare' mechanic so that player can choose when to engage in it ** Maximum price for each stock are randomly generated and change during each 'reset'
*** Gang Members can now be killed during territory 'warfare' ** Stock Market cycles are now accumulated/stored, much like it is for Gangs and Bladeburners
*** Changed BitNode-2 Multipliers to make hacking slightly less profitable ** Accumulated/stored cycles cause stock prices to update up to 50% faster (from every 6 seconds to 4 seconds)
*** Gang Member Equipment + Upgrades now get cheaper as your gang grows in power and respect *** This means that after coming back from being offline, stock prices will update faster to make up for offline time
*** The effects of Source-File 2 are now slightly more powerful
* RAM Cost of accessing the global document object lowered from 100 GB to 25 GB * Decreased the Hacking Level multiplier for BitNodes 6 and 7 to 0.4 (from 0.5)
* RAM Cost to use Singularity Functions outside of BitNode-4 lowered by 75%. They now only cost twice as much as they do in BitNode-4 * Bladeburner console history is now saved and persists when switching screens or closing/reopening the game
* b1t_flum3.exe now takes significantly less time to create * In Bladeburner, if your stamina reaches 0 your current action will be cancelled
* Crimes commited through Singularity function no longer give half money/exp * b1t_flum3.exe is no longer removed from your home computer upon reset
* Improved number formatting for Player 'work' actions (including crimes, etc.). These numbers should also adhere to locale settings now (by Kline-) * Added main menu link for the Stock Market (once you've purchased an account)
* The order that Augmentations are listed in (when purchasing from Faction and viewing your Augmentations) is now saved and persists when choosing different orders * Job main menu link only appears if you actually have a job
* getCharacterInformation() Singularity function now returns multiplier information (from Augmentations/Source Files) * Bug Fix: After installing Augs, the "Portfolio Mode" button on the Stock Market page should be properly reset
* Bug Fix: Calling print() in NetscriptJS no longer brings up the print dialog * Bug Fix: bladeburner.getActionCountRemaining()'s return value is now rounded down (by Kline-)
* Bug Fix: Fixed a bug that sometimes caused a blank black screen when destroying/resetting/switching BitNodes
* Bug Fix: Netscript calls that throw errors will now no longer cause the 'concurrent calls' error if they are caught in the script. i.e. try/catch should now work properly in scripts
* Bug Fix: Fixed a bug where sometimes the NeuroFlux Governor Augmentation level would be incorrectly calculated when the game was loaded
* Bug Fix: Fixed a bug where calling the scp() Netscript function with invalid hostname/ips would throw an unclear error message
* Bug Fix: Bladeburner API function getActionCountRemaining() should now work properly for BlackOps
* Bug Fix: Black Ops can no longer be attempted out-of-order or without the required rank via Bladeburner API
* Bug Fix: Dynamic RAM Calculation now properly accounts for number of threads
* RAM cost for basic Netscript functions added to documentation (by CBJamo)
` `
} }

@ -1,13 +1,19 @@
import {AugmentationNames} from "./Augmentations" import {AugmentationNames} from "./Augmentations";
import {Programs} from "./CreateProgram" import {generateRandomContract} from "./CodingContractGenerator";
import {Factions} from "./Faction"; import {Programs} from "./CreateProgram";
import {Player} from "./Player"; import {Factions} from "./Faction";
import {AllServers} from "./Server"; import {Player} from "./Player";
import {hackWorldDaemon} from "./RedPill"; import {AllServers} from "./Server";
import {Terminal} from "./Terminal"; import {hackWorldDaemon} from "./RedPill";
import {exceptionAlert} from "../utils/helpers/exceptionAlert"; import {StockMarket,
import {createElement} from "../utils/uiHelpers/createElement"; SymbolToStockMap} from "./StockMarket";
import {removeElementById} from "../utils/uiHelpers/removeElementById"; import {Stock} from "./Stock";
import {Terminal} from "./Terminal";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {createElement} from "../utils/uiHelpers/createElement";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
const devMenuContainerId = "dev-menu-container"; const devMenuContainerId = "dev-menu-container";
@ -60,7 +66,7 @@ export function createDevMenu() {
}, },
innerText: "Destroy Current BitNode", innerText: "Destroy Current BitNode",
tooltip: "Will grant Source-File for the BitNode", tooltip: "Will grant Source-File for the BitNode",
}) });
// Experience / stats // Experience / stats
const statsHeader = createElement("h2", { const statsHeader = createElement("h2", {
@ -321,6 +327,7 @@ export function createDevMenu() {
const bladeburnerGainRankInput = createElement("input", { const bladeburnerGainRankInput = createElement("input", {
class: "text-input", class: "text-input",
margin: "5px",
placeholder: "Rank to gain (or negative to lose rank)", placeholder: "Rank to gain (or negative to lose rank)",
type: "number", type: "number",
}); });
@ -343,6 +350,7 @@ export function createDevMenu() {
const gangStoredCyclesInput = createElement("input", { const gangStoredCyclesInput = createElement("input", {
class: "text-input", class: "text-input",
margin: "5px",
placeholder: "# Cycles to add", placeholder: "# Cycles to add",
type: "number", type: "number",
}); });
@ -358,7 +366,85 @@ export function createDevMenu() {
} }
}, },
innerText: "Add cycles to Gang mechanic", innerText: "Add cycles to Gang mechanic",
}) });
// Coding Contracts
const contractsHeader = createElement("h2", {innerText: "Coding Contracts"});
const generateRandomContractBtn = createElement("button", {
class: "std-button",
clickListener: () => {
generateRandomContract();
},
innerText: "Generate Random Contract",
});
// Stock Market
const stockmarketHeader = createElement("h2", {innerText: "Stock Market"});
const stockInput = createElement("input", {
class: "text-input",
display: "block",
placeholder: "Stock symbol(s), or 'all'",
});
function processStocks(cb) {
const input = stockInput.value.toString().replace(/\s/g, '');
// Empty input, or "all", will process all stocks
if (input === "" || input.toLowerCase() === "all") {
for (const name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
const stock = StockMarket[name];
if (stock instanceof Stock) {
cb(stock);
}
}
}
return;
}
const stockSymbols = input.split(",");
for (let i = 0; i < stockSymbols.length; ++i) {
const stock = SymbolToStockMap[stockSymbols];
if (stock instanceof Stock) {
cb(stock);
}
}
}
const stockPriceChangeInput = createElement("input", {
class: "text-input",
margin: "5px",
placeholder: "Price to change stock(s) to",
type: "number",
});
const stockPriceChangeBtn = createElement("button", {
class: "std-button",
clickListener: () => {
const price = parseInt(stockPriceChangeInput.value);
if (isNaN(price)) { return; }
processStocks((stock) => {
stock.price = price;
});
dialogBoxCreate(`Stock Prices changed to ${price}`);
},
innerText: "Change Stock Price(s)",
});
const stockViewPriceCapBtn = createElement("button", {
class: "std-button",
clickListener: () => {
let text = "";
processStocks((stock) => {
text += `${stock.symbol}: ${numeralWrapper.format(stock.cap, '$0.000a')}<br>`;
});
dialogBoxCreate(text);
},
innerText: "View Stock Price Caps",
});
// Add everything to container, then append to main menu // Add everything to container, then append to main menu
const devMenuContainer = createElement("div", { const devMenuContainer = createElement("div", {
@ -419,6 +505,14 @@ export function createDevMenu() {
devMenuContainer.appendChild(gangStoredCyclesInput); devMenuContainer.appendChild(gangStoredCyclesInput);
devMenuContainer.appendChild(gangAddStoredCycles); devMenuContainer.appendChild(gangAddStoredCycles);
devMenuContainer.appendChild(createElement("br")); devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(contractsHeader);
devMenuContainer.appendChild(generateRandomContractBtn);
devMenuContainer.appendChild(stockmarketHeader);
devMenuContainer.appendChild(stockInput);
devMenuContainer.appendChild(stockPriceChangeInput);
devMenuContainer.appendChild(stockPriceChangeBtn);
devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(stockViewPriceCapBtn);
const entireGameContainer = document.getElementById("entire-game-container"); const entireGameContainer = document.getElementById("entire-game-container");
if (entireGameContainer == null) { if (entireGameContainer == null) {

@ -394,7 +394,6 @@ function displayFactionContent(factionName) {
var hacking = false; var hacking = false;
if (factionName === "NiteSec" || factionName === "The Black Hand") {hacking = true;} if (factionName === "NiteSec" || factionName === "The Black Hand") {hacking = true;}
Player.startGang(factionName, hacking); Player.startGang(factionName, hacking);
document.getElementById("gang-tab").style.display = "list-item";
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
Engine.loadGangContent(); Engine.loadGangContent();

@ -31,7 +31,7 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox"; yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox";
// Constants // Constants
const GangRespectToReputationRatio = 2; // Respect is divided by this to get rep gain const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain
const MaximumGangMembers = 30; const MaximumGangMembers = 30;
const GangRecruitCostMultiplier = 2; const GangRecruitCostMultiplier = 2;
const CyclesPerTerritoryAndPowerUpdate = 100; const CyclesPerTerritoryAndPowerUpdate = 100;
@ -273,7 +273,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
// Multiplicative gain (50% chance) // Multiplicative gain (50% chance)
// This is capped per cycle, to prevent it from getting out of control // This is capped per cycle, to prevent it from getting out of control
const multiplicativeGain = AllGangs[name].power * 0.008; const multiplicativeGain = AllGangs[name].power * 0.008;
AllGangs[name].power += Math.min(1, multiplicativeGain); AllGangs[name].power += Math.min(0.9, multiplicativeGain);
} else { } else {
// Additive gain (50% chance) // Additive gain (50% chance)
const additiveGain = 0.5 * gainRoll * AllGangs[name].territory; const additiveGain = 0.5 * gainRoll * AllGangs[name].territory;
@ -357,6 +357,7 @@ Gang.prototype.getRespectNeededToRecruitMember = function() {
} }
Gang.prototype.recruitMember = function(name) { Gang.prototype.recruitMember = function(name) {
name = String(name);
if (name === "" || !this.canRecruitMember()) { return false; } if (name === "" || !this.canRecruitMember()) { return false; }
// Check for already-existing names // Check for already-existing names
@ -840,7 +841,7 @@ function GangMemberTask(name="", desc="", isHacking=false, isCombat=false,
this.chaWeight = params.chaWeight ? params.chaWeight : 0; this.chaWeight = params.chaWeight ? params.chaWeight : 0;
if (Math.round(this.hackWeight + this.strWeight + this.defWeight + this.dexWeight + this.agiWeight + this.chaWeight) != 100) { if (Math.round(this.hackWeight + this.strWeight + this.defWeight + this.dexWeight + this.agiWeight + this.chaWeight) != 100) {
throw new Error(`GangMemberTask ${this.name} weights do not add up to 100`); console.error(`GangMemberTask ${this.name} weights do not add up to 100`);
} }
// 1 - 100 // 1 - 100

@ -1953,7 +1953,6 @@ function initLocationButtons() {
name:companyName, name:companyName,
}); });
displayLocationContent(); displayLocationContent();
document.getElementById("corporation-tab").style.display = "list-item";
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
dialogBoxCreate("Congratulations! You just started your own corporation. You can visit " + dialogBoxCreate("Congratulations! You just started your own corporation. You can visit " +
@ -1983,7 +1982,6 @@ function initLocationButtons() {
Player.bladeburner = new Bladeburner({new:true}); Player.bladeburner = new Bladeburner({new:true});
dialogBoxCreate("You have been accepted into the Bladeburner division!"); dialogBoxCreate("You have been accepted into the Bladeburner division!");
displayLocationContent(); displayLocationContent();
document.getElementById("bladeburner-tab").style.display = "list-item";
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click();
} else { } else {

@ -35,10 +35,9 @@ import {Server, getServer, AddToAllServers,
import {Settings} from "./Settings"; import {Settings} from "./Settings";
import {SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerIps} from "./SpecialServerIps";
import {Stock} from "./Stock"; import {Stock} from "./Stock";
import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols, import {StockMarket, StockSymbols, SymbolToStockMap,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock, initStockMarket, initSymbolToStockMap, buyStock,
sellStock, updateStockPrices, displayStockMarketContent, sellStock, updateStockPlayerPosition,
updateStockTicker, updateStockPlayerPosition,
shortStock, sellShort, OrderTypes, shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket"; PositionTypes, placeOrder, cancelOrder} from "./StockMarket";
import {post} from "./ui/postToTerminal"; import {post} from "./ui/postToTerminal";
@ -107,8 +106,16 @@ var possibleLogs = {
getServerGrowth: true, getServerGrowth: true,
getServerNumPortsRequired: true, getServerNumPortsRequired: true,
getServerRam: true, getServerRam: true,
// TIX API
buyStock: true, buyStock: true,
sellStock: true, sellStock: true,
shortStock: true,
sellShort: true,
purchase4SMarketData: true,
purchase4SMarketDataTixApi: true,
// Singularity Functions
purchaseServer: true, purchaseServer: true,
deleteServer: true, deleteServer: true,
universityCourse: true, universityCourse: true,
@ -125,12 +132,18 @@ var possibleLogs = {
donateToFaction: true, donateToFaction: true,
createProgram: true, createProgram: true,
commitCrime: true, commitCrime: true,
shortStock: true,
sellShort: true, // Bladeburner API
startAction: true, startAction: true,
upgradeSkill: true, upgradeSkill: true,
setTeamSize: true, setTeamSize: true,
joinBladeburnerFaction: true, joinBladeburnerFaction: true,
// Gang API
recruitMember: true,
setMemberTask: true,
purchaseEquipment: true,
setTerritoryWarfare: true,
} }
//Used to check and set flags for every Source File, despite the name of the function //Used to check and set flags for every Source File, despite the name of the function
@ -535,8 +548,30 @@ function NetscriptFunctions(workerScript) {
} }
return workerScript.disableLogs[fn] ? false : true; return workerScript.disableLogs[fn] ? false : true;
}, },
getScriptLogs : function() { getScriptLogs : function(fn, ip) {
if (workerScript.checkingRam) {return 0;} if (workerScript.checkingRam) {return 0;}
if (fn != null && typeof fn === 'string') {
// Get Logs of another script
if (ip == null) { ip = workerScript.serverIp; }
const server = getServer(ip);
if (server == null) {
workerScript.log(`getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
throw makeRuntimeRejectMsg(workerScript, `getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
}
let argsForTarget = [];
for (let i = 2; i < arguments.length; ++i) {
argsForTarget.push(arguments[i]);
}
const runningScriptObj = findRunningScript(fn, argsForTarget, server);
if (runningScriptObj == null) {
workerScript.scriptRef.log(`getScriptLogs() failed. No such script ${fn} on ${server.hostname} with args: ${arrayToString(argsForTarget)}`);
return "";
}
return runningScriptObj.logs.slice();
}
return workerScript.scriptRef.logs.slice(); return workerScript.scriptRef.logs.slice();
}, },
nuke : function(ip){ nuke : function(ip){
@ -790,7 +825,7 @@ function NetscriptFunctions(workerScript) {
} }
NetscriptFunctions(workerScript).exit(); NetscriptFunctions(workerScript).exit();
}, },
kill : function(filename,ip) { kill : function(filename, ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("kill", CONSTANTS.ScriptKillRamCost); return updateStaticRam("kill", CONSTANTS.ScriptKillRamCost);
} }
@ -1686,6 +1721,68 @@ function NetscriptFunctions(workerScript) {
stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag; stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag;
return forecast / 100; //Convert from percentage to decimal return forecast / 100; //Convert from percentage to decimal
}, },
purchase4SMarketData : function() {
if (workerScript.checkingRam) {
return updateStaticRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use purchase4SMarketData()");
}
if (Player.has4SData) {
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Already purchased 4S Market Data");
}
return true;
}
if (Player.money.lt(CONSTANTS.MarketData4SCost)) {
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Failed to purchase 4S Market Data - Not enough money");
}
return false;
}
Player.has4SData = true;
Player.loseMoney(CONSTANTS.MarketData4SCost);
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Purchased 4S Market Data");
}
return true;
},
purchase4SMarketDataTixApi : function() {
if (workerScript.checkingRam) {
return updateStaticRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use purchase4SMarketDataTixApi()");
}
if (Player.has4SDataTixApi) {
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Already purchased 4S Market Data TIX API");
}
return true;
}
if (Player.money.lt(CONSTANTS.MarketDataTixApi4SCost)) {
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Failed to purchase 4S Market Data TIX API - Not enough money");
}
return false;
}
Player.has4SDataTixApi = true;
Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost);
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Purchased 4S Market Data TIX API");
}
return true;
},
getPurchasedServerLimit : function() { getPurchasedServerLimit : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit); return updateStaticRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit);
@ -1713,7 +1810,7 @@ function NetscriptFunctions(workerScript) {
cost = getPurchaseServerRamCostGuard(ram); cost = getPurchaseServerRamCostGuard(ram);
} catch (e) { } catch (e) {
workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' " + e.message); workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' " + e.message);
return ""; return Infinity;
} }
return cost; return cost;
@ -3647,7 +3744,7 @@ function NetscriptFunctions(workerScript) {
strength: member.str, strength: member.str,
strengthEquipMult: member.str_mult, strengthEquipMult: member.str_mult,
strengthAscensionMult: member.str_asc_mult, strengthAscensionMult: member.str_asc_mult,
task: member.task.name, task: member.task,
} }
} }
} }
@ -3679,7 +3776,16 @@ function NetscriptFunctions(workerScript) {
nsGang.checkGangApiAccess(workerScript, "recruitMember"); nsGang.checkGangApiAccess(workerScript, "recruitMember");
try { try {
return Player.gang.recruitMember(name); const res = Player.gang.recruitMember(name);
if (workerScript.shouldLog("recruitMember")) {
if (res) {
workerScript.log(`Successfully recruited Gang Member ${name}`);
} else {
workerScript.log(`Failed to recruit Gang Member ${name}`);
}
}
return res;
} catch(e) { } catch(e) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("recruitMember", e)); throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("recruitMember", e));
} }
@ -3709,7 +3815,16 @@ function NetscriptFunctions(workerScript) {
try { try {
for (const member of Player.gang.members) { for (const member of Player.gang.members) {
if (member.name === memberName) { if (member.name === memberName) {
return member.assignToTask(taskName); const res = member.assignToTask(taskName);
if (workerScript.shouldLog("setMemberTask")) {
if (res) {
workerScript.log(`Successfully assigned Gang Member ${memberName} to ${taskName} task`);
} else {
workerScript.log(`Failed to assign Gang Member ${memberName} to ${taskName} task. ${memberName} is now Unassigned`);
}
}
return res;
} }
} }
@ -3755,7 +3870,16 @@ function NetscriptFunctions(workerScript) {
try { try {
for (const member in Player.gang.members) { for (const member in Player.gang.members) {
if (member.name === memberName) { if (member.name === memberName) {
return member.buyUpgrade(equipName, Player, Player.gang); const res = member.buyUpgrade(equipName, Player, Player.gang);
if (workerScript.shouldLog("purchaseEquipment")) {
if (res) {
workerScript.log(`Purchased ${equipName} for Gang member ${memberName}`);
} else {
workerScript.log(`Failed to purchase ${equipName} for Gang member ${memberName}`);
}
}
return res;
} }
} }
@ -3779,7 +3903,7 @@ function NetscriptFunctions(workerScript) {
} }
} }
workerScript.log(`Invalid argument passed to gang.ascendMember(). No gang member could be found with name ${memberName}`); workerScript.log(`Invalid argument passed to gang.ascendMember(). No gang member could be found with name ${name}`);
return false; return false;
} catch(e) { } catch(e) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("ascendMember", e)); throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("ascendMember", e));
@ -3795,8 +3919,14 @@ function NetscriptFunctions(workerScript) {
try { try {
if (engage) { if (engage) {
Player.gang.territoryWarfareEngaged = true; Player.gang.territoryWarfareEngaged = true;
if (workerScript.shouldLog("setTerritoryWarfare")) {
workerScript.log("Engaging in Gang Territory Warfare");
}
} else { } else {
Player.gang.territoryWarfareEngaged = false; Player.gang.territoryWarfareEngaged = false;
if (workerScript.shouldLog("setTerritoryWarfare")) {
workerScript.log("Disengaging in Gang Territory Warfare");
}
} }
} catch(e) { } catch(e) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("setTerritoryWarfare", e)); throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("setTerritoryWarfare", e));

@ -73,7 +73,7 @@ export function _getScriptUrls(script, scripts, seen) {
// import {foo} from "blob://<uuid>" // import {foo} from "blob://<uuid>"
// //
// Where the blob URL contains the script content. // Where the blob URL contains the script content.
let transformedCode = script.code.replace(/((?:from|import)\s+(?:'|"))([^'"]+)('|";)/g, let transformedCode = script.code.replace(/((?:from|import)\s+(?:'|"))(?:\.\/)?([^'"]+)('|";)/g,
(unmodified, prefix, filename, suffix) => { (unmodified, prefix, filename, suffix) => {
const isAllowedImport = scripts.some(s => s.filename == filename); const isAllowedImport = scripts.some(s => s.filename == filename);
if (!isAllowedImport) return unmodified; if (!isAllowedImport) return unmodified;

@ -191,7 +191,6 @@ function PlayerObject() {
//Flags for determining whether certain "thresholds" have been achieved //Flags for determining whether certain "thresholds" have been achieved
this.firstFacInvRecvd = false; this.firstFacInvRecvd = false;
this.firstAugPurchased = false; this.firstAugPurchased = false;
this.firstJobRecvd = false;
this.firstTimeTraveled = false; this.firstTimeTraveled = false;
this.firstProgramAvailable = false; this.firstProgramAvailable = false;
@ -1687,12 +1686,8 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) {
this.companyName = company.companyName; this.companyName = company.companyName;
this.companyPosition = pos; this.companyPosition = pos;
if (this.firstJobRecvd === false) { document.getElementById("world-menu-header").click();
this.firstJobRecvd = true; document.getElementById("world-menu-header").click();
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
if (leaveCompany) { if (leaveCompany) {
if (sing) {return true;} if (sing) {return true;}
@ -1797,14 +1792,10 @@ PlayerObject.prototype.applyForAgentJob = function(sing=false) {
PlayerObject.prototype.applyForEmployeeJob = function(sing=false) { PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.Employee)) { if (this.isQualified(company, CompanyPositions.Employee)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
this.companyName = company.companyName; this.companyName = company.companyName;
this.companyPosition = CompanyPositions.Employee; this.companyPosition = CompanyPositions.Employee;
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;} if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed at " + this.companyName); dialogBoxCreate("Congratulations, you are now employed at " + this.companyName);
Engine.loadLocationContent(); Engine.loadLocationContent();
@ -1817,14 +1808,10 @@ PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) { PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.PartTimeEmployee)) { if (this.isQualified(company, CompanyPositions.PartTimeEmployee)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
this.companyName = company.companyName; this.companyName = company.companyName;
this.companyPosition = CompanyPositions.PartTimeEmployee; this.companyPosition = CompanyPositions.PartTimeEmployee;
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;} if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName); dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName);
Engine.loadLocationContent(); Engine.loadLocationContent();
@ -1837,14 +1824,10 @@ PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
PlayerObject.prototype.applyForWaiterJob = function(sing=false) { PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.Waiter)) { if (this.isQualified(company, CompanyPositions.Waiter)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
this.companyName = company.companyName; this.companyName = company.companyName;
this.companyPosition = CompanyPositions.Waiter; this.companyPosition = CompanyPositions.Waiter;
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;} if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName); dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName);
Engine.loadLocationContent(); Engine.loadLocationContent();
@ -1857,14 +1840,10 @@ PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) { PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.PartTimeWaiter)) { if (this.isQualified(company, CompanyPositions.PartTimeWaiter)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
this.companyName = company.companyName; this.companyName = company.companyName;
this.companyPosition = CompanyPositions.PartTimeWaiter; this.companyPosition = CompanyPositions.PartTimeWaiter;
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;} if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName); dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName);
Engine.loadLocationContent(); Engine.loadLocationContent();

@ -148,6 +148,10 @@ function prestigeAugmentation() {
Terminal.resetTerminalInput(); Terminal.resetTerminalInput();
Engine.loadTerminalContent(); Engine.loadTerminalContent();
// Refresh Main Menu (the 'World' menu, specifically)
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Red Pill //Red Pill
if (augmentationExists(AugmentationNames.TheRedPill) && if (augmentationExists(AugmentationNames.TheRedPill) &&
Augmentations[AugmentationNames.TheRedPill].owned) { Augmentations[AugmentationNames.TheRedPill].owned) {
@ -319,6 +323,10 @@ function prestigeSourceFile() {
Player.corporation = null; Player.corporation = null;
Player.bladeburner = null; Player.bladeburner = null;
// Refresh Main Menu (the 'World' menu, specifically)
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Gain int exp //Gain int exp
Player.gainIntelligenceExp(5); Player.gainIntelligenceExp(5);
} }

@ -198,16 +198,12 @@ function loadGame(saveString) {
//that everything should be available //that everything should be available
Player.firstFacInvRecvd = true; Player.firstFacInvRecvd = true;
Player.firstAugPurchased = true; Player.firstAugPurchased = true;
Player.firstJobRecvd = true;
Player.firstTimeTraveled = true; Player.firstTimeTraveled = true;
Player.firstProgramAvailable = true; Player.firstProgramAvailable = true;
} else { } else {
if (Player.factions.length > 0 || Player.factionInvitations.length > 0) { if (Player.factions.length > 0 || Player.factionInvitations.length > 0) {
Player.firstFacInvRecvd = true; Player.firstFacInvRecvd = true;
} }
if (Player.companyName !== "" || Player.companyPosition !== "") {
Player.firstJobRecvd = true;
}
if (Player.hacking_skill >= 25) { if (Player.hacking_skill >= 25) {
Player.firstScriptAvailable = true; Player.firstScriptAvailable = true;
} }
@ -417,16 +413,12 @@ function loadImportedGame(saveObj, saveString) {
//that everything should be available //that everything should be available
Player.firstFacInvRecvd = true; Player.firstFacInvRecvd = true;
Player.firstAugPurchased = true; Player.firstAugPurchased = true;
Player.firstJobRecvd = true;
Player.firstTimeTraveled = true; Player.firstTimeTraveled = true;
Player.firstProgramAvailable = true; Player.firstProgramAvailable = true;
} else { } else {
if (Player.factions.length > 0 || Player.factionInvitations.length > 0) { if (Player.factions.length > 0 || Player.factionInvitations.length > 0) {
Player.firstFacInvRecvd = true; Player.firstFacInvRecvd = true;
} }
if (Player.companyName !== "" || Player.companyPosition !== "") {
Player.firstJobRecvd = true;
}
if (Player.hacking_skill >= 25) { if (Player.hacking_skill >= 25) {
Player.firstScriptAvailable = true; Player.firstScriptAvailable = true;
} }

@ -344,12 +344,15 @@ function processSingleServerGrowth(server, numCycles) {
} }
function prestigeHomeComputer(homeComp) { function prestigeHomeComputer(homeComp) {
const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name);
homeComp.programs.length = 0; //Remove programs homeComp.programs.length = 0; //Remove programs
homeComp.runningScripts = []; homeComp.runningScripts = [];
homeComp.serversOnNetwork = []; homeComp.serversOnNetwork = [];
homeComp.isConnectedTo = true; homeComp.isConnectedTo = true;
homeComp.ramUsed = 0; homeComp.ramUsed = 0;
homeComp.programs.push(Programs.NukeProgram.name); homeComp.programs.push(Programs.NukeProgram.name);
if (hasBitflume) { homeComp.programs.push(Programs.BitFlume.name); }
//Update RAM usage on all scripts //Update RAM usage on all scripts
homeComp.scripts.forEach(function(script) { homeComp.scripts.forEach(function(script) {

@ -1,4 +1,5 @@
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { getRandomInt } from "../utils/helpers/getRandomInt";
/** /**
* Represents the valuation of a company in the World Stock Exchange. * Represents the valuation of a company in the World Stock Exchange.
@ -16,6 +17,11 @@ export class Stock {
*/ */
b: boolean; b: boolean;
/**
* Maximum price of a stock (per share)
*/
readonly cap: number;
/** /**
* Maximum volatility * Maximum volatility
*/ */
@ -83,6 +89,7 @@ export class Stock {
this.mv = mv; this.mv = mv;
this.b = b; this.b = b;
this.otlkMag = otlkMag; this.otlkMag = otlkMag;
this.cap = getRandomInt(initPrice * 1e3, initPrice * 25e3);
this.posTxtEl = null; this.posTxtEl = null;
} }

@ -23,8 +23,6 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxGetInput, yesNoBoxClose, yesNoTxtInpBoxGetInput, yesNoBoxClose,
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox"; yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox";
let StockPriceCap = 1e9; //Put a limit on how high a price can go
var OrderTypes = { var OrderTypes = {
LimitBuy: "Limit Buy Order", LimitBuy: "Limit Buy Order",
LimitSell: "Limit Sell Order", LimitSell: "Limit Sell Order",
@ -227,136 +225,138 @@ function initStockMarket() {
} }
} }
const randInt = getRandomInt;
var ecorp = Locations.AevumECorp; var ecorp = Locations.AevumECorp;
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], getRandomInt(40, 50)/100, true, 19, getRandomInt(17e3, 28e3)); var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], randInt(40, 50) / 100, true, 19, randInt(17e3, 28e3));
StockMarket[ecorp] = ecorpStk; StockMarket[ecorp] = ecorpStk;
var megacorp = Locations.Sector12MegaCorp; var megacorp = Locations.Sector12MegaCorp;
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], getRandomInt(40,50)/100, true, 19, getRandomInt(24e3, 34e3)); var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], randInt(40,50)/100, true, 19, randInt(24e3, 34e3));
StockMarket[megacorp] = megacorpStk; StockMarket[megacorp] = megacorpStk;
var blade = Locations.Sector12BladeIndustries; var blade = Locations.Sector12BladeIndustries;
var bladeStk = new Stock(blade, StockSymbols[blade], getRandomInt(70, 80)/100, true, 13, getRandomInt(12e3, 25e3)); var bladeStk = new Stock(blade, StockSymbols[blade], randInt(70, 80)/100, true, 13, randInt(12e3, 25e3));
StockMarket[blade] = bladeStk; StockMarket[blade] = bladeStk;
var clarke = Locations.AevumClarkeIncorporated; var clarke = Locations.AevumClarkeIncorporated;
var clarkeStk = new Stock(clarke, StockSymbols[clarke], getRandomInt(65, 75)/100, true, 12, getRandomInt(10e3, 25e3)); var clarkeStk = new Stock(clarke, StockSymbols[clarke], randInt(65, 75)/100, true, 12, randInt(10e3, 25e3));
StockMarket[clarke] = clarkeStk; StockMarket[clarke] = clarkeStk;
var omnitek = Locations.VolhavenOmniTekIncorporated; var omnitek = Locations.VolhavenOmniTekIncorporated;
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], getRandomInt(60, 70)/100, true, 12, getRandomInt(32e3, 43e3)); var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], randInt(60, 70)/100, true, 12, randInt(32e3, 43e3));
StockMarket[omnitek] = omnitekStk; StockMarket[omnitek] = omnitekStk;
var foursigma = Locations.Sector12FourSigma; var foursigma = Locations.Sector12FourSigma;
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], getRandomInt(100, 110)/100, true, 17, getRandomInt(50e3, 80e3)); var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], randInt(100, 110)/100, true, 17, randInt(50e3, 80e3));
StockMarket[foursigma] = foursigmaStk; StockMarket[foursigma] = foursigmaStk;
var kuaigong = Locations.ChongqingKuaiGongInternational; var kuaigong = Locations.ChongqingKuaiGongInternational;
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], getRandomInt(75, 85)/100, true, 10, getRandomInt(16e3, 28e3)); var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], randInt(75, 85)/100, true, 10, randInt(16e3, 28e3));
StockMarket[kuaigong] = kuaigongStk; StockMarket[kuaigong] = kuaigongStk;
var fulcrum = Locations.AevumFulcrumTechnologies; var fulcrum = Locations.AevumFulcrumTechnologies;
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], getRandomInt(120, 130)/100, true, 16, getRandomInt(29e3, 36e3)); var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], randInt(120, 130)/100, true, 16, randInt(29e3, 36e3));
StockMarket[fulcrum] = fulcrumStk; StockMarket[fulcrum] = fulcrumStk;
var storm = Locations.IshimaStormTechnologies; var storm = Locations.IshimaStormTechnologies;
var stormStk = new Stock(storm, StockSymbols[storm], getRandomInt(80, 90)/100, true, 7, getRandomInt(20e3, 25e3)); var stormStk = new Stock(storm, StockSymbols[storm], randInt(80, 90)/100, true, 7, randInt(20e3, 25e3));
StockMarket[storm] = stormStk; StockMarket[storm] = stormStk;
var defcomm = Locations.NewTokyoDefComm; var defcomm = Locations.NewTokyoDefComm;
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], getRandomInt(60, 70)/100, true, 10, getRandomInt(6e3, 19e3)); var defcommStk = new Stock(defcomm, StockSymbols[defcomm], randInt(60, 70)/100, true, 10, randInt(6e3, 19e3));
StockMarket[defcomm] = defcommStk; StockMarket[defcomm] = defcommStk;
var helios = Locations.VolhavenHeliosLabs; var helios = Locations.VolhavenHeliosLabs;
var heliosStk = new Stock(helios, StockSymbols[helios], getRandomInt(55, 65)/100, true, 9, getRandomInt(10e3, 18e3)); var heliosStk = new Stock(helios, StockSymbols[helios], randInt(55, 65)/100, true, 9, randInt(10e3, 18e3));
StockMarket[helios] = heliosStk; StockMarket[helios] = heliosStk;
var vitalife = Locations.NewTokyoVitaLife; var vitalife = Locations.NewTokyoVitaLife;
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], getRandomInt(70, 80)/100, true, 7, getRandomInt(8e3, 14e3)); var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], randInt(70, 80)/100, true, 7, randInt(8e3, 14e3));
StockMarket[vitalife] = vitalifeStk; StockMarket[vitalife] = vitalifeStk;
var icarus = Locations.Sector12IcarusMicrosystems; var icarus = Locations.Sector12IcarusMicrosystems;
var icarusStk = new Stock(icarus, StockSymbols[icarus], getRandomInt(60, 70)/100, true, 7.5, getRandomInt(12e3, 24e3)); var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3));
StockMarket[icarus] = icarusStk; StockMarket[icarus] = icarusStk;
var universalenergy = Locations.Sector12UniversalEnergy; var universalenergy = Locations.Sector12UniversalEnergy;
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], getRandomInt(50, 60)/100, true, 10, getRandomInt(16e3, 29e3)); var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], randInt(50, 60)/100, true, 10, randInt(16e3, 29e3));
StockMarket[universalenergy] = universalenergyStk; StockMarket[universalenergy] = universalenergyStk;
var aerocorp = Locations.AevumAeroCorp; var aerocorp = Locations.AevumAeroCorp;
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], getRandomInt(55, 65)/100, true, 6, getRandomInt(8e3, 17e3)); var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], randInt(55, 65)/100, true, 6, randInt(8e3, 17e3));
StockMarket[aerocorp] = aerocorpStk; StockMarket[aerocorp] = aerocorpStk;
var omnia = Locations.VolhavenOmniaCybersystems; var omnia = Locations.VolhavenOmniaCybersystems;
var omniaStk = new Stock(omnia, StockSymbols[omnia], getRandomInt(65, 75)/100, true, 4.5, getRandomInt(6e3, 15e3)); var omniaStk = new Stock(omnia, StockSymbols[omnia], randInt(65, 75)/100, true, 4.5, randInt(6e3, 15e3));
StockMarket[omnia] = omniaStk; StockMarket[omnia] = omniaStk;
var solaris = Locations.ChongqingSolarisSpaceSystems; var solaris = Locations.ChongqingSolarisSpaceSystems;
var solarisStk = new Stock(solaris, StockSymbols[solaris], getRandomInt(70, 80)/100, true, 8.5, getRandomInt(14e3, 28e3)); var solarisStk = new Stock(solaris, StockSymbols[solaris], randInt(70, 80)/100, true, 8.5, randInt(14e3, 28e3));
StockMarket[solaris] = solarisStk; StockMarket[solaris] = solarisStk;
var globalpharm = Locations.NewTokyoGlobalPharmaceuticals; var globalpharm = Locations.NewTokyoGlobalPharmaceuticals;
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], getRandomInt(55, 65)/100, true, 10.5, getRandomInt(12e3, 30e3)); var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], randInt(55, 65)/100, true, 10.5, randInt(12e3, 30e3));
StockMarket[globalpharm] = globalpharmStk; StockMarket[globalpharm] = globalpharmStk;
var nova = Locations.IshimaNovaMedical; var nova = Locations.IshimaNovaMedical;
var novaStk = new Stock(nova, StockSymbols[nova], getRandomInt(70, 80)/100, true, 5, getRandomInt(15e3, 27e3)); var novaStk = new Stock(nova, StockSymbols[nova], randInt(70, 80)/100, true, 5, randInt(15e3, 27e3));
StockMarket[nova] = novaStk; StockMarket[nova] = novaStk;
var watchdog = Locations.AevumWatchdogSecurity; var watchdog = Locations.AevumWatchdogSecurity;
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], getRandomInt(240, 260)/100, true, 1.5, getRandomInt(4e3, 8.5e3)); var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], randInt(240, 260)/100, true, 1.5, randInt(4e3, 8.5e3));
StockMarket[watchdog] = watchdogStk; StockMarket[watchdog] = watchdogStk;
var lexocorp = Locations.VolhavenLexoCorp; var lexocorp = Locations.VolhavenLexoCorp;
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], getRandomInt(115, 135)/100, true, 6, getRandomInt(4.5e3, 8e3)); var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], randInt(115, 135)/100, true, 6, randInt(4.5e3, 8e3));
StockMarket[lexocorp] = lexocorpStk; StockMarket[lexocorp] = lexocorpStk;
var rho = Locations.AevumRhoConstruction; var rho = Locations.AevumRhoConstruction;
var rhoStk = new Stock(rho, StockSymbols[rho], getRandomInt(50, 70)/100, true, 1, getRandomInt(2e3, 7e3)); var rhoStk = new Stock(rho, StockSymbols[rho], randInt(50, 70)/100, true, 1, randInt(2e3, 7e3));
StockMarket[rho] = rhoStk; StockMarket[rho] = rhoStk;
var alpha = Locations.Sector12AlphaEnterprises; var alpha = Locations.Sector12AlphaEnterprises;
var alphaStk = new Stock(alpha, StockSymbols[alpha], getRandomInt(175, 205)/100, true, 10, getRandomInt(4e3, 8.5e3)); var alphaStk = new Stock(alpha, StockSymbols[alpha], randInt(175, 205)/100, true, 10, randInt(4e3, 8.5e3));
StockMarket[alpha] = alphaStk; StockMarket[alpha] = alphaStk;
var syscore = Locations.VolhavenSysCoreSecurities; var syscore = Locations.VolhavenSysCoreSecurities;
var syscoreStk = new Stock(syscore, StockSymbols[syscore], getRandomInt(150, 170)/100, true, 3, getRandomInt(3e3, 8e3)); var syscoreStk = new Stock(syscore, StockSymbols[syscore], randInt(150, 170)/100, true, 3, randInt(3e3, 8e3));
StockMarket[syscore] = syscoreStk; StockMarket[syscore] = syscoreStk;
var computek = Locations.VolhavenCompuTek; var computek = Locations.VolhavenCompuTek;
var computekStk = new Stock(computek, StockSymbols[computek], getRandomInt(80, 100)/100, true, 4, getRandomInt(1e3, 6e3)); var computekStk = new Stock(computek, StockSymbols[computek], randInt(80, 100)/100, true, 4, randInt(1e3, 6e3));
StockMarket[computek] = computekStk; StockMarket[computek] = computekStk;
var netlink = Locations.AevumNetLinkTechnologies; var netlink = Locations.AevumNetLinkTechnologies;
var netlinkStk = new Stock(netlink, StockSymbols[netlink], getRandomInt(400, 430)/100, true, 1, getRandomInt(1e3, 5e3)); var netlinkStk = new Stock(netlink, StockSymbols[netlink], randInt(400, 430)/100, true, 1, randInt(1e3, 5e3));
StockMarket[netlink] = netlinkStk; StockMarket[netlink] = netlinkStk;
var omega = Locations.IshimaOmegaSoftware; var omega = Locations.IshimaOmegaSoftware;
var omegaStk = new Stock(omega, StockSymbols[omega], getRandomInt(90, 110)/100, true, 0.5, getRandomInt(1e3, 8e3)); var omegaStk = new Stock(omega, StockSymbols[omega], randInt(90, 110)/100, true, 0.5, randInt(1e3, 8e3));
StockMarket[omega] = omegaStk; StockMarket[omega] = omegaStk;
var fns = Locations.Sector12FoodNStuff; var fns = Locations.Sector12FoodNStuff;
var fnsStk = new Stock(fns, StockSymbols[fns], getRandomInt(70, 80)/100, false, 1, getRandomInt(500, 4.5e3)); var fnsStk = new Stock(fns, StockSymbols[fns], randInt(70, 80)/100, false, 1, randInt(500, 4.5e3));
StockMarket[fns] = fnsStk; StockMarket[fns] = fnsStk;
var sigmacosm = "Sigma Cosmetics"; var sigmacosm = "Sigma Cosmetics";
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], getRandomInt(260, 300)/100, true, 0, getRandomInt(1.5e3, 3.5e3)); var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], randInt(260, 300)/100, true, 0, randInt(1.5e3, 3.5e3));
StockMarket[sigmacosm] = sigmacosmStk; StockMarket[sigmacosm] = sigmacosmStk;
var joesguns = "Joes Guns"; var joesguns = "Joes Guns";
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], getRandomInt(360, 400)/100, true, 1, getRandomInt(250, 1.5e3)); var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], randInt(360, 400)/100, true, 1, randInt(250, 1.5e3));
StockMarket[joesguns] = joesgunsStk; StockMarket[joesguns] = joesgunsStk;
var catalyst = "Catalyst Ventures"; var catalyst = "Catalyst Ventures";
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], getRandomInt(120, 175)/100, true, 13.5, getRandomInt(250, 1.5e3)); var catalystStk = new Stock(catalyst, StockSymbols[catalyst], randInt(120, 175)/100, true, 13.5, randInt(250, 1.5e3));
StockMarket[catalyst] = catalystStk; StockMarket[catalyst] = catalystStk;
var microdyne = "Microdyne Technologies"; var microdyne = "Microdyne Technologies";
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], getRandomInt(70, 80)/100, true, 8, getRandomInt(15e3, 30e3)); var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], randInt(70, 80)/100, true, 8, randInt(15e3, 30e3));
StockMarket[microdyne] = microdyneStk; StockMarket[microdyne] = microdyneStk;
var titanlabs = "Titan Laboratories"; var titanlabs = "Titan Laboratories";
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], getRandomInt(50, 70)/100, true, 11, getRandomInt(12e3, 24e3)); var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], randInt(50, 70)/100, true, 11, randInt(12e3, 24e3));
StockMarket[titanlabs] = titanlabsStk; StockMarket[titanlabs] = titanlabsStk;
var orders = {}; var orders = {};
@ -368,6 +368,9 @@ function initStockMarket() {
} }
} }
StockMarket["Orders"] = orders; StockMarket["Orders"] = orders;
StockMarket.storedCycles = 0;
StockMarket.lastUpdate = 0;
} }
function initSymbolToStockMap() { function initSymbolToStockMap() {
@ -375,7 +378,7 @@ function initSymbolToStockMap() {
if (StockSymbols.hasOwnProperty(name)) { if (StockSymbols.hasOwnProperty(name)) {
var stock = StockMarket[name]; var stock = StockMarket[name];
if (stock == null) { if (stock == null) {
console.log("ERROR finding stock"); console.error(`Could not find Stock for ${name}`);
continue; continue;
} }
var symbol = StockSymbols[name]; var symbol = StockSymbols[name];
@ -543,25 +546,40 @@ function sellShort(stock, shares, workerScript=null) {
return true; return true;
} }
function updateStockPrices() { function processStockPrices(numCycles=1) {
if (StockMarket.storedCycles == null || isNaN(StockMarket.storedCycles)) { StockMarket.storedCycles = 0; }
StockMarket.storedCycles += numCycles;
// Stock Prices updated every 6 seconds on average. But if there are stored
// cycles they update 50% faster, so every 4 seconds
const msPerStockUpdate = 6e3;
const cyclesPerStockUpdate = msPerStockUpdate / CONSTANTS.MilliPerCycle;
if (StockMarket.storedCycles < cyclesPerStockUpdate) { return; }
const timeNow = new Date().getTime();
if (timeNow - StockMarket.lastUpdate < 4e3) { return; }
StockMarket.lastUpdate = timeNow;
StockMarket.storedCycles -= cyclesPerStockUpdate;
var v = Math.random(); var v = Math.random();
for (var name in StockMarket) { for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) { if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name]; var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;} if (!(stock instanceof Stock)) { continue; }
var av = (v * stock.mv) / 100; var av = (v * stock.mv) / 100;
if (isNaN(av)) {av = .02;} if (isNaN(av)) {av = .02;}
var chc = 50; var chc = 50;
if (stock.b) { if (stock.b) {
chc = (chc + stock.otlkMag)/100; chc = (chc + stock.otlkMag) / 100;
if (isNaN(chc)) {chc = 0.5;} if (isNaN(chc)) {chc = 0.5;}
} else { } else {
chc = (chc - stock.otlkMag)/100; chc = (chc - stock.otlkMag) / 100;
if (isNaN(chc)) {chc = 0.5;} if (isNaN(chc)) {chc = 0.5;}
} }
if (stock.price >= StockPriceCap) { if (stock.price >= stock.cap) {
chc = -1; //Limit on stock price chc = 0.1; // "Soft Limit" on stock price. It could still go up but its unlikely
stock.b = false; stock.b = false;
} }
@ -725,6 +743,7 @@ function displayStockMarketContent() {
"Buy 4S Market Data Access - " + numeralWrapper.format(CONSTANTS.MarketData4SCost, '($0.000a)'), "Buy 4S Market Data Access - " + numeralWrapper.format(CONSTANTS.MarketData4SCost, '($0.000a)'),
"4S Market Data - Purchased"); "4S Market Data - Purchased");
marketDataButton.addEventListener("click", function() { marketDataButton.addEventListener("click", function() {
if (Player.money.lt(CONSTANTS.MarketData4SCost)) { return false; }
Player.has4SData = true; Player.has4SData = true;
Player.loseMoney(CONSTANTS.MarketData4SCost); Player.loseMoney(CONSTANTS.MarketData4SCost);
displayStockMarketContent(); displayStockMarketContent();
@ -764,6 +783,7 @@ function displayStockMarketContent() {
"4S Market Data TIX API - Purchased"); "4S Market Data TIX API - Purchased");
if (Player.hasTixApiAccess) { if (Player.hasTixApiAccess) {
marketDataTixButton.addEventListener("click", function() { marketDataTixButton.addEventListener("click", function() {
if (Player.money.lt(CONSTANTS.MarketDataTixApi4SCost)) { return false; }
Player.has4SDataTixApi = true; Player.has4SDataTixApi = true;
Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost); Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost);
displayStockMarketContent(); displayStockMarketContent();
@ -875,6 +895,7 @@ function displayStockMarketContent() {
//Switch to Portfolio Mode Button //Switch to Portfolio Mode Button
if (modeBtn) { if (modeBtn) {
stockMarketPortfolioMode = false;
modeBtn.innerHTML = "Switch to 'Portfolio' Mode" + modeBtn.innerHTML = "Switch to 'Portfolio' Mode" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>"; "<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
modeBtn.addEventListener("click", switchToPortfolioMode); modeBtn.addEventListener("click", switchToPortfolioMode);
@ -1459,6 +1480,6 @@ function updateStockOrderList(stock) {
export {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols, export {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock, initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
sellStock, shortStock, sellShort, updateStockPrices, displayStockMarketContent, sellStock, shortStock, sellShort, processStockPrices, displayStockMarketContent,
updateStockTicker, updateStockPlayerPosition, loadStockMarket, updateStockTicker, updateStockPlayerPosition, loadStockMarket,
setStockMarketContentCreated, placeOrder, cancelOrder, Order, OrderTypes, PositionTypes}; setStockMarketContentCreated, placeOrder, cancelOrder, Order, OrderTypes, PositionTypes};

@ -105,7 +105,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
" 2 + 2\n", " 2 + 2\n",
" 2 + 1 + 1\n", " 2 + 1 + 1\n",
" 1 + 1 + 1 + 1\n\n", " 1 + 1 + 1 + 1\n\n",
`How many different ways can ${n} be written as a sum of at least`, `How many different ways can the number ${n} be written as a sum of at least`,
"two positive integers?"].join(" "); "two positive integers?"].join(" ");
}, },
difficulty: 1.5, difficulty: 1.5,
@ -230,8 +230,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
desc: (arr: number[]) => { desc: (arr: number[]) => {
return ["You are given the following array of integers:\n\n", return ["You are given the following array of integers:\n\n",
`${arr}\n\n`, `${arr}\n\n`,
"Each element in the array represents your maximum jump length", "Each element in the array represents your MAXIMUM jump length",
"at that position. Assuming you are initially positioned", "at that position. This means that if you are at position i and your",
"maximum jump length is n, you can jump to any position from",
"i to i+n.",
"\n\nAssuming you are initially positioned",
"at the start of the array, determine whether you are", "at the start of the array, determine whether you are",
"able to reach the last index exactly.\n\n", "able to reach the last index exactly.\n\n",
"Your answer should be submitted as 1 or 0, representing true and false respectively"].join(" "); "Your answer should be submitted as 1 or 0, representing true and false respectively"].join(" ");
@ -242,7 +245,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
const arr: number[] = []; const arr: number[] = [];
arr.length = len; arr.length = len;
for (let i: number = 0; i < arr.length; ++i) { for (let i: number = 0; i < arr.length; ++i) {
arr[i] = getRandomInt(0, 24); if (Math.random() < 0.2) {
arr[i] = 0; // 20% chance of being 0
} else {
arr[i] = getRandomInt(0, 10);
}
} }
return arr; return arr;
@ -278,7 +285,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
difficulty: 3, difficulty: 3,
gen: () => { gen: () => {
const intervals: number[][] = []; const intervals: number[][] = [];
const numIntervals: number = getRandomInt(1, 15); const numIntervals: number = getRandomInt(1, 20);
for (let i: number = 0; i < numIntervals; ++i) { for (let i: number = 0; i < numIntervals; ++i) {
const start: number = getRandomInt(1, 25); const start: number = getRandomInt(1, 25);
const end: number = start + getRandomInt(1, 10); const end: number = start + getRandomInt(1, 10);
@ -322,8 +329,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"an array with all possible valid IP address combinations", "an array with all possible valid IP address combinations",
"that can be created from the string:\n\n", "that can be created from the string:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Example:\n\n", "Note that an octet cannot begin with a '0' unless the number",
"25525511135 -> [255.255.11.135, 255.255.111.35]"].join(" "); "itself is actually 0. For example, '192.168.010.1' is not a valid IP.\n\n",
"Examples:\n\n",
"25525511135 -> [255.255.11.135, 255.255.111.35]\n",
"1938718066 -> [193.87.180.66]"].join(" ");
}, },
difficulty: 3, difficulty: 3,
gen: () => { gen: () => {
@ -377,8 +387,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
}, },
{ {
desc: (data: number[]) => { desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element", return ["You are given the following array of stock prices (which are numbers)",
"represents the stock price on day i:\n\n", "where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Determine the maximum possible profit you can earn using at most", "Determine the maximum possible profit you can earn using at most",
"one transaction (i.e. you can only buy and sell the stock once). If no profit can be made", "one transaction (i.e. you can only buy and sell the stock once). If no profit can be made",
@ -411,8 +421,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
}, },
{ {
desc: (data: number[]) => { desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element", return ["You are given the following array of stock prices (which are numbers)",
"represents the stock price on day i:\n\n", "where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Determine the maximum possible profit you can earn using as many", "Determine the maximum possible profit you can earn using as many",
"transactions as you'd like. A transaction is defined as buying", "transactions as you'd like. A transaction is defined as buying",
@ -445,8 +455,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
}, },
{ {
desc: (data: number[]) => { desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element", return ["You are given the following array of stock prices (which are numbers)",
"represents the stock price on day i:\n\n", "where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Determine the maximum possible profit you can earn using at most", "Determine the maximum possible profit you can earn using at most",
"two transactions. A transaction is defined as buying", "two transactions. A transaction is defined as buying",

@ -205,7 +205,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
isHacking: false, isHacking: false,
name: "Human Trafficking", name: "Human Trafficking",
params: { params: {
baseRespect: 0.005, baseWanted: 0.4, baseMoney: 120, baseRespect: 0.004, baseWanted: 1, baseMoney: 120,
hackWeight: 30, strWeight: 5, defWeight: 5, dexWeight: 30, chaWeight: 30, hackWeight: 30, strWeight: 5, defWeight: 5, dexWeight: 30, chaWeight: 30,
difficulty: 36, difficulty: 36,
territory: { territory: {
@ -221,7 +221,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
isHacking: false, isHacking: false,
name: "Terrorism", name: "Terrorism",
params: { params: {
baseRespect: 0.01, baseWanted: 1.5, baseRespect: 0.01, baseWanted: 5,
hackWeight: 20, strWeight: 20, defWeight: 20, dexWeight: 20, chaWeight: 20, hackWeight: 20, strWeight: 20, defWeight: 20, dexWeight: 20, chaWeight: 20,
difficulty: 36, difficulty: 36,
territory: { territory: {

@ -24,8 +24,7 @@ import {BitNodes, initBitNodes,
import {Bladeburner} from "./Bladeburner"; import {Bladeburner} from "./Bladeburner";
import {CharacterOverview} from "./CharacterOverview"; import {CharacterOverview} from "./CharacterOverview";
import {cinematicTextFlag} from "./CinematicText"; import {cinematicTextFlag} from "./CinematicText";
import {CodingContract, CodingContractRewardType, import {generateRandomContract} from "./CodingContractGenerator";
CodingContractTypes} from "./CodingContracts";
import {CompanyPositions, initCompanies} from "./Company"; import {CompanyPositions, initCompanies} from "./Company";
import {Corporation} from "./CompanyManagement"; import {Corporation} from "./CompanyManagement";
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "./Constants";
@ -67,7 +66,7 @@ import {SpecialServerIps, initSpecialServerIps} from "./SpecialServerIps
import {StockMarket, StockSymbols, import {StockMarket, StockSymbols,
SymbolToStockMap, initStockSymbols, SymbolToStockMap, initStockSymbols,
initSymbolToStockMap, stockMarketCycle, initSymbolToStockMap, stockMarketCycle,
updateStockPrices, processStockPrices,
displayStockMarketContent} from "./StockMarket"; displayStockMarketContent} from "./StockMarket";
import {Terminal, postNetburnerText} from "./Terminal"; import {Terminal, postNetburnerText} from "./Terminal";
import {KEY} from "../utils/helpers/keyCodes"; import {KEY} from "../utils/helpers/keyCodes";
@ -79,6 +78,7 @@ import 'normalize.css';
import "../css/styles.scss"; import "../css/styles.scss";
import "../css/buttons.scss"; import "../css/buttons.scss";
import "../css/mainmenu.scss"; import "../css/mainmenu.scss";
import "../css/characteroverview.scss";
import "../css/terminal.scss"; import "../css/terminal.scss";
import "../css/menupages.scss"; import "../css/menupages.scss";
import "../css/workinprogress.scss"; import "../css/workinprogress.scss";
@ -173,6 +173,7 @@ const Engine = {
worldMainMenuButton: null, worldMainMenuButton: null,
travelMainMenuButton: null, travelMainMenuButton: null,
jobMainMenuButton: null, jobMainMenuButton: null,
stockmarketMainMenuButton: null,
createProgramMainMenuButton: null, createProgramMainMenuButton: null,
factionsMainMenuButton: null, factionsMainMenuButton: null,
augmentationsMainMenuButton: null, augmentationsMainMenuButton: null,
@ -886,6 +887,11 @@ const Engine = {
} }
} }
// Update stock prices
if (Player.hasWseAccount) {
processStockPrices(numCycles);
}
//Gang, if applicable //Gang, if applicable
if (Player.bitNodeN == 2 && Player.inGang()) { if (Player.bitNodeN == 2 && Player.inGang()) {
Player.gang.process(numCycles, Player); Player.gang.process(numCycles, Player);
@ -936,7 +942,6 @@ const Engine = {
checkFactionInvitations: 100, //Check whether you qualify for any faction invitations checkFactionInvitations: 100, //Check whether you qualify for any faction invitations
passiveFactionGrowth: 600, passiveFactionGrowth: 600,
messages: 150, messages: 150,
stockTick: 30, //Update stock prices
sCr: 1500, sCr: 1500,
mechanicProcess: 5, //Processes certain mechanics (Corporation, Bladeburner) mechanicProcess: 5, //Processes certain mechanics (Corporation, Bladeburner)
contractGeneration: 3000 //Generate Coding Contracts contractGeneration: 3000 //Generate Coding Contracts
@ -1057,13 +1062,6 @@ const Engine = {
} }
} }
if (Engine.Counters.stockTick <= 0) {
if (Player.hasWseAccount) {
updateStockPrices();
}
Engine.Counters.stockTick = 30;
}
if (Engine.Counters.sCr <= 0) { if (Engine.Counters.sCr <= 0) {
if (Player.hasWseAccount) { if (Player.hasWseAccount) {
stockMarketCycle(); stockMarketCycle();
@ -1089,71 +1087,7 @@ const Engine = {
if (Engine.Counters.contractGeneration <= 0) { if (Engine.Counters.contractGeneration <= 0) {
// X% chance of a contract being generated // X% chance of a contract being generated
if (Math.random() <= 0.25) { if (Math.random() <= 0.25) {
// First select a random problem type generateRandomContract();
const problemTypes = Object.keys(CodingContractTypes);
let randIndex = getRandomInt(0, problemTypes.length - 1);
let problemType = problemTypes[randIndex];
// Then select a random reward type. 'Money' will always be the last reward type
var reward = {};
reward.type = getRandomInt(0, CodingContractRewardType.Money);
// Change type based on certain conditions
var factionsThatAllowHacking = Player.factions.filter((fac) => {
try {
return Factions[fac].getInfo().offerHackingWork;
} catch (e) {
console.error(`Error when trying to filter Hacking Factions for Coding Contract Generation: ${e}`);
return false;
}
});
if (reward.type === CodingContractRewardType.FactionReputation && factionsThatAllowHacking.length === 0) {
reward.type = CodingContractRewardType.CompanyReputation;
}
if (reward.type === CodingContractRewardType.FactionReputationAll && factionsThatAllowHacking.length === 0) {
reward.type = CodingContractRewardType.CompanyReputation;
}
if (reward.type === CodingContractRewardType.CompanyReputation && Player.companyName === "") {
reward.type = CodingContractRewardType.Money;
}
// Add additional information based on the reward type
switch (reward.type) {
case CodingContractRewardType.FactionReputation:
// Get a random faction that player is a part of. That
// faction must allow hacking contracts
var numFactions = factionsThatAllowHacking.length;
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction;
break;
case CodingContractRewardType.CompanyReputation:
if (Player.companyName !== "") {
reward.name = Player.companyName;
} else {
reward.type = CodingContractRewardType.Money;
}
break;
default:
break;
}
// Choose random server
const servers = Object.keys(AllServers);
randIndex = getRandomInt(0, servers.length - 1);
var randServer = AllServers[servers[randIndex]];
while (randServer.purchasedByPlayer === true) {
randIndex = getRandomInt(0, servers.length - 1);
randServer = AllServers[servers[randIndex]];
}
let contractFn = `contract-${getRandomInt(0, 1e6)}`;
while (randServer.contracts.filter((c) => {return c.fn === contractFn}).length > 0) {
contractFn = `contract-${getRandomInt(0, 1e6)}`;
}
if (reward.name) { contractFn += `-${reward.name.replace(/\s/g, "")}`; }
let contract = new CodingContract(contractFn, problemType, reward);
randServer.addContract(contract);
} }
Engine.Counters.contractGeneration = 3000; Engine.Counters.contractGeneration = 3000;
} }
@ -1254,6 +1188,7 @@ const Engine = {
var city = document.getElementById("city-tab"); var city = document.getElementById("city-tab");
var travel = document.getElementById("travel-tab"); var travel = document.getElementById("travel-tab");
var job = document.getElementById("job-tab"); var job = document.getElementById("job-tab");
var stockmarket = document.getElementById("stock-market-tab");
var bladeburner = document.getElementById("bladeburner-tab"); var bladeburner = document.getElementById("bladeburner-tab");
var corp = document.getElementById("corporation-tab"); var corp = document.getElementById("corporation-tab");
var gang = document.getElementById("gang-tab"); var gang = document.getElementById("gang-tab");
@ -1311,6 +1246,11 @@ const Engine = {
//Passive faction rep gain offline //Passive faction rep gain offline
processPassiveFactionRepGain(numCyclesOffline); processPassiveFactionRepGain(numCyclesOffline);
// Stock Market offline progress
if (Player.hasWseAccount) {
processStockPrices(numCyclesOffline);
}
//Gang progress for BitNode 2 //Gang progress for BitNode 2
if (Player.bitNodeN != null && Player.bitNodeN === 2 && Player.inGang()) { if (Player.bitNodeN != null && Player.bitNodeN === 2 && Player.inGang()) {
Player.gang.process(numCyclesOffline, Player); Player.gang.process(numCyclesOffline, Player);
@ -1343,12 +1283,14 @@ const Engine = {
else {factions.style.display = "none";} else {factions.style.display = "none";}
if (Player.firstAugPurchased) {visibleMenuTabs.push(augmentations);} if (Player.firstAugPurchased) {visibleMenuTabs.push(augmentations);}
else {augmentations.style.display = "none";} else {augmentations.style.display = "none";}
if (Player.firstJobRecvd) {visibleMenuTabs.push(job);} if (Player.companyPosition !== "") {visibleMenuTabs.push(job);}
else {job.style.display = "none";} else {job.style.display = "none";}
if (Player.firstTimeTraveled) {visibleMenuTabs.push(travel);} if (Player.firstTimeTraveled) {visibleMenuTabs.push(travel);}
else {travel.style.display = "none";} else {travel.style.display = "none";}
if (Player.firstProgramAvailable) {visibleMenuTabs.push(createProgram);} if (Player.firstProgramAvailable) {visibleMenuTabs.push(createProgram);}
else {createProgram.style.display = "none";} else {createProgram.style.display = "none";}
if (Player.hasWseAccount) {visibleMenuTabs.push(stockmarket);}
else {stockmarket.style.display = "none";}
if(Player.bladeburner instanceof Bladeburner) {visibleMenuTabs.push(bladeburner);} if(Player.bladeburner instanceof Bladeburner) {visibleMenuTabs.push(bladeburner);}
else {bladeburner.style.display = "none";} else {bladeburner.style.display = "none";}
if(Player.corporation instanceof Corporation) {visibleMenuTabs.push(corp);} if(Player.corporation instanceof Corporation) {visibleMenuTabs.push(corp);}
@ -1392,6 +1334,7 @@ const Engine = {
factions.style.display = "none"; factions.style.display = "none";
augmentations.style.display = "none"; augmentations.style.display = "none";
job.style.display = "none"; job.style.display = "none";
stockmarket.style.display = "none";
travel.style.display = "none"; travel.style.display = "none";
createProgram.style.display = "none"; createProgram.style.display = "none";
bladeburner.style.display = "none"; bladeburner.style.display = "none";
@ -1608,28 +1551,38 @@ const Engine = {
} }
worldHdr.onclick = function() { worldHdr.onclick = function() {
var city = document.getElementById("city-tab"); var city = document.getElementById("city-tab");
var cityLink = document.getElementById("city-menu-link"); var cityLink = document.getElementById("city-menu-link");
var travel = document.getElementById("travel-tab"); var travel = document.getElementById("travel-tab");
var travelLink = document.getElementById("travel-menu-link"); var travelLink = document.getElementById("travel-menu-link");
var job = document.getElementById("job-tab"); var job = document.getElementById("job-tab");
var jobLink = document.getElementById("job-menu-link"); var jobLink = document.getElementById("job-menu-link");
var bladeburner = document.getElementById("bladeburner-tab"); var stockmarket = document.getElementById("stock-market-tab");
var bladeburnerLink = document.getElementById("bladeburner-menu-link"); var stockmarketLink = document.getElementById("stock-market-menu-link");
var corporation = document.getElementById("corporation-tab"); var bladeburner = document.getElementById("bladeburner-tab");
var corporationLink = document.getElementById("corporation-menu-link"); var bladeburnerLink = document.getElementById("bladeburner-menu-link");
var gang = document.getElementById("gang-tab"); var corporation = document.getElementById("corporation-tab");
var gangLink = document.getElementById("gang-menu-link"); var corporationLink = document.getElementById("corporation-menu-link");
var gang = document.getElementById("gang-tab");
var gangLink = document.getElementById("gang-menu-link");
// Determine whether certain links should show up
job.style.display = Player.companyPosition !== "" ? "list-item" : "none";
stockmarket.style.display = Player.hasWseAccount ? "list-item" : "none";
bladeburner.style.display = Player.bladeburner instanceof Bladeburner ? "list-item" : "none";
corporation.style.display = Player.corporation instanceof Corporation ? "list-item" : "none";
gang.style.display = Player.inGang() ? "list-item" : "none";
this.classList.toggle("opened"); this.classList.toggle("opened");
if (city.style.maxHeight) { if (city.style.maxHeight) {
Engine.toggleMainMenuHeader(false, Engine.toggleMainMenuHeader(false,
[city, travel, job, bladeburner, corporation, gang], [city, travel, job, stockmarket, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, bladeburnerLink, corporationLink, gangLink] [cityLink, travelLink, jobLink, stockmarketLink, bladeburnerLink, corporationLink, gangLink]
); );
} else { } else {
Engine.toggleMainMenuHeader(true, Engine.toggleMainMenuHeader(true,
[city, travel, job, bladeburner, corporation, gang], [city, travel, job, stockmarket, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, bladeburnerLink, corporationLink, gangLink] [cityLink, travelLink, jobLink, stockmarketLink, bladeburnerLink, corporationLink, gangLink]
); );
} }
} }
@ -1702,6 +1655,12 @@ const Engine = {
return false; return false;
}); });
Engine.Clickables.stockmarketMainMenuButton = clearEventListeners("stock-market-menu-link");
Engine.Clickables.stockmarketMainMenuButton.addEventListener("click", function() {
Engine.loadStockMarketContent();
return false;
});
Engine.Clickables.createProgramMainMenuButton = clearEventListeners("create-program-menu-link"); Engine.Clickables.createProgramMainMenuButton = clearEventListeners("create-program-menu-link");
Engine.Clickables.createProgramMainMenuButton.addEventListener("click", function() { Engine.Clickables.createProgramMainMenuButton.addEventListener("click", function() {

@ -81,6 +81,9 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<li id="job-tab" class="mainmenu-accordion-panel"> <li id="job-tab" class="mainmenu-accordion-panel">
<button id="job-menu-link"> Job </button> <button id="job-menu-link"> Job </button>
</li> </li>
<li id="stock-market-tab" class="mainmenu-accordion-panel">
<button id="stock-market-menu-link"> Stock Market </button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel"> <li id="bladeburner-tab" class="mainmenu-accordion-panel">
<button id="bladeburner-menu-link"> Bladeburner </button> <button id="bladeburner-menu-link"> Bladeburner </button>
</li> </li>
@ -789,8 +792,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
</table> </table>
</div> </div>
<div class="character-quick-options"> <div class="character-quick-options">
<button id="character-overview-save-button">Save Game</button> <button id="character-overview-save-button" class="character-overview-btn">Save Game</button>
<button id="character-overview-options-button">Options</button> <button id="character-overview-options-button" class="character-overview-btn">Options</button>
</div> </div>
</div> </div>
</div> </div>