V0.35.0. Adding netscript_tests in game testbench.

This commit is contained in:
danielyxie 2018-03-03 15:05:33 -06:00
parent 271932b287
commit e3c435270b
57 changed files with 4621 additions and 2581 deletions

@ -155,10 +155,18 @@ a:link, a:visited {
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
border: 1px solid #333333; border: 1px solid #333333;
pointer-events: none;
cursor: default; cursor: default;
} }
.a-link-button-inactive:hover .tooltiptext,
.a-link-button-inactive:hover .tooltiptexthigh,
.a-link-button-inactive:hover .tooltiptextleft {
visibility: visible;
}
.a-link-button-inactive:active {
pointer-events: none;
}
/* Notification icon (for create program right now only) */ /* Notification icon (for create program right now only) */
#create-program-tab { #create-program-tab {
@ -332,6 +340,7 @@ a:link, a:visited {
top:0px; top:0px;
-webkit-animation:status-text 3s 1; -webkit-animation:status-text 3s 1;
background-color: transparent; background-color: transparent;
height: 15%;
} }
#status-text-container { #status-text-container {

@ -43,3 +43,15 @@
-webkit-hyphens: auto; -webkit-hyphens: auto;
-moz-hyphens: auto; -moz-hyphens: auto;
} }
#terminal-input-td {
display: flex;
}
#terminal-input-header {
white-space: pre;
}
#terminal-input-text-box {
flex: 1 1 auto;
}

4647
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -25,27 +25,27 @@ a value to these.
Note that these must be called on an element inside the *hacknetnodes* array, not the array itself. Note that these must be called on an element inside the *hacknetnodes* array, not the array itself.
.. js:function:: hacknetnodes[i].level .. js:attribute:: hacknetnodes[i].level
Returns the level of the corresponding Hacknet Node Returns the level of the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].ram .. js:attribute:: hacknetnodes[i].ram
Returns the amount of RAM on the corresponding Hacknet Node Returns the amount of RAM on the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].cores .. js:attribute:: hacknetnodes[i].cores
Returns the number of cores on the corresponding Hacknet Node Returns the number of cores on the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].totalMoneyGenerated .. js:attribute:: hacknetnodes[i].totalMoneyGenerated
Returns the total amount of money that the corresponding Hacknet Node has earned Returns the total amount of money that the corresponding Hacknet Node has earned
.. js:function:: hacknetnodes[i].onlineTimeSeconds .. js:attribute:: hacknetnodes[i].onlineTimeSeconds
Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed
.. js:function:: hacknetnodes[i].moneyGainRatePerSecond .. js:attribute:: hacknetnodes[i].moneyGainRatePerSecond
Returns the amount of income that the corresponding Hacknet Node earns Returns the amount of income that the corresponding Hacknet Node earns
@ -57,7 +57,7 @@ The following is a list of supported functions/methods for a Hacknet Node object
Note that these must be called on an element inside the *hacknetnodes* array, not the Note that these must be called on an element inside the *hacknetnodes* array, not the
array itself. array itself.
.. js:function:: hacknetnodes[i].upgradeLevel(n); .. js:method:: hacknetnodes[i].upgradeLevel(n)
:param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer :param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer
@ -65,27 +65,27 @@ array itself.
Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false
otherwise. otherwise.
.. js:function:: hacknetnodes[i].upgradeRam() .. js:method:: hacknetnodes[i].upgradeRam()
Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is
successfully upgraded and false otherwise. successfully upgraded and false otherwise.
.. js:function:: hacknetnodes[i].upgradeCore() .. js:method:: hacknetnodes[i].upgradeCore()
Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the
additional core is successfully purchased, and false otherwise. additional core is successfully purchased, and false otherwise.
.. js:function:: hacknetnodes[i].getLevelUpgradeCost(n); .. js:method:: hacknetnodes[i].getLevelUpgradeCost(n)
:param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer :param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer
Returns the cost of upgrading the specified Hacknet Node by *n* levels Returns the cost of upgrading the specified Hacknet Node by *n* levels
.. js:function:: hacknetnodes[i].getRamUpgradeCost() .. js:method:: hacknetnodes[i].getRamUpgradeCost()
Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it. Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it.
.. js:function:: hacknetnodes[i].getCoreUpgradeCost() .. js:method:: hacknetnodes[i].getCoreUpgradeCost()
Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's
number of cores adds one additional core. number of cores adds one additional core.

@ -1,6 +1,135 @@
Netscript Miscellaneous Netscript Miscellaneous
======================= =======================
Netscript Ports
---------------
Netscript ports are endpoints that can be used to communicate between scripts.
A port is implemented as a sort of serialized queue, where you can only write
and read one element at a time from the port. When you read data from a port,
the element that is read is removed from the port.
The :js:func:`read`, :js:func:`write`, :js:func:`clear`, and :js:func:`peek`
Netscript functions can be used to interact with ports.
Right now, there are only 20 ports for Netscript, denoted by the number 1
through 20. When using the functions above, the ports are specified
by passing the number as the first argument.
IMPORTANT: The data inside ports are not saved! This means if you close and
re-open the game, or reload the page then you will lose all of the data in
the ports!
**Example Usage**
Here's a brief example of how ports work. For the sake of simplicity we'll only deal with port 1.
Let's assume Port 1 starts out empty (no data inside). We'll represent the port as such::
[]
Now assume we ran the following simple script::
for (i = 0; i < 10; ++i) {
write(1, i); //Writes the value of i to port 1
}
After this script executes, our script will contain every number from 0 through 9, as so::
[0, 1, 2, 3, 4, 5, 6, 7 , 8, 9]
Then, assume we run the following script::
for (i = 0; i < 3; ++i) {
print(read(1)); //Reads a value from port 1 and then prints it
}
This script above will read the first three values from port 1 and then print them to the script's log. The log will end up looking like::
0
1
2
And the data in port 1 will look like::
[3, 4, 5, 6, 7, 8, 9]
**Port Handles**
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
This handle allows you to access several new port-related functions and the
port's underlying data structure, which is just a Javascript array. The functions are:
.. js:method:: NetscriptPort.write(data)
:param data: Data to write to the port
:returns: If the port is full, the item that is removed from the port is returned.
Otherwise, null is returned.
Writes `data` to the port. Works the same as the Netscript function `write`.
.. js:method:: NetscriptPort.tryWrite(data)
:param data: Data to try to write to the port
:returns: True if the data is successfully written to the port, and false otherwise.
Attempts to write `data` to the Netscript port. If the port is full, the data will
not be written. Otherwise, the data will be written normally.
.. js::method:: NetscriptPort.read()
:returns: The data read from the port. If the port is empty, "NULL PORT DATA" is returned
Removes and returns the first element from the port.
Works the same as the Netscript function `read`
.. js::method:: NetscriptPort.peek()
:returns: The first element in the port, or "NULL PORT DATA" if the port is empty.
Returns the first element in the port, but does not remove it.
Works the same as the Netscript function `peek`
.. js:method:: NetscriptPort.full()
:returns: True if the Netscript Port is full, and false otherwise
.. js:method:: NetscriptPort.empty()
:returns: True if the Netscript Port is empty, and false otherwise
.. js:method:: NetscriptPort.clear()
Clears all data from the port. Works the same as the Netscript function `clear`
.. js:attribute:: NetscriptPort.data
The Netscript port underlying data structure, which is just a Javascript array. All
valid Javascript Array methods can be called on this.
Port Handle Example::
port = getPortHandle(5);
back = port.data.pop(); //Get and remove last element in port
//Remove an element from the port
i = port.data.findIndex("foo");
if (i != -1) {
port.data.slice(i, 1);
}
//Wait for port data before reading
while(port.empty()) {
sleep(10000);
}
res = port.read();
//Wait for there to be room in a port before writing
while (!port.tryWrite(5)) {
sleep(5000);
}
//Successfully wrote to port!
Comments Comments
-------- --------
@ -35,3 +164,13 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
Example:: Example::
time = Date.now(); time = Date.now();
Javascript Number Module
------------------------
The `Javascript Number module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number>`_ is supported in Netscript.
Example::
tprint(Number.isInteger(1)); //True
tprint(Number.isInteger(1.534059)); //False

@ -229,41 +229,19 @@
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="netscriptfunctions.html#hack">hack() (built-in function)</a> <li><a href="netscriptfunctions.html#hack">hack() (built-in function)</a>
</li> </li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].cores">hacknetnodes[i].cores() (hacknetnodes[i] method)</a> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].cores">hacknetnodes[i].cores (hacknetnodes[i] attribute)</a>
</li> </li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].getCoreUpgradeCost">hacknetnodes[i].getCoreUpgradeCost() (hacknetnodes[i] method)</a> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].level">hacknetnodes[i].level (hacknetnodes[i] attribute)</a>
</li> </li>
<li> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].moneyGainRatePerSecond">hacknetnodes[i].moneyGainRatePerSecond (hacknetnodes[i] attribute)</a>
hacknetnodes[i].getLevelUpgradeCost(n)
<ul>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].getLevelUpgradeCost(n);">() (hacknetnodes[i] method)</a>
</li>
</ul></li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].getRamUpgradeCost">hacknetnodes[i].getRamUpgradeCost() (hacknetnodes[i] method)</a>
</li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].level">hacknetnodes[i].level() (hacknetnodes[i] method)</a>
</li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].moneyGainRatePerSecond">hacknetnodes[i].moneyGainRatePerSecond() (hacknetnodes[i] method)</a>
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].onlineTimeSeconds">hacknetnodes[i].onlineTimeSeconds() (hacknetnodes[i] method)</a> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].onlineTimeSeconds">hacknetnodes[i].onlineTimeSeconds (hacknetnodes[i] attribute)</a>
</li> </li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].ram">hacknetnodes[i].ram() (hacknetnodes[i] method)</a> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].ram">hacknetnodes[i].ram (hacknetnodes[i] attribute)</a>
</li> </li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].totalMoneyGenerated">hacknetnodes[i].totalMoneyGenerated() (hacknetnodes[i] method)</a> <li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].totalMoneyGenerated">hacknetnodes[i].totalMoneyGenerated (hacknetnodes[i] attribute)</a>
</li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].upgradeCore">hacknetnodes[i].upgradeCore() (hacknetnodes[i] method)</a>
</li>
<li>
hacknetnodes[i].upgradeLevel(n)
<ul>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].upgradeLevel(n);">() (hacknetnodes[i] method)</a>
</li>
</ul></li>
<li><a href="netscripthacknetnodeapi.html#hacknetnodes[i].upgradeRam">hacknetnodes[i].upgradeRam() (hacknetnodes[i] method)</a>
</li> </li>
<li><a href="netscriptfunctions.html#hasRootAccess">hasRootAccess() (built-in function)</a> <li><a href="netscriptfunctions.html#hasRootAccess">hasRootAccess() (built-in function)</a>
</li> </li>
@ -316,6 +294,10 @@
<h2 id="N">N</h2> <h2 id="N">N</h2>
<table style="width: 100%" class="indextable genindextable"><tr> <table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="netscriptmisc.html#NetscriptPort.data">NetscriptPort.data (NetscriptPort attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="netscriptfunctions.html#nuke">nuke() (built-in function)</a> <li><a href="netscriptfunctions.html#nuke">nuke() (built-in function)</a>
</li> </li>

@ -196,9 +196,11 @@ secrets that you've been searching for.</p>
</ul> </ul>
</li> </li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a><ul> <li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a><ul>
<li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#netscript-ports">Netscript Ports</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#comments">Comments</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#comments">Comments</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#javascript-math-module">Javascript Math Module</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#javascript-math-module">Javascript Math Module</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#javascript-date-module">Javascript Date Module</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#javascript-date-module">Javascript Date Module</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptmisc.html#javascript-number-module">Javascript Number Module</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -197,9 +197,11 @@ to reach out to the developer!</p>
</ul> </ul>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a><ul> <li class="toctree-l1"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a><ul>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#netscript-ports">Netscript Ports</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#comments">Comments</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#comments">Comments</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#javascript-math-module">Javascript Math Module</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#javascript-math-module">Javascript Math Module</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#javascript-date-module">Javascript Date Module</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#javascript-date-module">Javascript Date Module</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html#javascript-number-module">Javascript Number Module</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -122,6 +122,7 @@ will only give 10% of the money you would have received in BitNode-1. The object
<li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -112,6 +112,7 @@ can also change. For example, if a variable initially holds a number, it can lat
<li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -72,39 +72,39 @@ accessed using <em>hacknetnodes[0]</em>. The fourth Hacknet Node you purchase wi
<p>The following is a list of member variables for a Hacknet Node object. These variables are read-only, which means you cannot assign <p>The following is a list of member variables for a Hacknet Node object. These variables are read-only, which means you cannot assign
a value to these.</p> a value to these.</p>
<p>Note that these must be called on an element inside the <em>hacknetnodes</em> array, not the array itself.</p> <p>Note that these must be called on an element inside the <em>hacknetnodes</em> array, not the array itself.</p>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].level"> <dt id="hacknetnodes[i].level">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">level</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].level" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">level</code><a class="headerlink" href="#hacknetnodes[i].level" title="Permalink to this definition"></a></dt>
<dd><p>Returns the level of the corresponding Hacknet Node</p> <dd><p>Returns the level of the corresponding Hacknet Node</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].ram"> <dt id="hacknetnodes[i].ram">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">ram</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].ram" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">ram</code><a class="headerlink" href="#hacknetnodes[i].ram" title="Permalink to this definition"></a></dt>
<dd><p>Returns the amount of RAM on the corresponding Hacknet Node</p> <dd><p>Returns the amount of RAM on the corresponding Hacknet Node</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].cores"> <dt id="hacknetnodes[i].cores">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">cores</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].cores" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">cores</code><a class="headerlink" href="#hacknetnodes[i].cores" title="Permalink to this definition"></a></dt>
<dd><p>Returns the number of cores on the corresponding Hacknet Node</p> <dd><p>Returns the number of cores on the corresponding Hacknet Node</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].totalMoneyGenerated"> <dt id="hacknetnodes[i].totalMoneyGenerated">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">totalMoneyGenerated</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].totalMoneyGenerated" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">totalMoneyGenerated</code><a class="headerlink" href="#hacknetnodes[i].totalMoneyGenerated" title="Permalink to this definition"></a></dt>
<dd><p>Returns the total amount of money that the corresponding Hacknet Node has earned</p> <dd><p>Returns the total amount of money that the corresponding Hacknet Node has earned</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].onlineTimeSeconds"> <dt id="hacknetnodes[i].onlineTimeSeconds">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">onlineTimeSeconds</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].onlineTimeSeconds" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">onlineTimeSeconds</code><a class="headerlink" href="#hacknetnodes[i].onlineTimeSeconds" title="Permalink to this definition"></a></dt>
<dd><p>Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed</p> <dd><p>Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="attribute">
<dt id="hacknetnodes[i].moneyGainRatePerSecond"> <dt id="hacknetnodes[i].moneyGainRatePerSecond">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">moneyGainRatePerSecond</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].moneyGainRatePerSecond" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">moneyGainRatePerSecond</code><a class="headerlink" href="#hacknetnodes[i].moneyGainRatePerSecond" title="Permalink to this definition"></a></dt>
<dd><p>Returns the amount of income that the corresponding Hacknet Node earns</p> <dd><p>Returns the amount of income that the corresponding Hacknet Node earns</p>
</dd></dl> </dd></dl>
@ -114,9 +114,9 @@ a value to these.</p>
<p>The following is a list of supported functions/methods for a Hacknet Node object.</p> <p>The following is a list of supported functions/methods for a Hacknet Node object.</p>
<p>Note that these must be called on an element inside the <em>hacknetnodes</em> array, not the <p>Note that these must be called on an element inside the <em>hacknetnodes</em> array, not the
array itself.</p> array itself.</p>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].upgradeLevel(n);"> <dt id="hacknetnodes[i].upgradeLevel">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeLevel(n);</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeLevel(n);" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeLevel</code><span class="sig-paren">(</span><em>n</em><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeLevel" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none"> <dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" /> <col class="field-name" />
<col class="field-body" /> <col class="field-body" />
@ -133,23 +133,23 @@ Hacknet Node's level is successfully upgraded <em>n</em> times or up to the max
otherwise.</p> otherwise.</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].upgradeRam"> <dt id="hacknetnodes[i].upgradeRam">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeRam</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeRam" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeRam</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeRam" title="Permalink to this definition"></a></dt>
<dd><p>Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is <dd><p>Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is
successfully upgraded and false otherwise.</p> successfully upgraded and false otherwise.</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].upgradeCore"> <dt id="hacknetnodes[i].upgradeCore">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeCore</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeCore" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">upgradeCore</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].upgradeCore" title="Permalink to this definition"></a></dt>
<dd><p>Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the <dd><p>Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the
additional core is successfully purchased, and false otherwise.</p> additional core is successfully purchased, and false otherwise.</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].getLevelUpgradeCost(n);"> <dt id="hacknetnodes[i].getLevelUpgradeCost">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">getLevelUpgradeCost(n);</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getLevelUpgradeCost(n);" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">getLevelUpgradeCost</code><span class="sig-paren">(</span><em>n</em><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getLevelUpgradeCost" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none"> <dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" /> <col class="field-name" />
<col class="field-body" /> <col class="field-body" />
@ -164,13 +164,13 @@ additional core is successfully purchased, and false otherwise.</p>
<p>Returns the cost of upgrading the specified Hacknet Node by <em>n</em> levels</p> <p>Returns the cost of upgrading the specified Hacknet Node by <em>n</em> levels</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].getRamUpgradeCost"> <dt id="hacknetnodes[i].getRamUpgradeCost">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">getRamUpgradeCost</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getRamUpgradeCost" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">getRamUpgradeCost</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getRamUpgradeCost" title="Permalink to this definition"></a></dt>
<dd><p>Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it.</p> <dd><p>Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it.</p>
</dd></dl> </dd></dl>
<dl class="function"> <dl class="method">
<dt id="hacknetnodes[i].getCoreUpgradeCost"> <dt id="hacknetnodes[i].getCoreUpgradeCost">
<code class="descclassname">hacknetnodes[i].</code><code class="descname">getCoreUpgradeCost</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getCoreUpgradeCost" title="Permalink to this definition"></a></dt> <code class="descclassname">hacknetnodes[i].</code><code class="descname">getCoreUpgradeCost</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#hacknetnodes[i].getCoreUpgradeCost" title="Permalink to this definition"></a></dt>
<dd><p>Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's <dd><p>Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's
@ -241,6 +241,7 @@ Nodes to a level of at least 75, RAM to at least 8GB, and number of cores to at
</li> </li>
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -326,6 +326,7 @@ NOT case-sensitive.</li>
</ul> </ul>
</li> </li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -50,6 +50,162 @@
<div class="section" id="netscript-miscellaneous"> <div class="section" id="netscript-miscellaneous">
<h1>Netscript Miscellaneous<a class="headerlink" href="#netscript-miscellaneous" title="Permalink to this headline"></a></h1> <h1>Netscript Miscellaneous<a class="headerlink" href="#netscript-miscellaneous" title="Permalink to this headline"></a></h1>
<div class="section" id="netscript-ports">
<h2>Netscript Ports<a class="headerlink" href="#netscript-ports" title="Permalink to this headline"></a></h2>
<p>Netscript ports are endpoints that can be used to communicate between scripts.
A port is implemented as a sort of serialized queue, where you can only write
and read one element at a time from the port. When you read data from a port,
the element that is read is removed from the port.</p>
<p>The <code class="xref js js-func docutils literal"><span class="pre">read()</span></code>, <code class="xref js js-func docutils literal"><span class="pre">write()</span></code>, <code class="xref js js-func docutils literal"><span class="pre">clear()</span></code>, and <code class="xref js js-func docutils literal"><span class="pre">peek()</span></code>
Netscript functions can be used to interact with ports.</p>
<p>Right now, there are only 20 ports for Netscript, denoted by the number 1
through 20. When using the functions above, the ports are specified
by passing the number as the first argument.</p>
<p>IMPORTANT: The data inside ports are not saved! This means if you close and
re-open the game, or reload the page then you will lose all of the data in
the ports!</p>
<p><strong>Example Usage</strong></p>
<p>Here's a brief example of how ports work. For the sake of simplicity we'll only deal with port 1.</p>
<p>Let's assume Port 1 starts out empty (no data inside). We'll represent the port as such:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">[]</span>
</pre></div>
</div>
<p>Now assume we ran the following simple script:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span> <span class="o">//</span><span class="n">Writes</span> <span class="n">the</span> <span class="n">value</span> <span class="n">of</span> <span class="n">i</span> <span class="n">to</span> <span class="n">port</span> <span class="mi">1</span>
<span class="p">}</span>
</pre></div>
</div>
<p>After this script executes, our script will contain every number from 0 through 9, as so:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span> <span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
</pre></div>
</div>
<p>Then, assume we run the following script:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">print</span><span class="p">(</span><span class="n">read</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="o">//</span><span class="n">Reads</span> <span class="n">a</span> <span class="n">value</span> <span class="kn">from</span> <span class="nn">port</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">then</span> <span class="n">prints</span> <span class="n">it</span>
<span class="p">}</span>
</pre></div>
</div>
<p>This script above will read the first three values from port 1 and then print them to the script's log. The log will end up looking like:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="mi">0</span>
<span class="mi">1</span>
<span class="mi">2</span>
</pre></div>
</div>
<p>And the data in port 1 will look like:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
</pre></div>
</div>
<p><strong>Port Handles</strong></p>
<p>The <code class="xref js js-func docutils literal"><span class="pre">getPortHandle()</span></code> Netscript function can be used to get a handle to a Netscript Port.
This handle allows you to access several new port-related functions and the
port's underlying data structure, which is just a Javascript array. The functions are:</p>
<dl class="method">
<dt id="NetscriptPort.write">
<code class="descclassname">NetscriptPort.</code><code class="descname">write</code><span class="sig-paren">(</span><em>data</em><span class="sig-paren">)</span><a class="headerlink" href="#NetscriptPort.write" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Arguments:</th><td class="field-body"><ul class="first simple">
<li><strong>data</strong> -- Data to write to the port</li>
</ul>
</td>
</tr>
<tr class="field-even field"><th class="field-name">Returns:</th><td class="field-body"><p class="first last">If the port is full, the item that is removed from the port is returned.
Otherwise, null is returned.</p>
</td>
</tr>
</tbody>
</table>
<p>Writes <cite>data</cite> to the port. Works the same as the Netscript function <cite>write</cite>.</p>
</dd></dl>
<dl class="method">
<dt id="NetscriptPort.tryWrite">
<code class="descclassname">NetscriptPort.</code><code class="descname">tryWrite</code><span class="sig-paren">(</span><em>data</em><span class="sig-paren">)</span><a class="headerlink" href="#NetscriptPort.tryWrite" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Arguments:</th><td class="field-body"><ul class="first simple">
<li><strong>data</strong> -- Data to try to write to the port</li>
</ul>
</td>
</tr>
<tr class="field-even field"><th class="field-name">Returns:</th><td class="field-body"><p class="first last">True if the data is successfully written to the port, and false otherwise.</p>
</td>
</tr>
</tbody>
</table>
<p>Attempts to write <cite>data</cite> to the Netscript port. If the port is full, the data will
not be written. Otherwise, the data will be written normally.</p>
</dd></dl>
<dl class="method">
<dt id="NetscriptPort.full">
<code class="descclassname">NetscriptPort.</code><code class="descname">full</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#NetscriptPort.full" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Returns:</th><td class="field-body">True if the Netscript Port is full, and false otherwise</td>
</tr>
</tbody>
</table>
</dd></dl>
<dl class="method">
<dt id="NetscriptPort.empty">
<code class="descclassname">NetscriptPort.</code><code class="descname">empty</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#NetscriptPort.empty" title="Permalink to this definition"></a></dt>
<dd><table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Returns:</th><td class="field-body">True if the Netscript Port is empty, and false otherwise</td>
</tr>
</tbody>
</table>
</dd></dl>
<dl class="method">
<dt id="NetscriptPort.clear">
<code class="descclassname">NetscriptPort.</code><code class="descname">clear</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#NetscriptPort.clear" title="Permalink to this definition"></a></dt>
<dd><p>Clears all data from the port. Works the same as the Netscript function <cite>clear</cite></p>
</dd></dl>
<dl class="attribute">
<dt id="NetscriptPort.data">
<code class="descclassname">NetscriptPort.</code><code class="descname">data</code><a class="headerlink" href="#NetscriptPort.data" title="Permalink to this definition"></a></dt>
<dd><p>The Netscript port underlying data structure, which is just a Javascript array. All
valid Javascript Array methods can be called on this.</p>
</dd></dl>
<p>Port Handle Example:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>port = getPortHandle(5);
back = port.data.pop(); //Get and remove last element in port
//Remove an element from the port
i = port.data.findIndex(&quot;foo&quot;);
if (i != -1) {
port.data.slice(i, 1);
}
//Wait for port data before reading
while(port.empty()) {
sleep(10000);
}
res = port.read();
//Wait for there to be room in a port before writing
while (!port.tryWrite(5)) {
sleep(5000);
}
//Successfully wrote to port!
</pre></div>
</div>
</div>
<div class="section" id="comments"> <div class="section" id="comments">
<h2>Comments<a class="headerlink" href="#comments" title="Permalink to this headline"></a></h2> <h2>Comments<a class="headerlink" href="#comments" title="Permalink to this headline"></a></h2>
<p>Netscript supports comments using the same syntax as <a class="reference external" href="https://www.w3schools.com/js/js_comments.asp">Javascript comments</a>. <p>Netscript supports comments using the same syntax as <a class="reference external" href="https://www.w3schools.com/js/js_comments.asp">Javascript comments</a>.
@ -85,6 +241,15 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
</pre></div> </pre></div>
</div> </div>
</div> </div>
<div class="section" id="javascript-number-module">
<h2>Javascript Number Module<a class="headerlink" href="#javascript-number-module" title="Permalink to this headline"></a></h2>
<p>The <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">Javascript Number module</a> is supported in Netscript.</p>
<p>Example:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">tprint</span><span class="p">(</span><span class="n">Number</span><span class="o">.</span><span class="n">isInteger</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="o">//</span><span class="kc">True</span>
<span class="n">tprint</span><span class="p">(</span><span class="n">Number</span><span class="o">.</span><span class="n">isInteger</span><span class="p">(</span><span class="mf">1.534059</span><span class="p">));</span> <span class="o">//</span><span class="kc">False</span>
</pre></div>
</div>
</div>
</div> </div>
@ -107,9 +272,11 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#"> Miscellaneous</a><ul> <li class="toctree-l2 current"><a class="current reference internal" href="#"> Miscellaneous</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#netscript-ports">Netscript Ports</a></li>
<li class="toctree-l3"><a class="reference internal" href="#comments">Comments</a></li> <li class="toctree-l3"><a class="reference internal" href="#comments">Comments</a></li>
<li class="toctree-l3"><a class="reference internal" href="#javascript-math-module">Javascript Math Module</a></li> <li class="toctree-l3"><a class="reference internal" href="#javascript-math-module">Javascript Math Module</a></li>
<li class="toctree-l3"><a class="reference internal" href="#javascript-date-module">Javascript Date Module</a></li> <li class="toctree-l3"><a class="reference internal" href="#javascript-date-module">Javascript Date Module</a></li>
<li class="toctree-l3"><a class="reference internal" href="#javascript-number-module">Javascript Number Module</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -209,6 +209,7 @@ change the value of their operands. For example:</p>
<li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

