Merge branch 'dev' into typescript

This commit is contained in:
Steven Evans 2018-06-20 22:10:20 -04:00 committed by GitHub
commit 942c26b27e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1632 additions and 2109 deletions

@ -22,12 +22,6 @@
background-color: #777; background-color: #777;
} }
/* Select industry type when creating a new division */
.cmpy-mgmt-industry-select {
color:white;
background-color:black;
}
/* Switch between Cities */ /* Switch between Cities */
.cmpy-mgmt-city-tab { .cmpy-mgmt-city-tab {
display:inline-block; display:inline-block;

@ -501,18 +501,6 @@
width: 50%; width: 50%;
} }
.dev-text-input {
color: var(--my-font-color);
border: 1px solid white;
background-color:black;
}
.dev-dropdown-input {
color: var(--my-font-color);
border: 1px solid white;
background-color:black;
}
/* Location */ /* Location */
#location-container { #location-container {
position: fixed; position: fixed;

@ -197,6 +197,16 @@ a:link, a:visited {
pointer-events: none; pointer-events: none;
} }
.dropdown {
color:white;
background-color:black;
}
.text-input {
color:white;
background-color:black;
}
/* Notification icon (for create program right now only) */ /* Notification icon (for create program right now only) */
#create-program-tab { #create-program-tab {
position:relative; position:relative;

BIN
dist/android-chrome-192x192.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
dist/android-chrome-512x512.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
dist/apple-touch-icon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

9
dist/browserconfig.xml vendored Normal file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="dist/mstile-70x70.png"/>
<TileColor>#000000</TileColor>
</tile>
</msapplication>
</browserconfig>

3010
dist/engine.bundle.js vendored

File diff suppressed because it is too large Load Diff

BIN
dist/favicon-16x16.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

BIN
dist/favicon-32x32.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

BIN
dist/mstile-70x70.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

1
dist/safari-pinned-tab.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

19
dist/site.webmanifest vendored Normal file

