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;
margin: 5px;
border: 1px solid #333333;
pointer-events: none;
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) */
#create-program-tab {
@ -332,6 +340,7 @@ a:link, a:visited {
top:0px;
-webkit-animation:status-text 3s 1;
background-color: transparent;
height: 15%;
}
#status-text-container {

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

4641
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.
.. js:function:: hacknetnodes[i].level
.. js:attribute:: hacknetnodes[i].level
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
.. js:function:: hacknetnodes[i].cores
.. js:attribute:: hacknetnodes[i].cores
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
.. 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
.. js:function:: hacknetnodes[i].moneyGainRatePerSecond
.. js:attribute:: hacknetnodes[i].moneyGainRatePerSecond
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
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
@ -65,27 +65,27 @@ array itself.
Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false
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
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
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
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.
.. 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
number of cores adds one additional core.

@ -1,6 +1,135 @@
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
--------
@ -35,3 +164,13 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
Example::
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>
<li><a href="netscriptfunctions.html#hack">hack() (built-in function)</a>
</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><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>
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><a href="netscripthacknetnodeapi.html#hacknetnodes[i].moneyGainRatePerSecond">hacknetnodes[i].moneyGainRatePerSecond (hacknetnodes[i] attribute)</a>
</li>
</ul></td>
<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><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><a href="netscripthacknetnodeapi.html#hacknetnodes[i].totalMoneyGenerated">hacknetnodes[i].totalMoneyGenerated() (hacknetnodes[i] method)</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><a href="netscripthacknetnodeapi.html#hacknetnodes[i].totalMoneyGenerated">hacknetnodes[i].totalMoneyGenerated (hacknetnodes[i] attribute)</a>
</li>
<li><a href="netscriptfunctions.html#hasRootAccess">hasRootAccess() (built-in function)</a>
</li>
@ -316,6 +294,10 @@
<h2 id="N">N</h2>
<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>
<li><a href="netscriptfunctions.html#nuke">nuke() (built-in function)</a>
</li>

@ -196,9 +196,11 @@ secrets that you've been searching for.</p>
</ul>
</li>
<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#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-number-module">Javascript Number Module</a></li>
</ul>
</li>
</ul>

@ -197,9 +197,11 @@ to reach out to the developer!</p>
</ul>
</li>
<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#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-number-module">Javascript Number Module</a></li>
</ul>
</li>
</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="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="netscriptmisc.html"> Miscellaneous</a></li>
</ul>
</li>
</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="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="netscriptmisc.html"> Miscellaneous</a></li>
</ul>
</li>
</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
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>
<dl class="function">
<dl class="attribute">
<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></dl>
<dl class="function">
<dl class="attribute">
<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></dl>
<dl class="function">
<dl class="attribute">
<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></dl>
<dl class="function">
<dl class="attribute">
<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></dl>
<dl class="function">
<dl class="attribute">
<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></dl>
<dl class="function">
<dl class="attribute">
<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></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>Note that these must be called on an element inside the <em>hacknetnodes</em> array, not the
array itself.</p>
<dl class="function">
<dt id="hacknetnodes[i].upgradeLevel(n);">
<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>
<dl class="method">
<dt id="hacknetnodes[i].upgradeLevel">
<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">
<col class="field-name" />
<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>
</dd></dl>
<dl class="function">
<dl class="method">
<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>
<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>
</dd></dl>
<dl class="function">
<dl class="method">
<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>
<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>
</dd></dl>
<dl class="function">
<dt id="hacknetnodes[i].getLevelUpgradeCost(n);">
<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>
<dl class="method">
<dt id="hacknetnodes[i].getLevelUpgradeCost">
<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">
<col class="field-name" />
<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>
</dd></dl>
<dl class="function">
<dl class="method">
<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>
<dd><p>Returns the cost of upgrading the RAM of the specified Hacknet Node. Upgrading a Node's RAM doubles it.</p>
</dd></dl>
<dl class="function">
<dl class="method">
<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>
<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 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="netscriptmisc.html"> Miscellaneous</a></li>
</ul>
</li>
</ul>

@ -326,6 +326,7 @@ NOT case-sensitive.</li>
</ul>
</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>
</li>
</ul>

@ -50,6 +50,162 @@
<div class="section" id="netscript-miscellaneous">
<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">
<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>.
@ -85,6 +241,15 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
</pre></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>
@ -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="netscriptsingularityfunctions.html"> Singularity Functions</a></li>
<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="#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-number-module">Javascript Number Module</a></li>
</ul>
</li>
</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="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="netscriptmisc.html"> Miscellaneous</a></li>
</ul>
</li>
</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="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="netscriptmisc.html"> Miscellaneous</a></li>
</ul>
</li>
</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.
.. js:function:: hacknetnodes[i].level
.. js:attribute:: hacknetnodes[i].level
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
.. js:function:: hacknetnodes[i].cores
.. js:attribute:: hacknetnodes[i].cores
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
.. 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
.. js:function:: hacknetnodes[i].moneyGainRatePerSecond
.. js:attribute:: hacknetnodes[i].moneyGainRatePerSecond
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
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
@ -65,27 +65,27 @@ array itself.
Hacknet Node's level is successfully upgraded *n* times or up to the max level (200), and false
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
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
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
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.
.. 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
number of cores adds one additional core.