@ -87,6 +87,7 @@ script specified in the first argument with the amount of threads specified in t
<li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscripthacknetnodeapi.html"> Hacknet Node API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptixapi.html"> Trade Information eXchange (TIX) API</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptmisc.html"> Miscellaneous</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

Binary file not shown.

File diff suppressed because one or more lines are too long

@ -25,27 +25,27 @@ a value to these.
Note that these must be called on an element inside the *hacknetnodes* array, not the array itself. Note that these must be called on an element inside the *hacknetnodes* array, not the array itself.
.. js:function:: hacknetnodes[i].level .. js:attribute:: hacknetnodes[i].level
Returns the level of the corresponding Hacknet Node Returns the level of the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].ram .. js:attribute:: hacknetnodes[i].ram
Returns the amount of RAM on the corresponding Hacknet Node Returns the amount of RAM on the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].cores .. js:attribute:: hacknetnodes[i].cores
Returns the number of cores on the corresponding Hacknet Node Returns the number of cores on the corresponding Hacknet Node
.. js:function:: hacknetnodes[i].totalMoneyGenerated .. js:attribute:: hacknetnodes[i].totalMoneyGenerated
Returns the total amount of money that the corresponding Hacknet Node has earned Returns the total amount of money that the corresponding Hacknet Node has earned
.. js:function:: hacknetnodes[i].onlineTimeSeconds .. js:attribute:: hacknetnodes[i].onlineTimeSeconds
Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed Returns the total amount of time (in seconds) that the corresponding Hacknet Node has existed
.. js:function:: hacknetnodes[i].moneyGainRatePerSecond .. js:attribute:: hacknetnodes[i].moneyGainRatePerSecond
Returns the amount of income that the corresponding Hacknet Node earns Returns the amount of income that the corresponding Hacknet Node earns
@ -57,7 +57,7 @@ The following is a list of supported functions/methods for a Hacknet Node object
Note that these must be called on an element inside the *hacknetnodes* array, not the Note that these must be called on an element inside the *hacknetnodes* array, not the
array itself. array itself.
.. js:function:: hacknetnodes[i].upgradeLevel(n); .. js:method:: hacknetnodes[i].upgradeLevel(n)
:param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer :param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer
@ -65,27 +65,27 @@ array itself.
Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false
otherwise. otherwise.
.. js:function:: hacknetnodes[i].upgradeRam() .. js:method:: hacknetnodes[i].upgradeRam()
Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is Tries to upgrade the amount of RAM on the corresponding Hacknet Node. Returns true if the RAM is
successfully upgraded and false otherwise. successfully upgraded and false otherwise.
.. js:function:: hacknetnodes[i].upgradeCore() .. js:method:: hacknetnodes[i].upgradeCore()
Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the Tries to purchase an additional core for the corresponding Hacknet Node. Returns true if the
additional core is successfully purchased, and false otherwise. additional core is successfully purchased, and false otherwise.
.. js:function:: hacknetnodes[i].getLevelUpgradeCost(n); .. js:method:: hacknetnodes[i].getLevelUpgradeCost(n)
:param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer :param number n: Number of levels to upgrade. Must be positive. Rounded to nearest integer
Returns the cost of upgrading the specified Hacknet Node by *n* levels Returns the cost of upgrading the specified Hacknet Node by *n* levels
.. js:function:: hacknetnodes[i].getRamUpgradeCost() .. js:method:: hacknetnodes[i].getRamUpgradeCost()
Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it. Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it.
.. js:function:: hacknetnodes[i].getCoreUpgradeCost() .. js:method:: hacknetnodes[i].getCoreUpgradeCost()
Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's Returns the cost of upgrading the number of cores of the specified Hacknet Node. Upgrading a Node's
number of cores adds one additional core. number of cores adds one additional core.

@ -1,6 +1,135 @@
Netscript Miscellaneous Netscript Miscellaneous
======================= =======================
Netscript Ports
---------------
Netscript ports are endpoints that can be used to communicate between scripts.
A port is implemented as a sort of serialized queue, where you can only write
and read one element at a time from the port. When you read data from a port,
the element that is read is removed from the port.
The :js:func:`read`, :js:func:`write`, :js:func:`clear`, and :js:func:`peek`
Netscript functions can be used to interact with ports.
Right now, there are only 20 ports for Netscript, denoted by the number 1
through 20. When using the functions above, the ports are specified
by passing the number as the first argument.
IMPORTANT: The data inside ports are not saved! This means if you close and
re-open the game, or reload the page then you will lose all of the data in
the ports!
**Example Usage**
Here's a brief example of how ports work. For the sake of simplicity we'll only deal with port 1.
Let's assume Port 1 starts out empty (no data inside). We'll represent the port as such::
[]
Now assume we ran the following simple script::
for (i = 0; i < 10; ++i) {
write(1, i); //Writes the value of i to port 1
}
After this script executes, our script will contain every number from 0 through 9, as so::
[0, 1, 2, 3, 4, 5, 6, 7 , 8, 9]
Then, assume we run the following script::
for (i = 0; i < 3; ++i) {
print(read(1)); //Reads a value from port 1 and then prints it
}
This script above will read the first three values from port 1 and then print them to the script's log. The log will end up looking like::
0
1
2
And the data in port 1 will look like::
[3, 4, 5, 6, 7, 8, 9]
**Port Handles**
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
This handle allows you to access several new port-related functions and the
port's underlying data structure, which is just a Javascript array. The functions are:
.. js:method:: NetscriptPort.write(data)
:param data: Data to write to the port
:returns: If the port is full, the item that is removed from the port is returned.
Otherwise, null is returned.
Writes `data` to the port. Works the same as the Netscript function `write`.
.. js:method:: NetscriptPort.tryWrite(data)
:param data: Data to try to write to the port
:returns: True if the data is successfully written to the port, and false otherwise.
Attempts to write `data` to the Netscript port. If the port is full, the data will
not be written. Otherwise, the data will be written normally.
.. js::method:: NetscriptPort.read()
:returns: The data read from the port. If the port is empty, "NULL PORT DATA" is returned
Removes and returns the first element from the port.
Works the same as the Netscript function `read`
.. js::method:: NetscriptPort.peek()
:returns: The first element in the port, or "NULL PORT DATA" if the port is empty.
Returns the first element in the port, but does not remove it.
Works the same as the Netscript function `peek`
.. js:method:: NetscriptPort.full()
:returns: True if the Netscript Port is full, and false otherwise
.. js:method:: NetscriptPort.empty()
:returns: True if the Netscript Port is empty, and false otherwise
.. js:method:: NetscriptPort.clear()
Clears all data from the port. Works the same as the Netscript function `clear`
.. js:attribute:: NetscriptPort.data
The Netscript port underlying data structure, which is just a Javascript array. All
valid Javascript Array methods can be called on this.
Port Handle Example::
port = getPortHandle(5);
back = port.data.pop(); //Get and remove last element in port
//Remove an element from the port
i = port.data.findIndex("foo");
if (i != -1) {
port.data.slice(i, 1);
}
//Wait for port data before reading
while(port.empty()) {
sleep(10000);
}
res = port.read();
//Wait for there to be room in a port before writing
while (!port.tryWrite(5)) {
sleep(5000);
}
//Successfully wrote to port!
Comments Comments
-------- --------
@ -35,3 +164,13 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
Example:: Example::
time = Date.now(); time = Date.now();
Javascript Number Module
------------------------
The `Javascript Number module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number>`_ is supported in Netscript.
Example::
tprint(Number.isInteger(1)); //True
tprint(Number.isInteger(1.534059)); //False

@ -0,0 +1,140 @@
function test(name, val) {
if (val) {
tprint("Test " + name + ": OK");
} else {
tprint("Test " + name + ": FAILED");
//A failed test prints failure to results text file
write("tb_results.txt", "FAIL");
}
}
test("run", (args.length === 1 && args[0] === "OK"));
//Arithmetic
test("arith1", 0 * 10 === 0);
test("arith2", 0 + 1 === 1);
test("arith3", 10 / 5 === 2);
test("arith4", 6-3 === 3);
test("arith5", 14 % 10 === 4);
test("arith6", 5 === 24 / 6 + 1);
test("arith7", 6 === 2 * 3);
//Logical operators
test("logic1", true && true);
test("logic2", !(true && false));
test("logic3", true || false);
test("logic4", !(false || false));
test("logic5", 1 < 3);
test("logic6", !(3 < 1));
test("logic7", 5 >= 5);
test("logic8", !(5 <= 4));
test("logic9", true == true);
test("logic10", true === true);
test("logic11", !(0 != 0));
//Assignment
i = 5;
test("asgn1", i == 5);
i = 0;
test("asgn2", i === 0);
test("asgn3", 10 === (i = 10));
test("asgn4", i === 10);
//Basic array functionality
testArr = [1, 2, 3, 4, 5, "str1", "str2", "str3"];
test("arr1", testArr[0] == 1);
test("arr2", testArr[1] === 2);
test("arr3", testArr[5] === "str1");
test("arr4", testArr[7] === "str3");
x = 1;
y = 2;
z = 3;
testArr = [];
testArr.push(x);
testArr.push(y);
testArr.push(z);
test("arr5", testArr.length === 3);
test("arr6", 1 === testArr[0]);
test("arr7", 3 === testArr[2]);
x = 10;
y = 10;
z = 10;
test("arr8", testArr.toString() === "1,2,3");
testArr.push(4);
testArr.push("some str");
test("arr9", "1,2,3,4,some str" === testArr.toString());
testArr.pop();
test("arr10", "1,2,3,4" === testArr.toString());
testArr.shift();
test("arr11", "2,3,4" === testArr.toString());
testArr.unshift(1);
test("arr12", "1,2,3,4" === testArr.toString());
testArr[0] = 10;
foo = 1;
testArr[foo] = 11;
foo = 2;
testArr[foo] = 12;
foo = 3;
testArr[foo] = 13;
test("arr13", "10,11,12,13" === testArr.join());
testArr.splice(testArr.length, 2, 14, 15);
test("arr14", testArr.length === 6);
test("arr15", testArr.join() == "10,11,12,13,14,15");
//Short circuiting Logical Operators
results = [];
res = false || results.push("OK")
res = (0 < -5) || results.push("OK")
res = true && results.push("OK")
res = (1 > 0) && results.push("OK")
res = 5 && results.push("OK")
test("shortcircuit1", results.length === 5);
for (i = 0; i < results.length; ++i) {
test("shortcircuit" + (2 + i), results[i] === "OK");
}
res = true || tprint('1');
res = (true && true) || tprint('2');
res = 1 > 0 || tprint('3');
res = false && tprint('4');
res = 1 < 0 && tprint('5');
test("shortcircuit7", results.length === 5);
//Conditional Statements
results = [];
i = 1;
if (i == 0) {
results.push("FAIL");
} else if (i == 2) {
results.push("FAIL");
} else if (i == 1) {
results.push("OK");
} else {
results.push("FAIL");
}
i = 5;
if (i == 0) {
results.push("FAIL");
} else if (i == 2) {
results.push("FAIL");
} else if (i == 1) {
results.push("FAIL");
} else {
results.push("OK");
}
test("conditional1", results.length === 2);
test("conditional2", results[0] === "OK");
test("conditional3", results[1] === "OK");
run("tb_multiarray.script", 1, "OK");
exec("tb_ports.script", "home", 1, "OK");
write("tb_results.txt", ",tb_basic");