@ -0,0 +1,19 @@
{
"name": "Bitburner",
"short_name": "Bitburner",
"icons": [
{
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -387,6 +387,34 @@ ls
Returns an array with the filenames of all files on the specified server (as strings). The returned array Returns an array with the filenames of all files on the specified server (as strings). The returned array
is sorted in alphabetic order is sorted in alphabetic order
ps
^^
.. js:function:: ps(hostname/ip=current ip)
:param string ip: Hostname or IP address of the target server.
If not specified, it will be the current server's IP by default
Returns an array with general information about all scripts running on the specified
target server. The information for each server is given in an object with
the following structure::
{
filename: Script name,
threads: Number of threads script is running with,
args: Script's arguments
}
Example usage (using :doc:`netscriptjs`)::
export async function main(ns) {
const ps = ns.ps("home");
for (let i = 0; i < ps.length; ++i) {
ns.tprint(ps[i].filename + ' ' + ps[i].threads);
ns.tprint(ps[i].args);
}
}
hasRootAccess hasRootAccess
^^^^^^^^^^^^^ ^^^^^^^^^^^^^

@ -1,3 +1,5 @@
.. _netscriptjs:
NetscriptJS (Netscript 2.0) NetscriptJS (Netscript 2.0)
=========================== ===========================
Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that

@ -326,6 +326,8 @@
<li><a href="netscriptfunctions.html#print">print() (built-in function)</a> <li><a href="netscriptfunctions.html#print">print() (built-in function)</a>
</li> </li>
<li><a href="netscriptfunctions.html#prompt">prompt() (built-in function)</a> <li><a href="netscriptfunctions.html#prompt">prompt() (built-in function)</a>
</li>
<li><a href="netscriptfunctions.html#ps">ps() (built-in function)</a>
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>

@ -110,6 +110,7 @@ secrets that you've been searching for.</p>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#exit">exit</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#exit">exit</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#scp">scp</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#scp">scp</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#ls">ls</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#ls">ls</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#ps">ps</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#hasrootaccess">hasRootAccess</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#hasrootaccess">hasRootAccess</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#gethostname">getHostname</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#gethostname">getHostname</a></li>
<li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#gethackinglevel">getHackingLevel</a></li> <li class="toctree-l3"><a class="reference internal" href="netscriptfunctions.html#gethackinglevel">getHackingLevel</a></li>

@ -111,6 +111,7 @@ to reach out to the developer!</p>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#exit">exit</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#exit">exit</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#scp">scp</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#scp">scp</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#ls">ls</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#ls">ls</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#ps">ps</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#hasrootaccess">hasRootAccess</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#hasrootaccess">hasRootAccess</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#gethostname">getHostname</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#gethostname">getHostname</a></li>
<li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#gethackinglevel">getHackingLevel</a></li> <li class="toctree-l2"><a class="reference internal" href="netscriptfunctions.html#gethackinglevel">getHackingLevel</a></li>
@ -249,6 +250,7 @@ to reach out to the developer!</p>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li> <li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li>
<li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li> <li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li>
<li class="toctree-l1"><a class="reference internal" href="changelog.html"> Changelog</a></li>
</ul> </ul>
<div role="search"> <div role="search">

@ -671,6 +671,46 @@ then this function will return true if at least one of the files in the array is
is sorted in alphabetic order</p> is sorted in alphabetic order</p>
</dd></dl> </dd></dl>
</div>
<div class="section" id="ps">
<h2>ps<a class="headerlink" href="#ps" title="Permalink to this headline"></a></h2>
<dl class="function">
<dt>
<code class="descname">ps</code><span class="sig-paren">(</span><em>hostname/ip=current ip</em><span class="sig-paren">)</span></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 last simple">
<li><strong>ip</strong> (<em>string</em>) -- Hostname or IP address of the target server.
If not specified, it will be the current server's IP by default</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>Returns an array with general information about all scripts running on the specified
target server. The information for each server is given in an object with
the following structure:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="n">filename</span><span class="p">:</span> <span class="n">Script</span> <span class="n">name</span><span class="p">,</span>
<span class="n">threads</span><span class="p">:</span> <span class="n">Number</span> <span class="n">of</span> <span class="n">threads</span> <span class="n">script</span> <span class="ow">is</span> <span class="n">running</span> <span class="k">with</span><span class="p">,</span>
<span class="n">args</span><span class="p">:</span> <span class="n">Script</span><span class="s1">&#39;s arguments</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Example usage (using <a class="reference internal" href="netscriptjs.html"><span class="doc">NetscriptJS (Netscript 2.0)</span></a>):</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">export</span> <span class="k">async</span> <span class="n">function</span> <span class="n">main</span><span class="p">(</span><span class="n">ns</span><span class="p">)</span> <span class="p">{</span>
<span class="n">const</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">ps</span><span class="p">(</span><span class="s2">&quot;home&quot;</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">let</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="n">ps</span><span class="o">.</span><span class="n">length</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">ns</span><span class="o">.</span><span class="n">tprint</span><span class="p">(</span><span class="n">ps</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">filename</span> <span class="o">+</span> <span class="s1">&#39; &#39;</span> <span class="o">+</span> <span class="n">ps</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">threads</span><span class="p">);</span>
<span class="n">ns</span><span class="o">.</span><span class="n">tprint</span><span class="p">(</span><span class="n">ps</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
</dd></dl>
</div> </div>
<div class="section" id="hasrootaccess"> <div class="section" id="hasrootaccess">
<h2>hasRootAccess<a class="headerlink" href="#hasrootaccess" title="Permalink to this headline"></a></h2> <h2>hasRootAccess<a class="headerlink" href="#hasrootaccess" title="Permalink to this headline"></a></h2>
@ -1648,6 +1688,7 @@ you create in functions such as <a class="reference external" href="https://deve
<li class="toctree-l3"><a class="reference internal" href="#exit">exit</a></li> <li class="toctree-l3"><a class="reference internal" href="#exit">exit</a></li>
<li class="toctree-l3"><a class="reference internal" href="#scp">scp</a></li> <li class="toctree-l3"><a class="reference internal" href="#scp">scp</a></li>
<li class="toctree-l3"><a class="reference internal" href="#ls">ls</a></li> <li class="toctree-l3"><a class="reference internal" href="#ls">ls</a></li>
<li class="toctree-l3"><a class="reference internal" href="#ps">ps</a></li>
<li class="toctree-l3"><a class="reference internal" href="#hasrootaccess">hasRootAccess</a></li> <li class="toctree-l3"><a class="reference internal" href="#hasrootaccess">hasRootAccess</a></li>
<li class="toctree-l3"><a class="reference internal" href="#gethostname">getHostname</a></li> <li class="toctree-l3"><a class="reference internal" href="#gethostname">getHostname</a></li>
<li class="toctree-l3"><a class="reference internal" href="#gethackinglevel">getHackingLevel</a></li> <li class="toctree-l3"><a class="reference internal" href="#gethackinglevel">getHackingLevel</a></li>
@ -1702,6 +1743,7 @@ you create in functions such as <a class="reference external" href="https://deve
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li> <li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li>
<li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li> <li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li>
<li class="toctree-l1"><a class="reference internal" href="changelog.html"> Changelog</a></li>
</ul> </ul>
<div role="search"> <div role="search">

@ -52,7 +52,7 @@
<div class="body" role="main"> <div class="body" role="main">
<div class="section" id="netscriptjs-netscript-2-0"> <div class="section" id="netscriptjs-netscript-2-0">
<h1>NetscriptJS (Netscript 2.0)<a class="headerlink" href="#netscriptjs-netscript-2-0" title="Permalink to this headline"></a></h1> <span id="netscriptjs"></span><h1>NetscriptJS (Netscript 2.0)<a class="headerlink" href="#netscriptjs-netscript-2-0" title="Permalink to this headline"></a></h1>
<p>Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that <p>Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that
allows users to write (almost) full-fledged Javascript code in their scripts, while allows users to write (almost) full-fledged Javascript code in their scripts, while
still being able to access the Netscript functions.</p> still being able to access the Netscript functions.</p>
@ -278,6 +278,7 @@ NetscriptJS and report any serious exploits.</p>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li> <li class="toctree-l1"><a class="reference internal" href="terminal.html"> Terminal</a></li>
<li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li> <li class="toctree-l1"><a class="reference internal" href="shortcuts.html"> Keyboard Shortcuts</a></li>
<li class="toctree-l1"><a class="reference internal" href="changelog.html"> Changelog</a></li>
</ul> </ul>
<div role="search"> <div role="search">

Binary file not shown.

File diff suppressed because one or more lines are too long

@ -3,6 +3,21 @@
Changelog Changelog
========= =========
v0.38.1 - 6/15/2018
-------------------
* Bug Fix: Using 'Object.prototype' functions like toLocaleString() or toString() should no longer cause errors in NetscriptJS
* Implemented by Github user hydroflame:
* Accessing the 'window' and 'document' objects in Netscript JS now requires a large amount of RAM (100 GB)
* Added game option to suppress travel confirmation
* Text on buttons can no longer be highlighted
* Bug Fix: Fixed an issue that caused NaN values when exporting Real Estate in Corporations
* Bug Fix: Competition and Demand displays in Corporation are now correct (were reversed before)
* Added ps() Netscript function
* Bug Fix: grow() should no longer return/log a negative value when it runs on a server that's already at max money
* Bug Fix: serverExists() Netscript function should now properly return false for non-existent hostname/ips
* Bug Fix: Sever's security level should now properly increase when its money is grown to max value
v0.38.0 - 6/12/2018 v0.38.0 - 6/12/2018
------------------- -------------------
* New BitNode: BN-12 The Recursion - Implemented by Github user hydroflame * New BitNode: BN-12 The Recursion - Implemented by Github user hydroflame

@ -387,6 +387,34 @@ ls
Returns an array with the filenames of all files on the specified server (as strings). The returned array Returns an array with the filenames of all files on the specified server (as strings). The returned array
is sorted in alphabetic order is sorted in alphabetic order
ps
^^
.. js:function:: ps(hostname/ip=current ip)
:param string ip: Hostname or IP address of the target server.
If not specified, it will be the current server's IP by default
Returns an array with general information about all scripts running on the specified
target server. The information for each server is given in an object with
the following structure::
{
filename: Script name,
threads: Number of threads script is running with,
args: Script's arguments
}
Example usage (using :doc:`netscriptjs`)::
export async function main(ns) {
const ps = ns.ps("home");
for (let i = 0; i < ps.length; ++i) {
ns.tprint(ps[i].filename + ' ' + ps[i].threads);
ns.tprint(ps[i].args);
}
}
hasRootAccess hasRootAccess
^^^^^^^^^^^^^ ^^^^^^^^^^^^^

@ -1,3 +1,5 @@
.. _netscriptjs:
NetscriptJS (Netscript 2.0) NetscriptJS (Netscript 2.0)
=========================== ===========================
Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -3,6 +3,17 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Bitburner</title> <title>Bitburner</title>
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
<link rel="manifest" href="dist/site.webmanifest">
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000">
<link rel="shortcut icon" href="favicon.ico">
<meta name="apple-mobile-web-app-title" content="Bitburner">
<meta name="application-name" content="Bitburner">
<meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="dist/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" type="text/css" href="css/styles.css" /> <link rel="stylesheet" type="text/css" href="css/styles.css" />
<link rel="stylesheet" type="text/css" href="css/terminal.css" /> <link rel="stylesheet" type="text/css" href="css/terminal.css" />
<link rel="stylesheet" type="text/css" href="css/menupages.css" /> <link rel="stylesheet" type="text/css" href="css/menupages.css" />
@ -99,7 +110,7 @@
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"> </input> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div> </div>
<div id="javascript-editor"></div> <div id="javascript-editor"></div>
@ -149,9 +160,7 @@
<fieldset> <fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" <input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" />
step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"
</input>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset> </fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
@ -162,8 +171,7 @@
<table id="terminal"> <table id="terminal">
<tr id="terminal-input"> <tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$ <td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
onfocus="this.value = this.value;"/>
</td> </td>
</tr> </tr>
</table> </table>
@ -488,20 +496,20 @@
<p id='dev-menu-text'>Augmentation related: </p> <p id='dev-menu-text'>Augmentation related: </p>
<!-- gets populated with the list of all augments --> <!-- gets populated with the list of all augments -->
<select id="dev-menu-aug-dropdown" class="dev-dropdown-input"></select> <select id="dev-menu-aug-dropdown" class="dropdown"></select>
<a id="dev-add-aug" class="a-link-button tooltip">Queue Augmentation<span class="tooltiptext">May require save + reload</span></a> <a id="dev-add-aug" class="a-link-button tooltip">Queue Augmentation<span class="tooltiptext">May require save + reload</span></a>
<input id="dev-sf-n" type="number" class="dev-text-input" placeholder="SourceFile-N"><input id="dev-sf-lvl" type="number" class="dev-text-input" placeholder="SourceFile-Lvl"><a id="dev-add-source-file" class="a-link-button tooltip"> Add/Remove source file <span class="tooltiptext">If Lvl == 0 the sf will be removed, calling it with another level will replace your current source file. You CAN set a source file higher than it's maximum level.</span></a> <input id="dev-sf-n" type="number" class="text-input" placeholder="SourceFile-N"><input id="dev-sf-lvl" type="number" class="text-input" placeholder="SourceFile-Lvl"><a id="dev-add-source-file" class="a-link-button tooltip"> Add/Remove source file <span class="tooltiptext">If Lvl == 0 the sf will be removed, calling it with another level will replace your current source file. You CAN set a source file higher than it's maximum level.</span></a>
<p id='dev-menu-text'>Faction related: </p> <p id='dev-menu-text'>Faction related: </p>
<select id="dev-menu-faction-dropdown" class="dev-dropdown-input"></select> <select id="dev-menu-faction-dropdown" class="dropdown"></select>
<a id="dev-add-faction" class="a-link-button tooltip">Receive invite<span class="tooltiptext">May require save + reload</span></a> <a id="dev-add-faction" class="a-link-button tooltip">Receive invite<span class="tooltiptext">May require save + reload</span></a>
<p id='dev-menu-text'>Program related: </p> <p id='dev-menu-text'>Program related: </p>
<select id="dev-menu-connect-dropdown" class="dev-dropdown-input"></select> <select id="dev-menu-connect-dropdown" class="dropdown"></select>
<a id="dev-connect" class="a-link-button tooltip">Connect<span class="tooltiptext">Connect to the target server.</span></a> <a id="dev-connect" class="a-link-button tooltip">Connect<span class="tooltiptext">Connect to the target server.</span></a>
<select id="dev-menu-add-program-dropdown" class="dev-dropdown-input"></select> <select id="dev-menu-add-program-dropdown" class="dropdown"></select>
<a id="dev-add-program" class="a-link-button tooltip">Add Program<span class="tooltiptext">Add this program to the player home server, won't add the same program twice.</span></a> <a id="dev-add-program" class="a-link-button tooltip">Add Program<span class="tooltiptext">Add this program to the player home server, won't add the same program twice.</span></a>
<a id="dev-bit-flume" class="a-link-button tooltip">Trigger BitFlume<span class="tooltiptext">Quick escape to change BN, does not give SFs</span></a> <a id="dev-bit-flume" class="a-link-button tooltip">Trigger BitFlume<span class="tooltiptext">Quick escape to change BN, does not give SFs</span></a>
@ -512,19 +520,19 @@
<a id="dev-max-money" class="a-link-button tooltip">maximize all servers money<span class="tooltiptext">Set all servers available money to maximum for that server</span></a> <a id="dev-max-money" class="a-link-button tooltip">maximize all servers money<span class="tooltiptext">Set all servers available money to maximum for that server</span></a>
<p id='dev-menu-text'>Exp/stats related: </p> <p id='dev-menu-text'>Exp/stats related: </p>
<input id="dev-hacking-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-hacking-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-hacking" class="a-link-button tooltip">add hacking exp<span class="tooltiptext">Add that many hacking experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-hacking" class="a-link-button tooltip">add hacking exp<span class="tooltiptext">Add that many hacking experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-strength-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-strength-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-strength" class="a-link-button tooltip">add strength exp<span class="tooltiptext">Add that many strength experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-strength" class="a-link-button tooltip">add strength exp<span class="tooltiptext">Add that many strength experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-defense-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-defense-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-defense" class="a-link-button tooltip">add defense exp<span class="tooltiptext">Add that many defense experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-defense" class="a-link-button tooltip">add defense exp<span class="tooltiptext">Add that many defense experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-dexterity-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-dexterity-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-dexterity" class="a-link-button tooltip">add dexterity exp<span class="tooltiptext">Add that many dexterity experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-dexterity" class="a-link-button tooltip">add dexterity exp<span class="tooltiptext">Add that many dexterity experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-agility-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-agility-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-agility" class="a-link-button tooltip">add agility exp<span class="tooltiptext">Add that many agility experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-agility" class="a-link-button tooltip">add agility exp<span class="tooltiptext">Add that many agility experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-charisma-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-charisma-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-charisma" class="a-link-button tooltip">add charisma exp<span class="tooltiptext">Add that many charisma experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-charisma" class="a-link-button tooltip">add charisma exp<span class="tooltiptext">Add that many charisma experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-intelligence-exp" type="number" class="dev-text-input" placeholder="+exp/-exp (int)"> <input id="dev-intelligence-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-intelligence" class="a-link-button tooltip">add intelligence exp<span class="tooltiptext">Add that many intelligence experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-intelligence" class="a-link-button tooltip">add intelligence exp<span class="tooltiptext">Add that many intelligence experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<a id="dev-enable-intelligence" class="a-link-button tooltip"> enable intelligence<span class="tooltiptext">Enables the intelligence stat</span></a> <a id="dev-enable-intelligence" class="a-link-button tooltip"> enable intelligence<span class="tooltiptext">Enables the intelligence stat</span></a>
<a id="dev-disable-intelligence" class="a-link-button tooltip"> disable intelligence<span class="tooltiptext">Disables the intelligence stat</span></a> <a id="dev-disable-intelligence" class="a-link-button tooltip"> disable intelligence<span class="tooltiptext">Disables the intelligence stat</span></a>
@ -711,7 +719,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container"> <div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content"> <div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p> <p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"> </input> <input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span> <span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span> <span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
</div> </div>
@ -801,9 +809,7 @@
</span> </span>
</label> </label>
<input type ="range" max="250" min="15" <input type ="range" max="250" min="15" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="100" />
step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="100">
</input>
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -817,9 +823,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" <input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" />
step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50">
</input>
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -833,9 +837,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" <input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" />
step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50">
</input>
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -847,9 +849,7 @@
</span> </span>
</label> </label>
<input type="range" max="600" min="0" <input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" />
step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60">
</input>
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em> <em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -910,7 +910,7 @@
<div id="game-options-right-panel"> <div id="game-options-right-panel">
<a class="a-link-button" style="display:block;" href="https://bitburner.wikia.com/wiki/Changelog" target="_blank"> Changelog </a> <a class="a-link-button" style="display:block;" href="https://bitburner.wikia.com/wiki/Changelog" target="_blank"> Changelog </a>
<a class="a-link-button" style="display:block;" href="https://bitburner.wikia.com" target="_blank">Wiki</a> <a class="a-link-button" style="display:block;" href="https://bitburner.wikia.com" target="_blank">Wiki</a>
<a class="a-link-button" style="display:block;", href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a> <a class="a-link-button" style="display:block;" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a>
<a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a> <a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a>
<a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a> <a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a>
<a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a> <a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a>
@ -939,6 +939,6 @@
<div class="loaderspinner"></div> <div class="loaderspinner"></div>
<div class="loaderlabel">Loading Bitburner...</div> <div class="loaderlabel">Loading Bitburner...</div>
</div> </div>
</body>
<script src="dist/engine.bundle.js"></script> <script src="dist/engine.bundle.js"></script>
</body>
</html> </html>

@ -2431,8 +2431,9 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
}); });
//Select industry and city to export to //Select industry and city to export to
var citySelector = createElement("select"); var citySelector = createElement("select", {class: "dropdown"});
var industrySelector = createElement("select", { var industrySelector = createElement("select", {
class: "dropdown",
changeListener:()=>{ changeListener:()=>{
var industryName = industrySelector.options[industrySelector.selectedIndex].value; var industryName = industrySelector.options[industrySelector.selectedIndex].value;
for (var foo = 0; foo < company.divisions.length; ++foo) { for (var foo = 0; foo < company.divisions.length; ++foo) {
@ -2475,6 +2476,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
//Select amount to export //Select amount to export
var exportAmount = createElement("input", { var exportAmount = createElement("input", {
class: "text-input",
placeholder:"Export amount / s" placeholder:"Export amount / s"
}); });
@ -2696,11 +2698,12 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
//Completed products //Completed products
var cmpAndDmdText = ""; var cmpAndDmdText = "";
if (company.unlockUpgrades[2] === 1) { if (company.unlockUpgrades[2] === 1) {
cmpAndDmdText += "<br>Competition: " + formatNumber(product.cmp, 3);
}
if (company.unlockUpgrades[3] === 1) {
cmpAndDmdText += "<br>Demand: " + formatNumber(product.dmd, 3); cmpAndDmdText += "<br>Demand: " + formatNumber(product.dmd, 3);
} }
if (company.unlockUpgrades[3] === 1) {
cmpAndDmdText += "<br>Competition: " + formatNumber(product.cmp, 3);
}
var totalGain = product.data[city][1] - product.data[city][2]; //Production - sale var totalGain = product.data[city][1] - product.data[city][2]; //Production - sale
div.appendChild(createElement("p", { div.appendChild(createElement("p", {
innerHTML: "<p class='tooltip'>" + product.name + ": " + formatNumber(product.data[city][0], 3) + //Quantity innerHTML: "<p class='tooltip'>" + product.name + ": " + formatNumber(product.data[city][0], 3) + //Quantity
@ -3422,15 +3425,14 @@ Corporation.prototype.updateUIHeaderTabs = function() {
innerHTML: "Create a new division to expand into a new industry:", innerHTML: "Create a new division to expand into a new industry:",
}); });
var selector = createElement("select", { var selector = createElement("select", {
class:"cmpy-mgmt-industry-select" class:"dropdown"
}); });
var industryDescription = createElement("p", {}); var industryDescription = createElement("p", {});
var yesBtn; var yesBtn;
var nameInput = createElement("input", { var nameInput = createElement("input", {
type:"text", type:"text",
id:"cmpy-mgmt-expand-industry-name-input", id:"cmpy-mgmt-expand-industry-name-input",
color:"white", class: "text-input",
backgroundColor:"black",
display:"block", display:"block",
maxLength: 30, maxLength: 30,
pattern:"[a-zA-Z0-9-_]", pattern:"[a-zA-Z0-9-_]",
@ -4068,7 +4070,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
innerText: "Would you like to expand into a new city by opening an office? " + innerText: "Would you like to expand into a new city by opening an office? " +
"This would cost " + numeral(OfficeInitialCost).format('$0.000a'), "This would cost " + numeral(OfficeInitialCost).format('$0.000a'),
}); });
var citySelector = createElement("select", {margin:"5px"}); var citySelector = createElement("select", {class: "dropdown", margin:"5px"});
for (var cityName in division.offices) { for (var cityName in division.offices) {
if (division.offices.hasOwnProperty(cityName)) { if (division.offices.hasOwnProperty(cityName)) {
if (!(division.offices[cityName] instanceof OfficeSpace)) { if (!(division.offices[cityName] instanceof OfficeSpace)) {

@ -1,5 +1,5 @@
let CONSTANTS = { let CONSTANTS = {
Version: "0.38.0", Version: "0.38.1",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience //Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then //and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -43,7 +43,7 @@ let CONSTANTS = {
/* Netscript Constants */ /* Netscript Constants */
//RAM Costs for different commands //RAM Costs for different commands
ScriptBaseRamCost: 1.4, ScriptBaseRamCost: 1.4,
ScriptCheatRamCost: 1e21, // if someone tries to cheat by using window or document we just spike the ram cost. ScriptDomRamCost: 100,
ScriptWhileRamCost: 0.2, ScriptWhileRamCost: 0.2,
ScriptForRamCost: 0.2, ScriptForRamCost: 0.2,
ScriptIfRamCost: 0.15, ScriptIfRamCost: 0.15,
@ -489,20 +489,19 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.38.0<br>" + "v0.38.1<br>" +
"* New BitNode: BN-12 The Recursion - Implemented by Github user hydroflame<br>" + "* Bug Fix: Using 'Object.prototype' functions like toLocaleString() or toString() should no longer cause errors in NetscriptJS<br>" +
"* Bladeburner Changes:<br>" + "* Implemented by Github user hydroflame:<br>" +
"*** Bladeburner progress is no longer reset when installing Augmentations<br>" + "*** Accessing the 'window' and 'document' objects in Netscript JS now requires a large amount of RAM (100 GB)<br>" +
"*** The number of successess needed to increase a Contract/Operation's max level now scales with the current max level (gradually gets harder)<br>" + "*** Added game option to suppress travel confirmation<br>" +
"*** All Bladeburner Augmentations are now slightly more expensive and require more reputation<br>" + "*** Text on buttons can no longer be highlighted<br>" +
"*** Black Operations now give higher rank rewards<br>" + "*** Bug Fix: Fixed an issue that caused NaN values when exporting Real Estate in Corporations<br>" +
"*** Doubled the base amount of money gained from Contracts<br>" + "*** Bug Fix: Competition and Demand displays in Corporation are now correct (were reversed before)<br>" +
"*** Increased the amount of experience gained from Contracts/Actions<br>" + "*** Added ps() Netscript function<br>" +
"*** Added a new Augmentation: The Blade's Simulacrum<br>" + "*** Bug Fix: grow() should no longer return/log a negative value when it runs on a server that's already at max money<br>" +
"*** Bladeburner faction reputation gain is now properly affected by favor<br>" + "*** Bug Fix: serverExists() Netscript function should now properly return false for non-existent hostname/ips<br>" +
"* Hacking is now slightly less profitable in BitNode-3<br>" + "*** Bug Fix: Sever's security level should now properly increase when its money is grown to max value"
"* Updated Hacknet Nodes UI - Implemented by Github user kopelli<br>" +
"* Bug Fix: Fixed an exploit that allowed calling any Netscript function without incurring any RAM Cost in NetscriptJS<br>"
} }
export {CONSTANTS}; export {CONSTANTS};

@ -19,7 +19,6 @@ function checkIfConnectedToDarkweb() {
"to purchase an item"); "to purchase an item");
} }
} }
} }
//Handler for dark web commands. The terminal's executeCommand() function will pass //Handler for dark web commands. The terminal's executeCommand() function will pass
@ -49,150 +48,68 @@ function executeDarkwebTerminalCommand(commandArray) {
} }
function listAllDarkwebItems() { function listAllDarkwebItems() {
for (var item in DarkWebItems) { for(const key in DarkWebItems) {
if (DarkWebItems.hasOwnProperty(item)) { const item = DarkWebItems[key];
var item = DarkWebItems[item]; post(item.toString());
//Convert string using toLocaleString
var split = item.split(" - ");
if (split.length == 3 && split[1].charAt(0) == '$') {
split[1] = split[1].slice(1);
split[1] = split[1].replace(/,/g, '');
var price = parseFloat(split[1]);
if (isNaN(price)) {
post(item);
return;
}
price = formatNumber(price, 0);
split[1] = "$" + price.toString();
post(split.join(" - "));
} else {
post(item);
}
}
}
var priceString = split[1];
//Check for errors
if (priceString.length == 0 || priceString.charAt(0) != '$') {
return -1;
} }
//Remove dollar sign and commas
priceString = priceString.slice(1);
priceString = priceString.replace(/,/g, '');
//Convert string to numeric
var price = parseFloat(priceString);
if (isNaN(price)) {return -1;}
else {return price;}
} }
function buyDarkwebItem(itemName) { function buyDarkwebItem(itemName) {
if (itemName.toLowerCase() == Programs.BruteSSHProgram.toLowerCase()) { itemName = itemName.toLowerCase();
var price = parseDarkwebItemPrice(DarkWebItems.BruteSSHProgram);
if (price > 0 && Player.money.gt(price)) { // find the program that matches, if any
Player.loseMoney(price); let item = null;
Player.getHomeComputer().programs.push(Programs.BruteSSHProgram); for(const key in DarkWebItems) {
post("You have purchased the BruteSSH.exe program. The new program " + const i = DarkWebItems[key];
"can be found on your home computer."); if(i.program.toLowerCase() == itemName) {
} else { item = i;
post("Not enough money to purchase " + itemName);
} }
} else if (itemName.toLowerCase() == Programs.FTPCrackProgram.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.FTPCrackProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.FTPCrackProgram);
post("You have purchased the FTPCrack.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else if (itemName.toLowerCase() == Programs.RelaySMTPProgram.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.RelaySMTPProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.RelaySMTPProgram);
post("You have purchased the relaySMTP.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else if (itemName.toLowerCase() == Programs.HTTPWormProgram.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.HTTPWormProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.HTTPWormProgram);
post("You have purchased the HTTPWorm.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else if (itemName.toLowerCase() == Programs.SQLInjectProgram.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.SQLInjectProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.SQLInjectProgram);
post("You have purchased the SQLInject.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else if (itemName.toLowerCase() == Programs.DeepscanV1.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.DeepScanV1Program);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.DeepscanV1);
post("You have purchased the DeepscanV1.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else if (itemName.toLowerCase() == Programs.DeepscanV2.toLowerCase()) {
var price = parseDarkwebItemPrice(DarkWebItems.DeepScanV2Program);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.DeepscanV2);
post("You have purchased the DeepscanV2.exe program. The new program " +
"can be found on your home computer.");
} else {
post("Not enough money to purchase " + itemName);
}
} else {
post("Unrecognized item");
} }
}
function parseDarkwebItemPrice(itemDesc) { // return if invalid
var split = itemDesc.split(" - "); if(item === null) {
if (split.length == 3) { post("Unrecognized item: "+itemName);
var priceString = split[1]; return;
//Check for errors
if (priceString.length == 0 || priceString.charAt(0) != '$') {
return -1;
}
//Remove dollar sign and commas
priceString = priceString.slice(1);
priceString = priceString.replace(/,/g, '');
//Convert string to numeric
var price = parseFloat(priceString);
if (isNaN(price)) {return -1;}
else {return price;}
} else {
return -1;
} }
// return if the player already has it.
if(Player.hasProgram(item.program)) {
post('You already have the '+item.program+' program');
return;
}
// return if the player doesn't have enough money
if(Player.money.lt(item.price)) {
post("Not enough money to purchase " + item.program);
return;
}
// buy and push
Player.loseMoney(item.price);
Player.getHomeComputer().programs.push(item.program);
post('You have purchased the '+item.program+' program. The new program can be found on your home computer.');
} }
let DarkWebItems = { function DarkWebItem(program, price, description) {
BruteSSHProgram: "BruteSSH.exe - $500,000 - Opens up SSH Ports", this.program = program;
FTPCrackProgram: "FTPCrack.exe - $1,500,000 - Opens up FTP Ports", this.price = price;
RelaySMTPProgram: "relaySMTP.exe - $5,000,000 - Opens up SMTP Ports", this.description = description;
HTTPWormProgram: "HTTPWorm.exe - $30,000,000 - Opens up HTTP Ports",
SQLInjectProgram: "SQLInject.exe - $250,000,000 - Opens up SQL Ports",
DeepScanV1Program: "DeepscanV1.exe - $500,000 - Enables 'scan-analyze' with a depth up to 5",
DeepScanV2Program: "DeepscanV2.exe - $25,000,000 - Enables 'scan-analyze' with a depth up to 10",
} }
export {checkIfConnectedToDarkweb, executeDarkwebTerminalCommand, // formats the item for the terminal (eg. "BruteSSH.exe - $500,000 - Opens up SSH Ports")
listAllDarkwebItems, buyDarkwebItem, parseDarkwebItemPrice, DarkWebItem.prototype.toString = function() {
DarkWebItems}; return [this.program, "$"+formatNumber(this.price), this.description].join(' - ');
}
const DarkWebItems = {
BruteSSHProgram: new DarkWebItem(Programs.BruteSSHProgram, 500000, "Opens up SSH Ports"),
FTPCrackProgram: new DarkWebItem(Programs.FTPCrackProgram, 1500000, "Opens up FTP Ports"),
RelaySMTPProgram: new DarkWebItem(Programs.RelaySMTPProgram, 5000000, "Opens up SMTP Ports"),
HTTPWormProgram: new DarkWebItem(Programs.HTTPWormProgram, 30000000, "Opens up HTTP Ports"),
SQLInjectProgram: new DarkWebItem(Programs.SQLInjectProgram, 250000000, "Opens up SQL Ports"),
DeepscanV1: new DarkWebItem(Programs.DeepscanV1, 500000, "Enables 'scan-analyze' with a depth up to 5"),
DeepscanV2: new DarkWebItem(Programs.DeepscanV2, 25000000, "Enables 'scan-analyze' with a depth up to 10"),
};
export {checkIfConnectedToDarkweb, executeDarkwebTerminalCommand, DarkWebItems};

@ -279,7 +279,10 @@ function displayLocationContent() {
purchase256gb.innerHTML = "Purchase 256GB Server - $" + formatNumber(256*CONSTANTS.BaseCostFor1GBOfRamServer, 2); purchase256gb.innerHTML = "Purchase 256GB Server - $" + formatNumber(256*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
purchase512gb.innerHTML = "Purchase 512GB Server - $" + formatNumber(512*CONSTANTS.BaseCostFor1GBOfRamServer, 2); purchase512gb.innerHTML = "Purchase 512GB Server - $" + formatNumber(512*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
purchase1tb.innerHTML = "Purchase 1TB Server - $" + formatNumber(1024*CONSTANTS.BaseCostFor1GBOfRamServer, 2); purchase1tb.innerHTML = "Purchase 1TB Server - $" + formatNumber(1024*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
purchaseTor.innerHTML = "Purchase TOR Router - $" + formatNumber(CONSTANTS.TorRouterCost, 2); if (!SpecialServerIps.hasOwnProperty("Darkweb Server")) {
purchaseTor.innerHTML = "Purchase TOR Router - $" + formatNumber(CONSTANTS.TorRouterCost, 2);
}
travelAgencyText.style.display = "none"; travelAgencyText.style.display = "none";
travelToAevum.style.display = "none"; travelToAevum.style.display = "none";
@ -2005,7 +2008,7 @@ function purchaseTorRouter() {
const purchaseTor = document.getElementById("location-purchase-tor"); const purchaseTor = document.getElementById("location-purchase-tor");
purchaseTor.setAttribute("class", "a-link-button-bought"); purchaseTor.setAttribute("class", "a-link-button-bought");
purchaseTor.innerHTML = "TOR Router - purchased"; purchaseTor.innerHTML = "TOR Router - Purchased";
Player.getHomeComputer().serversOnNetwork.push(darkweb.ip); Player.getHomeComputer().serversOnNetwork.push(darkweb.ip);
darkweb.serversOnNetwork.push(Player.getHomeComputer().ip); darkweb.serversOnNetwork.push(Player.getHomeComputer().ip);

@ -12,7 +12,7 @@ import {Companies, Company, CompanyPosition,
CompanyPositions, companyExists} from "./Company.js"; CompanyPositions, companyExists} from "./Company.js";
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {Programs} from "./CreateProgram.js"; import {Programs} from "./CreateProgram.js";
import {parseDarkwebItemPrice, DarkWebItems} from "./DarkWeb.js"; import {DarkWebItems} from "./DarkWeb.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {AllGangs} from "./Gang.js"; import {AllGangs} from "./Gang.js";
import {Factions, Faction, joinFaction, import {Factions, Faction, joinFaction,
@ -81,6 +81,8 @@ var possibleLogs = {
relaysmtp: true, relaysmtp: true,
httpworm: true, httpworm: true,
sqlinject: true, sqlinject: true,
run:true,
exec:true,
spawn: true, spawn: true,
kill: true, kill: true,
killall: true, killall: true,
@ -325,7 +327,7 @@ function NetscriptFunctions(workerScript) {
} }
workerScript.scriptRef.onlineExpGained += expGain; workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain); Player.gainHackingExp(expGain);
return Promise.resolve(growthPercentage); return Promise.resolve(moneyAfter/moneyBefore);
}); });
}, },
weaken : function(ip){ weaken : function(ip){
@ -644,7 +646,22 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()"); throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()");
} }
setTimeout(()=>{ setTimeout(()=>{
NetscriptFunctions(workerScript).run.apply(this, arguments); if (scriptname === undefined) {
throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)");
}
if (isNaN(threads) || threads < 1) {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into run(). Must be numeric and greater than 0");
}
var argsForNewScript = [];
for (var i = 2; i < arguments.length; ++i) {
argsForNewScript.push(arguments[i]);
}
var scriptServer = getServer(workerScript.serverIp);
if (scriptServer == null) {
throw makeRuntimeRejectMsg(workerScript, "Could not find server. This is a bug in the game. Report to game dev");
}
return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads);
}, 20000); }, 20000);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.spawn == null) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.spawn == null) {
workerScript.scriptRef.log("spawn() will execute " + scriptname + " in 20 seconds"); workerScript.scriptRef.log("spawn() will execute " + scriptname + " in 20 seconds");
@ -709,10 +726,7 @@ function NetscriptFunctions(workerScript) {
return scriptsRunning; return scriptsRunning;
}, },
exit : function() { exit : function() {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {return 0;}
return updateStaticRam("exit", CONSTANTS.ScriptKillRamCost);
}
updateDynamicRam("exit", CONSTANTS.ScriptKillRamCost);
var server = getServer(workerScript.serverIp); var server = getServer(workerScript.serverIp);
if (server == null) { if (server == null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in exit(). This is a bug please contact game dev"); throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in exit(). This is a bug please contact game dev");
@ -2292,7 +2306,9 @@ function NetscriptFunctions(workerScript) {
AddToAllServers(darkweb); AddToAllServers(darkweb);
SpecialServerIps.addIp("Darkweb Server", darkweb.ip); SpecialServerIps.addIp("Darkweb Server", darkweb.ip);
document.getElementById("location-purchase-tor").setAttribute("class", "a-link-button-inactive"); const purchaseTor = document.getElementById("location-purchase-tor");
purchaseTor.setAttribute("class", "a-link-button-bought");
purchaseTor.innerHTML = "TOR Router - Purchased";
Player.getHomeComputer().serversOnNetwork.push(darkweb.ip); Player.getHomeComputer().serversOnNetwork.push(darkweb.ip);
darkweb.serversOnNetwork.push(Player.getHomeComputer().ip); darkweb.serversOnNetwork.push(Player.getHomeComputer().ip);
@ -2317,105 +2333,40 @@ function NetscriptFunctions(workerScript) {
} }
if (SpecialServerIps["Darkweb Server"] == null) { if (SpecialServerIps["Darkweb Server"] == null) {
workerScript.scriptRef.log("ERROR: You do not have TOR router. purchaseProgram() failed."); workerScript.scriptRef.log("ERROR: You do not have the TOR router. purchaseProgram() failed.");
return false; return false;
} }
switch(programName.toLowerCase()) { programName = programName.toLowerCase();
case Programs.BruteSSHProgram.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.BruteSSHProgram); let item = null;
if (price > 0 && Player.money.gt(price)) { for(const key in DarkWebItems) {
Player.loseMoney(price); const i = DarkWebItems[key];
Player.getHomeComputer().programs.push(Programs.BruteSSHProgram); if(i.program.toLowerCase() == programName) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) { item = i;
workerScript.scriptRef.log("You have purchased the BruteSSH.exe program. The new program can be found on your home computer."); }
} }
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName); if(item == null) {
return false; workerScript.scriptRef.log("ERROR: Invalid program name passed into purchaseProgram().");
} return false;
return true; }
case Programs.FTPCrackProgram.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.FTPCrackProgram); if(Player.money.lt(item.price)) {
if (price > 0 && Player.money.gt(price)) { workerScript.scriptRef.log("Not enough money to purchase " + item.program);
Player.loseMoney(price); return false;
Player.getHomeComputer().programs.push(Programs.FTPCrackProgram); }
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the FTPCrack.exe program. The new program can be found on your home computer.");
} if(Player.hasProgram(item.program)) {
} else { workerScript.scriptRef.log('You already have the '+item.program+' program');
workerScript.scriptRef.log("Not enough money to purchase " + programName); return true;
return false; }
}
return true; Player.loseMoney(item.price);
case Programs.RelaySMTPProgram.toLowerCase(): Player.getHomeComputer().programs.push(item.program);
var price = parseDarkwebItemPrice(DarkWebItems.RelaySMTPProgram); if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
if (price > 0 && Player.money.gt(price)) { workerScript.scriptRef.log("You have purchased the "+item.program+" program. The new program can be found on your home computer.");
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.RelaySMTPProgram);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the relaySMTP.exe program. The new program can be found on your home computer.");
}
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName);
return false;
}
return true;
case Programs.HTTPWormProgram.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.HTTPWormProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.HTTPWormProgram);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the HTTPWorm.exe program. The new program can be found on your home computer.");
}
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName);
return false;
}
return true;
case Programs.SQLInjectProgram.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.SQLInjectProgram);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.SQLInjectProgram);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the SQLInject.exe program. The new program can be found on your home computer.");
}
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName);
return false;
}
return true;
case Programs.DeepscanV1.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.DeepScanV1Program);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.DeepscanV1);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the DeepscanV1.exe program. The new program can be found on your home computer.");
}
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName);
return false;
}
return true;
case Programs.DeepscanV2.toLowerCase():
var price = parseDarkwebItemPrice(DarkWebItems.DeepScanV2Program);
if (price > 0 && Player.money.gt(price)) {
Player.loseMoney(price);
Player.getHomeComputer().programs.push(Programs.DeepscanV2);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.purchaseProgram == null) {
workerScript.scriptRef.log("You have purchased the DeepscanV2.exe program. The new program can be found on your home computer.");
}
} else {
workerScript.scriptRef.log("Not enough money to purchase " + programName);
return false;
}
return true;
default:
workerScript.scriptRef.log("ERROR: Invalid program passed into purchaseProgram().");
return false;
} }
return true; return true;
}, },
@ -2798,7 +2749,7 @@ function NetscriptFunctions(workerScript) {
} }
// if the player is in a gang and the target faction is any of the gang faction, fail // if the player is in a gang and the target faction is any of the gang faction, fail
if(Player.gang != null && AllGangs[name] !== undefined) { if(Player.inGang() && AllGangs[name] !== undefined) {
workerScript.scriptRef.log("ERROR: Faction specified in workForFaction() does not offer work at the moment."); workerScript.scriptRef.log("ERROR: Faction specified in workForFaction() does not offer work at the moment.");
return; return;
} }