@ -1,6 +1,135 @@
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
--------
@ -35,3 +164,13 @@ However, since the 'new' operator does not work in Netscript, only the Date modu
Example::
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 " +
"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 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>" +
"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>" +
@ -81,6 +81,7 @@ function initBitNodes() {
"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>" +
"Infiltration gives 50% more reputation and money<br>" +
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
"Augmentations are more expensive<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 " +
@ -125,6 +126,7 @@ function initBitNodes() {
"The growth rate of servers is halved<br>" +
"Weakening a server is twice as effective<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>" +
"Crime and Infiltration are more lucrative<br>" +
"Augmentations are twice as expensive<br><br>" +
@ -137,11 +139,11 @@ function initBitNodes() {
"Level 3: 42%");
//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["BitNode14"] = new BitNode(14, "", "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["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
@ -150,14 +152,6 @@ function initBitNodes() {
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "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 = {
@ -188,6 +182,8 @@ let BitNodeMultipliers = {
InfiltrationMoney: 1,
InfiltrationRep: 1,
CorporationValuation: 1,
}
function initBitNodeMultipliers() {
@ -215,8 +211,8 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.RepToDonateToFaction = 0.5;
BitNodeMultipliers.AugmentationRepCost = 3;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.ServerMaxMoney = 0.50;
BitNodeMultipliers.ServerStartingMoney = 0.50;
BitNodeMultipliers.ServerMaxMoney = 0.25;
BitNodeMultipliers.ServerStartingMoney = 0.25;
BitNodeMultipliers.ServerGrowthRate = 0.20;
BitNodeMultipliers.ScriptHackMoney = 0.25;
BitNodeMultipliers.CompanyWorkMoney = 0.25;
@ -241,13 +237,14 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.ServerMaxMoney = 2;
BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.ScriptHackMoney = 0.15;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.InfiltrationRep = 1.5;
BitNodeMultipliers.InfiltrationMoney = 1.5;
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
break;
case 8: //Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0;
@ -256,7 +253,8 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.CrimeMoney = 0;
BitNodeMultipliers.HacknetNodeMoney = 0;
BitNodeMultipliers.InfiltrationMoney = 0;
BitNodeMultipliers.RepToDonateToFaction = 0
BitNodeMultipliers.RepToDonateToFaction = 0;
BitNodeMultipliers.CorporationValuation = 0;
break;
case 11: //The Big Crash
BitNodeMultipliers.ServerMaxMoney = 0.1;
@ -269,6 +267,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.InfiltrationMoney = 2.5;
BitNodeMultipliers.InfiltrationRep = 2.5;
BitNodeMultipliers.CorporationValuation = 0.01;
break;
default:
console.log("WARNING: Player.bitNodeN invalid");

@ -1,3 +1,4 @@
import {BitNodeMultipliers} from "./BitNode.js";
import {Engine} from "./engine.js";
import {showLiterature} from "./Literature.js";
import {Locations} from "./Location.js";
@ -64,6 +65,9 @@ var OfficeInitialCost = 4e9;
var OfficeInitialSize = 3;
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={}) {
this.name = params.name ? params.name : "";
this.qty = 0; //Quantity
@ -101,67 +105,67 @@ Material.prototype.init = function(mats={}) {
this.dmd = 75; this.dmdR = [65, 85];
this.cmp = 50; this.cmpR = [40, 60];
this.bCost = 1000; this.mv = 0.2;
this.mku = 12;
this.mku = 6;
break;
case "Energy":
this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 80; this.cmpR = [65, 95];
this.bCost = 1500; this.mv = 0.2;
this.mku = 12;
this.mku = 6;
break;
case "Food":
this.dmd = 80; this.dmdR = [70, 90];
this.cmp = 60; this.cmpR = [35, 85];
this.bCost = 5000; this.mv = 1;
this.mku = 7.5;
this.mku = 3;
break;
case "Plants":
this.dmd = 70; this.dmdR = [20, 90];
this.cmp = 50; this.cmpR = [30, 70];
this.bCost = 3000; this.mv = 0.6;
this.mku = 10;
this.mku = 3.75;
break;
case "Metal":
this.dmd = 80; this.dmdR = [75, 85];
this.cmp = 70; this.cmpR = [60, 80];
this.bCost = 2650; this.mv = 1;
this.mku = 12;
this.mku = 6;
break;
case "Hardware":
this.dmd = 85; this.dmdR = [80, 90];
this.cmp = 80; this.cmpR = [65, 95];
this.bCost = 4000; this.mv = 0.5; //Less mv bc its processed twice
this.mku = 5.5;
this.mku = 1;
break;
case "Chemicals":
this.dmd = 55; this.dmdR = [40, 70];
this.cmp = 60; this.cmpR = [40, 80];
this.bCost = 6750; this.mv = 1.2;
this.mku = 6.5;
this.mku = 2;
break;
case "Real Estate":
this.dmd = 50; this.dmdR = [5, 100];
this.cmp = 50; this.cmpR = [25, 75];
this.bCost = 16e3; this.mv = 1.5; //Less mv bc its processed twice
this.mku = 5;
this.mku = 1.5;
break;
case "Drugs":
this.dmd = 60; this.dmdR = [45, 75];
this.cmp = 70; this.cmpR = [40, 100];
this.bCost = 8e3; this.mv = 1.6;
this.mku = 4;
this.mku = 1;
break;
case "Robots":
this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 90; this.cmpR = [80, 100];
this.bCost = 20e3; this.mv = 0.5; //Less mv bc its processed twice
this.mku = 2.5;
this.mku = 1;
break;
case "AI Cores":
this.dmd = 90; this.dmdR = [80, 100];
this.cmp = 90; this.cmpR = [80, 100];
this.bCost = 27e3; this.mv = 0.8; //Less mv bc its processed twice
this.mku = 1.8;
this.mku = 0.5;
break;
case "Scientific Research":
break;
@ -352,8 +356,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
(0.05 * employeeProd[EmployeePositions.Business]));
this.calculateRating(industry);
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.9) * (busRatio + mgmtRatio));
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.75) * (busRatio + mgmtRatio));
this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
this.cmp = getRandomInt(0, 70);
@ -548,11 +551,11 @@ var ProductRatingWeights = {
var IndustryUpgrades = {
"0": [0, 500e3, 1, 1.05,
"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 " +
"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 " +
"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."]
}
@ -626,6 +629,7 @@ function Industry(params={}) {
this.upgrades = Array(numUpgrades).fill(0);
this.state = "START";
this.newInd = true;
this.init();
}
@ -915,6 +919,10 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
this.thisCycleRevenue = 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)
var employeeSalary = 0;
for (var officeLoc in this.offices) {
@ -1256,7 +1264,9 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
var prod = this.products[prodName];
if (!prod.fin) {
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) {
ratio = 0;
} else {
@ -1437,7 +1447,7 @@ Industry.prototype.upgrade = function(upgrade, refs) {
this.awareness += (4 * advMult);
this.popularity += (1 * advMult);
this.awareness *= (1.01 * advMult);
this.popularity *= ((1 + getRandomInt(5, 10) / 100) * advMult);
this.popularity *= ((1 + getRandomInt(3, 6) / 100) * advMult);
break;
default:
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",
});
//Storage size
var totalExport = 0;
for (var i = 0; i < mat.exp.length; ++i) {
totalExport += mat.exp[i].amt;
@ -2160,17 +2169,27 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
div.appendChild(buttonPanel);
//Button to set purchase amount
buttonPanel.appendChild(createElement("a", {
innerText: "Buy (" + formatNumber(mat.buy, 3) + ")", display:"inline-block", class:"a-link-button",
var tutorial = industry.newInd && Object.keys(industry.reqMats).includes(mat.name) &&
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:()=>{
var txt = createElement("p", {
innerHTML: "Enter the amount of " + mat.name + " you would like " +
"to purchase per second. This material's cost changes constantly"
});
var confirmBtn;
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",
clickListener:()=>{
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", {
innerText:"Cancel", class:"a-link-button",
clickListener:()=>{
removeElementById(purchasePopupId);
}
});
createPopup(purchasePopupId, [txt, input, confirmBtn, cancelBtn]);
createPopup(purchasePopupId, [txt, input, confirmBtn, clearButton, cancelBtn]);
input.focus();
}
}));
};
if (tutorial) {
buyButtonParams.tooltip = "Purchase your required materials to get production started!";
}
buttonPanel.appendChild(createElement("a", buyButtonParams));
//Button to manage exports
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.",
});
var br = createElement("br", {});
var confirmBtn;
var inputQty = createElement("input", {
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", {
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",
clickListener:()=>{
//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 " +
"maximum possible amount of the material.<br><br>",
});
var confirmBtn;
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", {
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",
clickListener:()=>{
//Parse price
@ -2566,10 +2616,15 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
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."
});
var confirmBtn;
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',
clickListener:()=>{
if (input.value === "") {
@ -2698,10 +2753,10 @@ var CorporationUpgrades = {
"20 seconds."],
//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 " +
"firm. Each level of this upgrades increases the effectiveness of your " +
"advertising by 10% (additive)."],
"advertising by 1% (additive)."],
//Augmentation for employees, increases cre
"4": [4, 1e9, 1.06, 0.1,
@ -2776,11 +2831,32 @@ Corporation.prototype.process = function(numCycles=1) {
if (this.storedCycles >= CyclesPerIndustryStateCycle) {
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
if (state === "START") {
if (this.storedCycles >= 2*CyclesPerMarketCycle) {
//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 {
numMarketCyclesPersist = 1;
}
@ -2792,25 +2868,7 @@ Corporation.prototype.process = function(numCycles=1) {
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();
if (Engine.currentPage === Engine.Page.Corporation) {this.updateUIContent();}
@ -2833,7 +2891,7 @@ Corporation.prototype.determineValuation = function() {
}
val -= (val % 1e6); //Round down to nearest millionth
}
return val;
return val * BitNodeMultipliers.CorporationValuation;
}
Corporation.prototype.getInvestment = function() {
@ -2882,8 +2940,9 @@ Corporation.prototype.goPublic = function() {
var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to issue " +
"for your IPO. These shares will be publicly sold " +
"and you will no longer own them. You will receive " +
numeral(initialSharePrice).format('$0.000a') + " per share.<br><br>" +
"and you will no longer own them. Your Corporation will receive " +
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 " +
"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.",
@ -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
//Have Agriculture be first as recommended option
if (!ownedIndustries["Agriculture"]) {
selector.add(createElement("option", {
text:Industries["Agriculture"], value:"Agriculture"
}))
}));
}
for (var key in Industries) {
if (key !== "Agriculture" && Industries.hasOwnProperty(key)) {
if (key !== "Agriculture" && Industries.hasOwnProperty(key) && !ownedIndustries[key]) {
var ind = Industries[key];
selector.add(createElement("option", {
text: ind,value:key,
@ -3300,7 +3368,9 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
var popupId = "cmpy-mgmt-sell-shares-popup";
var currentStockPrice = this.sharePrice;
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"),
});
var profitIndicator = createElement("p", {});
@ -3308,7 +3378,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
type:"number", placeholder:"Shares to sell", margin:"5px",
inputListener: ()=> {
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"
} else if (numShares > this.numShares) {
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 currentStockPrice = this.sharePrice;
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") +
". Your company currently has " + formatNumber(this.issuedShares, 3) + " outstanding stock shares",
});
@ -3375,12 +3447,13 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
inputListener: ()=> {
var numShares = Math.round(input.value);
//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"
} else if (numShares > this.issuedShares) {
costIndicator.innerText = "There are not this many shares available to buy back. " +
"There are only " + this.issuedShares + " outstanding shares.";
} else {
console.log("here");
costIndicator.innerText = "Purchase " + numShares + " shares for a total of " +
numeral(numShares * currentStockPrice).format('$0.000a');
}
@ -3434,6 +3507,114 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
companyManagementPanel.appendChild(sellShares);
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 {
var findInvestors = createElement("a", {
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);
//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", {
class:"a-link-button",display:"inline-block",
innerText:"Hire Employee", fontSize:"13px",
@ -3907,6 +4101,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
return false;
}
});
}
industryEmployeePanel.appendChild(industryEmployeeHireButton);
//Autohire Employee button
@ -4240,6 +4435,9 @@ Corporation.prototype.updateDivisionContent = function(division) {
if (office.employees.length >= office.size) {
industryEmployeeHireButton.className = "a-link-button-inactive";
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 {
industryEmployeeHireButton.className = "a-link-button";
industryEmployeeAutohireButton.className = "a-link-button tooltip";

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

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

@ -1,4 +1,7 @@
import {HacknetNode} from "./HacknetNode.js";
import {NetscriptFunctions} from "./NetscriptFunctions.js";
import {NetscriptPort} from "./NetscriptPort.js";
/* Environment
* NetScript program environment
*/
@ -73,6 +76,14 @@ Environment.prototype = {
}
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;
},

@ -3,12 +3,12 @@ import {CONSTANTS} from "./Constants.js";
import {Player} from "./Player.js";
import {Environment} from "./NetscriptEnvironment.js";
import {WorkerScript, addWorkerScript} from "./NetscriptWorker.js";
import {Server} from "./Server.js";
import {Server, getServer} from "./Server.js";
import {Settings} from "./Settings.js";
import {Script, findRunningScript,
RunningScript} from "./Script.js";
import {Node} from "../utils/acorn.js";
import {parse, Node} from "../utils/acorn.js";
import {printArray} from "../utils/HelperFunctions.js";
import {isValidIPAddress} from "../utils/IPAddress.js";
import {isString} from "../utils/StringHelperFunctions.js";
@ -31,7 +31,7 @@ function evaluate(exp, workerScript) {
var env = workerScript.env;
if (env.stopFlag) {return Promise.reject(workerScript);}
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);}
switch (exp.type) {
@ -59,11 +59,11 @@ function evaluate(exp, workerScript) {
case "Identifier":
//Javascript constructor() method can be used as an exploit to run arbitrary code
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)){
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))
break;
@ -135,7 +135,7 @@ function evaluate(exp, workerScript) {
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) {
try {
if (func === "NETSCRIPTFOREACH") {
@ -148,7 +148,7 @@ function evaluate(exp, workerScript) {
var res = func.apply(object,args);
return Promise.resolve(res);
} catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e));
return Promise.reject(makeRuntimeRejectMsg(workerScript, e, exp));
}
});
} else {
@ -158,6 +158,11 @@ function evaluate(exp, workerScript) {
return out.then(function(res) {
return Promise.resolve(res)
}).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);
});
} else {
@ -165,9 +170,14 @@ function evaluate(exp, workerScript) {
}
} catch (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);
} 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){
return evaluate(exp.property, workerScript).then(function(index) {
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]);
}).catch(function(e) {
if (e instanceof WorkerScript || isScriptErrorMessage(e)) {
return Promise.reject(e);
} else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression"));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression", exp));
}
});
} else {
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") {
return "NETSCRIPTFOREACH";
@ -199,7 +209,7 @@ function evaluate(exp, workerScript) {
try {
return Promise.resolve(object[exp.property.name])
} 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){
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))
} 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 {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier"));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier", exp));
}
break;
case "EmptyStatement":
@ -263,8 +273,7 @@ function evaluate(exp, workerScript) {
return evaluateIf(exp, workerScript);
break;
case "SwitchStatement":
var lineNum = getErrorLineNumber(exp, workerScript);
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript (line " + (lineNum+1) + ")"));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript", exp));
break;
case "WhileStatement":
return evaluateWhile(exp, workerScript).then(function(res) {
@ -297,13 +306,24 @@ function evaluate(exp, workerScript) {
env.set(exp.id.name, exp);
return Promise.resolve(true);
} else {
var lineNum = getErrorLineNumber(exp, workerScript);
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration at line " + lineNum+1));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration", exp));
}
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:
var lineNum = getErrorLineNumber(exp, workerScript);
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + " (line " + (lineNum+1) + "). This is currently unsupported in Netscript"));
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". This is currently unsupported in Netscript", exp));
break;
} //End switch
}).catch(function(e) {
@ -438,6 +458,9 @@ function evalAssignment(exp, workerScript) {
return evaluate(exp.right, workerScript).then(function(expRight) {
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
//Array object designed by exp.left.object.name
//Index designated by exp.left.property
@ -450,37 +473,38 @@ function evalAssignment(exp, workerScript) {
var arrName = res.splice(1, 1);
arrName = arrName[0];
env.setArrayElement(arrName, res, expRight);
return Promise.resolve(false);
var res;
try {
res = env.setArrayElement(arrName, res, expRight);
} catch (e) {
return Promise.reject(makeRuntimeRejectMsg(workerScript, e));
}
return Promise.resolve(res);
}).catch(function(e) {
return Promise.reject(e);
});
} else {
//Other assignments
try {
var assign;
switch (exp.operator) {
case "=":
env.set(exp.left.name,expRight);
break;
assign = expRight; break;
case "+=":
env.set(exp.left.name,env.get(exp.left.name) + expRight);
break;
assign = env.get(exp.left.name) + expRight; break;
case "-=":
env.set(exp.left.name,env.get(exp.left.name) - expRight);
break;
assign = env.get(exp.left.name) - expRight; break;
case "*=":
env.set(exp.left.name,env.get(exp.left.name) * expRight);
break;
assign = env.get(exp.left.name) * expRight; break;
case "/=":
env.set(exp.left.name,env.get(exp.left.name) / expRight);
break;
assign = env.get(exp.left.name) / expRight; break;
case "%=":
env.set(exp.left.name,env.get(exp.left.name) % expRight);
break;
assign = env.get(exp.left.name) % expRight; break;
default:
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) {
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) {
/*
if (workerScript instanceof WorkerScript) {
if (workerScript.delay) {
workerScript.delay.cancel();
}
}
*/
if (workerScript instanceof WorkerScript) {
if (workerScript.delay) {
clearTimeout(workerScript.delay);
@ -654,19 +781,6 @@ function killNetscriptDelay(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) {
workerScript.delay = setTimeout(()=>{
workerScript.delay = null;
@ -676,40 +790,14 @@ function netscriptDelay(time, workerScript) {
});
}
function makeRuntimeRejectMsg(workerScript, msg) {
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg;
function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
var lineNum = "";
if (exp != null) {
var num = getErrorLineNumber(exp, workerScript);
lineNum = " (Line " + num + ")"
}
/*
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
throw new Error("Expected number but got " + x);
return x;
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg + lineNum;
}
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
function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
@ -759,13 +847,16 @@ function runScriptFromScript(server, scriptname, args, workerScript, threads=1)
return Promise.resolve(false);
}
//Takes in a
function getErrorLineNumber(exp, workerScript) {
var code = workerScript.scriptRef.scriptRef.code;
//Split code up to the start of the node
try {
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) {
@ -838,4 +929,4 @@ export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateHackingChance, scriptCalculateHackingTime,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime, evaluate,
isScriptErrorMessage, killNetscriptDelay};
isScriptErrorMessage, killNetscriptDelay, evaluateImport};

@ -54,6 +54,7 @@ import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime} from "./NetscriptEvaluator.js";
import {Environment} from "./NetscriptEnvironment.js";
import {NetscriptPort} from "./NetscriptPort.js";
import Decimal from '../utils/decimal.js';
import {dialogBoxCreate} from "../utils/DialogBox.js";
@ -99,6 +100,7 @@ function NetscriptFunctions(workerScript) {
return {
Math : Math,
Date : Date,
Number : Number,
hacknetnodes : Player.hacknetNodes,
sprintf : sprintf,
vsprintf: vsprintf,
@ -614,7 +616,7 @@ function NetscriptFunctions(workerScript) {
return CONSTANTS.ScriptSpawnRamCost;
}
}
if (scriptname == null || threads == 1) {
if (scriptname == null || threads == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()");
}
setTimeout(()=>{
@ -665,7 +667,7 @@ function NetscriptFunctions(workerScript) {
return false;
}
},
killall : function(ip){
killall : function(ip=workerScript.serverIp){
if (workerScript.checkingRam) {
if (workerScript.loadedFns.killall) {
return 0;
@ -1601,7 +1603,7 @@ function NetscriptFunctions(workerScript) {
}
}
var hostnameStr = String(hostname);
hostnameStr = hostnameStr.replace(/\s\s+/g, '');
hostnameStr = hostnameStr.replace(/\s+/g, '');
if (hostnameStr == "") {
workerScript.scriptRef.log("Error: Passed empty string for hostname argument of purchaseServer()");
return "";
@ -1744,20 +1746,15 @@ function NetscriptFunctions(workerScript) {
}
if (!isNaN(port)) { //Write to port
//Port 1-10
if (port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to write to invalid port: " + port + ". Only ports 1-10 are valid.");
port = Math.round(port);
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[portName];
if (port == null) {
var port = NetscriptPorts[port-1];
if (port == null || !(port instanceof NetscriptPort)) {
throw makeRuntimeRejectMsg(workerScript, "Could not find port: " + port + ". This is a bug contact the game developer");
}
port.push(data);
if (port.length > Settings.MaxPortCapacity) {
port.shift();
return true;
}
return false;
return port.write(data);
} else if (isString(port)) { //Write to text file
var fn = port;
var server = getServer(workerScript.serverIp);
@ -1790,19 +1787,15 @@ function NetscriptFunctions(workerScript) {
}
if (!isNaN(port)) { //Read from port
//Port 1-10
if (port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to read from invalid port: " + port + ". Only ports 1-10 are valid.");
port = Math.round(port);
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[portName];
if (port == null) {
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");
}
if (port.length === 0) {
return "NULL PORT DATA";
} else {
return port.shift();
}
return port.read();
} else if (isString(port)) { //Read from text file
var fn = port;
var server = getServer(workerScript.serverIp);
@ -1828,20 +1821,18 @@ function NetscriptFunctions(workerScript) {
return CONSTANTS.ScriptReadWriteRamCost;
}
}
if (isNaN(port) || port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "ERR: peek() called with invalid argument. Must be a port number between 1 and 10");
if (isNaN(port)) {
throw makeRuntimeRejectMsg(workerScript, "ERR: peek() called with invalid argument. Must be a port number between 1 and " + CONSTANTS.NumNetscriptPorts);
}
var portName = "Port" + String(port);
var port = NetscriptPorts[portName];
if (port == null) {
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
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");
}
if (port.length === 0) {
return "NULL PORT DATA";
} else {
var foo = port.slice();
return foo[0];
}
return port.peek();
},
clear : function(port) {
if (workerScript.checkingRam) {
@ -1853,15 +1844,15 @@ function NetscriptFunctions(workerScript) {
}
}
if (!isNaN(port)) { //Clear port
if (port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "ERR: Trying to read from invalid port: " + port + ". Only ports 1-10 are valid");
port = Math.round(port);
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[portName];
if (port == null) {
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");
}
port.length = 0;
return port.clear();
} else if (isString(port)) { //Clear text file
var fn = port;
var server = getServer(workerScript.serverIp);
@ -1877,6 +1868,28 @@ function NetscriptFunctions(workerScript) {
}
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) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.rm) {
@ -2153,7 +2166,7 @@ function NetscriptFunctions(workerScript) {
} else {
workerScript.loadedFns.universityCourse = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2246,7 +2259,7 @@ function NetscriptFunctions(workerScript) {
} else {
workerScript.loadedFns.gymWorkout = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2351,8 +2364,8 @@ function NetscriptFunctions(workerScript) {
return 0;
} else {
workerScript.loadedFns.travelToCity = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2390,7 +2403,7 @@ function NetscriptFunctions(workerScript) {
} else {
workerScript.loadedFns.purchaseTor = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2433,7 +2446,7 @@ function NetscriptFunctions(workerScript) {
} else {
workerScript.loadedFns.purchaseProgram = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2553,8 +2566,8 @@ function NetscriptFunctions(workerScript) {
return 0;
} else {
workerScript.loadedFns.getStats = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2575,14 +2588,55 @@ function NetscriptFunctions(workerScript) {
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() {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.isBusy) {
return 0;
} else {
workerScript.loadedFns.isBusy = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2600,11 +2654,17 @@ function NetscriptFunctions(workerScript) {
return 0;
} else {
workerScript.loadedFns.stopAction = true;
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn1RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
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) {
var txt = Player.singularityStopWork();
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.stopAction == null) {
@ -2614,14 +2674,14 @@ function NetscriptFunctions(workerScript) {
}
return false;
},
upgradeHomeRam() {
upgradeHomeRam : function() {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.upgradeHomeRam) {
return 0;
} else {
workerScript.loadedFns.upgradeHomeRam = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2658,14 +2718,14 @@ function NetscriptFunctions(workerScript) {
}
return true;
},
getUpgradeHomeRamCost() {
getUpgradeHomeRamCost : function() {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getUpgradeHomeRamCost) {
return 0;
} else {
workerScript.loadedFns.getUpgradeHomeRamCost = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 2;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2686,14 +2746,14 @@ function NetscriptFunctions(workerScript) {
var mult = Math.pow(1.55, numUpgrades);
return cost * mult;
},
workForCompany() {
workForCompany : function() {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.workForCompany) {
return 0;
} else {
workerScript.loadedFns.workForCompany = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2731,14 +2791,14 @@ function NetscriptFunctions(workerScript) {
}
return true;
},
applyToCompany(companyName, field) {
applyToCompany : function(companyName, field) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.applyToCompany) {
return 0;
} else {
workerScript.loadedFns.applyToCompany = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2816,14 +2876,14 @@ function NetscriptFunctions(workerScript) {
}
return res;
},
getCompanyRep(companyName) {
getCompanyRep : function(companyName) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCompanyRep) {
return 0;
} else {
workerScript.loadedFns.getCompanyRep = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2841,14 +2901,39 @@ function NetscriptFunctions(workerScript) {
}
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.loadedFns.checkFactionInvitations) {
return 0;
} else {
workerScript.loadedFns.checkFactionInvitations = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2861,14 +2946,14 @@ function NetscriptFunctions(workerScript) {
//Make a copy of Player.factionInvitations
return Player.factionInvitations.slice();
},
joinFaction(name) {
joinFaction : function(name) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.joinFaction) {
return 0;
} else {
workerScript.loadedFns.joinFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -2904,14 +2989,14 @@ function NetscriptFunctions(workerScript) {
}
return true;
},
workForFaction(name, type) {
workForFaction : function(name, type) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.workForFaction) {
return 0;
} else {
workerScript.loadedFns.workForFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3002,14 +3087,14 @@ function NetscriptFunctions(workerScript) {
}
return true;
},
getFactionRep(name) {
getFactionRep : function(name) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getFactionRep) {
return 0;
} else {
workerScript.loadedFns.getFactionRep = true;
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3027,14 +3112,39 @@ function NetscriptFunctions(workerScript) {
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.loadedFns.createProgram) {
return 0;
} else {
workerScript.loadedFns.createProgram = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3136,7 +3246,7 @@ function NetscriptFunctions(workerScript) {
} else {
workerScript.loadedFns.commitCrime = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3222,14 +3332,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crime);
}
},
getCrimeChance(crime) {
getCrimeChance : function(crime) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getCrimeChance) {
return 0;
} else {
workerScript.loadedFns.getCrimeChance = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3269,14 +3379,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into getCrimeChance(): " + crime);
}
},
getOwnedAugmentations(purchased=false) {
getOwnedAugmentations : function(purchased=false) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getOwnedAugmentations) {
return 0;
} else {
workerScript.loadedFns.getOwnedAugmentations = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3297,14 +3407,14 @@ function NetscriptFunctions(workerScript) {
}
return res;
},
getAugmentationsFromFaction(facname) {
getAugmentationsFromFaction : function(facname) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getAugmentationsFromFaction) {
return 0;
} else {
workerScript.loadedFns.getAugmentationsFromFaction = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3327,14 +3437,14 @@ function NetscriptFunctions(workerScript) {
}
return res;
},
getAugmentationCost(name) {
getAugmentationCost : function(name) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.getAugmentationCost) {
return 0;
} else {
workerScript.loadedFns.getAugmentationCost = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3353,14 +3463,14 @@ function NetscriptFunctions(workerScript) {
var aug = Augmentations[name];
return [aug.baseRepRequirement, aug.baseCost];
},
purchaseAugmentation(faction, name) {
purchaseAugmentation : function(faction, name) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.purchaseAugmentation) {
return 0;
} else {
workerScript.loadedFns.purchaseAugmentation = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
return ramCost;
}
}
@ -3422,14 +3532,14 @@ function NetscriptFunctions(workerScript) {
return false;
}
},
installAugmentations(cbScript) {
installAugmentations : function(cbScript) {
if (workerScript.checkingRam) {
if (workerScript.loadedFns.installAugmentations) {
return 0;
} else {
workerScript.loadedFns.installAugmentations = true;
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 10;}
if (Player.bitNodeN !== 4) {ramCost *= 8;}
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 {evaluate, isScriptErrorMessage,
killNetscriptDelay} from "./NetscriptEvaluator.js";
import {NetscriptPort} from "./NetscriptPort.js";
import {AllServers} from "./Server.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
let workerScripts = [];
let NetscriptPorts = {
Port1: [],
Port2: [],
Port3: [],
Port4: [],
Port5: [],
Port6: [],
Port7: [],
Port8: [],
Port9: [],
Port10: [],
var NetscriptPorts = [];
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
NetscriptPorts.push(new NetscriptPort());
}
function prestigeWorkerScripts() {
@ -95,8 +88,8 @@ function runScriptsLoop() {
//If it isn't running, start the script
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
try {
var ast = parse(workerScripts[i].code);
//console.log(ast);
var ast = parse(workerScripts[i].code, {sourceType:"module"});
console.log(ast);
} catch (e) {
console.log("Error parsing script: " + workerScripts[i].name);
dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":<br>" + e);
@ -168,9 +161,12 @@ function killWorkerScript(runningScriptObj, serverIp) {
compareArrays(workerScripts[i].args, runningScriptObj.args)) {
workerScripts[i].env.stopFlag = true;
killNetscriptDelay(workerScripts[i]);
if (workerScripts[i].fnWorker) {
workerScripts[i].fnWorker.env.stopFlag = true;
killNetscriptDelay(workerScripts[i].fnWorker);
//Recursively kill all functions
var curr = workerScripts[i];
while (curr.fnWorker) {
curr.fnWorker.env.stopFlag = true;
killNetscriptDelay(curr.fnWorker);
curr = curr.fnWorker;
}
return true;
}

@ -48,9 +48,9 @@ function PlayerObject() {
this.intelligence = 0;
//Hacking multipliers
this.hacking_chance_mult = 1; //Increase through ascensions/augmentations
this.hacking_speed_mult = 1; //Decrease through ascensions/augmentations
this.hacking_money_mult = 1; //Increase through ascensions/augmentations. Can't go above 1
this.hacking_chance_mult = 1;
this.hacking_speed_mult = 1;
this.hacking_money_mult = 1;
this.hacking_grow_mult = 1;
//Experience and multipliers
@ -692,11 +692,10 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadLocationContent();
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) + ", " +
formatNumber(this.workRepGained, 4) + " reputation, " +
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.workAgiExpGained, 4) + " agility exp, and " +
formatNumber(this.workChaExpGained, 4) + " charisma exp.";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
}
PlayerObject.prototype.startWork = function() {
@ -767,9 +769,17 @@ PlayerObject.prototype.work = function(numCycles) {
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");
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 earned: <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");
mainMenu.style.visibility = "visible";
this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadLocationContent();
if (sing) {
return "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
var res = "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
"earned a total of " +
"$" + formatNumber(this.workMoneyGained, 2) + ", " +
formatNumber(this.workRepGained, 4) + " reputation, " +
@ -899,7 +908,10 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
formatNumber(this.workDexExpGained, 4) + " dexterity exp, " +
formatNumber(this.workAgiExpGained, 4) + " agility exp, and " +
formatNumber(this.workChaExpGained, 4) + " charisma exp";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
}
/* Working for Faction */
@ -930,11 +942,10 @@ PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
this.isWorking = false;
//Engine.loadTerminalContent();
Engine.loadFactionContent();
displayFactionContent(faction.name);
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 " +
formatNumber(this.workRepGained, 4) + " rep, " +
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.workAgiExpGained, 4) + " agi exp, and " +
formatNumber(this.workChaExpGained, 4) + " cha exp.";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
}
PlayerObject.prototype.startFactionWork = function(faction) {
@ -1261,6 +1275,7 @@ PlayerObject.prototype.finishCreateProgramWork = function(cancelled, sing=false)
this.isWorking = false;
Engine.loadTerminalContent();
this.resetWorkStatus();
}
/* Studying/Taking Classes */
@ -1415,8 +1430,8 @@ PlayerObject.prototype.finishClass = function(sing=false) {
this.isWorking = false;
Engine.loadLocationContent();
if (sing) {return "After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " +
if (sing) {
var res="After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " +
"you spent a total of $" + formatNumber(this.workMoneyGained * -1, 2) + ". " +
"You earned a total of: " +
formatNumber(this.workHackExpGained, 3) + " hacking exp, " +
@ -1424,7 +1439,11 @@ PlayerObject.prototype.finishClass = function(sing=false) {
formatNumber(this.workDefExpGained, 3) + " defense exp, " +
formatNumber(this.workDexExpGained, 3) + " dexterity exp, " +
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
@ -1598,6 +1617,7 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
this.resetWorkStatus();
Engine.loadLocationContent();
}

@ -17,6 +17,7 @@ import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial.js";
import {evaluateImport} from "./NetscriptEvaluator.js";
import {NetscriptFunctions} from "./NetscriptFunctions.js";
import {addWorkerScript, killWorkerScript,
WorkerScript} from "./NetscriptWorker.js";
@ -96,19 +97,47 @@ function scriptEditorInit() {
/* Script editor options */
//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.onchange = function() {
var val = themeDropdown.value;
Settings.EditorTheme = val;
var themePath = "ace/theme/" + val.toLowerCase();
editor.setTheme(themePath);
};
themeDropdown.onchange();
//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() {
var val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
editor.setKeyboardHandler(keybindings[val.toLowerCase()]);
};
keybindingDropdown.onchange();
//Highlight Active line
var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline");
@ -174,7 +203,6 @@ function scriptEditorInit() {
}
editor.completers = [autocompleter];
}
document.addEventListener("DOMContentLoaded", scriptEditorInit, false);
//Updates RAM usage in script
function updateScriptEditorContent() {
@ -188,6 +216,8 @@ function updateScriptEditorContent() {
var ramUsage = calculateRamUsage(codeCopy);
if (ramUsage !== -1) {
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) {
//Create a temporary/mock WorkerScript and an AST from the code
var currServ = Player.getCurrentServer();
var workerScript = new WorkerScript({
filename:"foo",
scriptRef: {code:""},
args:[]
});
workerScript.checkingRam = true; //Netscript functions will return RAM usage
workerScript.serverIp = currServ.ip;
try {
var ast = parse(codeCopy);
var ast = parse(codeCopy, {sourceType:"module"});
} catch(e) {
return -1;
}
@ -315,6 +347,14 @@ function calculateRamUsage(codeCopy) {
while (queue.length != 0) {
var exp = queue.shift();
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 "Program":
for (var i = 0; i < exp.body.length; ++i) {
@ -659,4 +699,4 @@ AllServersMap.fromJSON = function(value) {
Reviver.constructors.AllServersMap = AllServersMap;
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) {
this.requiredHackingSkill = requiredHackingSkill;
if (isNaN(moneyAvailable)) {
this.moneyAvailable = 1000000;
this.moneyAvailable = 1e6;
} else {
this.moneyAvailable = moneyAvailable * BitNodeMultipliers.ServerStartingMoney;
}

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

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

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

@ -42,7 +42,7 @@ import {prestigeAugmentation,
prestigeSourceFile} from "./Prestige.js";
import {redPillFlag} from "./RedPill.js";
import {saveObject, loadGame} from "./SaveObject.js";
import {loadAllRunningScripts,
import {loadAllRunningScripts, scriptEditorInit,
updateScriptEditorContent} from "./Script.js";
import {AllServers, Server, initForeignServers} from "./Server.js";
import {Settings, setSettingsLabels} from "./Settings.js";
@ -605,7 +605,7 @@ let Engine = {
Engine.sector12LocationsList.style.display = "inline";
//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";
} else {
document.getElementById("sector12-cityhall-li").style.display = "none";
@ -1319,6 +1319,7 @@ let Engine = {
}
//Initialize labels on game settings
setSettingsLabels();
scriptEditorInit();
Terminal.resetTerminalInput();
},

@ -1,4 +1,5 @@
import {Faction, joinFaction} from "../src/Faction.js";
import {Engine} from "../src/engine.js";
import {Player} from "../src/Player.js";
import {clearEventListeners} from "./HelperFunctions.js";
@ -26,20 +27,33 @@ function factionInvitationSetMessage(msg) {
//ram argument is in GB
function factionInvitationBoxCreate(faction) {
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");
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);
factionInvitationBoxClose();
if (Engine.currentPage === Engine.Page.Factions) {
Engine.loadFactionsContent();
}
return false;
});
var noButton = clearEventListeners("faction-invitation-box-no");
noButton.addEventListener("click", function() {
factionInvitationBoxClose();
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
return false;
});

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