@ -0,0 +1,4 @@
while(true) {
print("hi");
sleep(5000);
}

@ -0,0 +1,119 @@
import {test} from "tb_basic.script";
test("run", (args.length === 1 && args[0] === "OK"));
//scan
res = scan();
test("scan1", res.includes("iron-gym") && res.includes("foodnstuff") &&
res.includes("sigma-cosmetics") && res.includes("joesguns") &&
res.includes("hong-fang-tea") && res.includes("harakiri-sushi"));
test("scan2", res.length === 6);
res = scan("foodnstuff");
test("scan3", res.includes("home"));
//hasRootAccess and nuke
svr = "foodnstuff";
test("hasRootAccess1", !hasRootAccess(svr));
test("nuke", nuke(svr));
test("hasRootAccess2", hasRootAccess(svr));
//sleep and Date.now();
time = Date.now();
sleep(10000);
time2 = Date.now();
test("sleep", time2 - time > 8000);
//hack, grow, weaken, and their effects
FORTIFYAMOUNT = 0.002;
WEAKENAMOUNT = 0.05;
playerStartingMoney = getServerMoneyAvailable("home");
serverStartingMoney = getServerMoneyAvailable("foodnstuff");
startingSecLvl = getServerSecurityLevel("foodnstuff");
res = hack(svr);
playerEndingMoney = getServerMoneyAvailable("home");
serverEndingMoney = getServerMoneyAvailable("foodnstuff");
endingSecLvl = getServerSecurityLevel("foodnstuff");
success = !(res == 0);
if (success) {
tprint("Hack succeeded");
test("hackplyrmoney", res === (playerEndingMoney - playerStartingMoney));
test("hacksvrmoney", res === (serverStartingMoney - serverEndingMoney));
//Dumb JS precision
test("seclevel", (endingSecLvl - startingSecLvl) - FORTIFYAMOUNT < 1e2 * Number.EPSILON);
} else {
tprint("Hack failed");
test("hackres", res === 0);
test("hackplymoney", playerEndingMoney === playerStartingMoney);
test("hacksvrmoney", serverEndingMoney === serverStartingMoney);
test("seclevel", endingSecLvl === startingSecLvl);
}
serverStartingMoney = getServerMoneyAvailable(svr);
res = grow(svr);
serverEndingMoney = getServerMoneyAvailable(svr);
test("grow", serverEndingMoney > serverStartingMoney);
test("growtest", res * serverStartingMoney - serverEndingMoney < Number.EPSILON);
startingSecLvl = getServerSecurityLevel(svr);
res = weaken(svr);
endingSecLvl = getServerSecurityLevel(svr);
test("weaken", res === WEAKENAMOUNT);
test("weakenres", startingSecLvl - endingSecLvl - res < 1e2 * Number.EPSILON);
//misc getter functions
test("getHostname", getHostname() === "home");
test("getHackingLevel", getHackingLevel() >= 1); //Can vary based on aug mults
res = getHackingMultipliers();
test("getHackingMultipliers", res.chance >= 1 && res.speed >= 1 &&
res.money >= 1 && res.growth >= 1);
svr = "joesguns";
baseSecLvl = getServerBaseSecurityLevel(svr);
minSecLvl = getServerMinSecurityLevel(svr);
test("getServerBase/MinSecurityLevel1", minSecLvl < baseSecLvl);
test("getServerBase/MinSecurityLevel1", minSecLvl === Math.max(1, Math.round(baseSecLvl / 3)));
test("getServerRequiredHackingLevel", getServerRequiredHackingLevel(svr) === 10);
test("getServerMaxMoney", getServerMaxMoney(svr) > getServerMoneyAvailable(svr)); //Can vary by BN
test("getServerGrowth", getServerGrowth(svr) >= 1); //Can vary by BN
test("getServerNumPortsRequired1", getServerNumPortsRequired(svr) === 0);
test("getServerNumPortsRequired1", getServerNumPortsRequired("neo-net") === 1);
test("getServerRam", getServerRam("home")[0] === 8 ||
getServerRam("home")[0] === 16 ||
getServerRam("home")[0] === 32);
test("serverExists1", serverExists("home"));
test("serverExists2", !serverExists("foofooofofofof"));
test("fileExists1", fileExists("tb_start.script"));
test("fileExists2", !fileExists("tosdifoodafgbiofdagbaod.txt"));
//run a misc script
test("isRunning1", !isRunning("tb_foo.script", "home"));
run("tb_foo.script", 1, 1, 2, 3);
test("isRunning2", !isRunning("tb_foo.script", "home", "foo"));
test("isRunning3", isRunning("tb_foo.script", "home", 1, 2, 3));
//kill
kill("tb_foo.script", "home", "fee");
sleep(10000);
test("isRunning4", isRunning("tb_foo.script", "home", 1, 2, 3));
kill("tb_foo.script", "home", 1, 2, 3);
sleep(10000);
test("isRunning4", !isRunning("tb_foo.script", "home", 1, 2, 3));
//scp
svr = "foodnstuff";
test("fileExists3", !fileExists("tb_remote.script", svr));
test("fileExists4", !fileExists("tb_basic.script", svr));
scp("tb_remote.script", "home", svr);
scp("tb_basic.script", svr);
test("fileExists5", fileExists("tb_remote.script", svr));
test("fileExists6", fileExists("tb_basic.script", svr));
write("tb_results.txt", ",tb_functions"); //Done, pretty much
//exec and spawn
exec("tb_remote.script", "foodnstuff", 1, "OK");
spawn("tb_functions2.script", 1, "OK");