@ -1,4 +1,5 @@
var ace = require('brace'); var ace = require('brace');
var beautify = require('js-beautify').js_beautify;
require('brace/mode/javascript'); require('brace/mode/javascript');
require('../netscript'); require('../netscript');
require('brace/theme/chaos'); require('brace/theme/chaos');
@ -57,6 +58,15 @@ function scriptEditorInit() {
console.log("Error finding 'script-editor-buttons-wrapper'"); console.log("Error finding 'script-editor-buttons-wrapper'");
return; return;
} }
var beautifyButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"beautify",
clickListener:()=>{
beautifyScript();
return false;
}
});
var closeButton = createElement("a", { var closeButton = createElement("a", {
class:"a-link-button", display:"inline-block", class:"a-link-button", display:"inline-block",
innerText:"Save & Close (Ctrl/Cmd + b)", innerText:"Save & Close (Ctrl/Cmd + b)",
@ -90,6 +100,7 @@ function scriptEditorInit() {
target:"_blank" target:"_blank"
}); });
wrapper.appendChild(beautifyButton);
wrapper.appendChild(closeButton); wrapper.appendChild(closeButton);
wrapper.appendChild(scriptEditorRamText); wrapper.appendChild(scriptEditorRamText);
wrapper.appendChild(scriptEditorRamCheck); wrapper.appendChild(scriptEditorRamCheck);
@ -243,6 +254,13 @@ $(document).keydown(function(e) {
} }
}); });
function beautifyScript() {
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
code = beautify(code, { indent_size: 4 })
editor.setValue(code);
}
function saveAndCloseScriptEditor() { function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value; var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor'); var editor = ace.edit('javascript-editor');
@ -448,7 +466,7 @@ function parseOnlyRamCalculate(server, code, workerScript) {
if (ref == specialReferenceFOR) ram += CONSTANTS.ScriptForRamCost; if (ref == specialReferenceFOR) ram += CONSTANTS.ScriptForRamCost;
if (ref == specialReferenceWHILE) ram += CONSTANTS.ScriptWhileRamCost; if (ref == specialReferenceWHILE) ram += CONSTANTS.ScriptWhileRamCost;
if (ref == "hacknetnodes") ram += CONSTANTS.ScriptHacknetNodesRamCost; if (ref == "hacknetnodes") ram += CONSTANTS.ScriptHacknetNodesRamCost;
if (ref == "document" || ref == "window") ram += CONSTANTS.ScriptCheatRamCost; if (ref == "document" || ref == "window") ram += CONSTANTS.ScriptDomRamCost;
// Check if this ident is a function in the workerscript env. If it is, then we need to // Check if this ident is a function in the workerscript env. If it is, then we need to
// get its RAM cost. We do this by calling it, which works because the running script // get its RAM cost. We do this by calling it, which works because the running script
@ -509,11 +527,15 @@ function parseOnlyCalculateDeps(code, currentModule) {
s.add(name); // For builtins like hack. s.add(name); // For builtins like hack.
} }
//A list of identifiers that resolve to "native Javascript code"
const objectPrototypeProperties = Object.getOwnPropertyNames(Object.prototype);
// If we discover a dependency identifier, state.key is the dependent identifier. // If we discover a dependency identifier, state.key is the dependent identifier.
// walkDeeper is for doing recursive walks of expressions in composites that we handle. // walkDeeper is for doing recursive walks of expressions in composites that we handle.
function commonVisitors() { function commonVisitors() {
return { return {
Identifier: (node, st, walkDeeper) => { Identifier: (node, st, walkDeeper) => {
if (objectPrototypeProperties.includes(node.name)) {return;}
addRef(st.key, node.name); addRef(st.key, node.name);
}, },
WhileStatement: (node, st, walkDeeper) => { WhileStatement: (node, st, walkDeeper) => {

@ -768,19 +768,31 @@ function initForeignServers() {
} }
} }
function numCycleForGrowth(server, growth) {
let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty;
if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) {
ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate;
}
const serverGrowthPercentage = server.serverGrowth / 100;
const cycles = Math.log(growth)/(Math.log(ajdGrowthRate)*Player.hacking_grow_mult*serverGrowthPercentage);
return cycles;
}
//Applied server growth for a single server. Returns the percentage growth //Applied server growth for a single server. Returns the percentage growth
function processSingleServerGrowth(server, numCycles) { function processSingleServerGrowth(server, numCycles) {
//Server growth processed once every 450 game cycles //Server growth processed once every 450 game cycles
var numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0); const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0);
//Get adjusted growth rate, which accounts for server security //Get adjusted growth rate, which accounts for server security
var growthRate = CONSTANTS.ServerBaseGrowthRate; const growthRate = CONSTANTS.ServerBaseGrowthRate;
var adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty; var adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty;
if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;} if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;}
//Calculate adjusted server growth rate based on parameters //Calculate adjusted server growth rate based on parameters
var serverGrowthPercentage = server.serverGrowth / 100; const serverGrowthPercentage = server.serverGrowth / 100;
var numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles //Apply serverGrowth for the calculated number of growth cycles
var serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * Player.hacking_grow_mult); var serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * Player.hacking_grow_mult);
@ -789,19 +801,26 @@ function processSingleServerGrowth(server, numCycles) {
serverGrowth = 1; serverGrowth = 1;
} }
var oldMoneyAvailable = server.moneyAvailable; const oldMoneyAvailable = server.moneyAvailable;
server.moneyAvailable *= serverGrowth; server.moneyAvailable *= serverGrowth;
// in case of data corruption
if (server.moneyMax && isNaN(server.moneyAvailable)) { if (server.moneyMax && isNaN(server.moneyAvailable)) {
server.moneyAvailable = server.moneyMax; server.moneyAvailable = server.moneyMax;
} }
// cap at max
if (server.moneyMax && server.moneyAvailable > server.moneyMax) { if (server.moneyMax && server.moneyAvailable > server.moneyMax) {
server.moneyAvailable = server.moneyMax; server.moneyAvailable = server.moneyMax;
return server.moneyAvailable / oldMoneyAvailable;
} }
//Growing increases server security twice as much as hacking // if there was any growth at all, increase security
server.fortify(2 * CONSTANTS.ServerFortifyAmount * numServerGrowthCycles); if(oldMoneyAvailable !== server.moneyAvailable) {
return serverGrowth; //Growing increases server security twice as much as hacking
const usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable);
server.fortify(2 * CONSTANTS.ServerFortifyAmount * Math.ceil(usedCycles));
}
return server.moneyAvailable / oldMoneyAvailable;
} }
function prestigeHomeComputer(homeComp) { function prestigeHomeComputer(homeComp) {
@ -873,9 +892,11 @@ function GetServerByHostname(hostname) {
function getServer(s) { function getServer(s) {
if (!isValidIPAddress(s)) { if (!isValidIPAddress(s)) {
return GetServerByHostname(s); return GetServerByHostname(s);
} else { }
if(AllServers[s] !== undefined) {
return AllServers[s]; return AllServers[s];
} }
return null;
} }
//Debugging tool //Debugging tool

@ -48,7 +48,7 @@ function setSettingsLabels() {
nsPortLimit.innerHTML = Settings.MaxPortCapacity; nsPortLimit.innerHTML = Settings.MaxPortCapacity;
suppressMsgs.checked = Settings.SuppressMessages; suppressMsgs.checked = Settings.SuppressMessages;
suppressFactionInv.checked = Settings.SuppressFactionInvites; suppressFactionInv.checked = Settings.SuppressFactionInvites;
suppressTravelConfirmation.checked = Settings.suppressTravelConfirmation; suppressTravelConfirmation.checked = Settings.SuppressTravelConfirmation;
autosaveInterval.innerHTML = Settings.AutosaveInterval; autosaveInterval.innerHTML = Settings.AutosaveInterval;
disableHotkeys.checked = Settings.DisableHotkeys; disableHotkeys.checked = Settings.DisableHotkeys;

@ -1729,7 +1729,9 @@ let Engine = {
//If DarkWeb already purchased, disable the button //If DarkWeb already purchased, disable the button
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) { if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
document.getElementById("location-purchase-tor").setAttribute("class", "a-link-button-inactive"); const purchaseTor = document.getElementById("location-purchase-tor");
purchaseTor.setAttribute("class", "a-link-button-bought");
purchaseTor.innerHTML = "TOR Router - Purchased";
} }
}, },