@ -0,0 +1,90 @@
//Tests for multidimensional arrays
import {test} from "tb_basic.script";
runSuccess = (args.length === 1 && args[0] === "OK");
test("run", runSuccess);
arr = [];
arr[0] = [];
arr[1] = [];
arr.push([]);
test("multiarr1", arr.toString() === ",,");
test("multiarr2", arr.length === 3);
arr[0].push(0);
arr[0].push(0);
arr[0].push(0);
test("multiarr3", arr[0].length === 3);
test("multiarr4", arr[0].toString() === "0,0,0");
arr[1] = [0, 0, 0];
test("multiarr5", arr.length === 3);
test("multiarr6", arr[1].length === 3);
test("multiarr7", arr[1].toString() === "0,0,0");
arr.pop();
arr.push([0,0,0]);
test("multiarr8", arr.length === 3);
test("multiarr9", arr[2].length === 3);
test("multiarr10", "0,0,0,0,0,0,0,0,0" === arr.toString());
for (r = 0; r < arr.length; ++r) {
for (c = 0; c < arr[r].length; ++c) {
arr[r][c] = r * 3 + c + 1;
}
}
test("multiarr11", "1,2,3,4,5,6,7,8,9" === arr.toString());
arr = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]];
test("multiarr12", 4 === arr.length);
for (i = 0; i < arr.length; ++i) {
test("multiarr" + (13 + i), arr[i].length === 4);
}
for (r = 0; r < arr.length; ++r) {
for (c = 0; c < arr[r].length; ++c) {
arr[r][c] = r * 10 + c + 1;
}
}
test("multiarr17", arr.toString() === "1,2,3,4,11,12,13,14,21,22,23,24,31,32,33,34");
//3D array
arr = [[], [], [], []];
arr[0].push([0, 0, 0]);
arr[0].push([0, 0, 0]);
arr[0].push([0, 0, 0]);
arr[1].push([0, 0, 0]);
arr[1].push([0, 0, 0]);
arr[1].push([0, 0, 0]);
arr[2].push([0, 0, 0]);
arr[2].push([0, 0, 0]);
arr[2].push([0, 0, 0]);
arr[3].push([0, 0, 0]);
arr[3].push([0, 0, 0]);
arr[3].push([0, 0, 0]);
i = 0;
for (r = 0; r < arr.length; ++r) {
for (c = 0; c < arr[r].length; ++c) {
for (d = 0; d < arr[r][c].length; ++d) {
arr[r][c][d] = i;
++i;
}
}
}
test("multiarr18", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35" === arr.toString());
ref = 0;
for (r = 0; r < arr.length; ++r) {
for (c = 0; c < arr[r].length; ++c) {
for (d = 0; d < arr[r][c].length; ++d) {
test("multiarr" + (19 + ref), arr[r][c][d] === ref);
++ref;
}
}
}
write("tb_results.txt", ",tb_multiarray");

@ -0,0 +1,73 @@
import {test} from "tb_basic.script";
test("run", (args.length === 1 && args[0] === "OK"));
MAXPORTS = 20;
MAXPORTSIZE = 100;
for (i = 1; i <= MAXPORTS; ++i) {
clear(i);
}
//write
for (i = 1; i <= MAXPORTS; ++i) {
for (j = 1; j <= 5; ++j) {
write(i, j);
}
}
for (i = 1; i <= MAXPORTS; ++i) {
port = getPortHandle(i);
test("write" + i, port.data.length === 5);
}
//read
for (i = 1; i <= MAXPORTS; ++i) {
for (j = 1; j <= 2; ++j) {
res = read(i);
test("read-p" + i + "-" + j, res === j);
}
}
for (i = 1; i <= MAXPORTS; ++i) {
port = getPortHandle(i); //Check that read removes elements
test("readpops" + i, port.data.length === 3);
}
//peek
for (i = 1; i <= MAXPORTS; ++i) {
test("peek" + i, peek(i) === 3);
port = getPortHandle(i);
test("peeknopop" + i, port.data.length === 3);
}
//clear and empty
for (i = 1; i <= MAXPORTS; ++i) {
clear(i);
port = getPortHandle(i);
test("clear" + i, port.data.length === 0);
test("empty" + i, port.empty());
}
//Write so that the port is full (only port 1 for this)
for (i = 0; i < MAXPORTSIZE + 1; ++i) {
write(1, i)
}
//full
port = getPortHandle(1);
test("full", port.full());
test("notempty", !port.empty());
//tryWrite
firstElem = peek(1);
test("trywritefails", !port.tryWrite("foo"));
test("trywritenochange", peek(1) === firstElem);
read(1);
test("trywritesucceeds", port.tryWrite("foo"));
test("trywritewrites", port.data.pop() === "foo");
test("notfull", !port.full());
write("tb_results.txt", ",tb_ports");
run("tb_functions.script", 1, "OK");

@ -0,0 +1,17 @@
import {test} from "tb_basic.script";
test("run", args.length === 1 && args[0] === "OK");
svr = "foodnstuff";
test("getHostname", getHostname() === svr);
//ls
res = ls(svr);
test("ls1", res.includes("sector-12-crime.lit"));
test("ls2", res.includes("tb_remote.script"));
test("ls3", res.includes("tb_basic.script"));
test("ls4", res.length === 3);
res = ls(svr, ".lit");
test("ls5", res.length === 1);
test("ls6", res.includes("sector-12-crime.lit"));
write("tb_results.txt", "tb_remote");

@ -0,0 +1,35 @@
tprint("Beginning testbench. A soft reset MUST be performed before running this.");
tprint("This should be run on a Bitnode between 1 and 7, with $100k+, 16GB RAM+ " +
"and access to Singularity functions otherwise some tests may fail");
run("tb_basic.script", 1, "OK");
write("tb_results.txt", "tb_start", "w");
while(true) {
sleep(10000);
res = read("tb_results.txt");
if (res.includes("FAIL")) {
tprint("TESTBENCH FAILED");
killall();
}
if (res.includes("tb_start") && res.includes("tb_basic") &&
res.includes("tb_ports") && res.includes("tb_multiarray") &&
res.includes("tb_functions")) {
break;
}
}
//Check remote
scp("tb_results.txt", "foodnstuff", "home");
res = read("tb_results.txt");
if (res.includes("FAIL")) {
tprint("TESTBENCH FAILED");
killall();
}
if (res.includes("tb_remote")) {
tprint("TESTBENCH SUCCESS");
} else {
tprint("TESTBENCH FAILED");
killall();
}

@ -51,7 +51,7 @@ function initBitNodes() {
"In this BitNode you can create and manage your own corporation. Running a successful corporation " + "In this BitNode you can create and manage your own corporation. Running a successful corporation " +
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" + "has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
"The price and reputation cost of all Augmentations is tripled<br>" + "The price and reputation cost of all Augmentations is tripled<br>" +
"The starting and maximum amount of money on servers is halved<br>" + "The starting and maximum amount of money on servers is reduced by 75%<br>" +
"Server growth rate is reduced by 80%<br>" + "Server growth rate is reduced by 80%<br>" +
"You will start out with $150b so that you can start your corporation<br>" + "You will start out with $150b so that you can start your corporation<br>" +
"You now only need 75 reputation with a faction in order to donate to it, rather than 150<br><br>" + "You now only need 75 reputation with a faction in order to donate to it, rather than 150<br><br>" +
@ -81,6 +81,7 @@ function initBitNodes() {
"The starting money on servers is halved, but the maximum money remains the same<br>" + "The starting money on servers is halved, but the maximum money remains the same<br>" +
"Most methods of earning money now give significantly less<br>" + "Most methods of earning money now give significantly less<br>" +
"Infiltration gives 50% more reputation and money<br>" + "Infiltration gives 50% more reputation and money<br>" +
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
"Augmentations are more expensive<br>" + "Augmentations are more expensive<br>" +
"Hacking experience gain rates are reduced<br><br>" + "Hacking experience gain rates are reduced<br><br>" +
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " + "Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
@ -125,6 +126,7 @@ function initBitNodes() {
"The growth rate of servers is halved<br>" + "The growth rate of servers is halved<br>" +
"Weakening a server is twice as effective<br>" + "Weakening a server is twice as effective<br>" +
"Company wages are decreased by 50%<br>" + "Company wages are decreased by 50%<br>" +
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
"Hacknet Node production is significantly decreased<br>" + "Hacknet Node production is significantly decreased<br>" +
"Crime and Infiltration are more lucrative<br>" + "Crime and Infiltration are more lucrative<br>" +
"Augmentations are twice as expensive<br><br>" + "Augmentations are twice as expensive<br><br>" +
@ -137,11 +139,11 @@ function initBitNodes() {
"Level 3: 42%"); "Level 3: 42%");
//Books: Frontera, Shiner //Books: Frontera, Shiner
BitNodes["BitNode12"] = new BitNode(12, "Eye of the World", "COMING SOON"); //Become AI BitNodes["BitNode12"] = new BitNode(12, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode13"] = new BitNode(13, "", "COMING SOON"); BitNodes["BitNode13"] = new BitNode(13, "", "COMING SOON");
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON"); BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON"); BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON"); BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON"); BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON"); BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
@ -150,14 +152,6 @@ function initBitNodes() {
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON"); BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON"); BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON"); BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
BitNodes["BitNode25"] = new BitNode(25, "", "COMING SOON");
BitNodes["BitNode26"] = new BitNode(26, "", "COMING SOON");
BitNodes["BitNode27"] = new BitNode(27, "", "COMING SOON");
BitNodes["BitNode28"] = new BitNode(28, "", "COMING SOON");
BitNodes["BitNode29"] = new BitNode(29, "", "COMING SOON");
BitNodes["BitNode30"] = new BitNode(30, "", "COMING SOON");
BitNodes["BitNode31"] = new BitNode(31, "", "COMING SOON");
BitNodes["BitNode32"] = new BitNode(32, "", "COMING SOON");
} }
let BitNodeMultipliers = { let BitNodeMultipliers = {
@ -188,6 +182,8 @@ let BitNodeMultipliers = {
InfiltrationMoney: 1, InfiltrationMoney: 1,
InfiltrationRep: 1, InfiltrationRep: 1,
CorporationValuation: 1,
} }
function initBitNodeMultipliers() { function initBitNodeMultipliers() {
@ -215,8 +211,8 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.RepToDonateToFaction = 0.5; BitNodeMultipliers.RepToDonateToFaction = 0.5;
BitNodeMultipliers.AugmentationRepCost = 3; BitNodeMultipliers.AugmentationRepCost = 3;
BitNodeMultipliers.AugmentationMoneyCost = 3; BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.ServerMaxMoney = 0.50; BitNodeMultipliers.ServerMaxMoney = 0.25;
BitNodeMultipliers.ServerStartingMoney = 0.50; BitNodeMultipliers.ServerStartingMoney = 0.25;
BitNodeMultipliers.ServerGrowthRate = 0.20; BitNodeMultipliers.ServerGrowthRate = 0.20;
BitNodeMultipliers.ScriptHackMoney = 0.25; BitNodeMultipliers.ScriptHackMoney = 0.25;
BitNodeMultipliers.CompanyWorkMoney = 0.25; BitNodeMultipliers.CompanyWorkMoney = 0.25;
@ -241,13 +237,14 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.ServerMaxMoney = 2; BitNodeMultipliers.ServerMaxMoney = 2;
BitNodeMultipliers.ServerStartingSecurity = 2; BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ServerStartingMoney = 0.5; BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.2; BitNodeMultipliers.ScriptHackMoney = 0.15;
BitNodeMultipliers.HacknetNodeMoney = 0.2; BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.5; BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.InfiltrationRep = 1.5; BitNodeMultipliers.InfiltrationRep = 1.5;
BitNodeMultipliers.InfiltrationMoney = 1.5; BitNodeMultipliers.InfiltrationMoney = 1.5;
BitNodeMultipliers.AugmentationMoneyCost = 2; BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5; BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
break; break;
case 8: //Ghost of Wall Street case 8: //Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0; BitNodeMultipliers.ScriptHackMoney = 0;
@ -256,7 +253,8 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.CrimeMoney = 0; BitNodeMultipliers.CrimeMoney = 0;
BitNodeMultipliers.HacknetNodeMoney = 0; BitNodeMultipliers.HacknetNodeMoney = 0;
BitNodeMultipliers.InfiltrationMoney = 0; BitNodeMultipliers.InfiltrationMoney = 0;
BitNodeMultipliers.RepToDonateToFaction = 0 BitNodeMultipliers.RepToDonateToFaction = 0;
BitNodeMultipliers.CorporationValuation = 0;
break; break;
case 11: //The Big Crash case 11: //The Big Crash
BitNodeMultipliers.ServerMaxMoney = 0.1; BitNodeMultipliers.ServerMaxMoney = 0.1;
@ -269,6 +267,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.AugmentationMoneyCost = 2; BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.InfiltrationMoney = 2.5; BitNodeMultipliers.InfiltrationMoney = 2.5;
BitNodeMultipliers.InfiltrationRep = 2.5; BitNodeMultipliers.InfiltrationRep = 2.5;
BitNodeMultipliers.CorporationValuation = 0.01;
break; break;
default: default:
console.log("WARNING: Player.bitNodeN invalid"); console.log("WARNING: Player.bitNodeN invalid");

@ -1,3 +1,4 @@
import {BitNodeMultipliers} from "./BitNode.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {showLiterature} from "./Literature.js"; import {showLiterature} from "./Literature.js";
import {Locations} from "./Location.js"; import {Locations} from "./Location.js";
@ -64,6 +65,9 @@ var OfficeInitialCost = 4e9;
var OfficeInitialSize = 3; var OfficeInitialSize = 3;
var OfficeUpgradeBaseCost = 1e9; var OfficeUpgradeBaseCost = 1e9;
var BribeThreshold = 100e12; //Money needed to be able to bribe for faction rep
var BribeToRepRatio = 1e9; //Bribe Value divided by this = rep gain
function Material(params={}) { function Material(params={}) {
this.name = params.name ? params.name : ""; this.name = params.name ? params.name : "";
this.qty = 0; //Quantity this.qty = 0; //Quantity
@ -101,67 +105,67 @@ Material.prototype.init = function(mats={}) {
this.dmd = 75; this.dmdR = [65, 85]; this.dmd = 75; this.dmdR = [65, 85];
this.cmp = 50; this.cmpR = [40, 60]; this.cmp = 50; this.cmpR = [40, 60];
this.bCost = 1000; this.mv = 0.2; this.bCost = 1000; this.mv = 0.2;
this.mku = 12; this.mku = 6;
break; break;
case "Energy": case "Energy":
this.dmd = 90; this.dmdR = [80, 100]; this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 80; this.cmpR = [65, 95]; this.cmp = 80; this.cmpR = [65, 95];
this.bCost = 1500; this.mv = 0.2; this.bCost = 1500; this.mv = 0.2;
this.mku = 12; this.mku = 6;
break; break;
case "Food": case "Food":
this.dmd = 80; this.dmdR = [70, 90]; this.dmd = 80; this.dmdR = [70, 90];
this.cmp = 60; this.cmpR = [35, 85]; this.cmp = 60; this.cmpR = [35, 85];
this.bCost = 5000; this.mv = 1; this.bCost = 5000; this.mv = 1;
this.mku = 7.5; this.mku = 3;
break; break;
case "Plants": case "Plants":
this.dmd = 70; this.dmdR = [20, 90]; this.dmd = 70; this.dmdR = [20, 90];
this.cmp = 50; this.cmpR = [30, 70]; this.cmp = 50; this.cmpR = [30, 70];
this.bCost = 3000; this.mv = 0.6; this.bCost = 3000; this.mv = 0.6;
this.mku = 10; this.mku = 3.75;
break; break;
case "Metal": case "Metal":
this.dmd = 80; this.dmdR = [75, 85]; this.dmd = 80; this.dmdR = [75, 85];
this.cmp = 70; this.cmpR = [60, 80]; this.cmp = 70; this.cmpR = [60, 80];
this.bCost = 2650; this.mv = 1; this.bCost = 2650; this.mv = 1;
this.mku = 12; this.mku = 6;
break; break;
case "Hardware": case "Hardware":
this.dmd = 85; this.dmdR = [80, 90]; this.dmd = 85; this.dmdR = [80, 90];
this.cmp = 80; this.cmpR = [65, 95]; this.cmp = 80; this.cmpR = [65, 95];
this.bCost = 4000; this.mv = 0.5; //Less mv bc its processed twice this.bCost = 4000; this.mv = 0.5; //Less mv bc its processed twice
this.mku = 5.5; this.mku = 1;
break; break;
case "Chemicals": case "Chemicals":
this.dmd = 55; this.dmdR = [40, 70]; this.dmd = 55; this.dmdR = [40, 70];
this.cmp = 60; this.cmpR = [40, 80]; this.cmp = 60; this.cmpR = [40, 80];
this.bCost = 6750; this.mv = 1.2; this.bCost = 6750; this.mv = 1.2;
this.mku = 6.5; this.mku = 2;
break; break;
case "Real Estate": case "Real Estate":
this.dmd = 50; this.dmdR = [5, 100]; this.dmd = 50; this.dmdR = [5, 100];
this.cmp = 50; this.cmpR = [25, 75]; this.cmp = 50; this.cmpR = [25, 75];
this.bCost = 16e3; this.mv = 1.5; //Less mv bc its processed twice this.bCost = 16e3; this.mv = 1.5; //Less mv bc its processed twice
this.mku = 5; this.mku = 1.5;
break; break;
case "Drugs": case "Drugs":
this.dmd = 60; this.dmdR = [45, 75]; this.dmd = 60; this.dmdR = [45, 75];
this.cmp = 70; this.cmpR = [40, 100]; this.cmp = 70; this.cmpR = [40, 100];
this.bCost = 8e3; this.mv = 1.6; this.bCost = 8e3; this.mv = 1.6;
this.mku = 4; this.mku = 1;
break; break;
case "Robots": case "Robots":
this.dmd = 90; this.dmdR = [80, 100]; this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 90; this.cmpR = [80, 100]; this.cmp = 90; this.cmpR = [80, 100];
this.bCost = 20e3; this.mv = 0.5; //Less mv bc its processed twice this.bCost = 20e3; this.mv = 0.5; //Less mv bc its processed twice
this.mku = 2.5; this.mku = 1;
break; break;
case "AI Cores": case "AI Cores":
this.dmd = 90; this.dmdR = [80, 100]; this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 90; this.cmpR = [80, 100]; this.cmp = 90; this.cmpR = [80, 100];
this.bCost = 27e3; this.mv = 0.8; //Less mv bc its processed twice this.bCost = 27e3; this.mv = 0.8; //Less mv bc its processed twice
this.mku = 1.8; this.mku = 0.5;
break; break;
case "Scientific Research": case "Scientific Research":
break; break;
@ -352,8 +356,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
(0.05 * employeeProd[EmployeePositions.Business])); (0.05 * employeeProd[EmployeePositions.Business]));
this.calculateRating(industry); this.calculateRating(industry);
var advMult = 1 + (Math.pow(this.advCost, 0.1) / 100); var advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
console.log("advMult: " + advMult); this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.75) * (busRatio + mgmtRatio));
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.9) * (busRatio + mgmtRatio));
this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness))); this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
this.cmp = getRandomInt(0, 70); this.cmp = getRandomInt(0, 70);
@ -548,11 +551,11 @@ var ProductRatingWeights = {
var IndustryUpgrades = { var IndustryUpgrades = {
"0": [0, 500e3, 1, 1.05, "0": [0, 500e3, 1, 1.05,
"Coffee", "Provide your employees with coffee, increasing their energy by 5%."], "Coffee", "Provide your employees with coffee, increasing their energy by 5%."],
"1": [1, 1e9, 1.02, 1.01, "1": [1, 1e9, 1.03, 1.03,
"AdVert.Inc", "Hire AdVert.Inc to advertise your company. Each level of " + "AdVert.Inc", "Hire AdVert.Inc to advertise your company. Each level of " +
"this upgrade grants your company a static increase of 4 and 1 to its awareness and " + "this upgrade grants your company a static increase of 4 and 1 to its awareness and " +
"popularity, respectively. It will then increase your company's awareness by 1%, and its popularity " + "popularity, respectively. It will then increase your company's awareness by 1%, and its popularity " +
"by a random percentage between 5% and 10%. These effects are increased by other upgrades " + "by a random percentage between 3% and 6%. These effects are increased by other upgrades " +
"that increase the power of your advertising."] "that increase the power of your advertising."]
} }
@ -626,6 +629,7 @@ function Industry(params={}) {
this.upgrades = Array(numUpgrades).fill(0); this.upgrades = Array(numUpgrades).fill(0);
this.state = "START"; this.state = "START";
this.newInd = true;
this.init(); this.init();
} }
@ -915,6 +919,10 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
this.thisCycleRevenue = new Decimal(0); this.thisCycleRevenue = new Decimal(0);
this.thisCycleExpenses = new Decimal(0); this.thisCycleExpenses = new Decimal(0);
//Once you start making revenue, the player should no longer be
//considered new, and therefore no longer needs the 'tutorial' UI elements
if (this.lastCycleRevenue.gt(0)) {this.newInd = false;}
//Process offices (and the employees in them) //Process offices (and the employees in them)
var employeeSalary = 0; var employeeSalary = 0;
for (var officeLoc in this.offices) { for (var officeLoc in this.offices) {
@ -1256,7 +1264,9 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
var prod = this.products[prodName]; var prod = this.products[prodName];
if (!prod.fin) { if (!prod.fin) {
var city = prod.createCity, office = this.offices[city]; var city = prod.createCity, office = this.offices[city];
var total = office.employeeProd["total"], ratio; var total = office.employeeProd[EmployeePositions.Operations] +
office.employeeProd[EmployeePositions.Engineer] +
office.employeeProd[EmployeePositions.Management], ratio;
if (total === 0) { if (total === 0) {
ratio = 0; ratio = 0;
} else { } else {
@ -1437,7 +1447,7 @@ Industry.prototype.upgrade = function(upgrade, refs) {
this.awareness += (4 * advMult); this.awareness += (4 * advMult);
this.popularity += (1 * advMult); this.popularity += (1 * advMult);
this.awareness *= (1.01 * advMult); this.awareness *= (1.01 * advMult);
this.popularity *= ((1 + getRandomInt(5, 10) / 100) * advMult); this.popularity *= ((1 + getRandomInt(3, 6) / 100) * advMult);
break; break;
default: default:
console.log("ERROR: Un-implemented function index: " + upgN); console.log("ERROR: Un-implemented function index: " + upgN);
@ -2121,7 +2131,6 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
class:"cmpy-mgmt-warehouse-material-div", class:"cmpy-mgmt-warehouse-material-div",
}); });
//Storage size
var totalExport = 0; var totalExport = 0;
for (var i = 0; i < mat.exp.length; ++i) { for (var i = 0; i < mat.exp.length; ++i) {
totalExport += mat.exp[i].amt; totalExport += mat.exp[i].amt;
@ -2160,17 +2169,27 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
div.appendChild(buttonPanel); div.appendChild(buttonPanel);
//Button to set purchase amount //Button to set purchase amount
buttonPanel.appendChild(createElement("a", { var tutorial = industry.newInd && Object.keys(industry.reqMats).includes(mat.name) &&
innerText: "Buy (" + formatNumber(mat.buy, 3) + ")", display:"inline-block", class:"a-link-button", mat.buy === 0 && mat.imp === 0;
var buyButtonParams = {
innerText: "Buy (" + formatNumber(mat.buy, 3) + ")", display:"inline-block",
class: tutorial ? "a-link-button flashing-button" : "a-link-button",
clickListener:()=>{ clickListener:()=>{
var txt = createElement("p", { var txt = createElement("p", {
innerHTML: "Enter the amount of " + mat.name + " you would like " + innerHTML: "Enter the amount of " + mat.name + " you would like " +
"to purchase per second. This material's cost changes constantly" "to purchase per second. This material's cost changes constantly"
}); });
var confirmBtn;
var input = createElement("input", { var input = createElement("input", {
type:"number", value:mat.buy ? mat.buy : null, placeholder: "Purchase amount" type:"number", value:mat.buy ? mat.buy : null, placeholder: "Purchase amount",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {
confirmBtn.click();
}
}
}); });
var confirmBtn = createElement("a", { confirmBtn = createElement("a", {
innerText:"Confirm", class:"a-link-button", innerText:"Confirm", class:"a-link-button",
clickListener:()=>{ clickListener:()=>{
if (isNaN(input.value)) { if (isNaN(input.value)) {
@ -2184,16 +2203,29 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
} }
} }
}); });
var clearButton = createElement("a", {
innerText:"Clear Purchase", class:"a-link-button",
clickListener:()=>{
mat.buy = 0;
removeElementById(purchasePopupId);
this.createUI(parentRefs);
return false;
}
});
var cancelBtn = createElement("a", { var cancelBtn = createElement("a", {
innerText:"Cancel", class:"a-link-button", innerText:"Cancel", class:"a-link-button",
clickListener:()=>{ clickListener:()=>{
removeElementById(purchasePopupId); removeElementById(purchasePopupId);
} }
}); });
createPopup(purchasePopupId, [txt, input, confirmBtn, cancelBtn]); createPopup(purchasePopupId, [txt, input, confirmBtn, clearButton, cancelBtn]);
input.focus(); input.focus();
} }
})); };
if (tutorial) {
buyButtonParams.tooltip = "Purchase your required materials to get production started!";
}
buttonPanel.appendChild(createElement("a", buyButtonParams));
//Button to manage exports //Button to manage exports
if (company.unlockUpgrades[0] === 1) { //Export unlock upgrade if (company.unlockUpgrades[0] === 1) { //Export unlock upgrade
@ -2366,15 +2398,24 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"to 'MP+10' then it will always be sold at $10 above the market price.", "to 'MP+10' then it will always be sold at $10 above the market price.",
}); });
var br = createElement("br", {}); var br = createElement("br", {});
var confirmBtn;
var inputQty = createElement("input", { var inputQty = createElement("input", {
type:"text", marginTop:"4px", type:"text", marginTop:"4px",
value: mat.sllman[1] ? mat.sllman[1] : null, placeholder: "Sell amount" value: mat.sllman[1] ? mat.sllman[1] : null, placeholder: "Sell amount",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
}
}); });
var inputPx = createElement("input", { var inputPx = createElement("input", {
type:"text", marginTop:"4px", type:"text", marginTop:"4px",
value: mat.sCost ? mat.sCost : null, placeholder: "Sell price" value: mat.sCost ? mat.sCost : null, placeholder: "Sell price",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
}
}); });
var confirmBtn = createElement("a", { confirmBtn = createElement("a", {
innerText:"Confirm", class:"a-link-button", margin:"6px", innerText:"Confirm", class:"a-link-button", margin:"6px",
clickListener:()=>{ clickListener:()=>{
//Parse price //Parse price
@ -2501,13 +2542,22 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
"Setting the sell amount to 'MAX' will result in you always selling the " + "Setting the sell amount to 'MAX' will result in you always selling the " +
"maximum possible amount of the material.<br><br>", "maximum possible amount of the material.<br><br>",
}); });
var confirmBtn;
var inputQty = createElement("input", { var inputQty = createElement("input", {
type:"text", value:product.sllman[city][1] ? product.sllman[city][1] : null, placeholder: "Sell amount" type:"text", value:product.sllman[city][1] ? product.sllman[city][1] : null, placeholder: "Sell amount",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
}
}); });
var inputPx = createElement("input", { var inputPx = createElement("input", {
type:"text", value: product.sCost ? product.sCost : null, placeholder: "Sell price" type:"text", value: product.sCost ? product.sCost : null, placeholder: "Sell price",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
}
}); });
var confirmBtn = createElement("a", { confirmBtn = createElement("a", {
class:"a-link-button", innerText:"Confirm", class:"a-link-button", innerText:"Confirm",
clickListener:()=>{ clickListener:()=>{
//Parse price //Parse price
@ -2566,10 +2616,15 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
innerText:"Enter a limit to the amount of this product you would " + innerText:"Enter a limit to the amount of this product you would " +
"like to product per second. Leave the box empty to set no limit." "like to product per second. Leave the box empty to set no limit."
}); });
var confirmBtn;
var input = createElement("input", { var input = createElement("input", {
type:"number", placeholder:"Limit" type:"number", placeholder:"Limit",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
}
}); });
var confirmBtn = createElement("a", { confirmBtn = createElement("a", {
class:"a-link-button", display:"inline-block", innerText:"Limit production", margin:'6px', class:"a-link-button", display:"inline-block", innerText:"Limit production", margin:'6px',
clickListener:()=>{ clickListener:()=>{
if (input.value === "") { if (input.value === "") {
@ -2698,10 +2753,10 @@ var CorporationUpgrades = {
"20 seconds."], "20 seconds."],
//Makes advertising more effective //Makes advertising more effective
"3": [3, 4e9, 1.11, 0.1, "3": [3, 4e9, 1.12, 0.01,
"Wilson Analytics", "Purchase data and analysis from Wilson, a marketing research " + "Wilson Analytics", "Purchase data and analysis from Wilson, a marketing research " +
"firm. Each level of this upgrades increases the effectiveness of your " + "firm. Each level of this upgrades increases the effectiveness of your " +
"advertising by 10% (additive)."], "advertising by 1% (additive)."],
//Augmentation for employees, increases cre //Augmentation for employees, increases cre
"4": [4, 1e9, 1.06, 0.1, "4": [4, 1e9, 1.06, 0.1,
@ -2776,11 +2831,32 @@ Corporation.prototype.process = function(numCycles=1) {
if (this.storedCycles >= CyclesPerIndustryStateCycle) { if (this.storedCycles >= CyclesPerIndustryStateCycle) {
var state = this.getState(); var state = this.getState();
//At the start of a new cycle, calculate profits from previous cycle
if (state === "START") {
this.revenue = new Decimal(0);
this.expenses = new Decimal(0);
this.divisions.forEach((ind)=>{
this.revenue = this.revenue.plus(ind.lastCycleRevenue);
this.expenses = this.expenses.plus(ind.lastCycleExpenses);
});
var profit = this.revenue.minus(this.expenses);
var cycleProfit = profit.times(numMarketCyclesPersist * SecsPerMarketCycle);
if (isNaN(this.funds)) {
dialogBoxCreate("There was an error calculating your Corporations funds and they got reset to 0. " +
"This is a bug. Please report to game developer.<br><br>" +
"(Your funds have been set to $150b for the inconvenience)");
this.funds = new Decimal(150e9);
}
this.funds = this.funds.plus(cycleProfit);
this.updateSharePrice();
}
//Determine number of market cycles at the START state //Determine number of market cycles at the START state
if (state === "START") { if (state === "START") {
if (this.storedCycles >= 2*CyclesPerMarketCycle) { if (this.storedCycles >= 2*CyclesPerMarketCycle) {
//Enough cycles stored for 2+ market cycles //Enough cycles stored for 2+ market cycles
numMarketCyclesPersist = Math.floor(this.storedCycles / CyclesPerMarketCycle); //Capped out at 3 to prevent weird behavior
numMarketCyclesPersist = Math.max(3, Math.floor(this.storedCycles / CyclesPerMarketCycle));
} else { } else {
numMarketCyclesPersist = 1; numMarketCyclesPersist = 1;
} }
@ -2792,25 +2868,7 @@ Corporation.prototype.process = function(numCycles=1) {
ind.process(marketCycles, state, corp); ind.process(marketCycles, state, corp);
}); });
//At the start of a new cycle, calculate profits from previous cycle
if (state === "START") {
this.revenue = new Decimal(0);
this.expenses = new Decimal(0);
this.divisions.forEach((ind)=>{
this.revenue = this.revenue.plus(ind.lastCycleRevenue);
this.expenses = this.expenses.plus(ind.lastCycleExpenses);
});
var profit = this.revenue.minus(this.expenses);
var cycleProfit = profit.times(marketCycles * SecsPerMarketCycle);
if (isNaN(this.funds)) {
dialogBoxCreate("There was an error calculating your Corporations funds and they got reset to 0. " +
"This is a bug. Please report to game developer.<br><br>" +
"(Your funds have been set to $150b for the inconvenience)");
this.funds = new Decimal(150e9);
}
this.funds = this.funds.plus(cycleProfit);
this.updateSharePrice();
}
this.state.nextState(); this.state.nextState();
if (Engine.currentPage === Engine.Page.Corporation) {this.updateUIContent();} if (Engine.currentPage === Engine.Page.Corporation) {this.updateUIContent();}
@ -2833,7 +2891,7 @@ Corporation.prototype.determineValuation = function() {
} }
val -= (val % 1e6); //Round down to nearest millionth val -= (val % 1e6); //Round down to nearest millionth
} }
return val; return val * BitNodeMultipliers.CorporationValuation;
} }
Corporation.prototype.getInvestment = function() { Corporation.prototype.getInvestment = function() {
@ -2882,8 +2940,9 @@ Corporation.prototype.goPublic = function() {
var txt = createElement("p", { var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to issue " + innerHTML: "Enter the number of shares you would like to issue " +
"for your IPO. These shares will be publicly sold " + "for your IPO. These shares will be publicly sold " +
"and you will no longer own them. You will receive " + "and you will no longer own them. Your Corporation will receive " +
numeral(initialSharePrice).format('$0.000a') + " per share.<br><br>" + numeral(initialSharePrice).format('$0.000a') + " per share " +
"(the IPO money will be deposited directly into your Corporation's funds).<br><br>" +
"Furthermore, issuing more shares now will help drive up " + "Furthermore, issuing more shares now will help drive up " +
"your company's stock price in the future.<br><br>" + "your company's stock price in the future.<br><br>" +
"You have a total of " + numeral(this.numShares).format("0.000a") + " of shares that you can issue.", "You have a total of " + numeral(this.numShares).format("0.000a") + " of shares that you can issue.",
@ -3166,13 +3225,22 @@ Corporation.prototype.updateUIHeaderTabs = function() {
} }
}); });
//Make an object to keep track of what industries you're already in
var ownedIndustries = {}
for (var i = 0; i < this.divisions.length; ++i) {
ownedIndustries[this.divisions[i].type] = true;
}
//Add industry types to selector //Add industry types to selector
//Have Agriculture be first as recommended option //Have Agriculture be first as recommended option
if (!ownedIndustries["Agriculture"]) {
selector.add(createElement("option", { selector.add(createElement("option", {
text:Industries["Agriculture"], value:"Agriculture" text:Industries["Agriculture"], value:"Agriculture"
})) }));
}
for (var key in Industries) { for (var key in Industries) {
if (key !== "Agriculture" && Industries.hasOwnProperty(key)) { if (key !== "Agriculture" && Industries.hasOwnProperty(key) && !ownedIndustries[key]) {
var ind = Industries[key]; var ind = Industries[key];
selector.add(createElement("option", { selector.add(createElement("option", {
text: ind,value:key, text: ind,value:key,
@ -3300,7 +3368,9 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
var popupId = "cmpy-mgmt-sell-shares-popup"; var popupId = "cmpy-mgmt-sell-shares-popup";
var currentStockPrice = this.sharePrice; var currentStockPrice = this.sharePrice;
var txt = createElement("p", { var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to sell. The current price of your " + innerHTML: "Enter the number of shares you would like to sell. The money from " +
"selling your shares will go directly to you (NOT your Corporation). " +
"The current price of your " +
"company's stock is " + numeral(currentStockPrice).format("$0.000a"), "company's stock is " + numeral(currentStockPrice).format("$0.000a"),
}); });
var profitIndicator = createElement("p", {}); var profitIndicator = createElement("p", {});
@ -3308,7 +3378,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
type:"number", placeholder:"Shares to sell", margin:"5px", type:"number", placeholder:"Shares to sell", margin:"5px",
inputListener: ()=> { inputListener: ()=> {
var numShares = Math.round(input.value); var numShares = Math.round(input.value);
if (isNaN(numShares) || shares <= 0) { if (isNaN(numShares) || numShares <= 0) {
profitIndicator.innerText = "ERROR: Invalid value entered for number of shares to sell" profitIndicator.innerText = "ERROR: Invalid value entered for number of shares to sell"
} else if (numShares > this.numShares) { } else if (numShares > this.numShares) {
profitIndicator.innerText = "You don't have this many shares to sell!"; profitIndicator.innerText = "You don't have this many shares to sell!";
@ -3365,7 +3435,9 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
var popupId = "cmpy-mgmt-buyback-shares-popup"; var popupId = "cmpy-mgmt-buyback-shares-popup";
var currentStockPrice = this.sharePrice; var currentStockPrice = this.sharePrice;
var txt = createElement("p", { var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to buy back at market price. The current price of your " + innerHTML: "Enter the number of shares you would like to buy back at market price. To purchase " +
"these shares, you must use your own money (NOT your Corporation's funds). " +
"The current price of your " +
"company's stock is " + numeral(currentStockPrice).format("$0.000a") + "company's stock is " + numeral(currentStockPrice).format("$0.000a") +
". Your company currently has " + formatNumber(this.issuedShares, 3) + " outstanding stock shares", ". Your company currently has " + formatNumber(this.issuedShares, 3) + " outstanding stock shares",
}); });
@ -3375,12 +3447,13 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
inputListener: ()=> { inputListener: ()=> {
var numShares = Math.round(input.value); var numShares = Math.round(input.value);
//TODO add conditional for if player doesn't have enough money //TODO add conditional for if player doesn't have enough money
if (isNaN(numShares) || shares <= 0) { if (isNaN(numShares) || numShares <= 0) {
costIndicator.innerText = "ERROR: Invalid value entered for number of shares to buyback" costIndicator.innerText = "ERROR: Invalid value entered for number of shares to buyback"
} else if (numShares > this.issuedShares) { } else if (numShares > this.issuedShares) {
costIndicator.innerText = "There are not this many shares available to buy back. " + costIndicator.innerText = "There are not this many shares available to buy back. " +
"There are only " + this.issuedShares + " outstanding shares."; "There are only " + this.issuedShares + " outstanding shares.";
} else { } else {
console.log("here");
costIndicator.innerText = "Purchase " + numShares + " shares for a total of " + costIndicator.innerText = "Purchase " + numShares + " shares for a total of " +
numeral(numShares * currentStockPrice).format('$0.000a'); numeral(numShares * currentStockPrice).format('$0.000a');
} }
@ -3434,6 +3507,114 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
companyManagementPanel.appendChild(sellShares); companyManagementPanel.appendChild(sellShares);
companyManagementPanel.appendChild(buybackShares); companyManagementPanel.appendChild(buybackShares);
//If your Corporation is big enough, buy faction influence through bribes
var canBribe = this.determineValuation() >= BribeThreshold;
var bribeFactions = createElement("a", {
class: canBribe ? "a-link-button" : "a-link-button-inactive",
innerText:"Bribe Factions", display:"inline-block",
tooltip:canBribe
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
: "Your Corporation is not powerful enough to bribe Faction leaders",
clickListener:()=>{
var popupId = "cmpy-mgmt-bribe-factions-popup";
var txt = createElement("p", {
innerText:"You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation"
});
var factionSelector = createElement("select", {margin:"3px"});
for (var facName in Player.factions) {
if (Player.factions.hasOwnProperty(facName)) {
factionSelector.add(createElement("option"), {
text:facName, value:facName
});
}
}
var repGainText = createElement("p");
var stockSharesInput;
var moneyInput = createElement("input", {
type:"number", placeholder:"Corporation funds", margin:"5px",
inputListener:()=>{
var money = moneyInput.value == null ? 0 : moneyInput.value;
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
}
}
});
stockSharesInput = createElement("input", {
type:"number", placeholder:"Stock Shares", margin: "5px",
inputListener:()=>{
var money = moneyInput.value == null ? 0 : moneyInput.value;
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
}
}
});
var confirmButton = createElement("a", {
class:"a-link-button", innerText:"Bribe", display:"inline-block",
clickListener:()=>{
var money = moneyInput.value == null ? 0 : moneyInput.value;
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null ? 0 : Math.round(stockSharesInput.value);
var fac = Factions[factionSelector.options[factionSelector.selectedIndex].value];
if (fac == null) {
dialogBoxCreate("ERROR: You must select a faction to bribe");
return false;
}
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
dialogBoxCreate("ERROR: Invalid value(s) entered");
} else if (this.funds.lt(money)) {
dialogBoxCreate("ERROR: You do not have this much money to bribe with");
} else if (this.stockShares > this.numShares) {
dialogBoxCreate("ERROR: You do not have this many shares to bribe with");
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
dialogBoxCreate("You gained " + formatNumber(repGain, 0) +
" reputation with " + fac.name + " by bribing them.");
fac.playerReputation += repGain;
this.funds = this.funds.lt(money);
this.numShares -= stockShares;
removeElementById(popupId);
return false;
}
}
});
var cancelButton = createElement("a", {
class:"a-link-button", innerText:"Cancel", display:"inline-block",
clickListener:()=>{
removeElementById(popupId);
return false;
}
});
}
});
companyManagementPanel.appendChild(bribeFactions);
} else { } else {
var findInvestors = createElement("a", { var findInvestors = createElement("a", {
class: this.fundingRound >= 4 ? "a-link-button-inactive" : "a-link-button tooltip", class: this.fundingRound >= 4 ? "a-link-button-inactive" : "a-link-button tooltip",
@ -3899,6 +4080,19 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
industryEmployeePanel.appendChild(industryEmployeeText); industryEmployeePanel.appendChild(industryEmployeeText);
//Hire Employee button //Hire Employee button
if (office.employees.length === 0) {
industryEmployeeHireButton = createElement("a", {
class:"a-link-button",display:"inline-block",
innerText:"Hire Employee", fontSize:"13px",
tooltip:"You'll need to hire some employees to get your operations started! " +
"It's recommended to have at least one employee in every position",
clickListener:()=>{
office.findEmployees({corporation:this, division:division});
return false;
}
});
//industryEmployeeHireButton.classList.add("flashing-button");
} else {
industryEmployeeHireButton = createElement("a", { industryEmployeeHireButton = createElement("a", {
class:"a-link-button",display:"inline-block", class:"a-link-button",display:"inline-block",
innerText:"Hire Employee", fontSize:"13px", innerText:"Hire Employee", fontSize:"13px",
@ -3907,6 +4101,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
return false; return false;
} }
}); });
}
industryEmployeePanel.appendChild(industryEmployeeHireButton); industryEmployeePanel.appendChild(industryEmployeeHireButton);
//Autohire Employee button //Autohire Employee button
@ -4240,6 +4435,9 @@ Corporation.prototype.updateDivisionContent = function(division) {
if (office.employees.length >= office.size) { if (office.employees.length >= office.size) {
industryEmployeeHireButton.className = "a-link-button-inactive"; industryEmployeeHireButton.className = "a-link-button-inactive";
industryEmployeeAutohireButton.className = "a-link-button-inactive tooltip"; industryEmployeeAutohireButton.className = "a-link-button-inactive tooltip";
} else if (office.employees.length === 0) {
industryEmployeeHireButton.className = "a-link-button tooltip flashing-button";
industryEmployeeAutohireButton.className = "a-link-button tooltip";
} else { } else {
industryEmployeeHireButton.className = "a-link-button"; industryEmployeeHireButton.className = "a-link-button";
industryEmployeeAutohireButton.className = "a-link-button tooltip"; industryEmployeeAutohireButton.className = "a-link-button tooltip";

@ -38,6 +38,7 @@ let CONSTANTS = {
//NeuroFlux Governor cost multiplier as you level up //NeuroFlux Governor cost multiplier as you level up
NeuroFluxGovernorLevelMult: 1.14, NeuroFluxGovernorLevelMult: 1.14,
/* Netscript Constants */
//RAM Costs for different commands //RAM Costs for different commands
ScriptWhileRamCost: 0.2, ScriptWhileRamCost: 0.2,
ScriptForRamCost: 0.2, ScriptForRamCost: 0.2,
@ -79,6 +80,8 @@ let CONSTANTS = {
MultithreadingRAMCost: 1, MultithreadingRAMCost: 1,
NumNetscriptPorts: 20,
//Server constants //Server constants
ServerBaseGrowthRate: 1.03, //Unadjusted Growth rate ServerBaseGrowthRate: 1.03, //Unadjusted Growth rate
ServerMaxGrowthRate: 1.0035, //Maximum possible growth rate (max rate accounting for server security) ServerMaxGrowthRate: 1.0035, //Maximum possible growth rate (max rate accounting for server security)
@ -1135,38 +1138,39 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.34.5<br>" + "v0.35.0<br>" +
"-Minor rebalancing of BitNodes due to the fact that Corporations provide a (relatively) new method of " +
"progressing<br>" +
"-Corporation Management Changes:<br>" + "-Corporation Management Changes:<br>" +
"---Market Research unlocks are now cheaper<br>" + "---Once your Corporation gets big/powerful enough, you can now bribe Factions for reputation using company funds an/or stock shares<br>" +
"---New 'VeChain' upgrade: displays useful statistics about Corporation<br>" + "---You can now only create one Division for every Industry type<br>" +
"---Corporation cycles are processed 25% faster<br>" + "---Added several new UI/UX elements<br>" +
"---Corporation valuation was lowered by ~10% (this affects stock price and investments)<br>" + "---Wilson Analytics multiplier was significantly reduced to 1% per level (additive).<br>" +
"---Rebalanced the effects of advertising. Should now be more effective for every Industry<br>" + "---Reduced the effect of Advert Inc upgrade. Advert Inc. upgrade price increases faster<br>" +
"---Fixed several bugs/exploits involving selling and buying back stock shares<br>" + "---Materials can now be marked up at higher prices<br>" +
"---You will now receive a Corporation Handbook (.lit file) when starting out BitNode-3. It contains a brief guide to help you get started. " + "-Added Javascript's built-in Number object to Netscript<br>" +
"This same handbook can be viewed from the Corporation management screen<br>" + "-Added getCharacterInformation(), getCompanyFavor(), and getFactionFavor() Netscript Singularity functions<br>" +
"---Slightly decreased the amount by which a Product's sell price can be marked up<br>" + "-Rebalanced Singularity Function RAM Costs. They now cost x8 as much when outside of BN-4 (rather than x10). Also, " +
"---Employees can now be assigned to a 'Training' task, during which they will slowly increase several of their stats<br>" + "many of the functions now use significantly less RAM<br>" +
"-Hopefully fixed an exploit with Array.forEach(). If there are any issues with using forEach, let me know<br>" + "-Refactored Netscript Ports. You can now get a handle for a Netscript port using the " +
"-Arguments passed into a script are now passed by value. This means modifying the 'args' array in a script " + "getPortHandle() Netscript function. This allows you to access a port's underlying queue (which is just an array) and also " +
"should no longer cause issues<br>" + "makes several new functions available such as tryWrite(), full(), and empty().<br>" +
"-Scripts executed programatically (via run(), exec(), etc.) will now fail if null/undefined is passed in " + "-Number of Netscript Ports increased from 10 to 20<br>" +
"as an argument<br>" + "-Netscript assignments should now return proper values. i.e. i = 5 should return 5.<br>" +
"-Added peek() Netscript function<br>" + "-Added throw statements to Netscript. It's not super useful since 'catch' isn't implemented, but it can be used " +
"-killall() Netscript function now returns true if any scripts were killed, and false otherwise.<br>" + "to generate custom runtime error messages.<br>" +
"-hack() Netscript function now returns the amount of money gained for successful hacks, and 0 for failed hacks<br>" + "-Added import declaration to Netscript. With this, you are able to import functions (and only functions) from " +
"-scp Terminal command and Netscript function now work for txt files<br>" + "other files. Using export declarations is not necessary<br>" +
"-Changes courtesy of Wraithan:<br>" + "-Most Netscript Runtime errors (the ones that cause your script to crash) should now include the line number where the error occured<br>" +
"---Text files are now displayed using 'pre' rather than 'p' elements when using the 'cat' Terminal command. " + "-When working for a company, your current company reputation is now displayed<br>" +
"This means tabs are retained and lines don't automatically wrap<br>" + "-Whenever you get a Faction Invite it will be immediately appended to your 'invited factions' list. " +
"---ls() Netscript function now returns text files as well<br>" + "Therefore the checkFactionInvitations() Singularity Function should now be properly useable since you no longer " +
"-Removed round() Netscript function, since you can just use Math.round() instead<br>" + "need to decline a Faction Invitation before it shows up in the result.<br>" +
"-Added disableLog() and enableLog() Netscript functions<br>" + "-Bug Fix: When purchasing servers, whitespace should now automatically be removed from the hostname<br>" +
"-Removed the 'log' argument from sleep(), since you can now use the new disableLog function<br>" + "-Bug Fix: Can no longer have whitespace in the filename of text files created using write()<br>" +
"-'Netscript Documentation' button on script editor now points to new readthedocs documentation rather than wiki<br>" + "-Bug Fix: In Netscript, you can no longer assign a Hacknet Node handle (hacknetnodes[i]) to another value <br>" +
"-When working for a faction, your current faction reputation is now displayed<br>" + "-Bug Fix: If you are in the Factions tab when you accept an invitation from a Faction, the page will now properly 'refresh'<br>" +
"-Bug Fix: Hacking Missions should no longer break when dragging an existing connection to another Node<br>" + "-Bug Fix: Scripts that run recursive functions should now be killed properly<br>"
"-Bug Fix: Fixed RAM usage of getNextHacknetNodeCost() (is not 1.5GB instead of 4GB)<br>"
} }
export {CONSTANTS}; export {CONSTANTS};

@ -14,6 +14,7 @@ import {factionInvitationBoxCreate} from "../utils/FactionInvitation
import {clearEventListeners} from "../utils/HelperFunctions.js"; import {clearEventListeners} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js"; Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber, isPositiveNumber} from "../utils/StringHelperFunctions.js"; import {formatNumber, isPositiveNumber} from "../utils/StringHelperFunctions.js";
import {yesNoBoxCreate, yesNoBoxGetYesButton, import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox.js"; yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox.js";
@ -831,11 +832,13 @@ function displayFactionAugmentations(factionName) {
for (var j = 0; j < Player.queuedAugmentations.length; ++j) { for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name == aug.name) { if (Player.queuedAugmentations[j].name == aug.name) {
owned = true; owned = true;
break;
} }
} }
for (var j = 0; j < Player.augmentations.length; ++j) { for (var j = 0; j < Player.augmentations.length; ++j) {
if (Player.augmentations[j].name == aug.name) { if (Player.augmentations[j].name == aug.name) {
owned = true; owned = true;
break;
} }
} }
@ -844,7 +847,6 @@ function displayFactionAugmentations(factionName) {
var aDiv = document.createElement("div"); var aDiv = document.createElement("div");
var aElem = document.createElement("a"); var aElem = document.createElement("a");
var pElem = document.createElement("p"); var pElem = document.createElement("p");
aElem.setAttribute("href", "#");
var req = aug.baseRepRequirement * faction.augmentationRepRequirementMult; var req = aug.baseRepRequirement * faction.augmentationRepRequirementMult;
var hasPrereqs = hasAugmentationPrereqs(aug); var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) { if (!hasPrereqs) {
@ -856,10 +858,11 @@ function displayFactionAugmentations(factionName) {
pElem.innerHTML = "ALREADY OWNED"; pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) { } else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button"); aElem.setAttribute("class", "a-link-button");
pElem.innerHTML = "UNLOCKED - $" + formatNumber(aug.baseCost * faction.augmentationPriceMult, 2); //pElem.innerHTML = "UNLOCKED - $" + formatNumber(aug.baseCost * faction.augmentationPriceMult, 2);
pElem.innerHTML = "UNLOCKED - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a");
} else { } else {
aElem.setAttribute("class", "a-link-button-inactive"); aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - $" + formatNumber(aug.baseCost * faction.augmentationPriceMult, 2); pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a");
pElem.style.color = "red"; pElem.style.color = "red";
} }
aElem.style.display = "inline"; aElem.style.display = "inline";

@ -1,4 +1,7 @@
import {HacknetNode} from "./HacknetNode.js";
import {NetscriptFunctions} from "./NetscriptFunctions.js"; import {NetscriptFunctions} from "./NetscriptFunctions.js";
import {NetscriptPort} from "./NetscriptPort.js";
/* Environment /* Environment
* NetScript program environment * NetScript program environment
*/ */
@ -73,6 +76,14 @@ Environment.prototype = {
} }
res = res[i]; res = res[i];
} }
//Cant assign to ports or HacknetNodes
if (res[idx[idx.length-1]] instanceof HacknetNode) {
throw new Error("Cannot assign a Hacknet Node handle to a new value");
}
if (res[idx[idx.length-1]] instanceof NetscriptPort) {
throw new Error("Cannot assign a Netscript Port handle to a new value");
}
return res[idx[idx.length-1]] = value; return res[idx[idx.length-1]] = value;
}, },

@ -3,12 +3,12 @@ import {CONSTANTS} from "./Constants.js";
import {Player} from "./Player.js"; import {Player} from "./Player.js";
import {Environment} from "./NetscriptEnvironment.js"; import {Environment} from "./NetscriptEnvironment.js";
import {WorkerScript, addWorkerScript} from "./NetscriptWorker.js"; import {WorkerScript, addWorkerScript} from "./NetscriptWorker.js";
import {Server} from "./Server.js"; import {Server, getServer} from "./Server.js";
import {Settings} from "./Settings.js"; import {Settings} from "./Settings.js";
import {Script, findRunningScript, import {Script, findRunningScript,
RunningScript} from "./Script.js"; RunningScript} from "./Script.js";
import {Node} from "../utils/acorn.js"; import {parse, Node} from "../utils/acorn.js";
import {printArray} from "../utils/HelperFunctions.js"; import {printArray} from "../utils/HelperFunctions.js";
import {isValidIPAddress} from "../utils/IPAddress.js"; import {isValidIPAddress} from "../utils/IPAddress.js";
import {isString} from "../utils/StringHelperFunctions.js"; import {isString} from "../utils/StringHelperFunctions.js";
@ -31,7 +31,7 @@ function evaluate(exp, workerScript) {
var env = workerScript.env; var env = workerScript.env;
if (env.stopFlag) {return Promise.reject(workerScript);} if (env.stopFlag) {return Promise.reject(workerScript);}
if (exp == null) { if (exp == null) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Error: NULL expression")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Error: NULL expression", exp));
} }
if (env.stopFlag) {return Promise.reject(workerScript);} if (env.stopFlag) {return Promise.reject(workerScript);}
switch (exp.type) { switch (exp.type) {
@ -59,11 +59,11 @@ function evaluate(exp, workerScript) {
case "Identifier": case "Identifier":
//Javascript constructor() method can be used as an exploit to run arbitrary code //Javascript constructor() method can be used as an exploit to run arbitrary code
if (exp.name == "constructor") { if (exp.name == "constructor") {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.", exp));
} }
if (!(exp.name in env.vars)){ if (!(exp.name in env.vars)){
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.name + " not defined")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.name + " not defined", exp));
} }
return Promise.resolve(env.get(exp.name)) return Promise.resolve(env.get(exp.name))
break; break;
@ -135,7 +135,7 @@ function evaluate(exp, workerScript) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e.toString())); return Promise.reject(makeRuntimeRejectMsg(workerScript, e.toString()));
} }
}); });
} else if (exp.callee.type == "MemberExpression"){ } else if (exp.callee.type === "MemberExpression"){
return evaluate(exp.callee.object, workerScript).then(function(object) { return evaluate(exp.callee.object, workerScript).then(function(object) {
try { try {
if (func === "NETSCRIPTFOREACH") { if (func === "NETSCRIPTFOREACH") {
@ -148,7 +148,7 @@ function evaluate(exp, workerScript) {
var res = func.apply(object,args); var res = func.apply(object,args);
return Promise.resolve(res); return Promise.resolve(res);
} catch (e) { } catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e)); return Promise.reject(makeRuntimeRejectMsg(workerScript, e, exp));
} }
}); });
} else { } else {
@ -158,6 +158,11 @@ function evaluate(exp, workerScript) {
return out.then(function(res) { return out.then(function(res) {
return Promise.resolve(res) return Promise.resolve(res)
}).catch(function(e) { }).catch(function(e) {
if (isScriptErrorMessage(e)) {
//Functions don't have line number appended in error message, so add it
var num = getErrorLineNumber(exp, workerScript);
e += " (Line " + num + ")";
}
return Promise.reject(e); return Promise.reject(e);
}); });
} else { } else {
@ -165,9 +170,14 @@ function evaluate(exp, workerScript) {
} }
} catch (e) { } catch (e) {
if (isScriptErrorMessage(e)) { if (isScriptErrorMessage(e)) {
if (isScriptErrorMessage(e)) {
//Functions don't have line number appended in error message, so add it
var num = getErrorLineNumber(exp, workerScript);
e += " (Line " + num + ")";
}
return Promise.reject(e); return Promise.reject(e);
} else { } else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e)); return Promise.reject(makeRuntimeRejectMsg(workerScript, e, exp));
} }
} }
} }
@ -179,19 +189,19 @@ function evaluate(exp, workerScript) {
if (exp.computed){ if (exp.computed){
return evaluate(exp.property, workerScript).then(function(index) { return evaluate(exp.property, workerScript).then(function(index) {
if (index >= object.length) { if (index >= object.length) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid index for arrays")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid index for arrays", exp));
} }
return Promise.resolve(object[index]); return Promise.resolve(object[index]);
}).catch(function(e) { }).catch(function(e) {
if (e instanceof WorkerScript || isScriptErrorMessage(e)) { if (e instanceof WorkerScript || isScriptErrorMessage(e)) {
return Promise.reject(e); return Promise.reject(e);
} else { } else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression", exp));
} }
}); });
} else { } else {
if (exp.property.name === "constructor") { if (exp.property.name === "constructor") {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.", exp));
} }
if (object != null && object instanceof Array && exp.property.name === "forEach") { if (object != null && object instanceof Array && exp.property.name === "forEach") {
return "NETSCRIPTFOREACH"; return "NETSCRIPTFOREACH";
@ -199,7 +209,7 @@ function evaluate(exp, workerScript) {
try { try {
return Promise.resolve(object[exp.property.name]) return Promise.resolve(object[exp.property.name])
} catch (e) { } catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString())); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString(), exp));
} }
} }
}); });
@ -235,14 +245,14 @@ function evaluate(exp, workerScript) {
} }
switch (exp.operator){ switch (exp.operator){
default: default:
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". You are trying to use code that is currently unsupported")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". You are trying to use code that is currently unsupported", exp));
} }
return Promise.resolve(env.get(exp.argument.name)) return Promise.resolve(env.get(exp.argument.name))
} else { } else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.argument.name + " not defined")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.argument.name + " not defined", exp));
} }
} else { } else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier", exp));
} }
break; break;
case "EmptyStatement": case "EmptyStatement":
@ -263,8 +273,7 @@ function evaluate(exp, workerScript) {
return evaluateIf(exp, workerScript); return evaluateIf(exp, workerScript);
break; break;
case "SwitchStatement": case "SwitchStatement":
var lineNum = getErrorLineNumber(exp, workerScript); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript", exp));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript (line " + (lineNum+1) + ")"));
break; break;
case "WhileStatement": case "WhileStatement":
return evaluateWhile(exp, workerScript).then(function(res) { return evaluateWhile(exp, workerScript).then(function(res) {
@ -297,13 +306,24 @@ function evaluate(exp, workerScript) {
env.set(exp.id.name, exp); env.set(exp.id.name, exp);
return Promise.resolve(true); return Promise.resolve(true);
} else { } else {
var lineNum = getErrorLineNumber(exp, workerScript); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration", exp));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration at line " + lineNum+1));
} }
break; break;
case "ImportDeclaration":
return evaluateImport(exp, workerScript).then(function(res) {
return Promise.resolve(res);
}).catch(function(e) {
return Promise.reject(e);
});
break;
case "ThrowStatement":
//return Promise.reject(makeRuntimeRejectMsg(workerScript))
return evaluate(exp.argument, workerScript).then(function(res) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, res));
});
break;
default: default:
var lineNum = getErrorLineNumber(exp, workerScript); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". This is currently unsupported in Netscript", exp));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + " (line " + (lineNum+1) + "). This is currently unsupported in Netscript"));
break; break;
} //End switch } //End switch
}).catch(function(e) { }).catch(function(e) {
@ -438,6 +458,9 @@ function evalAssignment(exp, workerScript) {
return evaluate(exp.right, workerScript).then(function(expRight) { return evaluate(exp.right, workerScript).then(function(expRight) {
if (exp.left.type == "MemberExpression") { if (exp.left.type == "MemberExpression") {
if (!exp.left.computed) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Cannot assign to an object's property. This is currently unsupported in Netscript", exp));
}
//Assign to array element //Assign to array element
//Array object designed by exp.left.object.name //Array object designed by exp.left.object.name
//Index designated by exp.left.property //Index designated by exp.left.property
@ -450,37 +473,38 @@ function evalAssignment(exp, workerScript) {
var arrName = res.splice(1, 1); var arrName = res.splice(1, 1);
arrName = arrName[0]; arrName = arrName[0];
env.setArrayElement(arrName, res, expRight); var res;
return Promise.resolve(false); try {
res = env.setArrayElement(arrName, res, expRight);
} catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e));
}
return Promise.resolve(res);
}).catch(function(e) { }).catch(function(e) {
return Promise.reject(e); return Promise.reject(e);
}); });
} else { } else {
//Other assignments //Other assignments
try { try {
var assign;
switch (exp.operator) { switch (exp.operator) {
case "=": case "=":
env.set(exp.left.name,expRight); assign = expRight; break;
break;
case "+=": case "+=":
env.set(exp.left.name,env.get(exp.left.name) + expRight); assign = env.get(exp.left.name) + expRight; break;
break;
case "-=": case "-=":
env.set(exp.left.name,env.get(exp.left.name) - expRight); assign = env.get(exp.left.name) - expRight; break;
break;
case "*=": case "*=":
env.set(exp.left.name,env.get(exp.left.name) * expRight); assign = env.get(exp.left.name) * expRight; break;
break;
case "/=": case "/=":
env.set(exp.left.name,env.get(exp.left.name) / expRight); assign = env.get(exp.left.name) / expRight; break;
break;
case "%=": case "%=":
env.set(exp.left.name,env.get(exp.left.name) % expRight); assign = env.get(exp.left.name) % expRight; break;
break;
default: default:
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Bitwise assignment is not implemented")); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Bitwise assignment is not implemented"));
} }
return Promise.resolve(false); env.set(exp.left.name, assign);
return Promise.resolve(assign);
} catch (e) { } catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString())); return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString()));
} }
@ -637,14 +661,117 @@ function evaluateProg(exp, workerScript, index) {
} }
} }
function evaluateImport(exp, workerScript, checkingRam=false) {
//When its checking RAM, it exports an array of nodes for each imported function
var ramCheckRes = [];
var env = workerScript.env;
if (env.stopFlag) {
if (checkingRam) {return ramCheckRes;}
return Promise.reject(workerScript);
}
//Get source script and name of all functions to import
var scriptName = exp.source.value;
var namespace, namespaceObj, allFns = false, fnNames = [];
if (exp.specifiers.length === 1 && exp.specifiers[0].type === "ImportNamespaceSpecifier") {
allFns = true;
namespace = exp.specifiers[0].local.name;
} else {
for (var i = 0; i < exp.specifiers.length; ++i) {
fnNames.push(exp.specifiers[i].local.name);
}
}
//Get the code
var server = getServer(workerScript.serverIp), code = "";
if (server == null) {
if (checkingRam) {return ramCheckRes;}
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to identify server. This is a bug please report to dev", exp));
}
for (var i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === scriptName) {
code = server.scripts[i].code;
break;
}
}
if (code === "") {
if (checkingRam) {return ramCheckRes;}
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Could not find script " + scriptName + " to import", exp));
}
//Create the AST
try {
var ast = parse(code, {sourceType:"module"});
} catch(e) {
console.log("Failed to parse import script");
if (checkingRam) {return ramCheckRes;}
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to import functions from " + scriptName +
" This is most likely due to a syntax error in the imported script", exp));
}
if (allFns) {
//A namespace is implemented as a JS obj
env.set(namespace, {});
namespaceObj = env.get(namespace);
}
//Search through the AST for all imported functions
var queue = [ast];
while (queue.length != 0) {
var node = queue.shift();
switch (node.type) {
case "BlockStatement":
case "Program":
for (var i = 0; i < node.body.length; ++i) {
if (node.body[i] instanceof Node) {
queue.push(node.body[i]);
}
}
break;
case "FunctionDeclaration":
if (node.id && node.id.name) {
if (allFns) {
//Import all functions under this namespace
if (checkingRam) {
ramCheckRes.push(node);
} else {
namespaceObj[node.id.name] = node;
}
} else {
//Only import specified functions
if (fnNames.includes(node.id.name)) {
if (checkingRam) {
ramCheckRes.push(node);
} else {
env.set(node.id.name, node);
}
}
}
} else {
if (checkingRam) {return ramCheckRes;}
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration in imported script " + scriptName, exp));
}
break;
default:
break;
}
for (var prop in node) {
if (node.hasOwnProperty(prop)) {
if (node[prop] instanceof Node) {
queue.push(node[prop]);
}
}
}
}
if (!checkingRam) {workerScript.scriptRef.log("Imported functions from " + scriptName);}
if (checkingRam) {return ramCheckRes;}
return Promise.resolve(true);
}
function killNetscriptDelay(workerScript) { function killNetscriptDelay(workerScript) {
/*
if (workerScript instanceof WorkerScript) {
if (workerScript.delay) {
workerScript.delay.cancel();
}
}
*/
if (workerScript instanceof WorkerScript) { if (workerScript instanceof WorkerScript) {
if (workerScript.delay) { if (workerScript.delay) {
clearTimeout(workerScript.delay); clearTimeout(workerScript.delay);
@ -654,19 +781,6 @@ function killNetscriptDelay(workerScript) {
} }
function netscriptDelay(time, workerScript) { function netscriptDelay(time, workerScript) {
/*
workerScript.delay = new Promise(function(resolve, reject, onCancel) {
Promise.delay(time).then(function() {
resolve();
workerScript.delay = null;
});
onCancel(function() {
console.log("Cancelling and rejecting this Promise");
reject(workerScript);
})
});
return workerScript.delay;
*/
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
workerScript.delay = setTimeout(()=>{ workerScript.delay = setTimeout(()=>{
workerScript.delay = null; workerScript.delay = null;
@ -676,41 +790,15 @@ function netscriptDelay(time, workerScript) {
}); });
} }
function makeRuntimeRejectMsg(workerScript, msg) { function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg; var lineNum = "";
if (exp != null) {
var num = getErrorLineNumber(exp, workerScript);
lineNum = " (Line " + num + ")"
}
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg + lineNum;
} }
/*
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
throw new Error("Expected number but got " + x);
return x;
}
function div(x) {
if (num(x) == 0)
throw new Error("Divide by zero");
return x;
}
switch (op) {
case "+": return a + b;
case "-": return num(a) - num(b);
case "*": return num(a) * num(b);
case "/": return num(a) / div(b);
case "%": return num(a) % div(b);
case "&&": return a !== false && b;
case "||": return a !== false ? a : b;
case "<": return num(a) < num(b);
case ">": return num(a) > num(b);
case "<=": return num(a) <= num(b);
case ">=": return num(a) >= num(b);
case "==": return a === b;
case "!=": return a !== b;
}
throw new Error("Can't apply operator " + op);
}
*/
//Run a script from inside a script using run() command //Run a script from inside a script using run() command
function runScriptFromScript(server, scriptname, args, workerScript, threads=1) { function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
//Check if the script is already running //Check if the script is already running
@ -759,13 +847,16 @@ function runScriptFromScript(server, scriptname, args, workerScript, threads=1)
return Promise.resolve(false); return Promise.resolve(false);
} }
//Takes in a
function getErrorLineNumber(exp, workerScript) { function getErrorLineNumber(exp, workerScript) {
var code = workerScript.scriptRef.scriptRef.code; var code = workerScript.scriptRef.scriptRef.code;
//Split code up to the start of the node //Split code up to the start of the node
try {
code = code.substring(0, exp.start); code = code.substring(0, exp.start);
return (code.match(/\n/g) || []).length; return (code.match(/\n/g) || []).length + 1;
} catch(e) {
return -1;
}
} }
function isScriptErrorMessage(msg) { function isScriptErrorMessage(msg) {
@ -838,4 +929,4 @@ export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateHackingChance, scriptCalculateHackingTime, scriptCalculateHackingChance, scriptCalculateHackingTime,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked, scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime, evaluate, scriptCalculateGrowTime, scriptCalculateWeakenTime, evaluate,
isScriptErrorMessage, killNetscriptDelay}; isScriptErrorMessage, killNetscriptDelay, evaluateImport};

@ -54,6 +54,7 @@ import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked, scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime} from "./NetscriptEvaluator.js"; scriptCalculateGrowTime, scriptCalculateWeakenTime} from "./NetscriptEvaluator.js";
import {Environment} from "./NetscriptEnvironment.js"; import {Environment} from "./NetscriptEnvironment.js";
import {NetscriptPort} from "./NetscriptPort.js";
import Decimal from '../utils/decimal.js'; import Decimal from '../utils/decimal.js';
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
@ -99,6 +100,7 @@ function NetscriptFunctions(workerScript) {
return { return {
Math : Math, Math : Math,
Date : Date, Date : Date,
Number : Number,
hacknetnodes : Player.hacknetNodes, hacknetnodes : Player.hacknetNodes,
sprintf : sprintf, sprintf : sprintf,
vsprintf: vsprintf, vsprintf: vsprintf,
@ -614,7 +616,7 @@ function NetscriptFunctions(workerScript) {
return CONSTANTS.ScriptSpawnRamCost; return CONSTANTS.ScriptSpawnRamCost;
} }
} }
if (scriptname == null || threads == 1) { if (scriptname == null || threads == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()"); throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()");
} }
setTimeout(()=>{ setTimeout(()=>{
@ -665,7 +667,7 @@ function NetscriptFunctions(workerScript) {
return false; return false;
} }
}, },
killall : function(ip){ killall : function(ip=workerScript.serverIp){
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.killall) { if (workerScript.loadedFns.killall) {
return 0; return 0;
@ -1601,7 +1603,7 @@ function NetscriptFunctions(workerScript) {
} }
} }
var hostnameStr = String(hostname); var hostnameStr = String(hostname);
hostnameStr = hostnameStr.replace(/\s\s+/g, ''); hostnameStr = hostnameStr.replace(/\s+/g, '');
if (hostnameStr == "") { if (hostnameStr == "") {
workerScript.scriptRef.log("Error: Passed empty string for hostname argument of purchaseServer()"); workerScript.scriptRef.log("Error: Passed empty string for hostname argument of purchaseServer()");
return ""; return "";
@ -1744,20 +1746,15 @@ function NetscriptFunctions(workerScript) {
} }
if (!isNaN(port)) { //Write to port if (!isNaN(port)) { //Write to port
//Port 1-10 //Port 1-10
if (port < 1 || port > 10) { port = Math.round(port);
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to write to invalid port: " + port + ". Only ports 1-10 are valid."); if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to write to invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid.");
} }
var portName = "Port" + String(port); var port = NetscriptPorts[port-1];
var port = NetscriptPorts[portName]; if (port == null || !(port instanceof NetscriptPort)) {
if (port == null) {
throw makeRuntimeRejectMsg(workerScript, "Could not find port: " + port + ". This is a bug contact the game developer"); throw makeRuntimeRejectMsg(workerScript, "Could not find port: " + port + ". This is a bug contact the game developer");
} }
port.push(data); return port.write(data);
if (port.length > Settings.MaxPortCapacity) {
port.shift();
return true;
}
return false;
} else if (isString(port)) { //Write to text file } else if (isString(port)) { //Write to text file
var fn = port; var fn = port;
var server = getServer(workerScript.serverIp); var server = getServer(workerScript.serverIp);
@ -1790,19 +1787,15 @@ function NetscriptFunctions(workerScript) {
} }
if (!isNaN(port)) { //Read from port if (!isNaN(port)) { //Read from port
//Port 1-10 //Port 1-10
if (port < 1 || port > 10) { port = Math.round(port);
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to read from invalid port: " + port + ". Only ports 1-10 are valid."); if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to read from invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid.");
} }
var portName = "Port" + String(port); var port = NetscriptPorts[port-1];
var port = NetscriptPorts[portName]; if (port == null || !(port instanceof NetscriptPort)) {
if (port == null) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer"); throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer");
} }
if (port.length === 0) { return port.read();
return "NULL PORT DATA";
} else {
return port.shift();
}
} else if (isString(port)) { //Read from text file } else if (isString(port)) { //Read from text file
var fn = port; var fn = port;
var server = getServer(workerScript.serverIp); var server = getServer(workerScript.serverIp);
@ -1828,20 +1821,18 @@ function NetscriptFunctions(workerScript) {
return CONSTANTS.ScriptReadWriteRamCost; return CONSTANTS.ScriptReadWriteRamCost;
} }
} }
if (isNaN(port) || port < 1 || port > 10) { if (isNaN(port)) {
throw makeRuntimeRejectMsg(workerScript, "ERR: peek() called with invalid argument. Must be a port number between 1 and 10"); throw makeRuntimeRejectMsg(workerScript, "ERR: peek() called with invalid argument. Must be a port number between 1 and " + CONSTANTS.NumNetscriptPorts);
} }
var portName = "Port" + String(port); port = Math.round(port);
var port = NetscriptPorts[portName]; if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
if (port == null) { throw makeRuntimeRejectMsg(workerScript, "ERR: peek() called with invalid argument. Must be a port number between 1 and " + CONSTANTS.NumNetscriptPorts);
}
var port = NetscriptPorts[port-1];
if (port == null || !(port instanceof NetscriptPort)) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer"); throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer");
} }
if (port.length === 0) { return port.peek();
return "NULL PORT DATA";
} else {
var foo = port.slice();
return foo[0];
}
}, },
clear : function(port) { clear : function(port) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
@ -1853,15 +1844,15 @@ function NetscriptFunctions(workerScript) {
} }
} }
if (!isNaN(port)) { //Clear port if (!isNaN(port)) { //Clear port
if (port < 1 || port > 10) { port = Math.round(port);
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to read from invalid port: " + port + ". Only ports 1-10 are valid"); if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to clear invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid");
} }
var portName = "Port" + String(port); var port = NetscriptPorts[port-1];
var port = NetscriptPorts[portName]; if (port == null || !(port instanceof NetscriptPort)) {
if (port == null) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer"); throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer");
} }
port.length = 0; return port.clear();
} else if (isString(port)) { //Clear text file } else if (isString(port)) { //Clear text file
var fn = port; var fn = port;
var server = getServer(workerScript.serverIp); var server = getServer(workerScript.serverIp);
@ -1877,6 +1868,28 @@ function NetscriptFunctions(workerScript) {
} }
return 0; return 0;
}, },
getPortHandle : function(port) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getPortHandle) {
return 0;
} else {
workerScript.loadedFns.getPortHandle = true;
return CONSTANTS.ScriptReadWriteRamCost * 10;
}
}
if (isNaN(port)) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Invalid argument passed into getPortHandle(). Must be an integer between 1 and " + CONSTANTS.NumNetscriptPorts);
}
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERR: getPortHandle() called with invalid port number: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid");
}
var port = NetscriptPorts[port-1];
if (port == null || !(port instanceof NetscriptPort)) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Could not find port: " + port + ". This is a bug contact the game developer");
}
return port;
},
rm : function(fn) { rm : function(fn) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.rm) { if (workerScript.loadedFns.rm) {
@ -2153,7 +2166,7 @@ function NetscriptFunctions(workerScript) {
} else { } else {
workerScript.loadedFns.universityCourse = true; workerScript.loadedFns.universityCourse = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2246,7 +2259,7 @@ function NetscriptFunctions(workerScript) {
} else { } else {
workerScript.loadedFns.gymWorkout = true; workerScript.loadedFns.gymWorkout = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2351,8 +2364,8 @@ function NetscriptFunctions(workerScript) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.travelToCity = true; workerScript.loadedFns.travelToCity = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2390,7 +2403,7 @@ function NetscriptFunctions(workerScript) {
} else { } else {
workerScript.loadedFns.purchaseTor = true; workerScript.loadedFns.purchaseTor = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2433,7 +2446,7 @@ function NetscriptFunctions(workerScript) {
} else { } else {
workerScript.loadedFns.purchaseProgram = true; workerScript.loadedFns.purchaseProgram = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2553,8 +2566,8 @@ function NetscriptFunctions(workerScript) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getStats = true; workerScript.loadedFns.getStats = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2575,14 +2588,55 @@ function NetscriptFunctions(workerScript) {
intelligence: Player.intelligence intelligence: Player.intelligence
} }
}, },
getCharacterInformation : function() {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCharacterInformation) {
return 0;
} else {
workerScript.loadedFns.getCharacterInformation = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getCharacterInformation(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return {};
}
}
var companyPositionTitle = "";
if (Player.companyPosition instanceof CompanyPosition) {
companyPositionTitle = Player.companyPosition.positionName;
}
return {
bitnode: Player.bitNodeN,
company: Player.companyName,
jobTitle: companyPositionTitle,
city: Player.city,
factions: Player.factions.slice(),
tor: SpecialServerIps.hasOwnProperty("Darkweb Server"),
timeWorked: Player.timeWorked,
workHackExpGain: Player.workHackExpGained,
workStrExpGain: Player.workStrExpGained,
workDefExpGain: Player.workDefExpGained,
workDexExpGain: Player.workDexExpGained,
workAgiExpGain: Player.workAgiExpGained,
workChaExpGain: Player.workChaExpGained,
workRepGain: Player.workRepGained,
workMoneyGain: Player.workMoneyGained,
};
},
isBusy : function() { isBusy : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.isBusy) { if (workerScript.loadedFns.isBusy) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.isBusy = true; workerScript.loadedFns.isBusy = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2600,11 +2654,17 @@ function NetscriptFunctions(workerScript) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.stopAction = true; workerScript.loadedFns.stopAction = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost; var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run stopAction(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return false;
}
}
if (Player.isWorking) { if (Player.isWorking) {
var txt = Player.singularityStopWork(); var txt = Player.singularityStopWork();
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.stopAction == null) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.stopAction == null) {
@ -2614,14 +2674,14 @@ function NetscriptFunctions(workerScript) {
} }
return false; return false;
}, },
upgradeHomeRam() { upgradeHomeRam : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.upgradeHomeRam) { if (workerScript.loadedFns.upgradeHomeRam) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.upgradeHomeRam = true; workerScript.loadedFns.upgradeHomeRam = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2658,14 +2718,14 @@ function NetscriptFunctions(workerScript) {
} }
return true; return true;
}, },
getUpgradeHomeRamCost() { getUpgradeHomeRamCost : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getUpgradeHomeRamCost) { if (workerScript.loadedFns.getUpgradeHomeRamCost) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getUpgradeHomeRamCost = true; workerScript.loadedFns.getUpgradeHomeRamCost = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2686,14 +2746,14 @@ function NetscriptFunctions(workerScript) {
var mult = Math.pow(1.55, numUpgrades); var mult = Math.pow(1.55, numUpgrades);
return cost * mult; return cost * mult;
}, },
workForCompany() { workForCompany : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.workForCompany) { if (workerScript.loadedFns.workForCompany) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.workForCompany = true; workerScript.loadedFns.workForCompany = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2731,14 +2791,14 @@ function NetscriptFunctions(workerScript) {
} }
return true; return true;
}, },
applyToCompany(companyName, field) { applyToCompany : function(companyName, field) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.applyToCompany) { if (workerScript.loadedFns.applyToCompany) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.applyToCompany = true; workerScript.loadedFns.applyToCompany = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2816,14 +2876,14 @@ function NetscriptFunctions(workerScript) {
} }
return res; return res;
}, },
getCompanyRep(companyName) { getCompanyRep : function(companyName) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCompanyRep) { if (workerScript.loadedFns.getCompanyRep) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getCompanyRep = true; workerScript.loadedFns.getCompanyRep = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2841,14 +2901,39 @@ function NetscriptFunctions(workerScript) {
} }
return company.playerReputation; return company.playerReputation;
}, },
checkFactionInvitations() { getCompanyFavor : function(companyName) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCompanyFavor) {
return 0;
} else {
workerScript.loadedFns.getCompanyFavor = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getCompanyFavor(). It is a Singularity Function and requires SourceFile-4 (level 2) to run.");
return false;
}
}
var company = Companies[companyName];
if (company == null || !(company instanceof Company)) {
workerScript.scriptRef.log("ERROR: Invalid companyName passed into getCompanyFavor(): " + companyName);
return -1;
}
return company.favor;
},
checkFactionInvitations : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.checkFactionInvitations) { if (workerScript.loadedFns.checkFactionInvitations) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.checkFactionInvitations = true; workerScript.loadedFns.checkFactionInvitations = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2861,14 +2946,14 @@ function NetscriptFunctions(workerScript) {
//Make a copy of Player.factionInvitations //Make a copy of Player.factionInvitations
return Player.factionInvitations.slice(); return Player.factionInvitations.slice();
}, },
joinFaction(name) { joinFaction : function(name) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.joinFaction) { if (workerScript.loadedFns.joinFaction) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.joinFaction = true; workerScript.loadedFns.joinFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -2904,14 +2989,14 @@ function NetscriptFunctions(workerScript) {
} }
return true; return true;
}, },
workForFaction(name, type) { workForFaction : function(name, type) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.workForFaction) { if (workerScript.loadedFns.workForFaction) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.workForFaction = true; workerScript.loadedFns.workForFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3002,14 +3087,14 @@ function NetscriptFunctions(workerScript) {
} }
return true; return true;
}, },
getFactionRep(name) { getFactionRep : function(name) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getFactionRep) { if (workerScript.loadedFns.getFactionRep) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getFactionRep = true; workerScript.loadedFns.getFactionRep = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3027,14 +3112,39 @@ function NetscriptFunctions(workerScript) {
return Factions[name].playerReputation; return Factions[name].playerReputation;
}, },
createProgram(name) { getFactionFavor : function(name) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getFactionFavor) {
return 0;
} else {
workerScript.loadedFns.getFactionFavor = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getFactionFavor(). It is a Singularity Function and requires SourceFile-4 (level 2) to run.");
return -1;
}
}
if (!factionExists(name)) {
workerScript.scriptRef.log("ERROR: Faction specified in getFactionFavor() does not exist.");
return -1;
}
return Factions[name].favor;
},
createProgram : function(name) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.createProgram) { if (workerScript.loadedFns.createProgram) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.createProgram = true; workerScript.loadedFns.createProgram = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3136,7 +3246,7 @@ function NetscriptFunctions(workerScript) {
} else { } else {
workerScript.loadedFns.commitCrime = true; workerScript.loadedFns.commitCrime = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3222,14 +3332,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crime); throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crime);
} }
}, },
getCrimeChance(crime) { getCrimeChance : function(crime) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCrimeChance) { if (workerScript.loadedFns.getCrimeChance) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getCrimeChance = true; workerScript.loadedFns.getCrimeChance = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3269,14 +3379,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into getCrimeChance(): " + crime); throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into getCrimeChance(): " + crime);
} }
}, },
getOwnedAugmentations(purchased=false) { getOwnedAugmentations : function(purchased=false) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getOwnedAugmentations) { if (workerScript.loadedFns.getOwnedAugmentations) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getOwnedAugmentations = true; workerScript.loadedFns.getOwnedAugmentations = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3297,14 +3407,14 @@ function NetscriptFunctions(workerScript) {
} }
return res; return res;
}, },
getAugmentationsFromFaction(facname) { getAugmentationsFromFaction : function(facname) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getAugmentationsFromFaction) { if (workerScript.loadedFns.getAugmentationsFromFaction) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getAugmentationsFromFaction = true; workerScript.loadedFns.getAugmentationsFromFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3327,14 +3437,14 @@ function NetscriptFunctions(workerScript) {
} }
return res; return res;
}, },
getAugmentationCost(name) { getAugmentationCost : function(name) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.getAugmentationCost) { if (workerScript.loadedFns.getAugmentationCost) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.getAugmentationCost = true; workerScript.loadedFns.getAugmentationCost = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3353,14 +3463,14 @@ function NetscriptFunctions(workerScript) {
var aug = Augmentations[name]; var aug = Augmentations[name];
return [aug.baseRepRequirement, aug.baseCost]; return [aug.baseRepRequirement, aug.baseCost];
}, },
purchaseAugmentation(faction, name) { purchaseAugmentation : function(faction, name) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.purchaseAugmentation) { if (workerScript.loadedFns.purchaseAugmentation) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.purchaseAugmentation = true; workerScript.loadedFns.purchaseAugmentation = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }
@ -3422,14 +3532,14 @@ function NetscriptFunctions(workerScript) {
return false; return false;
} }
}, },
installAugmentations(cbScript) { installAugmentations : function(cbScript) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
if (workerScript.loadedFns.installAugmentations) { if (workerScript.loadedFns.installAugmentations) {
return 0; return 0;
} else { } else {
workerScript.loadedFns.installAugmentations = true; workerScript.loadedFns.installAugmentations = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost; return ramCost;
} }
} }

51
src/NetscriptPort.js Normal file

@ -0,0 +1,51 @@
import {Settings} from "./Settings.js";
function NetscriptPort() {
this.data = [];
}
NetscriptPort.prototype.write = function(data) {
this.data.push(data);
if (this.data.length > Settings.MaxPortCapacity) {
return this.data.shift();
}
return null;
}
NetscriptPort.prototype.tryWrite = function(data) {
if (this.data.length >= Settings.MaxPortCapacity) {
return false;
}
this.data.push(data);
return true;
}
NetscriptPort.prototype.read = function() {
if (this.data.length === 0) {
return "NULL PORT DATA";
}
return this.data.shift();
}
NetscriptPort.prototype.peek = function() {
if (this.data.length === 0) {
return "NULL PORT DATA";
} else {
var foo = this.data.slice();
return foo[0];
}
}
NetscriptPort.prototype.full = function() {
return this.data.length == Settings.MaxPortCapacity;
}
NetscriptPort.prototype.empty = function() {
return this.data.length === 0;
}
NetscriptPort.prototype.clear = function() {
this.data.length = 0;
}
export {NetscriptPort};

@ -6,6 +6,7 @@ import {Engine} from "./engine.js";
import {Environment} from "./NetscriptEnvironment.js"; import {Environment} from "./NetscriptEnvironment.js";
import {evaluate, isScriptErrorMessage, import {evaluate, isScriptErrorMessage,
killNetscriptDelay} from "./NetscriptEvaluator.js"; killNetscriptDelay} from "./NetscriptEvaluator.js";
import {NetscriptPort} from "./NetscriptPort.js";
import {AllServers} from "./Server.js"; import {AllServers} from "./Server.js";
import {Settings} from "./Settings.js"; import {Settings} from "./Settings.js";
@ -40,17 +41,9 @@ WorkerScript.prototype.getServer = function() {
//Array containing all scripts that are running across all servers, to easily run them all //Array containing all scripts that are running across all servers, to easily run them all
let workerScripts = []; let workerScripts = [];
let NetscriptPorts = { var NetscriptPorts = [];
Port1: [], for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
Port2: [], NetscriptPorts.push(new NetscriptPort());
Port3: [],
Port4: [],
Port5: [],
Port6: [],
Port7: [],
Port8: [],
Port9: [],
Port10: [],
} }
function prestigeWorkerScripts() { function prestigeWorkerScripts() {
@ -95,8 +88,8 @@ function runScriptsLoop() {
//If it isn't running, start the script //If it isn't running, start the script
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) { if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
try { try {
var ast = parse(workerScripts[i].code); var ast = parse(workerScripts[i].code, {sourceType:"module"});
//console.log(ast); console.log(ast);
} catch (e) { } catch (e) {
console.log("Error parsing script: " + workerScripts[i].name); console.log("Error parsing script: " + workerScripts[i].name);
dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":<br>" + e); dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":<br>" + e);
@ -168,9 +161,12 @@ function killWorkerScript(runningScriptObj, serverIp) {
compareArrays(workerScripts[i].args, runningScriptObj.args)) { compareArrays(workerScripts[i].args, runningScriptObj.args)) {
workerScripts[i].env.stopFlag = true; workerScripts[i].env.stopFlag = true;
killNetscriptDelay(workerScripts[i]); killNetscriptDelay(workerScripts[i]);
if (workerScripts[i].fnWorker) { //Recursively kill all functions
workerScripts[i].fnWorker.env.stopFlag = true; var curr = workerScripts[i];
killNetscriptDelay(workerScripts[i].fnWorker); while (curr.fnWorker) {
curr.fnWorker.env.stopFlag = true;
killNetscriptDelay(curr.fnWorker);
curr = curr.fnWorker;
} }
return true; return true;
} }

@ -48,9 +48,9 @@ function PlayerObject() {
this.intelligence = 0; this.intelligence = 0;
//Hacking multipliers //Hacking multipliers
this.hacking_chance_mult = 1; //Increase through ascensions/augmentations this.hacking_chance_mult = 1;
this.hacking_speed_mult = 1; //Decrease through ascensions/augmentations this.hacking_speed_mult = 1;
this.hacking_money_mult = 1; //Increase through ascensions/augmentations. Can't go above 1 this.hacking_money_mult = 1;
this.hacking_grow_mult = 1; this.hacking_grow_mult = 1;
//Experience and multipliers //Experience and multipliers
@ -692,11 +692,10 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadLocationContent(); Engine.loadLocationContent();
if (sing) { if (sing) {
return "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " + var res = "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
"earned $" + formatNumber(this.workMoneyGained, 2) + ", " + "earned $" + formatNumber(this.workMoneyGained, 2) + ", " +
formatNumber(this.workRepGained, 4) + " reputation, " + formatNumber(this.workRepGained, 4) + " reputation, " +
formatNumber(this.workHackExpGained, 4) + " hacking exp, " + formatNumber(this.workHackExpGained, 4) + " hacking exp, " +
@ -705,7 +704,10 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
formatNumber(this.workDexExpGained, 4) + " dexterity exp, " + formatNumber(this.workDexExpGained, 4) + " dexterity exp, " +
formatNumber(this.workAgiExpGained, 4) + " agility exp, and " + formatNumber(this.workAgiExpGained, 4) + " agility exp, and " +
formatNumber(this.workChaExpGained, 4) + " charisma exp."; formatNumber(this.workChaExpGained, 4) + " charisma exp.";
this.resetWorkStatus();
return res;
} }
this.resetWorkStatus();
} }
PlayerObject.prototype.startWork = function() { PlayerObject.prototype.startWork = function() {
@ -767,9 +769,17 @@ PlayerObject.prototype.work = function(numCycles) {
return; return;
} }
var comp = Companies[this.companyName], companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.log("ERROR: Could not find Company: " + this.companyName);
} else {
companyRep = comp.playerReputation;
}
var txt = document.getElementById("work-in-progress-text"); var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName + txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName +
" at " + this.companyName + "<br><br>" + " at " + this.companyName + " (Current Company Reputation: " +
formatNumber(companyRep, 0) + ")<br><br>" +
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + "You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" + "You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" + "$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" +
@ -886,10 +896,9 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadLocationContent(); Engine.loadLocationContent();
if (sing) { if (sing) {
return "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " + var res = "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
"earned a total of " + "earned a total of " +
"$" + formatNumber(this.workMoneyGained, 2) + ", " + "$" + formatNumber(this.workMoneyGained, 2) + ", " +
formatNumber(this.workRepGained, 4) + " reputation, " + formatNumber(this.workRepGained, 4) + " reputation, " +
@ -899,7 +908,10 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
formatNumber(this.workDexExpGained, 4) + " dexterity exp, " + formatNumber(this.workDexExpGained, 4) + " dexterity exp, " +
formatNumber(this.workAgiExpGained, 4) + " agility exp, and " + formatNumber(this.workAgiExpGained, 4) + " agility exp, and " +
formatNumber(this.workChaExpGained, 4) + " charisma exp"; formatNumber(this.workChaExpGained, 4) + " charisma exp";
this.resetWorkStatus();
return res;
} }
this.resetWorkStatus();
} }
/* Working for Faction */ /* Working for Faction */
@ -930,11 +942,10 @@ PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
this.isWorking = false; this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadFactionContent(); Engine.loadFactionContent();
displayFactionContent(faction.name); displayFactionContent(faction.name);
if (sing) { if (sing) {
return "You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " + var res="You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " +
"You earned " + "You earned " +
formatNumber(this.workRepGained, 4) + " rep, " + formatNumber(this.workRepGained, 4) + " rep, " +
formatNumber(this.workHackExpGained, 4) + " hacking exp, " + formatNumber(this.workHackExpGained, 4) + " hacking exp, " +
@ -943,7 +954,10 @@ PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
formatNumber(this.workDexExpGained, 4) + " dex exp, " + formatNumber(this.workDexExpGained, 4) + " dex exp, " +
formatNumber(this.workAgiExpGained, 4) + " agi exp, and " + formatNumber(this.workAgiExpGained, 4) + " agi exp, and " +
formatNumber(this.workChaExpGained, 4) + " cha exp."; formatNumber(this.workChaExpGained, 4) + " cha exp.";
this.resetWorkStatus();
return res;
} }
this.resetWorkStatus();
} }
PlayerObject.prototype.startFactionWork = function(faction) { PlayerObject.prototype.startFactionWork = function(faction) {
@ -1261,6 +1275,7 @@ PlayerObject.prototype.finishCreateProgramWork = function(cancelled, sing=false)
this.isWorking = false; this.isWorking = false;
Engine.loadTerminalContent(); Engine.loadTerminalContent();
this.resetWorkStatus();
} }
/* Studying/Taking Classes */ /* Studying/Taking Classes */
@ -1415,8 +1430,8 @@ PlayerObject.prototype.finishClass = function(sing=false) {
this.isWorking = false; this.isWorking = false;
Engine.loadLocationContent(); Engine.loadLocationContent();
if (sing) {
if (sing) {return "After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " + var res="After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " +
"you spent a total of $" + formatNumber(this.workMoneyGained * -1, 2) + ". " + "you spent a total of $" + formatNumber(this.workMoneyGained * -1, 2) + ". " +
"You earned a total of: " + "You earned a total of: " +
formatNumber(this.workHackExpGained, 3) + " hacking exp, " + formatNumber(this.workHackExpGained, 3) + " hacking exp, " +
@ -1424,7 +1439,11 @@ PlayerObject.prototype.finishClass = function(sing=false) {
formatNumber(this.workDefExpGained, 3) + " defense exp, " + formatNumber(this.workDefExpGained, 3) + " defense exp, " +
formatNumber(this.workDexExpGained, 3) + " dexterity exp, " + formatNumber(this.workDexExpGained, 3) + " dexterity exp, " +
formatNumber(this.workAgiExpGained, 3) + " agility exp, and " + formatNumber(this.workAgiExpGained, 3) + " agility exp, and " +
formatNumber(this.workChaExpGained, 3) + " charisma exp";} formatNumber(this.workChaExpGained, 3) + " charisma exp";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
} }
//The EXP and $ gains are hardcoded. Time is in ms //The EXP and $ gains are hardcoded. Time is in ms
@ -1598,6 +1617,7 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
this.resetWorkStatus();
Engine.loadLocationContent(); Engine.loadLocationContent();
} }

@ -17,6 +17,7 @@ import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {iTutorialSteps, iTutorialNextStep, import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial.js"; iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial.js";
import {evaluateImport} from "./NetscriptEvaluator.js";
import {NetscriptFunctions} from "./NetscriptFunctions.js"; import {NetscriptFunctions} from "./NetscriptFunctions.js";
import {addWorkerScript, killWorkerScript, import {addWorkerScript, killWorkerScript,
WorkerScript} from "./NetscriptWorker.js"; WorkerScript} from "./NetscriptWorker.js";
@ -96,19 +97,47 @@ function scriptEditorInit() {
/* Script editor options */ /* Script editor options */
//Theme //Theme
var themeDropdown = document.getElementById("script-editor-option-theme"); var themeDropdown = document.getElementById("script-editor-option-theme");
if (Settings.EditorTheme) {
var initialIndex = 2;
for (var i = 0; i < themeDropdown.options.length; ++i) {
if (themeDropdown.options[i].value === Settings.EditorTheme) {
initialIndex = i;
break;
}
}
themeDropdown.selectedIndex = initialIndex;
} else {
themeDropdown.selectedIndex = 2; themeDropdown.selectedIndex = 2;
}
themeDropdown.onchange = function() { themeDropdown.onchange = function() {
var val = themeDropdown.value; var val = themeDropdown.value;
Settings.EditorTheme = val;
var themePath = "ace/theme/" + val.toLowerCase(); var themePath = "ace/theme/" + val.toLowerCase();
editor.setTheme(themePath); editor.setTheme(themePath);
}; };
themeDropdown.onchange();
//Keybinding //Keybinding
var keybindingDropdown = document.getElementById("script-editor-option-keybinding"); var keybindingDropdown = document.getElementById("script-editor-option-keybinding");
if (Settings.EditorKeybinding) {
var initialIndex = 0;
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
initialIndex = i;
break;
}
}
keybindingDropdown.selectedIndex = initialIndex;
} else {
keybindingDropdown.selectedIndex = 0;
}
keybindingDropdown.onchange = function() { keybindingDropdown.onchange = function() {
var val = keybindingDropdown.value; var val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
editor.setKeyboardHandler(keybindings[val.toLowerCase()]); editor.setKeyboardHandler(keybindings[val.toLowerCase()]);
}; };
keybindingDropdown.onchange();
//Highlight Active line //Highlight Active line
var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline"); var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline");
@ -174,7 +203,6 @@ function scriptEditorInit() {
} }
editor.completers = [autocompleter]; editor.completers = [autocompleter];
} }
document.addEventListener("DOMContentLoaded", scriptEditorInit, false);
//Updates RAM usage in script //Updates RAM usage in script
function updateScriptEditorContent() { function updateScriptEditorContent() {
@ -188,6 +216,8 @@ function updateScriptEditorContent() {
var ramUsage = calculateRamUsage(codeCopy); var ramUsage = calculateRamUsage(codeCopy);
if (ramUsage !== -1) { if (ramUsage !== -1) {
scriptEditorRamText.innerText = "RAM: " + formatNumber(ramUsage, 2).toString() + "GB"; scriptEditorRamText.innerText = "RAM: " + formatNumber(ramUsage, 2).toString() + "GB";
} else {
scriptEditorRamText.innerText = "RAM: Syntax Error";
} }
} }
@ -295,15 +325,17 @@ Script.prototype.updateRamUsage = function() {
function calculateRamUsage(codeCopy) { function calculateRamUsage(codeCopy) {
//Create a temporary/mock WorkerScript and an AST from the code //Create a temporary/mock WorkerScript and an AST from the code
var currServ = Player.getCurrentServer();
var workerScript = new WorkerScript({ var workerScript = new WorkerScript({
filename:"foo", filename:"foo",
scriptRef: {code:""}, scriptRef: {code:""},
args:[] args:[]
}); });
workerScript.checkingRam = true; //Netscript functions will return RAM usage workerScript.checkingRam = true; //Netscript functions will return RAM usage
workerScript.serverIp = currServ.ip;
try { try {
var ast = parse(codeCopy); var ast = parse(codeCopy, {sourceType:"module"});
} catch(e) { } catch(e) {
return -1; return -1;
} }
@ -315,6 +347,14 @@ function calculateRamUsage(codeCopy) {
while (queue.length != 0) { while (queue.length != 0) {
var exp = queue.shift(); var exp = queue.shift();
switch (exp.type) { switch (exp.type) {
case "ImportDeclaration":
//Gets an array of all imported functions as AST expressions
//and pushes them on the queue.
var res = evaluateImport(exp, workerScript, true);
for (var i = 0; i < res.length; ++i) {
queue.push(res[i]);
}
break;
case "BlockStatement": case "BlockStatement":
case "Program": case "Program":
for (var i = 0; i < exp.body.length; ++i) { for (var i = 0; i < exp.body.length; ++i) {
@ -659,4 +699,4 @@ AllServersMap.fromJSON = function(value) {
Reviver.constructors.AllServersMap = AllServersMap; Reviver.constructors.AllServersMap = AllServersMap;
export {updateScriptEditorContent, loadAllRunningScripts, findRunningScript, export {updateScriptEditorContent, loadAllRunningScripts, findRunningScript,
RunningScript, Script, AllServersMap}; RunningScript, Script, AllServersMap, scriptEditorInit};

@ -75,7 +75,7 @@ function Server(ip=createRandomIp(), hostname="", organizationName="",
Server.prototype.setHackingParameters = function(requiredHackingSkill, moneyAvailable, hackDifficulty, serverGrowth) { Server.prototype.setHackingParameters = function(requiredHackingSkill, moneyAvailable, hackDifficulty, serverGrowth) {
this.requiredHackingSkill = requiredHackingSkill; this.requiredHackingSkill = requiredHackingSkill;
if (isNaN(moneyAvailable)) { if (isNaN(moneyAvailable)) {
this.moneyAvailable = 1000000; this.moneyAvailable = 1e6;
} else { } else {
this.moneyAvailable = moneyAvailable * BitNodeMultipliers.ServerStartingMoney; this.moneyAvailable = moneyAvailable * BitNodeMultipliers.ServerStartingMoney;
} }

@ -11,6 +11,8 @@ let Settings = {
ThemeHighlightColor: "#ffffff", ThemeHighlightColor: "#ffffff",
ThemeFontColor: "#66ff33", ThemeFontColor: "#66ff33",
ThemeBackgroundColor: "#000000", ThemeBackgroundColor: "#000000",
EditorTheme: "Monokai",
EditorKeybinding: "ace",
} }
function loadSettings(saveString) { function loadSettings(saveString) {

@ -434,9 +434,9 @@ let Terminal = {
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>'; '<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
var hdr = document.getElementById("terminal-input-header"); var hdr = document.getElementById("terminal-input-header");
hdr.style.display = "inline"; hdr.style.display = "inline";
var lineWidth = document.getElementById("terminal-input-td").offsetWidth; //var lineWidth = document.getElementById("terminal-input-td").offsetWidth;
var width = lineWidth - hdr.offsetWidth - 10; //var width = lineWidth - hdr.offsetWidth - 10;
document.getElementById("terminal-input-text-box").style.width = width + "px"; //document.getElementById("terminal-input-text-box").style.width = width + "px";
}, },
//Complete the hack/analyze command //Complete the hack/analyze command

@ -5,6 +5,7 @@ import {Reviver, Generic_toJSON,
function TextFile(fn="", txt="") { function TextFile(fn="", txt="") {
this.fn = fn.endsWith(".txt") ? fn : fn + ".txt"; this.fn = fn.endsWith(".txt") ? fn : fn + ".txt";
this.fn = this.fn.replace(/\s+/g, '');
this.text = String(txt); this.text = String(txt);
} }

@ -42,7 +42,7 @@ import {prestigeAugmentation,
prestigeSourceFile} from "./Prestige.js"; prestigeSourceFile} from "./Prestige.js";
import {redPillFlag} from "./RedPill.js"; import {redPillFlag} from "./RedPill.js";
import {saveObject, loadGame} from "./SaveObject.js"; import {saveObject, loadGame} from "./SaveObject.js";
import {loadAllRunningScripts, import {loadAllRunningScripts, scriptEditorInit,
updateScriptEditorContent} from "./Script.js"; updateScriptEditorContent} from "./Script.js";
import {AllServers, Server, initForeignServers} from "./Server.js"; import {AllServers, Server, initForeignServers} from "./Server.js";
import {Settings, setSettingsLabels} from "./Settings.js"; import {Settings, setSettingsLabels} from "./Settings.js";
@ -605,7 +605,7 @@ let Engine = {
Engine.sector12LocationsList.style.display = "inline"; Engine.sector12LocationsList.style.display = "inline";
//City hall only in BitNode-3/with Source-File 3 //City hall only in BitNode-3/with Source-File 3
if (Player.bitNodeN === 3 || hasCorporationSF) { if ((Player.bitNodeN === 3 || hasCorporationSF) && Player.bitNodeN !== 8) {
document.getElementById("sector12-cityhall-li").style.display = "block"; document.getElementById("sector12-cityhall-li").style.display = "block";
} else { } else {
document.getElementById("sector12-cityhall-li").style.display = "none"; document.getElementById("sector12-cityhall-li").style.display = "none";
@ -1319,6 +1319,7 @@ let Engine = {
} }
//Initialize labels on game settings //Initialize labels on game settings
setSettingsLabels(); setSettingsLabels();
scriptEditorInit();
Terminal.resetTerminalInput(); Terminal.resetTerminalInput();
}, },

@ -1,4 +1,5 @@
import {Faction, joinFaction} from "../src/Faction.js"; import {Faction, joinFaction} from "../src/Faction.js";
import {Engine} from "../src/engine.js";
import {Player} from "../src/Player.js"; import {Player} from "../src/Player.js";
import {clearEventListeners} from "./HelperFunctions.js"; import {clearEventListeners} from "./HelperFunctions.js";
@ -26,20 +27,33 @@ function factionInvitationSetMessage(msg) {
//ram argument is in GB //ram argument is in GB
function factionInvitationBoxCreate(faction) { function factionInvitationBoxCreate(faction) {
factionInvitationSetText("You have received a faction invitation from " + faction.name); factionInvitationSetText("You have received a faction invitation from " + faction.name);
//TODO Faction invitation message faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (Engine.currentPage === Engine.Page.Factions) {
Engine.loadFactionsContent();
}
var newYesButton = clearEventListeners("faction-invitation-box-yes"); var newYesButton = clearEventListeners("faction-invitation-box-yes");
newYesButton.addEventListener("click", function() { newYesButton.addEventListener("click", function() {
//Remove from invited factions
var i = Player.factionInvitations.findIndex((facName)=>{return facName === faction.name});
if (i === -1) {
console.log("ERROR: Could not find faction in Player.factionInvitations");
} else {
Player.factionInvitations.splice(i, 1);
}
joinFaction(faction); joinFaction(faction);
factionInvitationBoxClose(); factionInvitationBoxClose();
if (Engine.currentPage === Engine.Page.Factions) {
Engine.loadFactionsContent();
}
return false; return false;
}); });
var noButton = clearEventListeners("faction-invitation-box-no"); var noButton = clearEventListeners("faction-invitation-box-no");
noButton.addEventListener("click", function() { noButton.addEventListener("click", function() {
factionInvitationBoxClose(); factionInvitationBoxClose();
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
return false; return false;
}); });

@ -65,7 +65,7 @@ function removeChildrenFromElement(el) {
} }
} }
function createElement(type, params) { function createElement(type, params={}) {
var el = document.createElement(type); var el = document.createElement(type);
if (params.id) {el.id = params.id;} if (params.id) {el.id = params.id;}
if (params.class) {el.className = params.class;} if (params.class) {el.className = params.class;}