mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 20:55:44 +01:00
Merge branch 'dev' into sleeve-buy-aug-api
This commit is contained in:
commit
0f8f572519
@ -51,6 +51,44 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Checkbox for (de)selecting autoleveling */
|
||||
.bbcheckbox {
|
||||
position: relative;
|
||||
display: inline;
|
||||
label {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: black;
|
||||
border-width: 1px;
|
||||
border-color: white;
|
||||
border-style: solid;
|
||||
&:after {
|
||||
content: '';
|
||||
width: 9px;
|
||||
height: 5px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
border: 3px solid white;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
opacity: 0;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
input[type=checkbox] {
|
||||
margin: 3px;
|
||||
visibility: hidden;
|
||||
&:checked + label:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bladeburner Console */
|
||||
.bladeburner-console-div {
|
||||
display: inline-block;
|
||||
|
@ -38,6 +38,7 @@ button {
|
||||
}
|
||||
|
||||
.a-link-button-inactive,
|
||||
.std-button-disabled,
|
||||
.std-button:disabled {
|
||||
text-decoration: none;
|
||||
background-color: #333;
|
||||
|
@ -2,7 +2,7 @@
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Character Overview Panel (top-right)
|
||||
* Styling for the Character Overview Panel (top-right panel)
|
||||
*/
|
||||
|
||||
#character-overview-wrapper {
|
||||
|
36
css/dev-menu.css
Normal file
36
css/dev-menu.css
Normal file
@ -0,0 +1,36 @@
|
||||
.add-exp-button {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.remove-exp-button {
|
||||
margin-left:0px;
|
||||
}
|
||||
|
||||
.exp-input {
|
||||
margin-right: 0px;
|
||||
margin-left:0px;
|
||||
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.touch-right {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.touch-left {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.touch-sides {
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
6
css/grid.min.css
vendored
Normal file
6
css/grid.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
75
css/hacknetnodes.scss
Normal file
75
css/hacknetnodes.scss
Normal file
@ -0,0 +1,75 @@
|
||||
@import "mixins";
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Hacknet Nodes UI Page
|
||||
*/
|
||||
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.hacknet-general-info {
|
||||
margin: 10px;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
@ -138,81 +138,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Hacknet Nodes */
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-text,
|
||||
#hacknet-nodes-container li {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-page-text {
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
|
@ -62,6 +62,9 @@ a:visited {
|
||||
.text-input {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
/* Notification icon (for create program right now only) */
|
||||
|
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
2578
dist/engine.css
vendored
2578
dist/engine.css
vendored
File diff suppressed because it is too large
Load Diff
152
dist/vendor.bundle.js
vendored
152
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -3,6 +3,42 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.45.1 - 3/23/2019
|
||||
-------------------
|
||||
* Added two new Corporation Researches
|
||||
* General UI improvements (by hydroflame and koriar)
|
||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
||||
|
||||
v0.45.0 - 3/22/2019
|
||||
-------------------
|
||||
* Corporation changes:
|
||||
* Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
||||
* This means that each Corporation 'state' will now only take 2 seconds, rather than 3
|
||||
* Increased initial salaries for newly-hired employees
|
||||
* Increased the cost multiplier for upgrading office size (the cost will increase faster)
|
||||
* The stats of your employees now has a slightly larger effect on production & sales
|
||||
* Added several new Research upgrades
|
||||
* Market-TA research now allows you to automatically set sale price at optimal values
|
||||
* Market-TA research now works for Products (not just Materials)
|
||||
* Reduced the amount of Scientific Research needed to unlock the Hi-Tech R&D Laboratory from 10k to 5k
|
||||
* Energy Material requirement of the Software industry reduced from 1 to 0.5
|
||||
* It is now slightly easier to increase the Software industry's production multiplier
|
||||
* Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
|
||||
* You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
|
||||
* Significantly changed the effects of the different employee positions. See updated descriptions
|
||||
* Reduced the amount of money you gain from private investors
|
||||
* Training employees is now 3x more effective
|
||||
* Bug Fix: An industry's products are now properly separated between different cities
|
||||
|
||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
||||
* Modified the multipliers of BitNode-3 and BitNode-8 to make them slightly harder
|
||||
* After installing Augmentations, Duplicate Sleeves will now default to Synchronize if their Shock is 0
|
||||
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
|
||||
* Bug Fix: growthAnalyze() function now properly accounts for BitNode multipliers
|
||||
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
|
||||
|
||||
v0.44.1 - 3/4/2019
|
||||
------------------
|
||||
* Duplicate Sleeve changes:
|
||||
|
@ -1,7 +1,7 @@
|
||||
getSleeveStats() Netscript Function
|
||||
===================================
|
||||
|
||||
.. js:function:: getStatus(sleeveNumber)
|
||||
.. js:function:: getSleeveStats(sleeveNumber)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to get stats of. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
|
||||
|
42
index.html
42
index.html
@ -131,7 +131,7 @@
|
||||
<h1 style="color:white;"> Script Editor Options </h1>
|
||||
<fieldset>
|
||||
<label for="script-editor-option-editor">Editor</label>
|
||||
<select id="script-editor-option-editor">
|
||||
<select id="script-editor-option-editor" class="dropdown">
|
||||
<option value="Ace">Ace</option>
|
||||
<option value="CodeMirror">CodeMirror</option>
|
||||
</select>
|
||||
@ -139,12 +139,12 @@
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-theme">Theme</label>
|
||||
<select id="script-editor-option-theme"></select>
|
||||
<select id="script-editor-option-theme" class="dropdown"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-keybinding">Key Binding</label>
|
||||
<select id="script-editor-option-keybinding"></select>
|
||||
<select id="script-editor-option-keybinding" class="dropdown"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
@ -689,7 +689,7 @@
|
||||
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
|
||||
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
|
||||
<br/><br/>
|
||||
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
||||
<input id="stock-market-watchlist-filter" class="text-input" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
||||
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
|
||||
<ul id="stock-market-list" style="list-style:none;">
|
||||
</ul>
|
||||
@ -744,7 +744,7 @@
|
||||
<p id="infiltration-box-text"> </p>
|
||||
|
||||
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br/><br/>
|
||||
<select id="infiltration-faction-select"> </select> <br/>
|
||||
<select id="infiltration-faction-select" class="dropdown"> </select> <br/>
|
||||
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
|
||||
|
||||
</div>
|
||||
@ -783,35 +783,7 @@
|
||||
<div id="character-overview-wrapper">
|
||||
<div id="character-overview-container">
|
||||
<div id="character-overview-text">
|
||||
<table>
|
||||
<tr id="character-hp-wrapper">
|
||||
<td>Hp:</td><td id="character-hp-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-money-wrapper">
|
||||
<td>Money: </td><td id="character-money-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-hack-wrapper">
|
||||
<td>Hack: </td><td id="character-hack-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-str-wrapper">
|
||||
<td>Str: </td><td id="character-str-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-def-wrapper">
|
||||
<td>Def: </td><td id="character-def-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-dex-wrapper">
|
||||
<td>Dex: </td><td id="character-dex-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-agi-wrapper">
|
||||
<td>Agi: </td><td id="character-agi-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-cha-wrapper">
|
||||
<td>Cha: </td><td id="character-cha-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
<tr id="character-int-wrapper">
|
||||
<td>Int: </td><td id="character-int-text" class="character-stat-cell"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- ReactJS Component -->
|
||||
</div>
|
||||
<div class="character-quick-options">
|
||||
<button id="character-overview-save-button" class="character-overview-btn">Save Game</button>
|
||||
@ -959,7 +931,7 @@
|
||||
Sets the locale for displaying numbers. Defaults to 'en'
|
||||
</span>
|
||||
</label>
|
||||
<select name="settingsLocale" id="settingsLocale">
|
||||
<select name="settingsLocale" id="settingsLocale" class="dropdown">
|
||||
<option value="en">en</option>
|
||||
<option value="bg">bg</option>
|
||||
<option value="cs">cs</option>
|
||||
|
317
package-lock.json
generated
317
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "0.40.2",
|
||||
"version": "0.45.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -773,7 +773,8 @@
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"dev": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
@ -1200,7 +1201,8 @@
|
||||
"bluebird": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
||||
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
|
||||
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==",
|
||||
"dev": true
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "4.11.8",
|
||||
@ -1446,10 +1448,11 @@
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz",
|
||||
"integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bluebird": "3.5.1",
|
||||
"chownr": "1.0.1",
|
||||
"glob": "7.1.2",
|
||||
"chownr": "1.1.1",
|
||||
"glob": "7.1.3",
|
||||
"graceful-fs": "4.1.11",
|
||||
"lru-cache": "4.1.1",
|
||||
"mississippi": "2.0.0",
|
||||
@ -1458,14 +1461,15 @@
|
||||
"promise-inflight": "1.0.1",
|
||||
"rimraf": "2.6.2",
|
||||
"ssri": "5.3.0",
|
||||
"unique-filename": "1.1.0",
|
||||
"unique-filename": "1.1.1",
|
||||
"y18n": "4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
@ -1478,7 +1482,8 @@
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2012,9 +2017,10 @@
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
|
||||
"dev": true
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
"version": "1.0.0",
|
||||
@ -2360,7 +2366,8 @@
|
||||
"commondir": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||
"dev": true
|
||||
},
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
@ -2420,6 +2427,7 @@
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
|
||||
"integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4",
|
||||
@ -2499,6 +2507,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
|
||||
"integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aproba": "1.2.0",
|
||||
"fs-write-stream-atomic": "1.0.10",
|
||||
@ -2775,7 +2784,8 @@
|
||||
"cyclist": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
|
||||
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
|
||||
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
|
||||
"dev": true
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.0",
|
||||
@ -3103,9 +3113,10 @@
|
||||
}
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz",
|
||||
"integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==",
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"inherits": "2.0.3",
|
||||
@ -3189,6 +3200,7 @@
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
||||
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0"
|
||||
}
|
||||
@ -4087,6 +4099,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
|
||||
"integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commondir": "1.0.1",
|
||||
"make-dir": "1.2.0",
|
||||
@ -4097,6 +4110,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
|
||||
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "2.0.0"
|
||||
}
|
||||
@ -4120,12 +4134,39 @@
|
||||
"dev": true
|
||||
},
|
||||
"flush-write-stream": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz",
|
||||
"integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
|
||||
"integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4"
|
||||
"readable-stream": "2.3.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "2.0.0",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.1.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
@ -4209,6 +4250,7 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
|
||||
"integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4"
|
||||
@ -4218,6 +4260,7 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
|
||||
"integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"iferr": "0.1.5",
|
||||
@ -4991,7 +5034,8 @@
|
||||
"iferr": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
|
||||
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
|
||||
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "3.3.7",
|
||||
@ -5030,7 +5074,8 @@
|
||||
"imurmurhash": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
|
||||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
|
||||
"dev": true
|
||||
},
|
||||
"in-publish": {
|
||||
"version": "2.0.0",
|
||||
@ -5741,15 +5786,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"jshint": {
|
||||
"version": "2.9.7",
|
||||
"resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz",
|
||||
"integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==",
|
||||
"version": "2.10.2",
|
||||
"resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz",
|
||||
"integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==",
|
||||
"requires": {
|
||||
"cli": "1.0.1",
|
||||
"console-browserify": "1.1.0",
|
||||
"exit": "0.1.2",
|
||||
"htmlparser2": "3.8.3",
|
||||
"lodash": "4.17.10",
|
||||
"lodash": "4.17.11",
|
||||
"minimatch": "3.0.4",
|
||||
"shelljs": "0.3.0",
|
||||
"strip-json-comments": "1.0.4"
|
||||
@ -5794,6 +5839,11 @@
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
@ -6255,6 +6305,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
|
||||
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "2.0.0",
|
||||
"path-exists": "3.0.0"
|
||||
@ -6421,6 +6472,7 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
|
||||
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pseudomap": "1.0.2",
|
||||
"yallist": "2.1.2"
|
||||
@ -6430,6 +6482,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz",
|
||||
"integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pify": "3.0.0"
|
||||
},
|
||||
@ -6437,7 +6490,8 @@
|
||||
"pify": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
|
||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -6759,7 +6813,8 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "3.0.2",
|
||||
@ -6775,17 +6830,18 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
|
||||
"integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"concat-stream": "1.6.0",
|
||||
"duplexify": "3.5.4",
|
||||
"duplexify": "3.7.1",
|
||||
"end-of-stream": "1.4.1",
|
||||
"flush-write-stream": "1.0.2",
|
||||
"flush-write-stream": "1.1.1",
|
||||
"from2": "2.3.0",
|
||||
"parallel-transform": "1.1.0",
|
||||
"pump": "2.0.1",
|
||||
"pumpify": "1.4.0",
|
||||
"stream-each": "1.2.2",
|
||||
"through2": "2.0.3"
|
||||
"pumpify": "1.5.1",
|
||||
"stream-each": "1.2.3",
|
||||
"through2": "2.0.5"
|
||||
}
|
||||
},
|
||||
"mixin-deep": {
|
||||
@ -6831,6 +6887,7 @@
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -6892,6 +6949,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
"integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aproba": "1.2.0",
|
||||
"copy-concurrently": "1.0.5",
|
||||
@ -7529,6 +7587,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
|
||||
"integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "1.0.0"
|
||||
}
|
||||
@ -7537,6 +7596,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
|
||||
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "1.2.0"
|
||||
}
|
||||
@ -7550,7 +7610,8 @@
|
||||
"p-try": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
|
||||
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
|
||||
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
|
||||
"dev": true
|
||||
},
|
||||
"pako": {
|
||||
"version": "1.0.6",
|
||||
@ -7561,6 +7622,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
|
||||
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cyclist": "0.2.2",
|
||||
"inherits": "2.0.3",
|
||||
@ -7668,7 +7730,8 @@
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
@ -7749,6 +7812,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
|
||||
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "2.1.0"
|
||||
}
|
||||
@ -8650,7 +8714,8 @@
|
||||
"promise-inflight": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
|
||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||
"dev": true
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
@ -8686,7 +8751,8 @@
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
|
||||
"dev": true
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.1.31",
|
||||
@ -8711,17 +8777,19 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
|
||||
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"once": "1.4.0"
|
||||
}
|
||||
},
|
||||
"pumpify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz",
|
||||
"integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
|
||||
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"duplexify": "3.5.4",
|
||||
"duplexify": "3.7.1",
|
||||
"inherits": "2.0.3",
|
||||
"pump": "2.0.1"
|
||||
}
|
||||
@ -9379,6 +9447,7 @@
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
|
||||
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2"
|
||||
},
|
||||
@ -9387,6 +9456,7 @@
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
@ -9421,6 +9491,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
|
||||
"integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aproba": "1.2.0"
|
||||
}
|
||||
@ -9817,9 +9888,10 @@
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz",
|
||||
"integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU="
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
|
||||
"integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==",
|
||||
"dev": true
|
||||
},
|
||||
"serve-index": {
|
||||
"version": "1.9.1",
|
||||
@ -10293,7 +10365,8 @@
|
||||
"source-list-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz",
|
||||
"integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A=="
|
||||
"integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
@ -10446,6 +10519,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
|
||||
"integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
@ -10560,9 +10634,10 @@
|
||||
}
|
||||
},
|
||||
"stream-each": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
|
||||
"integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
|
||||
"integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"stream-shift": "1.0.0"
|
||||
@ -10610,7 +10685,8 @@
|
||||
"stream-shift": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
|
||||
"dev": true
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
@ -11360,12 +11436,39 @@
|
||||
"dev": true
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
|
||||
"integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "2.3.4",
|
||||
"readable-stream": "2.3.6",
|
||||
"xtend": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "2.0.0",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.1.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"thunky": {
|
||||
@ -11694,7 +11797,8 @@
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.9.2",
|
||||
@ -11706,6 +11810,7 @@
|
||||
"version": "3.3.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
|
||||
"integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "2.13.0",
|
||||
"source-map": "0.6.1"
|
||||
@ -11714,12 +11819,14 @@
|
||||
"commander": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
|
||||
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
|
||||
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11765,59 +11872,42 @@
|
||||
"optional": true
|
||||
},
|
||||
"uglifyjs-webpack-plugin": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz",
|
||||
"integrity": "sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
|
||||
"integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cacache": "10.0.4",
|
||||
"find-cache-dir": "1.0.0",
|
||||
"schema-utils": "0.4.5",
|
||||
"serialize-javascript": "1.4.0",
|
||||
"serialize-javascript": "1.6.1",
|
||||
"source-map": "0.6.1",
|
||||
"uglify-es": "3.3.9",
|
||||
"webpack-sources": "1.1.0",
|
||||
"worker-farm": "1.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.1.tgz",
|
||||
"integrity": "sha512-pgZos1vgOHDiC7gKNbZW8eKvCnNXARv2oqrGQT7Hzbq5Azp7aZG6DJzADnkuSq7RH6qkXp4J/m68yPX/2uBHyQ==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "2.0.1",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"json-schema-traverse": "0.4.1",
|
||||
"uri-js": "4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
|
||||
"integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz",
|
||||
"integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==",
|
||||
"requires": {
|
||||
"ajv": "6.5.1",
|
||||
"ajv-keywords": "3.2.0"
|
||||
}
|
||||
"commander": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
|
||||
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-es": {
|
||||
"version": "3.3.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
|
||||
"integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "2.13.0",
|
||||
"source-map": "0.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11893,17 +11983,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"unique-filename": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz",
|
||||
"integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
|
||||
"integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"unique-slug": "2.0.0"
|
||||
"unique-slug": "2.0.1"
|
||||
}
|
||||
},
|
||||
"unique-slug": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
|
||||
"integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
|
||||
"integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"imurmurhash": "0.1.4"
|
||||
}
|
||||
@ -12024,6 +12116,7 @@
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "2.1.1"
|
||||
},
|
||||
@ -12031,7 +12124,8 @@
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12328,7 +12422,7 @@
|
||||
"node-libs-browser": "2.1.0",
|
||||
"schema-utils": "0.4.5",
|
||||
"tapable": "1.0.0",
|
||||
"uglifyjs-webpack-plugin": "1.2.5",
|
||||
"uglifyjs-webpack-plugin": "1.3.0",
|
||||
"watchpack": "1.6.0",
|
||||
"webpack-sources": "1.1.0"
|
||||
},
|
||||
@ -12736,6 +12830,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",
|
||||
"integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-list-map": "2.0.0",
|
||||
"source-map": "0.6.1"
|
||||
@ -12744,7 +12839,8 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12811,6 +12907,7 @@
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
|
||||
"integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"errno": "0.1.7"
|
||||
}
|
||||
@ -12913,7 +13010,8 @@
|
||||
"xtend": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
@ -12924,7 +13022,8 @@
|
||||
"yallist": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "11.1.0",
|
||||
|
@ -24,7 +24,7 @@
|
||||
"file-saver": "^1.3.8",
|
||||
"interpret": "^1.0.0",
|
||||
"jquery": "^3.3.1",
|
||||
"jshint": "^2.9.7",
|
||||
"jshint": "^2.10.2",
|
||||
"json-loader": "^0.5.4",
|
||||
"jsplumb": "^2.6.8",
|
||||
"jszip": "^3.1.5",
|
||||
@ -37,7 +37,6 @@
|
||||
"react-dom": "^16.8.3",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"tapable": "^1.0.0",
|
||||
"uglifyjs-webpack-plugin": "^1.2.5",
|
||||
"uuid": "^3.2.1",
|
||||
"w3c-blob": "0.0.1"
|
||||
},
|
||||
@ -83,6 +82,8 @@
|
||||
"ts-loader": "^4.4.1",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.2",
|
||||
"uglify-es": "^3.3.9",
|
||||
"uglifyjs-webpack-plugin": "^1.3.0",
|
||||
"url-loader": "^1.0.1",
|
||||
"watchpack": "^1.6.0",
|
||||
"webpack": "^4.12.0",
|
||||
@ -113,5 +114,5 @@
|
||||
"watch": "webpack --watch --mode production",
|
||||
"watch:dev": "webpack --watch --mode development"
|
||||
},
|
||||
"version": "0.40.2"
|
||||
"version": "0.45.0"
|
||||
}
|
||||
|
@ -1218,14 +1218,14 @@ function initAugmentations() {
|
||||
"quantum supercomputer, allowing you to access and use its incredible " +
|
||||
"computing power.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's hacking skill by 50%.<br>" +
|
||||
"Increases the player's hacking speed by 50%.<br>" +
|
||||
"Increases the player's hacking skill by 75%.<br>" +
|
||||
"Increases the player's hacking speed by 100%.<br>" +
|
||||
"Increases the player's chance of successfully performing a hack by 150%.<br>" +
|
||||
"Increases the amount of money the player gains from hacking by 500%.",
|
||||
hacking_speed_mult: 1.5,
|
||||
"Increases the amount of money the player gains from hacking by 300%.",
|
||||
hacking_mult: 1.75,
|
||||
hacking_speed_mult: 2,
|
||||
hacking_chance_mult: 2.5,
|
||||
hacking_money_mult: 6,
|
||||
hacking_mult: 1.5,
|
||||
hacking_money_mult: 4,
|
||||
});
|
||||
QLink.addToFactions(["Illuminati"]);
|
||||
if (augmentationExists(AugmentationNames.QLink)) {
|
||||
@ -2069,7 +2069,7 @@ function installAugmentations(cbScript=null) {
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, []); //No args
|
||||
runningScriptObj.threads = 1; //Only 1 thread
|
||||
home.runningScripts.push(runningScriptObj);
|
||||
home.runScript(runningScriptObj, Player);
|
||||
addWorkerScript(runningScriptObj, home);
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,10 @@ export function initBitNodes() {
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Do Androids Dream?", "COMING SOON");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||
"powering the Hacknet, ");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||
@ -245,9 +248,9 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
}
|
||||
|
||||
switch (p.bitNodeN) {
|
||||
case 1: //Source Genesis (every multiplier is 1)
|
||||
case 1: // Source Genesis (every multiplier is 1)
|
||||
break;
|
||||
case 2: //Rise of the Underworld
|
||||
case 2: // Rise of the Underworld
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.8;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.2;
|
||||
@ -257,7 +260,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
break;
|
||||
case 3: //Corporatocracy
|
||||
case 3: // Corporatocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.RepToDonateToFaction = 0.5;
|
||||
BitNodeMultipliers.AugmentationRepCost = 3;
|
||||
@ -272,7 +275,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||
break;
|
||||
case 4: //The Singularity
|
||||
case 4: // The Singularity
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
||||
@ -286,7 +289,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||
break;
|
||||
case 5: //Artificial intelligence
|
||||
case 5: // Artificial intelligence
|
||||
BitNodeMultipliers.ServerMaxMoney = 2;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@ -299,7 +302,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
break;
|
||||
case 6: //Bladeburner
|
||||
case 6: // Bladeburner
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.4;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@ -314,7 +317,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.25;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 7: //Bladeburner 2079
|
||||
case 7: // Bladeburner 2079
|
||||
BitNodeMultipliers.BladeburnerRank = 0.6;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 3;
|
||||
@ -334,7 +337,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 8: //Ghost of Wall Street
|
||||
case 8: // Ghost of Wall Street
|
||||
BitNodeMultipliers.ScriptHackMoney = 0;
|
||||
BitNodeMultipliers.ManualHackMoney = 0;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0;
|
||||
@ -345,6 +348,19 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 3;
|
||||
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.1;
|
||||
BitNodeMultipliers.HackExpGain = 0.1;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
||||
|
@ -120,7 +120,8 @@ interface IBitNodeMultipliers {
|
||||
HackingLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences how much money each Hacknet node can generate.
|
||||
* Influences how much money is produced by Hacknet Nodes.
|
||||
* Influeces the hash rate of Hacknet Servers (unlocked in BitNode-9)
|
||||
*/
|
||||
HacknetNodeMoney: number;
|
||||
|
||||
|
@ -31,6 +31,8 @@ import { getTimestamp } from "../utils/helpers/getTi
|
||||
import { removeElement } from "../utils/uiHelpers/removeElement";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
const stealthIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 166 132" style="fill:#adff2f;"><g><path d="M132.658-0.18l-24.321,24.321c-7.915-2.71-16.342-4.392-25.087-4.392c-45.84,0-83,46-83,46 s14.1,17.44,35.635,30.844L12.32,120.158l12.021,12.021L144.68,11.841L132.658-0.18z M52.033,80.445 c-2.104-4.458-3.283-9.438-3.283-14.695c0-19.054,15.446-34.5,34.5-34.5c5.258,0,10.237,1.179,14.695,3.284L52.033,80.445z"/><path d="M134.865,37.656l-18.482,18.482c0.884,3.052,1.367,6.275,1.367,9.612c0,19.055-15.446,34.5-34.5,34.5 c-3.337,0-6.56-0.483-9.611-1.367l-10.124,10.124c6.326,1.725,12.934,2.743,19.735,2.743c45.84,0,83-46,83-46 S153.987,50.575,134.865,37.656z"/></g></svg> `
|
||||
const killIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="-22 0 511 511.99561" style="fill:#adff2f;"><path d="m.496094 466.242188 39.902344-39.902344 45.753906 45.753906-39.898438 39.902344zm0 0"/><path d="m468.421875 89.832031-1.675781-89.832031-300.265625 300.265625 45.753906 45.753906zm0 0"/><path d="m95.210938 316.785156 16.84375 16.847656h.003906l83.65625 83.65625 22.753906-22.753906-100.503906-100.503906zm0 0"/><path d="m101.445312 365.300781-39.902343 39.902344 45.753906 45.753906 39.902344-39.902343-39.90625-39.902344zm0 0"/></svg>`
|
||||
|
||||
const CityNames = ["Aevum", "Chongqing", "Sector-12", "New Tokyo", "Ishima", "Volhaven"];
|
||||
|
||||
@ -1799,6 +1801,12 @@ Bladeburner.prototype.createContent = function() {
|
||||
DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv);
|
||||
DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv);
|
||||
|
||||
|
||||
// legend
|
||||
const legend = createElement("div")
|
||||
legend.innerHTML = `<span class="text">${stealthIcon}= This action requires stealth, ${killIcon} = This action involves retirement</span>`
|
||||
DomElems.bladeburnerDiv.appendChild(legend);
|
||||
|
||||
document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv);
|
||||
|
||||
if (this.consoleLogs.length === 0) {
|
||||
@ -2166,12 +2174,12 @@ Bladeburner.prototype.createBlackOpsContent = function() {
|
||||
return (a.reqdRank - b.reqdRank);
|
||||
});
|
||||
|
||||
for (var i = 0; i < blackops.length; ++i) {
|
||||
for (var i = blackops.length-1; i >= 0 ; --i) {
|
||||
if (this.blackops[[blackops[i].name]] == null && i !== 0 && this.blackops[[blackops[i-1].name]] == null) {continue;} // If this one nor the next are completed then this isn't unlocked yet.
|
||||
DomElems.blackops[blackops[i].name] = createElement("div", {
|
||||
class:"bladeburner-action", name:blackops[i].name
|
||||
});
|
||||
DomElems.actionsAndSkillsList.appendChild(DomElems.blackops[blackops[i].name]);
|
||||
if (this.blackops[[blackops[i].name]] == null) {break;} //Can't be found in completed blackops
|
||||
}
|
||||
}
|
||||
|
||||
@ -2504,7 +2512,8 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
|
||||
el.appendChild(createElement("pre", { //Info
|
||||
display:"inline-block",
|
||||
innerHTML:action.desc + "\n\n" +
|
||||
"Estimated success chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" +
|
||||
`Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
|
||||
|
||||
"Time Required (s): " + formatNumber(actionTime, 0) + "\n" +
|
||||
"Contracts remaining: " + Math.floor(action.count) + "\n" +
|
||||
"Successes: " + action.successes + "\n" +
|
||||
@ -2518,14 +2527,21 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
|
||||
for:autolevelCheckboxId, innerText:"Autolevel",color:"white",
|
||||
tooltip:"Automatically increase contract level when possible"
|
||||
}));
|
||||
var autolevelCheckbox = createElement("input", {
|
||||
type:"checkbox", id:autolevelCheckboxId, margin:"4px",
|
||||
checked:action.autoLevel,
|
||||
changeListener:()=>{
|
||||
action.autoLevel = autolevelCheckbox.checked;
|
||||
}
|
||||
|
||||
const checkboxDiv = createElement("div", { class: "bbcheckbox" });
|
||||
const checkboxInput = createElement("input", {
|
||||
type:"checkbox",
|
||||
id: autolevelCheckboxId,
|
||||
checked: action.autoLevel,
|
||||
changeListener: () => {
|
||||
action.autoLevel = checkboxInput.checked;
|
||||
},
|
||||
});
|
||||
el.appendChild(autolevelCheckbox);
|
||||
const checkmarkLabel = createElement("label", { for: autolevelCheckboxId });
|
||||
checkboxDiv.appendChild(checkboxInput);
|
||||
checkboxDiv.appendChild(checkmarkLabel);
|
||||
|
||||
el.appendChild(checkboxDiv);
|
||||
}
|
||||
|
||||
Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
|
||||
@ -2640,7 +2656,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
|
||||
el.appendChild(createElement("pre", {
|
||||
display:"inline-block",
|
||||
innerHTML:action.desc + "\n\n" +
|
||||
"Estimated success chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" +
|
||||
`Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
|
||||
"Time Required(s): " + formatNumber(actionTime, 0) + "\n" +
|
||||
"Operations remaining: " + Math.floor(action.count) + "\n" +
|
||||
"Successes: " + action.successes + "\n" +
|
||||
@ -2654,14 +2670,21 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
|
||||
for:autolevelCheckboxId, innerText:"Autolevel",color:"white",
|
||||
tooltip:"Automatically increase operation level when possible"
|
||||
}));
|
||||
var autolevelCheckbox = createElement("input", {
|
||||
type:"checkbox", id:autolevelCheckboxId, margin:"4px",
|
||||
checked:action.autoLevel,
|
||||
changeListener:()=>{
|
||||
action.autoLevel = autolevelCheckbox.checked;
|
||||
}
|
||||
|
||||
const checkboxDiv = createElement("div", { class: "bbcheckbox" });
|
||||
const checkboxInput = createElement("input", {
|
||||
type:"checkbox",
|
||||
id: autolevelCheckboxId,
|
||||
checked: action.autoLevel,
|
||||
changeListener: () => {
|
||||
action.autoLevel = checkboxInput.checked;
|
||||
},
|
||||
});
|
||||
el.appendChild(autolevelCheckbox);
|
||||
const checkmarkLabel = createElement("label", { for: autolevelCheckboxId });
|
||||
checkboxDiv.appendChild(checkboxInput);
|
||||
checkboxDiv.appendChild(checkmarkLabel);
|
||||
|
||||
el.appendChild(checkboxDiv);
|
||||
}
|
||||
|
||||
Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
|
||||
@ -2760,7 +2783,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
|
||||
}));
|
||||
el.appendChild(createElement("p", {
|
||||
display:"inline-block",
|
||||
innerHTML:"Estimated Success Chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" +
|
||||
innerHTML:`Estimated Success Chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
|
||||
"Time Required(s): " + formatNumber(actionTime, 0),
|
||||
}))
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
/**
|
||||
* Generic Game Constants
|
||||
*
|
||||
* Constants for specific mechanics or features will NOT be here.
|
||||
*/
|
||||
import {IMap} from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.44.1",
|
||||
Version: "0.45.1",
|
||||
|
||||
//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
|
||||
@ -17,24 +22,9 @@ export let CONSTANTS: IMap<any> = {
|
||||
/* Base costs */
|
||||
BaseCostFor1GBOfRamHome: 32000,
|
||||
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
||||
BaseCostFor1GBOfRamHacknetNode: 30000,
|
||||
|
||||
TravelCost: 200e3,
|
||||
|
||||
BaseCostForHacknetNode: 1000,
|
||||
BaseCostForHacknetNodeCore: 500000,
|
||||
|
||||
/* Hacknet Node constants */
|
||||
HacknetNodeMoneyGainPerLevel: 1.6,
|
||||
HacknetNodePurchaseNextMult: 1.85, //Multiplier when purchasing an additional hacknet node
|
||||
HacknetNodeUpgradeLevelMult: 1.04, //Multiplier for cost when upgrading level
|
||||
HacknetNodeUpgradeRamMult: 1.28, //Multiplier for cost when upgrading RAM
|
||||
HacknetNodeUpgradeCoreMult: 1.48, //Multiplier for cost when buying another core
|
||||
|
||||
HacknetNodeMaxLevel: 200,
|
||||
HacknetNodeMaxRam: 64,
|
||||
HacknetNodeMaxCores: 16,
|
||||
|
||||
/* Faction and Company favor */
|
||||
BaseFavorToDonate: 150,
|
||||
DonateMoneyToRepDivisor: 1e6,
|
||||
@ -126,6 +116,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
|
||||
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
|
||||
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
|
||||
InfiltrationExpPow: 0.7,
|
||||
|
||||
//Stock market constants
|
||||
WSEAccountCost: 200e6,
|
||||
@ -282,6 +273,12 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.45.1
|
||||
* Added two new Corporation Researches
|
||||
* General UI improvements (by hydroflame and koriar)
|
||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
||||
|
||||
v0.45.0
|
||||
* Corporation changes:
|
||||
** Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
||||
@ -301,13 +298,12 @@ export let CONSTANTS: IMap<any> = {
|
||||
** Reduced the amount of money you gain from private investors
|
||||
** Training employees is now 3x more effective
|
||||
** Bug Fix: An industry's products are now properly separated between different cities
|
||||
|
||||
* The QLink Augemntation is now significantly stronger (by hydroflame)
|
||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
||||
* Modified the multipliers of BitNode-3 and BitNode-8 to make them slightly harder
|
||||
* After installing Augmentations, Duplicate Sleeves will now default to Synchronize if their Shock is 0
|
||||
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
|
||||
* Bug Fix: growthAnalyze() function now properly accounts for BitNode multipliers
|
||||
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
|
||||
`
|
||||
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ import { CorporationRouting } from "./ui/Routing";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
|
||||
|
||||
/* Constants */
|
||||
export const INITIALSHARES = 1e9; //Total number of shares you have at your company
|
||||
export const SHARESPERPRICEUPDATE = 1e6; //When selling large number of shares, price is dynamically updated for every batch of this amount
|
||||
@ -490,11 +489,11 @@ 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
|
||||
// Once you start making revenue, the player should no longer be
|
||||
// considered new, and therefore no longer needs the 'tutorial' UI elements
|
||||
if (this.lastCycleRevenue.gt(0)) {this.newInd = false;}
|
||||
|
||||
//Process offices (and the employees in them)
|
||||
// Process offices (and the employees in them)
|
||||
var employeeSalary = 0;
|
||||
for (var officeLoc in this.offices) {
|
||||
if (this.offices[officeLoc] instanceof OfficeSpace) {
|
||||
@ -503,15 +502,15 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
|
||||
}
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(employeeSalary);
|
||||
|
||||
//Process change in demand/competition of materials/products
|
||||
// Process change in demand/competition of materials/products
|
||||
this.processMaterialMarket(marketCycles);
|
||||
this.processProductMarket(marketCycles);
|
||||
|
||||
//Process loss of popularity
|
||||
// Process loss of popularity
|
||||
this.popularity -= (marketCycles * .0001);
|
||||
this.popularity = Math.max(0, this.popularity);
|
||||
|
||||
//Process Dreamsense gains
|
||||
// Process Dreamsense gains
|
||||
var popularityGain = company.getDreamSenseGain(), awarenessGain = popularityGain * 4;
|
||||
if (popularityGain > 0) {
|
||||
this.popularity += (popularityGain * marketCycles);
|
||||
@ -521,19 +520,19 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Process production, purchase, and import/export of materials
|
||||
// Process production, purchase, and import/export of materials
|
||||
var res = this.processMaterials(marketCycles, company);
|
||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||
|
||||
//Process creation, production & sale of products
|
||||
// Process creation, production & sale of products
|
||||
res = this.processProducts(marketCycles, company);
|
||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||
|
||||
}
|
||||
|
||||
//Process change in demand and competition for this industry's materials
|
||||
// Process change in demand and competition for this industry's materials
|
||||
Industry.prototype.processMaterialMarket = function(marketCycles=1) {
|
||||
//References to prodMats and reqMats
|
||||
var reqMats = this.reqMats, prodMats = this.prodMats;
|
||||
@ -1334,10 +1333,8 @@ Industry.prototype.createResearchBox = function() {
|
||||
researchTreeBox = null;
|
||||
}
|
||||
|
||||
this.updateResearchTree();
|
||||
const researchTree = IndustryResearchTrees[this.type];
|
||||
|
||||
|
||||
// Create the popup first, so that the tree diagram can be added to it
|
||||
// This is handled by Treant
|
||||
researchTreeBox = createPopup(boxId, [], { backgroundColor: "black" });
|
||||
@ -1387,6 +1384,10 @@ Industry.prototype.createResearchBox = function() {
|
||||
researchTree.research(allResearch[i]);
|
||||
this.researched[allResearch[i]] = true;
|
||||
|
||||
dialogBoxCreate(`Researched ${allResearch[i]}. It may take a market cycle ` +
|
||||
`(~${SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`);
|
||||
|
||||
return this.createResearchBox();
|
||||
} else {
|
||||
dialogBoxCreate(`You do not have enough Scientific Research for ${research.name}`);
|
||||
@ -1647,6 +1648,14 @@ OfficeSpace.prototype.atCapacity = function() {
|
||||
OfficeSpace.prototype.process = function(marketCycles=1, parentRefs) {
|
||||
var corporation = parentRefs.corporation, industry = parentRefs.industry;
|
||||
|
||||
// HRBuddy AutoRecruitment and training
|
||||
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
|
||||
const emp = this.hireRandomEmployee();
|
||||
if (industry.hasResearch("HRBuddy-Training")) {
|
||||
emp.pos = EmployeePositions.Training;
|
||||
}
|
||||
}
|
||||
|
||||
// Process Office properties
|
||||
this.maxEne = 100;
|
||||
this.maxHap = 100;
|
||||
@ -1828,8 +1837,7 @@ OfficeSpace.prototype.hireEmployee = function(employee, parentRefs) {
|
||||
yesNoTxtInpBoxCreate("Give your employee a nickname!");
|
||||
}
|
||||
|
||||
OfficeSpace.prototype.hireRandomEmployee = function(parentRefs) {
|
||||
var company = parentRefs.corporation, division = parentRefs.industry;
|
||||
OfficeSpace.prototype.hireRandomEmployee = function() {
|
||||
if (this.atCapacity()) { return; }
|
||||
if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) {return;}
|
||||
|
||||
@ -1853,13 +1861,15 @@ OfficeSpace.prototype.hireRandomEmployee = function(parentRefs) {
|
||||
|
||||
var name = generateRandomString(7);
|
||||
|
||||
for (var i = 0; i < this.employees.length; ++i) {
|
||||
for (let i = 0; i < this.employees.length; ++i) {
|
||||
if (this.employees[i].name === name) {
|
||||
return this.hireRandomEmployee(parentRefs);
|
||||
return this.hireRandomEmployee();
|
||||
}
|
||||
}
|
||||
emp.name = name;
|
||||
this.employees.push(emp);
|
||||
|
||||
return emp;
|
||||
}
|
||||
|
||||
//Finds the first unassigned employee and assigns its to the specified job
|
||||
@ -2346,8 +2356,6 @@ Corporation.prototype.rerender = function() {
|
||||
}
|
||||
if (!routing.isOn(Page.Corporation)) { return; }
|
||||
|
||||
console.log("Re-rendering...");
|
||||
|
||||
ReactDOM.render(<CorporationRoot
|
||||
corp={this}
|
||||
routing={corpRouting}
|
||||
|
@ -27,6 +27,8 @@ function createBaseResearchTreeNodes(): Node {
|
||||
const dronesAssembly: Node = makeNode("Drones - Assembly");
|
||||
const dronesTransport: Node = makeNode("Drones - Transport");
|
||||
const goJuice: Node = makeNode("Go-Juice");
|
||||
const hrRecruitment: Node = makeNode("HRBuddy-Recruitment");
|
||||
const hrTraining: Node = makeNode("HRBuddy-Training");
|
||||
const joywire: Node = makeNode("JoyWire");
|
||||
const marketta1: Node = makeNode("Market-TA.I");
|
||||
const marketta2: Node = makeNode("Market-TA.II");
|
||||
@ -40,6 +42,8 @@ function createBaseResearchTreeNodes(): Node {
|
||||
drones.addChild(dronesAssembly);
|
||||
drones.addChild(dronesTransport);
|
||||
|
||||
hrRecruitment.addChild(hrTraining);
|
||||
|
||||
marketta1.addChild(marketta2);
|
||||
|
||||
overclock.addChild(stimu);
|
||||
@ -49,6 +53,7 @@ function createBaseResearchTreeNodes(): Node {
|
||||
rootNode.addChild(autoDrugs);
|
||||
rootNode.addChild(bulkPurchasing);
|
||||
rootNode.addChild(drones);
|
||||
rootNode.addChild(hrRecruitment);
|
||||
rootNode.addChild(joywire);
|
||||
rootNode.addChild(marketta1);
|
||||
rootNode.addChild(overclock);
|
||||
|
@ -77,6 +77,21 @@ export const researchMetadata: IConstructorParams[] = [
|
||||
"production by 10%.",
|
||||
sciResearchMult: 1.1,
|
||||
},
|
||||
{
|
||||
name: "HRBuddy-Recruitment",
|
||||
cost: 15e3,
|
||||
desc: "Use automated software to handle the hiring of employees. With this " +
|
||||
"research, each office will automatically hire one employee per " +
|
||||
"market cycle if there is available space."
|
||||
|
||||
},
|
||||
{
|
||||
name: "HRBuddy-Training",
|
||||
cost: 20e3,
|
||||
desc: "Use automated software to handle the training of employees. With this " +
|
||||
"research, each employee hired with HRBuddy-Recruitment will automatically " +
|
||||
"be assigned to 'Training', rather than being unassigned."
|
||||
},
|
||||
{
|
||||
name: "JoyWire",
|
||||
cost: 20e3,
|
||||
|
@ -300,7 +300,7 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
<br />
|
||||
|
||||
<p>Avg Employee Morale: {numeralWrapper.format(avgMorale, "0.000")}</p>
|
||||
<p>Avg Happiness Morale: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Employee Happiness: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Energy Morale: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
||||
{
|
||||
@ -554,7 +554,7 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
}
|
||||
const autohireEmployeeButtonOnClick = () => {
|
||||
if (office.atCapacity()) { return; }
|
||||
office.hireRandomEmployee({ corporation: corp, industry: division });
|
||||
office.hireRandomEmployee();
|
||||
this.corp().rerender();
|
||||
}
|
||||
|
||||
|
740
src/DevMenu.js
740
src/DevMenu.js
@ -1,740 +0,0 @@
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { CodingContractTypes } from "./CodingContracts";
|
||||
import { generateContract,
|
||||
generateRandomContract,
|
||||
generateRandomContractOnHome } from "./CodingContractGenerator";
|
||||
import { Companies } from "./Company/Companies";
|
||||
import { Company } from "./Company/Company";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { AllServers } from "./Server/AllServers";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { StockMarket,
|
||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
const devMenuContainerId = "dev-menu-container";
|
||||
|
||||
export function createDevMenu() {
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
throw new Error("Cannot create Dev Menu because you are not in a dev build");
|
||||
}
|
||||
|
||||
const devMenuText = createElement("h1", {
|
||||
display: "block",
|
||||
innerText: "Development Menu - Only meant to be used for testing/debugging",
|
||||
});
|
||||
|
||||
// Generic
|
||||
const genericHeader = createElement("h2", {
|
||||
display: "block",
|
||||
innerText: "Generic"
|
||||
});
|
||||
|
||||
const addMoney = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.gainMoney(1e15);
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Add $1000t",
|
||||
});
|
||||
|
||||
const addMoney2 = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.gainMoney(1e12);
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Add $1t",
|
||||
})
|
||||
|
||||
const addRam = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.getHomeComputer().maxRam *= 2;
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Double Home Computer RAM",
|
||||
});
|
||||
|
||||
const triggerBitflume = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
hackWorldDaemon(Player.bitNodeN, true);
|
||||
},
|
||||
innerText: "Trigger BitFlume",
|
||||
});
|
||||
|
||||
const destroyCurrentBitnode = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
hackWorldDaemon(Player.bitNodeN);
|
||||
},
|
||||
innerText: "Destroy Current BitNode",
|
||||
tooltip: "Will grant Source-File for the BitNode",
|
||||
});
|
||||
|
||||
// Experience / stats
|
||||
const statsHeader = createElement("h2", {
|
||||
display: "block",
|
||||
innerText: "Experience/Stats"
|
||||
});
|
||||
|
||||
const statsHackingExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- hacking exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsHackingExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsHackingExpInput.value);
|
||||
Player.gainHackingExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Hacking Exp",
|
||||
});
|
||||
|
||||
const statsStrengthExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- strength exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsStrengthExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsStrengthExpInput.value);
|
||||
Player.gainStrengthExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Strength Exp",
|
||||
});
|
||||
|
||||
const statsDefenseExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- defense exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsDefenseExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsDefenseExpInput.value);
|
||||
Player.gainDefenseExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Defense Exp",
|
||||
});
|
||||
|
||||
const statsDexterityExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- dexterity exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsDexterityExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsDexterityExpInput.value);
|
||||
Player.gainDexterityExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Dexterity Exp",
|
||||
});
|
||||
|
||||
const statsAgilityExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- agility exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsAgilityExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsAgilityExpInput.value);
|
||||
Player.gainAgilityExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Agility Exp",
|
||||
});
|
||||
|
||||
const statsCharismaExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- charisma exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsCharismaExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsCharismaExpInput.value);
|
||||
Player.gainCharismaExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Charisma Exp",
|
||||
});
|
||||
|
||||
const statsIntelligenceExpInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "+/- intelligence exp",
|
||||
type: "number",
|
||||
});
|
||||
const statsIntelligenceExpButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const exp = parseInt(statsIntelligenceExpInput.value);
|
||||
Player.gainIntelligenceExp(exp);
|
||||
Player.updateSkillLevels();
|
||||
},
|
||||
innerText: "Add Intelligence Exp",
|
||||
});
|
||||
|
||||
const statsEnableIntelligenceButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.intelligence = 1;
|
||||
},
|
||||
innerText: "Enable Intelligence"
|
||||
});
|
||||
|
||||
const statsDisableIntelligenceButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.intelligence = 0;
|
||||
},
|
||||
innerText: "Disable Intelligence"
|
||||
});
|
||||
|
||||
// Factions
|
||||
const factionsHeader = createElement("h2", {innerText: "Factions"});
|
||||
|
||||
const factionsDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (const i in Factions) {
|
||||
factionsDropdown.options[factionsDropdown.options.length] = new Option(Factions[i].name, Factions[i].name);
|
||||
}
|
||||
|
||||
const factionsAddButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const facName = factionsDropdown.options[factionsDropdown.selectedIndex].value;
|
||||
Player.receiveInvite(facName);
|
||||
},
|
||||
innerText: "Receive Invite to Faction",
|
||||
});
|
||||
|
||||
const factionsReputationInput = createElement("input", {
|
||||
placeholder: "Rep to add to faction",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const factionsReputationButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Add rep to faction",
|
||||
clickListener: () => {
|
||||
const facName = getSelectText(factionsDropdown);
|
||||
const fac = Factions[facName];
|
||||
const rep = parseFloat(factionsReputationInput.value);
|
||||
if (fac != null && !isNaN(rep)) {
|
||||
fac.playerReputation += rep;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Augmentations
|
||||
const augmentationsHeader = createElement("h2", {innerText: "Augmentations"});
|
||||
|
||||
const augmentationsDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (const i in AugmentationNames) {
|
||||
const augName = AugmentationNames[i];
|
||||
augmentationsDropdown.options[augmentationsDropdown.options.length] = new Option(augName, augName);
|
||||
}
|
||||
|
||||
const augmentationsQueueButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
Player.queueAugmentation(augmentationsDropdown.options[augmentationsDropdown.selectedIndex].value);
|
||||
},
|
||||
innerText: "Queue Augmentation",
|
||||
});
|
||||
|
||||
const giveAllAugmentationsButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
for (const i in AugmentationNames) {
|
||||
const augName = AugmentationNames[i];
|
||||
Player.queueAugmentation(augName);
|
||||
}
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Queue All Augmentations",
|
||||
});
|
||||
|
||||
// Source Files
|
||||
const sourceFilesHeader = createElement("h2", { innerText: "Source-Files" });
|
||||
|
||||
const removeSourceFileDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (let i = 0; i < 24; ++i) {
|
||||
removeSourceFileDropdown.add(createOptionElement(String(i)));
|
||||
}
|
||||
|
||||
const removeSourceFileButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const numToRemove = parseInt(getSelectText(removeSourceFileDropdown));
|
||||
for (let i = 0; i < Player.sourceFiles.length; ++i) {
|
||||
if (Player.sourceFiles[i].n === numToRemove) {
|
||||
Player.sourceFiles.splice(i, 1);
|
||||
hackWorldDaemon(Player.bitNodeN, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
innerText: "Remove Source File and Trigger Bitflume",
|
||||
});
|
||||
|
||||
// Programs
|
||||
const programsHeader = createElement("h2", {innerText: "Programs"});
|
||||
|
||||
const programsAddDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (const i in Programs) {
|
||||
const progName = Programs[i].name;
|
||||
programsAddDropdown.options[programsAddDropdown.options.length] = new Option(progName, progName);
|
||||
}
|
||||
|
||||
const programsAddButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const program = programsAddDropdown.options[programsAddDropdown.selectedIndex].value;
|
||||
if(!Player.hasProgram(program)) {
|
||||
Player.getHomeComputer().programs.push(program);
|
||||
}
|
||||
},
|
||||
innerText: "Add Program",
|
||||
})
|
||||
|
||||
// Servers
|
||||
const serversHeader = createElement("h2", {innerText: "Servers"});
|
||||
|
||||
const serversOpenAll = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
for (const i in AllServers) {
|
||||
AllServers[i].hasAdminRights = true;
|
||||
AllServers[i].sshPortOpen = true;
|
||||
AllServers[i].ftpPortOpen = true;
|
||||
AllServers[i].smtpPortOpen = true;
|
||||
AllServers[i].httpPortOpen = true;
|
||||
AllServers[i].sqlPortOpen = true;
|
||||
AllServers[i].openPortCount = 5;
|
||||
}
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Get Admin Rights to all servers",
|
||||
});
|
||||
|
||||
const serversMinSecurityAll = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
for (const i in AllServers) {
|
||||
AllServers[i].hackDifficulty = AllServers[i].minDifficulty;
|
||||
}
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Set all servers to min security",
|
||||
});
|
||||
|
||||
const serversMaxMoneyAll = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
for (const i in AllServers) {
|
||||
AllServers[i].moneyAvailable = AllServers[i].moneyMax;
|
||||
}
|
||||
},
|
||||
display: "block",
|
||||
innerText: "Set all servers to max money",
|
||||
});
|
||||
|
||||
const serversConnectToDropdown = createElement("select", {class: "dropdown"});
|
||||
for (const i in AllServers) {
|
||||
const hn = AllServers[i].hostname;
|
||||
serversConnectToDropdown.options[serversConnectToDropdown.options.length] = new Option(hn, hn);
|
||||
}
|
||||
|
||||
const serversConnectToButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const host = serversConnectToDropdown.options[serversConnectToDropdown.selectedIndex].value;
|
||||
Terminal.connectToServer(host);
|
||||
},
|
||||
innerText: "Connect to server",
|
||||
});
|
||||
|
||||
// Companies
|
||||
const companiesHeader = createElement("h2", { innerText: "Companies" });
|
||||
|
||||
const companiesDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (const c in Companies) {
|
||||
companiesDropdown.add(createOptionElement(Companies[c].name));
|
||||
}
|
||||
|
||||
const companyReputationInput = createElement("input", {
|
||||
margin: "5px",
|
||||
placeholder: "Rep to add to company",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const companyReputationButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Add rep to company",
|
||||
clickListener: () => {
|
||||
const compName = getSelectText(companiesDropdown);
|
||||
const company = Companies[compName];
|
||||
const rep = parseFloat(companyReputationInput.value);
|
||||
if (company != null && !isNaN(rep)) {
|
||||
company.playerReputation += rep;
|
||||
} else {
|
||||
console.warn(`Invalid input for Dev Menu Company Rep. Company Name: ${compName}. Rep: ${rep}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Bladeburner
|
||||
const bladeburnerHeader = createElement("h2", {innerText: "Bladeburner"});
|
||||
|
||||
const bladeburnerGainRankInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "Rank to gain (or negative to lose rank)",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const bladeburnerGainRankButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
try {
|
||||
const rank = parseInt(bladeburnerGainRankInput.value);
|
||||
Player.bladeburner.changeRank(rank);
|
||||
} catch(e) {
|
||||
exceptionAlert(`Failed to change Bladeburner Rank in dev menu: ${e}`);
|
||||
}
|
||||
},
|
||||
innerText: "Gain Bladeburner Rank",
|
||||
});
|
||||
|
||||
const bladeburnerStoredCyclesInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "# Cycles to Add",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const bladeburnerStoredCyclesButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
try {
|
||||
const cycles = parseInt(bladeburnerStoredCyclesInput.value);
|
||||
Player.bladeburner.storedCycles += cycles;
|
||||
} catch(e) {
|
||||
exceptionAlert(`Failed to add cycles to Bladeburner in dev menu: ${e}`);
|
||||
}
|
||||
},
|
||||
innerText: "Add Cycles to Bladeburner mechanic",
|
||||
});
|
||||
|
||||
// Gang
|
||||
const gangHeader = createElement("h2", {innerText: "Gang"});
|
||||
|
||||
const gangStoredCyclesInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "# Cycles to add",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const gangAddStoredCycles = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
try {
|
||||
const cycles = parseInt(gangStoredCyclesInput.value);
|
||||
Player.gang.storedCycles += cycles;
|
||||
} catch(e) {
|
||||
exceptionAlert(`Failed to add stored cycles to gang mechanic: ${e}`);
|
||||
}
|
||||
},
|
||||
innerText: "Add cycles to Gang mechanic",
|
||||
});
|
||||
|
||||
// Corporation
|
||||
const corpHeader = createElement("h2", { innerText: "Corporation" });
|
||||
|
||||
const corpStoredCyclesInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "# Cycles to Add",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const corpStoredCyclesButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
try {
|
||||
const cycles = parseInt(bladeburnerStoredCyclesInput.value);
|
||||
Player.corporation.storeCycles(cycles);
|
||||
} catch(e) {
|
||||
exceptionAlert(`Failed to add cycles to Bladeburner in dev menu: ${e}`);
|
||||
}
|
||||
},
|
||||
innerText: "Add Cycles to Corporation mechanic",
|
||||
});
|
||||
|
||||
// Coding Contracts
|
||||
const contractsHeader = createElement("h2", {innerText: "Coding Contracts"});
|
||||
|
||||
const generateRandomContractBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
generateRandomContract();
|
||||
},
|
||||
innerText: "Generate Random Contract",
|
||||
});
|
||||
|
||||
const generateRandomContractOnHomeBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
generateRandomContractOnHome();
|
||||
},
|
||||
innerText: "Generate Random Contract on Home Comp",
|
||||
});
|
||||
|
||||
const generateContractWithTypeSelector = createElement("select", { margin: "5px" });
|
||||
const contractTypes = Object.keys(CodingContractTypes);
|
||||
for (let i = 0; i < contractTypes.length; ++i) {
|
||||
generateContractWithTypeSelector.add(createOptionElement(contractTypes[i]));
|
||||
}
|
||||
|
||||
const generateContractWithTypeBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
generateContract({
|
||||
problemType: getSelectText(generateContractWithTypeSelector),
|
||||
server: "home",
|
||||
});
|
||||
},
|
||||
innerText: "Generate Specified Contract Type on Home Comp",
|
||||
});
|
||||
|
||||
// Stock Market
|
||||
const stockmarketHeader = createElement("h2", {innerText: "Stock Market"});
|
||||
|
||||
const stockInput = createElement("input", {
|
||||
class: "text-input",
|
||||
display: "block",
|
||||
placeholder: "Stock symbol(s), or 'all'",
|
||||
});
|
||||
|
||||
function processStocks(cb) {
|
||||
const input = stockInput.value.toString().replace(/\s/g, '');
|
||||
|
||||
// Empty input, or "all", will process all stocks
|
||||
if (input === "" || input.toLowerCase() === "all") {
|
||||
for (const name in StockMarket) {
|
||||
if (StockMarket.hasOwnProperty(name)) {
|
||||
const stock = StockMarket[name];
|
||||
if (stock instanceof Stock) {
|
||||
cb(stock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const stockSymbols = input.split(",");
|
||||
for (let i = 0; i < stockSymbols.length; ++i) {
|
||||
const stock = SymbolToStockMap[stockSymbols];
|
||||
if (stock instanceof Stock) {
|
||||
cb(stock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stockPriceChangeInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "Price to change stock(s) to",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const stockPriceChangeBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const price = parseInt(stockPriceChangeInput.value);
|
||||
if (isNaN(price)) { return; }
|
||||
|
||||
processStocks((stock) => {
|
||||
stock.price = price;
|
||||
});
|
||||
dialogBoxCreate(`Stock Prices changed to ${price}`);
|
||||
},
|
||||
innerText: "Change Stock Price(s)",
|
||||
});
|
||||
|
||||
const stockViewPriceCapBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
let text = "";
|
||||
processStocks((stock) => {
|
||||
text += `${stock.symbol}: ${numeralWrapper.format(stock.cap, '$0.000a')}<br>`;
|
||||
});
|
||||
dialogBoxCreate(text);
|
||||
},
|
||||
innerText: "View Stock Price Caps",
|
||||
});
|
||||
|
||||
// Sleeves
|
||||
const sleevesHeader = createElement("h2", { innerText: "Sleeves" });
|
||||
|
||||
const sleevesRemoveAllShockRecovery = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "block",
|
||||
innerText: "Set Shock Recovery of All Sleeves to 0",
|
||||
clickListener: () => {
|
||||
for (let i = 0; i < Player.sleeves.length; ++i) {
|
||||
Player.sleeves[i].shock = 100;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add everything to container, then append to main menu
|
||||
const devMenuContainer = createElement("div", {
|
||||
class: "generic-menupage-container",
|
||||
id: devMenuContainerId,
|
||||
});
|
||||
|
||||
devMenuContainer.appendChild(devMenuText);
|
||||
devMenuContainer.appendChild(genericHeader);
|
||||
devMenuContainer.appendChild(addMoney);
|
||||
devMenuContainer.appendChild(addMoney2);
|
||||
devMenuContainer.appendChild(addRam);
|
||||
devMenuContainer.appendChild(triggerBitflume);
|
||||
devMenuContainer.appendChild(destroyCurrentBitnode);
|
||||
devMenuContainer.appendChild(statsHeader);
|
||||
devMenuContainer.appendChild(statsHackingExpInput);
|
||||
devMenuContainer.appendChild(statsHackingExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsStrengthExpInput);
|
||||
devMenuContainer.appendChild(statsStrengthExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsDefenseExpInput);
|
||||
devMenuContainer.appendChild(statsDefenseExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsDexterityExpInput);
|
||||
devMenuContainer.appendChild(statsDexterityExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsAgilityExpInput);
|
||||
devMenuContainer.appendChild(statsAgilityExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsCharismaExpInput);
|
||||
devMenuContainer.appendChild(statsCharismaExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsIntelligenceExpInput);
|
||||
devMenuContainer.appendChild(statsIntelligenceExpButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(statsEnableIntelligenceButton);
|
||||
devMenuContainer.appendChild(statsDisableIntelligenceButton);
|
||||
devMenuContainer.appendChild(factionsHeader);
|
||||
devMenuContainer.appendChild(factionsDropdown);
|
||||
devMenuContainer.appendChild(factionsAddButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(factionsReputationInput);
|
||||
devMenuContainer.appendChild(factionsReputationButton);
|
||||
devMenuContainer.appendChild(augmentationsHeader);
|
||||
devMenuContainer.appendChild(augmentationsDropdown);
|
||||
devMenuContainer.appendChild(augmentationsQueueButton);
|
||||
devMenuContainer.appendChild(giveAllAugmentationsButton);
|
||||
devMenuContainer.appendChild(sourceFilesHeader);
|
||||
devMenuContainer.appendChild(removeSourceFileDropdown);
|
||||
devMenuContainer.appendChild(removeSourceFileButton);
|
||||
devMenuContainer.appendChild(programsHeader);
|
||||
devMenuContainer.appendChild(programsAddDropdown);
|
||||
devMenuContainer.appendChild(programsAddButton);
|
||||
devMenuContainer.appendChild(serversHeader);
|
||||
devMenuContainer.appendChild(serversOpenAll);
|
||||
devMenuContainer.appendChild(serversMinSecurityAll);
|
||||
devMenuContainer.appendChild(serversMaxMoneyAll);
|
||||
devMenuContainer.appendChild(serversConnectToDropdown);
|
||||
devMenuContainer.appendChild(serversConnectToButton);
|
||||
devMenuContainer.appendChild(companiesHeader);
|
||||
devMenuContainer.appendChild(companiesDropdown);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(companyReputationInput);
|
||||
devMenuContainer.appendChild(companyReputationButton);
|
||||
devMenuContainer.appendChild(bladeburnerHeader);
|
||||
devMenuContainer.appendChild(bladeburnerGainRankInput);
|
||||
devMenuContainer.appendChild(bladeburnerGainRankButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(bladeburnerStoredCyclesInput);
|
||||
devMenuContainer.appendChild(bladeburnerStoredCyclesButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(gangHeader);
|
||||
devMenuContainer.appendChild(gangStoredCyclesInput);
|
||||
devMenuContainer.appendChild(gangAddStoredCycles);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(corpHeader);
|
||||
devMenuContainer.appendChild(corpStoredCyclesInput);
|
||||
devMenuContainer.appendChild(corpStoredCyclesButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(contractsHeader);
|
||||
devMenuContainer.appendChild(generateRandomContractBtn);
|
||||
devMenuContainer.appendChild(generateRandomContractOnHomeBtn);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(generateContractWithTypeSelector);
|
||||
devMenuContainer.appendChild(generateContractWithTypeBtn);
|
||||
devMenuContainer.appendChild(stockmarketHeader);
|
||||
devMenuContainer.appendChild(stockInput);
|
||||
devMenuContainer.appendChild(stockPriceChangeInput);
|
||||
devMenuContainer.appendChild(stockPriceChangeBtn);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(stockViewPriceCapBtn);
|
||||
devMenuContainer.appendChild(sleevesHeader);
|
||||
devMenuContainer.appendChild(sleevesRemoveAllShockRecovery);
|
||||
|
||||
const entireGameContainer = document.getElementById("entire-game-container");
|
||||
if (entireGameContainer == null) {
|
||||
throw new Error("Could not find entire-game-container DOM element");
|
||||
}
|
||||
entireGameContainer.appendChild(devMenuContainer);
|
||||
}
|
||||
|
||||
export function closeDevMenu() {
|
||||
removeElementById(devMenuContainerId);
|
||||
}
|
1213
src/DevMenu.jsx
Normal file
1213
src/DevMenu.jsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -199,7 +199,7 @@ function displayFactionContent(factionName) {
|
||||
innerText:"This donation will result in 0.000 reputation gain"
|
||||
});
|
||||
var donateAmountInput = createElement("input", {
|
||||
placeholder:"Donation amount",
|
||||
class: "text-input", placeholder:"Donation amount",
|
||||
inputListener:()=>{
|
||||
let amt = 0;
|
||||
if(donateAmountInput.value !== "") {
|
||||
|
@ -1784,6 +1784,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
||||
id: name + "gang-member-task",
|
||||
});
|
||||
const taskSelector = createElement("select", {
|
||||
class: "dropdown",
|
||||
id: name + "gang-member-task-selector",
|
||||
});
|
||||
|
||||
|
431
src/Hacknet/HacknetHelpers.jsx
Normal file
431
src/Hacknet/HacknetHelpers.jsx
Normal file
@ -0,0 +1,431 @@
|
||||
import { HacknetNode,
|
||||
BaseCostForHacknetNode,
|
||||
HacknetNodePurchaseNextMult,
|
||||
HacknetNodeMaxLevel,
|
||||
HacknetNodeMaxRam,
|
||||
HacknetNodeMaxCores } from "./HacknetNode";
|
||||
import { HacknetServer,
|
||||
BaseCostForHacknetServer,
|
||||
HacknetServerPurchaseMult,
|
||||
HacknetServerMaxLevel,
|
||||
HacknetServerMaxRam,
|
||||
HacknetServerMaxCores,
|
||||
HacknetServerMaxCache,
|
||||
MaxNumberHacknetServers } from "./HacknetServer";
|
||||
import { HashManager } from "./HashManager";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { generateRandomContractOnHome } from "../CodingContractGenerator";
|
||||
import { iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "../InteractiveTutorial";
|
||||
import { Player } from "../Player";
|
||||
import { AddToAllServers } from "../Server/AllServers";
|
||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
|
||||
import {getElementById} from "../../utils/uiHelpers/getElementById";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { HacknetRoot } from "./ui/Root";
|
||||
|
||||
let hacknetNodesDiv;
|
||||
function hacknetNodesInit() {
|
||||
hacknetNodesDiv = document.getElementById("hacknet-nodes-container");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit, false);
|
||||
|
||||
// Returns a boolean indicating whether the player has Hacknet Servers
|
||||
// (the upgraded form of Hacknet Nodes)
|
||||
export function hasHacknetServers() {
|
||||
return (Player.bitNodeN === 9 || SourceFileFlags[9] > 0);
|
||||
}
|
||||
|
||||
export function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* END INTERACTIVE TUTORIAL */
|
||||
|
||||
if (hasHacknetServers()) {
|
||||
const cost = getCostOfNextHacknetServer();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetServer is NaN`)
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
|
||||
// Auto generate a hostname for this Server
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const name = `hacknet-node-${numOwned}`;
|
||||
const server = new HacknetServer({
|
||||
adminRights: true,
|
||||
hostname: name,
|
||||
player: Player,
|
||||
});
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(server);
|
||||
|
||||
// Configure the HacknetServer to actually act as a Server
|
||||
AddToAllServers(server);
|
||||
const homeComputer = Player.getHomeComputer();
|
||||
homeComputer.serversOnNetwork.push(server.ip);
|
||||
server.serversOnNetwork.push(homeComputer.ip);
|
||||
|
||||
return numOwned;
|
||||
} else {
|
||||
const cost = getCostOfNextHacknetNode();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetNode is NaN`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
|
||||
// Auto generate a name for the Node
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const name = "hacknet-node-" + numOwned;
|
||||
const node = new HacknetNode(name);
|
||||
node.updateMoneyGainRate(Player);
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(node);
|
||||
|
||||
return numOwned;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasMaxNumberHacknetServers() {
|
||||
return hasHacknetServers() && Player.hacknetNodes.length >= MaxNumberHacknetServers;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetNode() {
|
||||
// Cost increases exponentially based on how many you own
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetNodePurchaseNextMult;
|
||||
|
||||
return BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetServer() {
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetServerPurchaseMult;
|
||||
|
||||
if (numOwned > MaxNumberHacknetServers) { return Infinity; }
|
||||
|
||||
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node
|
||||
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
let levelsToMax = maxLevel - nodeObj.level;
|
||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
while (min <= max) {
|
||||
var curr = (min + max) / 2 | 0;
|
||||
if (curr !== maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player)) &&
|
||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let levelsToMax;
|
||||
if (nodeObj instanceof HacknetServer) {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.maxRam));
|
||||
} else {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
|
||||
}
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//We'll just loop until we find the max
|
||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cores;
|
||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player)) &&
|
||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cache;
|
||||
if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
// Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) &&
|
||||
!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
max = curr -1 ;
|
||||
} else if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initial construction of Hacknet Nodes UI
|
||||
export function renderHacknetNodesUI() {
|
||||
if (!routing.isOn(Page.HacknetNodes)) { return; }
|
||||
|
||||
ReactDOM.render(<HacknetRoot />, hacknetNodesDiv);
|
||||
}
|
||||
|
||||
export function clearHacknetNodesUI() {
|
||||
if (hacknetNodesDiv instanceof HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(hacknetNodesDiv);
|
||||
}
|
||||
|
||||
hacknetNodesDiv.style.display = "none";
|
||||
}
|
||||
|
||||
export function processHacknetEarnings(numCycles) {
|
||||
// Determine if player has Hacknet Nodes or Hacknet Servers, then
|
||||
// call the appropriate function
|
||||
if (Player.hacknetNodes.length === 0) { return 0; }
|
||||
if (hasHacknetServers()) {
|
||||
return processAllHacknetServerEarnings();
|
||||
} else if (Player.hacknetNodes[0] instanceof HacknetNode) {
|
||||
return processAllHacknetNodeEarnings();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function processAllHacknetNodeEarnings(numCycles) {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function processSingleHacknetNodeEarnings(numCycles, nodeObj) {
|
||||
const totalEarnings = nodeObj.process(numCycles);
|
||||
Player.gainMoney(totalEarnings);
|
||||
Player.recordMoneySource(totalEarnings, "hacknetnode");
|
||||
|
||||
return totalEarnings;
|
||||
}
|
||||
|
||||
function processAllHacknetServerEarnings(numCycles) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`)
|
||||
}
|
||||
|
||||
let hashes = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
hashes += Player.hacknetNodes[i].process(numCycles);
|
||||
}
|
||||
|
||||
Player.hashManager.storeHashes(hashes);
|
||||
|
||||
return hashes;
|
||||
}
|
||||
|
||||
export function getHacknetNode(name) {
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (Player.hacknetNodes[i].name == name) {
|
||||
return Player.hacknetNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
console.error(`Player does not have a HashManager`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// HashManager handles the transaction. This just needs to actually implement
|
||||
// the effects of the upgrade
|
||||
if (Player.hashManager.upgrade(upgName)) {
|
||||
const upg = HashUpgrades[upgName];
|
||||
|
||||
switch (upgName) {
|
||||
case "Sell for Money": {
|
||||
Player.gainMoney(upg.value);
|
||||
break;
|
||||
}
|
||||
case "Sell for Corporation Funds": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
Player.corporation.funds = Player.corporation.funds.plus(upg.value);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Reduce Minimum Security": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMinimumSecurity(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Increase Maximum Money": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMaximumMoney(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Improve Studying": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Improve Gym Training": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Exchange for Corporation Research": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
for (const division of Player.corporation.divisions) {
|
||||
division.sciResearch.qty += upg.value;
|
||||
}
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Exchange for Bladeburner Rank": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
for (const division of Player.corporation.divisions) {
|
||||
division.sciResearch.qty += upg.value;
|
||||
}
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Generate Coding Contract": {
|
||||
generateRandomContractOnHome();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`)
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log("Hash Upgrade successfully purchased");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
285
src/Hacknet/HacknetNode.ts
Normal file
285
src/Hacknet/HacknetNode.ts
Normal file
@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Hacknet Node Class
|
||||
*
|
||||
* Hacknet Nodes are specialized machines that passively earn the player money over time.
|
||||
* They can be upgraded to increase their production
|
||||
*/
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Node production
|
||||
export const HacknetNodeMoneyGainPerLevel: number = 1.6; // Base production per level
|
||||
|
||||
// Constants for Hacknet Node purchase/upgrade costs
|
||||
export const BaseCostForHacknetNode: number = 1000;
|
||||
export const BaseCostFor1GBOfRamHacknetNode: number = 30e3;
|
||||
export const BaseCostForHacknetNodeCore: number = 500e3;
|
||||
export const HacknetNodePurchaseNextMult: number = 1.85; // Multiplier when purchasing an additional hacknet node
|
||||
export const HacknetNodeUpgradeLevelMult: number = 1.04; // Multiplier for cost when upgrading level
|
||||
export const HacknetNodeUpgradeRamMult: number = 1.28; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetNodeUpgradeCoreMult: number = 1.48; // Multiplier for cost when buying another core
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Nodes
|
||||
export const HacknetNodeMaxLevel: number = 200;
|
||||
export const HacknetNodeMaxRam: number = 64;
|
||||
export const HacknetNodeMaxCores: number = 16;
|
||||
|
||||
export class HacknetNode implements IHacknetNode {
|
||||
/**
|
||||
* Initiatizes a HacknetNode object from a JSON save state.
|
||||
*/
|
||||
static fromJSON(value: any): HacknetNode {
|
||||
return Generic_fromJSON(HacknetNode, value.data);
|
||||
}
|
||||
|
||||
// Node's number of cores
|
||||
cores: number = 1;
|
||||
|
||||
// Node's Level
|
||||
level: number = 1;
|
||||
|
||||
// Node's production per second
|
||||
moneyGainRatePerSecond: number = 0;
|
||||
|
||||
// Identifier for Node. Includes the full "name" (hacknet-node-N)
|
||||
name: string;
|
||||
|
||||
// How long this Node has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Node's RAM (GB)
|
||||
ram: number = 1;
|
||||
|
||||
// Total money earned by this Node
|
||||
totalMoneyGenerated: number = 0;
|
||||
|
||||
constructor(name: string="") {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's number of cores
|
||||
calculateCoreUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetNodeMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const coreBaseCost = BaseCostForHacknetNodeCore;
|
||||
const mult = HacknetNodeUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
|
||||
++currentCores;
|
||||
}
|
||||
|
||||
totalCost *= p.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's level
|
||||
calculateLevelUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetNodeMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetNodeUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return BaseCostForHacknetNode / 2 * totalMultiplier * p.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's RAM
|
||||
calculateRamUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.ram >= HacknetNodeMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.ram));
|
||||
let currentRam = this.ram;
|
||||
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBOfRamHacknetNode;
|
||||
let mult = Math.pow(HacknetNodeUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
|
||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Node in the game loop.
|
||||
// Returns the amount of money generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
let gain = this.moneyGainRatePerSecond * seconds;
|
||||
if (isNaN(gain)) {
|
||||
console.error(`Hacknet Node ${this.name} calculated earnings of NaN`);
|
||||
gain = 0;
|
||||
}
|
||||
|
||||
this.totalMoneyGenerated += gain;
|
||||
this.onlineTimeSeconds += seconds;
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
||||
// Upgrade this Node's number of cores, if possible
|
||||
// Returns a boolean indicating whether new cores were successfully bought
|
||||
purchaseCoreUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.cores >= HacknetNodeMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max Cores, calculate
|
||||
// the max possible number of upgrades and use that
|
||||
if (this.cores + sanitizedLevels > HacknetNodeMaxCores) {
|
||||
const diff = Math.max(0, HacknetNodeMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + sanitizedLevels); // Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Upgrade this Node's level, if possible
|
||||
// Returns a boolean indicating whether the level was successfully updated
|
||||
purchaseLevelUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're at max level, return false
|
||||
if (this.level >= HacknetNodeMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number of specified upgrades would exceed the max level, calculate
|
||||
// the maximum number of upgrades and use that
|
||||
if (this.level + sanitizedLevels > HacknetNodeMaxLevel) {
|
||||
var diff = Math.max(0, HacknetNodeMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.level = Math.round(this.level + sanitizedLevels); // Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Upgrade this Node's RAM, if possible
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
purchaseRamUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.ram >= HacknetNodeMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number of specified upgrades would exceed the max RAM, calculate the
|
||||
// max possible number of upgrades and use that
|
||||
if (this.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
|
||||
var diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / this.ram)));
|
||||
return this.purchaseRamUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
this.ram *= 2; // Ram is always doubled
|
||||
}
|
||||
this.ram = Math.round(this.ram); // Handle any floating point precision issues
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
|
||||
updateMoneyGainRate(p: IPlayer): void {
|
||||
//How much extra $/s is gained per level
|
||||
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
|
||||
|
||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||
Math.pow(1.035, this.ram - 1) *
|
||||
((this.cores + 5) / 6) *
|
||||
p.hacknet_node_money_mult *
|
||||
BitNodeMultipliers.HacknetNodeMoney;
|
||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetNode", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetNode = HacknetNode;
|
348
src/Hacknet/HacknetServer.ts
Normal file
348
src/Hacknet/HacknetServer.ts
Normal file
@ -0,0 +1,348 @@
|
||||
/**
|
||||
* Hacknet Servers - Reworked Hacknet Node mechanic for BitNode-9
|
||||
*/
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { IHacknetNode } from "../Hacknet/IHacknetNode";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Server stats/production
|
||||
export const HacknetServerHashesPerLevel: number = 0.001;
|
||||
|
||||
// Constants for Hacknet Server purchase/upgrade costs
|
||||
export const BaseCostForHacknetServer: number = 10e3;
|
||||
export const BaseCostFor1GBHacknetServerRam: number = 200e3;
|
||||
export const BaseCostForHacknetServerCore: number = 1e6;
|
||||
export const BaseCostForHacknetServerCache: number = 10e6;
|
||||
|
||||
export const HacknetServerPurchaseMult: number = 3.2; // Multiplier for puchasing an additional Hacknet Server
|
||||
export const HacknetServerUpgradeLevelMult: number = 1.1; // Multiplier for cost when upgrading level
|
||||
export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
|
||||
export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
|
||||
|
||||
export const MaxNumberHacknetServers: number = 25; // Max number of Hacknet Servers you can own
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Server
|
||||
export const HacknetServerMaxLevel: number = 300;
|
||||
export const HacknetServerMaxRam: number = 8192;
|
||||
export const HacknetServerMaxCores: number = 128;
|
||||
export const HacknetServerMaxCache: number = 15; // Max cache level. So max capacity is 2 ^ 12
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
organizationName?: string;
|
||||
player?: IPlayer;
|
||||
}
|
||||
|
||||
export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
// Initializes a HacknetServer Object from a JSON save state
|
||||
static fromJSON(value: any): HacknetServer {
|
||||
return Generic_fromJSON(HacknetServer, value.data);
|
||||
}
|
||||
|
||||
// Cache level. Affects hash Capacity
|
||||
cache: number = 1;
|
||||
|
||||
// Number of cores. Improves hash production
|
||||
cores: number = 1;
|
||||
|
||||
// Number of hashes that can be stored by this Hacknet Server
|
||||
hashCapacity: number = 0;
|
||||
|
||||
// Hashes produced per second
|
||||
hashRate: number = 0;
|
||||
|
||||
// Similar to Node level. Improves hash production
|
||||
level: number = 1;
|
||||
|
||||
// How long this HacknetServer has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Total number of hashes earned by this
|
||||
totalHashesGenerated: number = 0;
|
||||
|
||||
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
|
||||
super(params);
|
||||
|
||||
this.maxRam = 1;
|
||||
this.updateHashCapacity();
|
||||
|
||||
if (params.player) {
|
||||
this.updateHashRate(params.player);
|
||||
}
|
||||
}
|
||||
|
||||
calculateCacheUpgradeCost(levels: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cache >= HacknetServerMaxCache) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCacheMult;
|
||||
let totalCost = 0;
|
||||
let currentCache = this.cache;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCache - 1);
|
||||
++currentCache;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCache;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateCoreUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetServerMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCores-1);
|
||||
++currentCores;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCore;
|
||||
totalCost *= p.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateLevelUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetServerMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return 10 * BaseCostForHacknetServer * totalMultiplier * p.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
calculateRamUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.maxRam >= HacknetServerMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.maxRam));
|
||||
let currentRam = this.maxRam;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBHacknetServerRam;
|
||||
let mult = Math.pow(HacknetServerUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Server in the game loop.
|
||||
// Returns the number of hashes generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
|
||||
return this.hashRate * seconds;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the cache was successfully upgraded
|
||||
purchaseCacheUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCacheUpgradeCost(levels);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cache >= HacknetServerMaxCache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// the maximum possible
|
||||
if (this.cache + levels > HacknetServerMaxCache) {
|
||||
const diff = Math.max(0, HacknetServerMaxCache - this.cache);
|
||||
return this.purchaseCacheUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cache = Math.round(this.cache + sanitizedLevels);
|
||||
this.updateHashCapacity();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the number of cores was successfully upgraded
|
||||
purchaseCoreUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetServerMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// the maximum possible
|
||||
if (this.cores + sanitizedLevels > HacknetServerMaxCores) {
|
||||
const diff = Math.max(0, HacknetServerMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + sanitizedLevels);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the level was successfully upgraded
|
||||
purchaseLevelUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetServerMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase the
|
||||
// maximum possible
|
||||
if (this.level + sanitizedLevels > HacknetServerMaxLevel) {
|
||||
const diff = Math.max(0, HacknetServerMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.level = Math.round(this.level + sanitizedLevels);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
purchaseRamUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
|
||||
if(isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.maxRam >= HacknetServerMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// just the maximum possible
|
||||
if (this.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
|
||||
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / this.maxRam)));
|
||||
return this.purchaseRamUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
this.maxRam *= 2;
|
||||
}
|
||||
this.maxRam = Math.round(this.maxRam);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever a script is run, we must update this server's hash rate
|
||||
*/
|
||||
runScript(script: RunningScript, p?: IPlayer): void {
|
||||
super.runScript(script);
|
||||
if (p) {
|
||||
this.updateHashRate(p);
|
||||
}
|
||||
}
|
||||
|
||||
updateHashCapacity(): void {
|
||||
this.hashCapacity = 16 * Math.pow(2, this.cache);
|
||||
}
|
||||
|
||||
updateHashRate(p: IPlayer): void {
|
||||
const baseGain = HacknetServerHashesPerLevel * this.level;
|
||||
const coreMultiplier = Math.pow(1.1, this.cores - 1);
|
||||
const ramRatio = (1 - this.ramUsed / this.maxRam);
|
||||
|
||||
const hashRate = baseGain * coreMultiplier * ramRatio;
|
||||
|
||||
this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney;
|
||||
|
||||
if (isNaN(this.hashRate)) {
|
||||
this.hashRate = 0;
|
||||
dialogBoxCreate(`Error calculating Hacknet Server hash production. This is a bug. Please report to game dev`, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetServer", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetServer = HacknetServer;
|
151
src/Hacknet/HashManager.ts
Normal file
151
src/Hacknet/HashManager.ts
Normal file
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* This is a central class for storing and managing the player's hashes,
|
||||
* which are generated by Hacknet Servers
|
||||
*
|
||||
* It is also used to keep track of what upgrades the player has bought with
|
||||
* his hashes, and contains method for grabbing the data/multipliers from
|
||||
* those upgrades
|
||||
*/
|
||||
import { HacknetServer } from "./HacknetServer";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { IMap } from "../types";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export class HashManager {
|
||||
// Initiatizes a HashManager object from a JSON save state.
|
||||
static fromJSON(value: any): HashManager {
|
||||
return Generic_fromJSON(HashManager, value.data);
|
||||
}
|
||||
|
||||
// Max number of hashes this can hold. Equal to the sum of capacities of
|
||||
// all Hacknet Servers
|
||||
capacity: number = 0;
|
||||
|
||||
// Number of hashes currently in storage
|
||||
hashes: number = 0;
|
||||
|
||||
// Map of Hash Upgrade Name -> levels in that upgrade
|
||||
upgrades: IMap<number> = {};
|
||||
|
||||
constructor() {
|
||||
for (const name in HashUpgrades) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic helper function for getting a multiplier from a HashUpgrade
|
||||
*/
|
||||
getMult(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Could not find Hash Study upgrade`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1 + ((upg.value * currLevel) / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves studying. This returns that multiplier
|
||||
*/
|
||||
getStudyMult(): number {
|
||||
const upgName = "Improve Studying";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves gym training. This returns that multiplier
|
||||
*/
|
||||
getTrainingMult(): number {
|
||||
const upgName = "Improve Gym Training";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cost (in hashes) of an upgrade
|
||||
*/
|
||||
getUpgradeCost(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.getUpgradeCost(): ${upgName}`);
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return upg.getCost(currLevel);
|
||||
}
|
||||
|
||||
storeHashes(numHashes: number): void {
|
||||
this.hashes += numHashes;
|
||||
this.hashes = Math.min(this.hashes, this.capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts an upgrade and refunds the hashes used to buy it
|
||||
*/
|
||||
refundUpgrade(upgName: string): void {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null || currLevel === 0) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce the level first, so we get the right cost
|
||||
--this.upgrades[upgName];
|
||||
const cost = upg.getCost(currLevel);
|
||||
this.hashes += cost;
|
||||
}
|
||||
|
||||
updateCapacity(p: IPlayer): void {
|
||||
if (p.hacknetNodes.length <= 0) { this.capacity = 0; }
|
||||
if (!(p.hacknetNodes[0] instanceof HacknetServer)) { this.capacity = 0; }
|
||||
|
||||
let total: number = 0;
|
||||
for (let i = 0; i < p.hacknetNodes.length; ++i) {
|
||||
const hacknetServer = <HacknetServer>(p.hacknetNodes[i]);
|
||||
total += hacknetServer.hashCapacity;
|
||||
}
|
||||
|
||||
this.capacity = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean indicating whether or not the upgrade was successfully purchased
|
||||
* Note that this does NOT actually implement the effect
|
||||
*/
|
||||
upgrade(upgName: string): boolean {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = upg.getCost(currLevel);
|
||||
|
||||
if (this.hashes < cost) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hashes -= cost;
|
||||
++this.upgrades[upgName];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Serialize the current object to a JSON save state.
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HashManager", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HashManager = HashManager;
|
48
src/Hacknet/HashUpgrade.ts
Normal file
48
src/Hacknet/HashUpgrade.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Object representing an upgrade that can be purchased with hashes
|
||||
*/
|
||||
export interface IConstructorParams {
|
||||
costPerLevel: number;
|
||||
desc: string;
|
||||
hasTargetServer?: boolean;
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export class HashUpgrade {
|
||||
/**
|
||||
* Base cost for this upgrade. Every time the upgrade is purchased,
|
||||
* its cost increases by this same amount (so its 1x, 2x, 3x, 4x, etc.)
|
||||
*/
|
||||
costPerLevel: number = 0;
|
||||
|
||||
/**
|
||||
* Description of what the upgrade does
|
||||
*/
|
||||
desc: string = "";
|
||||
|
||||
/**
|
||||
* Boolean indicating that this upgrade's effect affects a single server,
|
||||
* the "target" server
|
||||
*/
|
||||
hasTargetServer: boolean = false;
|
||||
|
||||
// Name of upgrade
|
||||
name: string = "";
|
||||
|
||||
// Generic value used to indicate the potency/amount of this upgrade's effect
|
||||
// The meaning varies between different upgrades
|
||||
value: number = 0;
|
||||
|
||||
constructor(p: IConstructorParams) {
|
||||
this.costPerLevel = p.costPerLevel;
|
||||
this.desc = p.desc;
|
||||
this.hasTargetServer = p.hasTargetServer ? p.hasTargetServer : false;
|
||||
this.name = p.name;
|
||||
this.value = p.value;
|
||||
}
|
||||
|
||||
getCost(levels: number): number {
|
||||
return Math.round((levels + 1) * this.costPerLevel);
|
||||
}
|
||||
}
|
18
src/Hacknet/HashUpgrades.ts
Normal file
18
src/Hacknet/HashUpgrades.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Map of all Hash Upgrades
|
||||
* Key = Hash name, Value = HashUpgrade object
|
||||
*/
|
||||
import { HashUpgrade,
|
||||
IConstructorParams } from "./HashUpgrade";
|
||||
import { HashUpgradesMetadata } from "./data/HashUpgradesMetadata";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const HashUpgrades: IMap<HashUpgrade> = {};
|
||||
|
||||
function createHashUpgrade(p: IConstructorParams) {
|
||||
HashUpgrades[p.name] = new HashUpgrade(p);
|
||||
}
|
||||
|
||||
for (const metadata of HashUpgradesMetadata) {
|
||||
createHashUpgrade(metadata);
|
||||
}
|
16
src/Hacknet/IHacknetNode.ts
Normal file
16
src/Hacknet/IHacknetNode.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// Interface for a Hacknet Node. Implemented by both a basic Hacknet Node,
|
||||
// and the upgraded Hacknet Server in BitNode-9
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export interface IHacknetNode {
|
||||
cores: number;
|
||||
level: number;
|
||||
onlineTimeSeconds: number;
|
||||
|
||||
calculateCoreUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
calculateLevelUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
calculateRamUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
purchaseCoreUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
purchaseLevelUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
purchaseRamUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
}
|
64
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
64
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
@ -0,0 +1,64 @@
|
||||
// Metadata used to construct all Hash Upgrades
|
||||
import { IConstructorParams } from "../HashUpgrade";
|
||||
|
||||
export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
costPerLevel: 2,
|
||||
desc: "Sell hashes for $1m",
|
||||
name: "Sell for Money",
|
||||
value: 1e6,
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Sell hashes for $1b in Corporation funds",
|
||||
name: "Sell for Corporation Funds",
|
||||
value: 1e9,
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Use hashes to decrease the minimum security of a single server by 5%. " +
|
||||
"Note that a server's minimum security cannot go below 1.",
|
||||
hasTargetServer: true,
|
||||
name: "Reduce Minimum Security",
|
||||
value: 0.95,
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Use hashes to increase the maximum amount of money on a single server by 5%",
|
||||
hasTargetServer: true,
|
||||
name: "Increase Maximum Money",
|
||||
value: 1.05,
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Use hashes to improve the experience earned when studying at a university. " +
|
||||
"This effect persists until you install Augmentations",
|
||||
name: "Improve Studying",
|
||||
value: 20, // Improves studying by value%
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Use hashes to improve the experience earned when training at the gym. This effect " +
|
||||
"persists until you install Augmentations",
|
||||
name: "Improve Gym Training",
|
||||
value: 20, // Improves training by value%
|
||||
},
|
||||
{
|
||||
costPerLevel: 250,
|
||||
desc: "Exchange hashes for 1k Scientific Research in all of your Corporation's Industries",
|
||||
name: "Exchange for Corporation Research",
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
costPerLevel: 250,
|
||||
desc: "Exchange hashes for 100 Bladeburner Rank",
|
||||
name: "Exchange for Bladeburner Rank",
|
||||
value: 100,
|
||||
},
|
||||
{
|
||||
costPerLevel: 200,
|
||||
desc: "Generate a random Coding Contract on your home computer",
|
||||
name: "Generate Coding Contract",
|
||||
value: 1,
|
||||
},
|
||||
]
|
56
src/Hacknet/ui/GeneralInfo.jsx
Normal file
56
src/Hacknet/ui/GeneralInfo.jsx
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI
|
||||
*
|
||||
* Displays general information about Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers } from "../HacknetHelpers";
|
||||
|
||||
export class GeneralInfo extends React.Component {
|
||||
getSecondParagraph() {
|
||||
if (hasHacknetServers()) {
|
||||
return `Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` +
|
||||
`Hacknet Servers will perform computations and operations on the network, earning ` +
|
||||
`you hashes. Hashes can be spent on a variety of different upgrades.`;
|
||||
} else {
|
||||
return `Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
|
||||
`and contribute its resources to the Hacknet networ. This allows you to take ` +
|
||||
`a small percentage of profits from hacks performed on the network. Essentially, ` +
|
||||
`you are renting out your Node's computing power.`;
|
||||
}
|
||||
}
|
||||
|
||||
getThirdParagraph() {
|
||||
if (hasHacknetServers()) {
|
||||
return `Hacknet Servers can also be used as servers to run scripts. However, running scripts ` +
|
||||
`on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` +
|
||||
`rate will be reduced by the percentage of RAM that is being used by that Server to run ` +
|
||||
`scripts.`
|
||||
} else {
|
||||
return `Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` +
|
||||
`can be upgraded in order to increase its computing power and thereby increase ` +
|
||||
`the profit you earn from it.`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p className={"hacknet-general-info"}>
|
||||
The Hacknet is a global, decentralized network of machines. It is used by
|
||||
hackers all around the world to anonymously share computing power and
|
||||
perform distributed cyberattacks without the fear of being traced.
|
||||
</p>
|
||||
<br />
|
||||
<p className={"hacknet-general-info"}>
|
||||
{this.getSecondParagraph()}
|
||||
</p>
|
||||
<br />
|
||||
<p className={"hacknet-general-info"}>
|
||||
{this.getThirdParagraph()}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
153
src/Hacknet/ui/HacknetNode.jsx
Normal file
153
src/Hacknet/ui/HacknetNode.jsx
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI.
|
||||
* This Component displays the panel for a single Hacknet Node
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { HacknetNodeMaxLevel,
|
||||
HacknetNodeMaxRam,
|
||||
HacknetNodeMaxCores } from "../HacknetNode";
|
||||
import { getMaxNumberLevelUpgrades,
|
||||
getMaxNumberRamUpgrades,
|
||||
getMaxNumberCoreUpgrades } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export class HacknetNode extends React.Component {
|
||||
render() {
|
||||
const node = this.props.node;
|
||||
const purchaseMult = this.props.purchaseMultiplier;
|
||||
const recalculate = this.props.recalculate;
|
||||
|
||||
// Upgrade Level Button
|
||||
let upgradeLevelText, upgradeLevelClass;
|
||||
if (node.level >= HacknetNodeMaxLevel) {
|
||||
upgradeLevelText = "MAX LEVEL";
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
||||
} else {
|
||||
const levelsToMax = HacknetNodeMaxLevel - node.level;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeLevelClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeLevelOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
||||
}
|
||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
let upgradeRamText, upgradeRamClass;
|
||||
if (node.ram >= HacknetNodeMaxRam) {
|
||||
upgradeRamText = "MAX RAM";
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
||||
} else {
|
||||
const levelsToMax = Math.round(Math.log2(HacknetNodeMaxRam / node.ram));
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeRamClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeRamOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
||||
}
|
||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
let upgradeCoresText, upgradeCoresClass;
|
||||
if (node.cores >= HacknetNodeMaxCores) {
|
||||
upgradeCoresText = "MAX CORES";
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
||||
} else {
|
||||
const levelsToMax = HacknetNodeMaxCores - node.cores;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCoresClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCoresOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
||||
}
|
||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={"hacknet-node"}>
|
||||
<div className={"hacknet-node-container"}>
|
||||
<div className={"row"}>
|
||||
<p>Node name:</p>
|
||||
<span className={"text"}>{node.name}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Production:</p>
|
||||
<span className={"text money-gold"}>
|
||||
{numeralWrapper.formatMoney(node.totalMoneyGenerated)} ({numeralWrapper.formatMoney(node.moneyGainRatePerSecond)} / sec)
|
||||
</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span>
|
||||
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
|
||||
{upgradeLevelText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>RAM:</p><span className={"text upgradable-info"}>{node.ram}GB</span>
|
||||
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
|
||||
{upgradeRamText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span>
|
||||
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
|
||||
{upgradeCoresText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
200
src/Hacknet/ui/HacknetServer.jsx
Normal file
200
src/Hacknet/ui/HacknetServer.jsx
Normal file
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI.
|
||||
* This Component displays the panel for a single Hacknet Node
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { HacknetServerMaxLevel,
|
||||
HacknetServerMaxRam,
|
||||
HacknetServerMaxCores,
|
||||
HacknetServerMaxCache } from "../HacknetServer";
|
||||
import { getMaxNumberLevelUpgrades,
|
||||
getMaxNumberRamUpgrades,
|
||||
getMaxNumberCoreUpgrades,
|
||||
getMaxNumberCacheUpgrades } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export class HacknetServer extends React.Component {
|
||||
render() {
|
||||
const node = this.props.node;
|
||||
const purchaseMult = this.props.purchaseMultiplier;
|
||||
const recalculate = this.props.recalculate;
|
||||
|
||||
// Upgrade Level Button
|
||||
let upgradeLevelText, upgradeLevelClass;
|
||||
if (node.level >= HacknetServerMaxLevel) {
|
||||
upgradeLevelText = "MAX LEVEL";
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxLevel - node.level;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeLevelClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeLevelOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
||||
}
|
||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade RAM Button
|
||||
let upgradeRamText, upgradeRamClass;
|
||||
if (node.maxRam >= HacknetServerMaxRam) {
|
||||
upgradeRamText = "MAX RAM";
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
||||
} else {
|
||||
const levelsToMax = Math.round(Math.log2(HacknetServerMaxRam / node.maxRam));
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeRamClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeRamOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
||||
}
|
||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade Cores Button
|
||||
let upgradeCoresText, upgradeCoresClass;
|
||||
if (node.cores >= HacknetServerMaxCores) {
|
||||
upgradeCoresText = "MAX CORES";
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxCores - node.cores;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCoresClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCoresOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
||||
}
|
||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade Cache button
|
||||
let upgradeCacheText, upgradeCacheClass;
|
||||
if (node.cache >= HacknetServerMaxCache) {
|
||||
upgradeCacheText = "MAX CACHE";
|
||||
upgradeCacheClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxCache - node.cache;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier);
|
||||
upgradeCacheText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCacheCost)}`;
|
||||
if (Player.money.lt(upgradeCacheCost)) {
|
||||
upgradeCacheClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCacheClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCacheOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
||||
}
|
||||
node.purchaseCacheUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={"hacknet-node"}>
|
||||
<div className={"hacknet-node-container"}>
|
||||
<div className={"row"}>
|
||||
<p>Node name:</p>
|
||||
<span className={"text"}>{node.hostname}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Production:</p>
|
||||
<span className={"text money-gold"}>
|
||||
{numeralWrapper.formatBigNumber(node.totalHashesGenerated)} ({numeralWrapper.formatBigNumber(node.hashRate)} / sec)
|
||||
</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Hash Capacity:</p>
|
||||
<span className={"text"}>{node.hashCapacity}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span>
|
||||
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
|
||||
{upgradeLevelText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>RAM:</p><span className={"text upgradable-info"}>{node.maxRam}GB</span>
|
||||
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
|
||||
{upgradeRamText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span>
|
||||
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
|
||||
{upgradeCoresText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cache Level:</p><span className={"text upgradable-info"}>{node.cache}</span>
|
||||
<button className={upgradeCacheClass} onClick={upgradeCacheOnClick}>
|
||||
{upgradeCacheText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
137
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
137
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Create the pop-up for purchasing upgrades with hashes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { purchaseHashUpgrade } from "../HacknetHelpers";
|
||||
import { HashManager } from "../HashManager";
|
||||
import { HashUpgrades } from "../HashUpgrades";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { ServerDropdown,
|
||||
ServerType } from "../../ui/React/ServerDropdown"
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
class HashUpgrade extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedServer: "foodnstuff",
|
||||
}
|
||||
|
||||
this.changeTargetServer = this.changeTargetServer.bind(this);
|
||||
this.purchase = this.purchase.bind(this, this.props.hashManager, this.props.upg);
|
||||
}
|
||||
|
||||
changeTargetServer(e) {
|
||||
this.setState({
|
||||
selectedServer: e.target.value
|
||||
});
|
||||
}
|
||||
|
||||
purchase(hashManager, upg) {
|
||||
const canPurchase = hashManager.hashes >= hashManager.getUpgradeCost(upg.name);
|
||||
if (canPurchase) {
|
||||
const res = purchaseHashUpgrade(upg.name, this.state.selectedServer);
|
||||
if (res) {
|
||||
this.props.rerender();
|
||||
} else {
|
||||
dialogBoxCreate("Failed to purchase upgrade. This may be because you do not have enough hashes, " +
|
||||
"or because you do not have access to the feature this upgrade affects.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const hashManager = this.props.hashManager;
|
||||
const upg = this.props.upg;
|
||||
const cost = hashManager.getUpgradeCost(upg.name);
|
||||
|
||||
// Purchase button
|
||||
const canPurchase = hashManager.hashes >= cost;
|
||||
const btnClass = canPurchase ? "std-button" : "std-button-disabled";
|
||||
|
||||
// We'll reuse a Bladeburner css class
|
||||
return (
|
||||
<div className={"bladeburner-action"}>
|
||||
<h2>{upg.name}</h2>
|
||||
<p>Cost: {numeralWrapper.format(cost, "0.000a")}</p>
|
||||
<p>{upg.desc}</p>
|
||||
<button className={btnClass} onClick={this.purchase}>
|
||||
Purchase
|
||||
</button>
|
||||
{
|
||||
upg.hasTargetServer &&
|
||||
<ServerDropdown
|
||||
serverType={ServerType.Foreign}
|
||||
onChange={this.changeTargetServer}
|
||||
style={{margin: "5px"}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HashUpgradePopup extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.closePopup = this.closePopup.bind(this);
|
||||
|
||||
this.state = {
|
||||
totalHashes: Player.hashManager.hashes,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.interval = setInterval(() => this.tick(), 1e3);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
closePopup() {
|
||||
removePopup(this.props.popupId);
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.setState({
|
||||
totalHashes: Player.hashManager.hashes,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const rerender = this.props.rerender;
|
||||
|
||||
const hashManager = Player.hashManager;
|
||||
if (!(hashManager instanceof HashManager)) {
|
||||
throw new Error(`Player does not have a HashManager)`);
|
||||
}
|
||||
|
||||
const upgradeElems = Object.keys(HashUpgrades).map((upgName) => {
|
||||
const upg = HashUpgrades[upgName];
|
||||
return <HashUpgrade upg={upg} hashManager={hashManager} key={upg.name} rerender={rerender} />
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className={"std-button"} onClick={this.closePopup}>
|
||||
Close
|
||||
</button>
|
||||
<p>Spend your hashes on a variety of different upgrades</p>
|
||||
<p>Hashes: {numeralWrapper.formatBigNumber(this.state.totalHashes)}</p>
|
||||
{upgradeElems}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
41
src/Hacknet/ui/MultiplierButtons.jsx
Normal file
41
src/Hacknet/ui/MultiplierButtons.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* React Component for the Multiplier buttons on the Hacknet page.
|
||||
* These buttons let the player control how many Nodes/Upgrades they're
|
||||
* purchasing when using the UI (x1, x5, x10, MAX)
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { PurchaseMultipliers } from "./Root";
|
||||
|
||||
function MultiplierButton(props) {
|
||||
return (
|
||||
<button className={props.className} onClick={props.onClick}>{props.text}</button>
|
||||
)
|
||||
}
|
||||
|
||||
export function MultiplierButtons(props) {
|
||||
if (props.purchaseMultiplier == null) {
|
||||
throw new Error(`MultiplierButtons constructed without required props`);
|
||||
}
|
||||
|
||||
const mults = ["x1", "x5", "x10", "MAX"];
|
||||
const onClicks = props.onClicks;
|
||||
const buttons = [];
|
||||
for (let i = 0; i < mults.length; ++i) {
|
||||
const mult = mults[i];
|
||||
const btnProps = {
|
||||
className: props.purchaseMultiplier === PurchaseMultipliers[mult] ? "std-button-disabled" : "std-button",
|
||||
key: mult,
|
||||
onClick: onClicks[i],
|
||||
text: mult,
|
||||
}
|
||||
|
||||
buttons.push(<MultiplierButton {...btnProps} />)
|
||||
}
|
||||
|
||||
return (
|
||||
<span id={"hacknet-nodes-multipliers"}>
|
||||
{buttons}
|
||||
</span>
|
||||
)
|
||||
}
|
51
src/Hacknet/ui/PlayerInfo.jsx
Normal file
51
src/Hacknet/ui/PlayerInfo.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* React Component for displaying Player info and stats on the Hacknet Node UI.
|
||||
* This includes:
|
||||
* - Player's money
|
||||
* - Player's production from Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers } from "../HacknetHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export function PlayerInfo(props) {
|
||||
const hasServers = hasHacknetServers();
|
||||
|
||||
let prod;
|
||||
if (hasServers) {
|
||||
prod = numeralWrapper.format(props.totalProduction, "0.000a") + " hashes / sec";
|
||||
} else {
|
||||
prod = numeralWrapper.formatMoney(props.totalProduction) + " / sec";
|
||||
}
|
||||
|
||||
let hashInfo;
|
||||
if (hasServers) {
|
||||
hashInfo = numeralWrapper.format(Player.hashManager.hashes, "0.000a") + " / " +
|
||||
numeralWrapper.format(Player.hashManager.capacity, "0.000a");
|
||||
}
|
||||
|
||||
return (
|
||||
<p id={"hacknet-nodes-money"}>
|
||||
<span>Money:</span>
|
||||
<span className={"money-gold"}>{numeralWrapper.formatMoney(Player.money.toNumber())}</span><br />
|
||||
|
||||
{
|
||||
hasServers &&
|
||||
<span>Hashes:</span>
|
||||
}
|
||||
{
|
||||
hasServers &&
|
||||
<span className={"money-gold"}>{hashInfo}</span>
|
||||
}
|
||||
{
|
||||
hasServers &&
|
||||
<br />
|
||||
}
|
||||
|
||||
<span>Total Hacknet Node Production:</span>
|
||||
<span className={"money-gold"}>{prod}</span>
|
||||
</p>
|
||||
)
|
||||
}
|
40
src/Hacknet/ui/PurchaseButton.jsx
Normal file
40
src/Hacknet/ui/PurchaseButton.jsx
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* React Component for the button that is used to purchase new Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers,
|
||||
hasMaxNumberHacknetServers } from "../HacknetHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export function PurchaseButton(props) {
|
||||
if (props.multiplier == null || props.onClick == null) {
|
||||
throw new Error(`PurchaseButton constructed without required props`);
|
||||
}
|
||||
|
||||
const cost = props.cost;
|
||||
let className = Player.canAfford(cost) ? "std-button" : "std-button-disabled";
|
||||
let text;
|
||||
let style = null;
|
||||
if (hasHacknetServers()) {
|
||||
if (hasMaxNumberHacknetServers()) {
|
||||
className = "std-button-disabled";
|
||||
text = "Hacknet Server limit reached";
|
||||
style = {color: "red"};
|
||||
} else {
|
||||
text = `Purchase Hacknet Server - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
} else {
|
||||
text = `Purchase Hacknet Node - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className}
|
||||
onClick={props.onClick}
|
||||
style={style}>
|
||||
{text}
|
||||
</button>
|
||||
|
||||
)
|
||||
}
|
144
src/Hacknet/ui/Root.jsx
Normal file
144
src/Hacknet/ui/Root.jsx
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Root React Component for the Hacknet Node UI
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { GeneralInfo } from "./GeneralInfo";
|
||||
import { HacknetNode } from "./HacknetNode";
|
||||
import { HacknetServer } from "./HacknetServer";
|
||||
import { HashUpgradePopup } from "./HashUpgradePopup";
|
||||
import { MultiplierButtons } from "./MultiplierButtons";
|
||||
import { PlayerInfo } from "./PlayerInfo";
|
||||
import { PurchaseButton } from "./PurchaseButton";
|
||||
|
||||
import { getCostOfNextHacknetNode,
|
||||
getCostOfNextHacknetServer,
|
||||
hasHacknetServers,
|
||||
purchaseHacknet } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
|
||||
export const PurchaseMultipliers = Object.freeze({
|
||||
"x1": 1,
|
||||
"x5": 5,
|
||||
"x10": 10,
|
||||
"MAX": "MAX",
|
||||
});
|
||||
|
||||
export class HacknetRoot extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
purchaseMultiplier: PurchaseMultipliers.x1,
|
||||
totalProduction: 0, // Total production ($ / s) of Hacknet Nodes
|
||||
}
|
||||
|
||||
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.recalculateTotalProduction();
|
||||
}
|
||||
|
||||
createHashUpgradesPopup() {
|
||||
const id = "hacknet-server-hash-upgrades-popup";
|
||||
createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup });
|
||||
}
|
||||
|
||||
recalculateTotalProduction() {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (hasHacknetServers()) {
|
||||
total += Player.hacknetNodes[i].hashRate;
|
||||
} else {
|
||||
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
totalProduction: total,
|
||||
});
|
||||
}
|
||||
|
||||
setPurchaseMultiplier(mult) {
|
||||
this.setState({
|
||||
purchaseMultiplier: mult,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// Cost to purchase a new Hacknet Node
|
||||
let purchaseCost;
|
||||
if (hasHacknetServers()) {
|
||||
purchaseCost = getCostOfNextHacknetServer();
|
||||
} else {
|
||||
purchaseCost = getCostOfNextHacknetNode();
|
||||
}
|
||||
|
||||
// onClick event handler for purchase button
|
||||
const purchaseOnClick = () => {
|
||||
if (purchaseHacknet() >= 0) {
|
||||
this.recalculateTotalProduction();
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
}
|
||||
}
|
||||
|
||||
// onClick event handlers for purchase multiplier buttons
|
||||
const purchaseMultiplierOnClicks = [
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x5),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x10),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.MAX),
|
||||
];
|
||||
|
||||
// HacknetNode components
|
||||
const nodes = Player.hacknetNodes.map((node) => {
|
||||
if (hasHacknetServers()) {
|
||||
return (
|
||||
<HacknetServer
|
||||
key={node.hostname}
|
||||
node={node}
|
||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<HacknetNode
|
||||
key={node.name}
|
||||
node={node}
|
||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Hacknet Nodes</h1>
|
||||
<GeneralInfo />
|
||||
|
||||
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={purchaseOnClick} />
|
||||
|
||||
<br />
|
||||
<div id={"hacknet-nodes-money-multipliers-div"}>
|
||||
<PlayerInfo totalProduction={this.state.totalProduction} />
|
||||
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={this.state.purchaseMultiplier} />
|
||||
</div>
|
||||
|
||||
{
|
||||
hasHacknetServers() &&
|
||||
<button className={"std-button"} onClick={this.createHashUpgradesPopup} style={{display: "block"}}>
|
||||
{"Spend Hashes on Upgrades"}
|
||||
</button>
|
||||
}
|
||||
|
||||
<ul id={"hacknet-nodes-list"}>{nodes}</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,694 +0,0 @@
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import {iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "./InteractiveTutorial";
|
||||
import {Player} from "./Player";
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {Reviver, Generic_toJSON,
|
||||
Generic_fromJSON} from "../utils/JSONReviver";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {getElementById} from "../utils/uiHelpers/getElementById";
|
||||
|
||||
// Stores total money gain rate from all of the player's Hacknet Nodes
|
||||
let TotalHacknetNodeProduction = 0;
|
||||
|
||||
/**
|
||||
* Overwrites the inner text of the specified HTML element if it is different from what currently exists.
|
||||
* @param {string} elementId The HTML ID to find the first instance of.
|
||||
* @param {string} text The inner text that should be set.
|
||||
*/
|
||||
function updateText(elementId, text) {
|
||||
var el = getElementById(elementId);
|
||||
if (el.innerText != text) {
|
||||
el.innerText = text;
|
||||
}
|
||||
};
|
||||
|
||||
/* HacknetNode.js */
|
||||
function hacknetNodesInit() {
|
||||
var performMapping = function(x) {
|
||||
getElementById("hacknet-nodes-" + x.id + "-multiplier")
|
||||
.addEventListener("click", function() {
|
||||
hacknetNodePurchaseMultiplier = x.multiplier;
|
||||
updateHacknetNodesMultiplierButtons();
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
var mappings = [
|
||||
{ id: "1x", multiplier: 1 },
|
||||
{ id: "5x", multiplier: 5 },
|
||||
{ id: "10x", multiplier: 10 },
|
||||
{ id: "max", multiplier: 0 }
|
||||
];
|
||||
for (var elem of mappings) {
|
||||
// Encapsulate in a function so that the appropriate scope is kept in the click handler.
|
||||
performMapping(elem);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit, false);
|
||||
|
||||
function HacknetNode(name) {
|
||||
this.level = 1;
|
||||
this.ram = 1; //GB
|
||||
this.cores = 1;
|
||||
|
||||
this.name = name;
|
||||
|
||||
this.totalMoneyGenerated = 0;
|
||||
this.onlineTimeSeconds = 0;
|
||||
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.updateMoneyGainRate = function() {
|
||||
//How much extra $/s is gained per level
|
||||
var gainPerLevel = CONSTANTS.HacknetNodeMoneyGainPerLevel;
|
||||
|
||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||
Math.pow(1.035, this.ram-1) *
|
||||
((this.cores + 5) / 6) *
|
||||
Player.hacknet_node_money_mult *
|
||||
BitNodeMultipliers.HacknetNodeMoney;
|
||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer");
|
||||
}
|
||||
|
||||
updateTotalHacknetProduction();
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateLevelUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
var mult = CONSTANTS.HacknetNodeUpgradeLevelMult;
|
||||
var totalMultiplier = 0; //Summed
|
||||
var currLevel = this.level;
|
||||
for (var i = 0; i < levels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return CONSTANTS.BaseCostForHacknetNode / 2 * totalMultiplier * Player.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseLevelUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateLevelUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If we're at max level, return false
|
||||
if (this.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the number of specified upgrades would exceed the max level, calculate
|
||||
//the maximum number of upgrades and use that
|
||||
if (this.level + levels > CONSTANTS.HacknetNodeMaxLevel) {
|
||||
var diff = Math.max(0, CONSTANTS.HacknetNodeMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
this.level = Math.round(this.level + levels); //Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateRamUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.ram));
|
||||
let currentRam = this.ram;
|
||||
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
let baseCost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHacknetNode;
|
||||
let mult = Math.pow(CONSTANTS.HacknetNodeUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
|
||||
totalCost *= Player.hacknet_node_ram_cost_mult
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseRamUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateRamUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the number of specified upgrades would exceed the max RAM, calculate the
|
||||
//max possible number of upgrades and use that
|
||||
if (this.ram * Math.pow(2, levels) > CONSTANTS.HacknetNodeMaxRam) {
|
||||
var diff = Math.max(0, Math.log2(Math.round(CONSTANTS.HacknetNodeMaxRam / this.ram)));
|
||||
return this.purchaseRamUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
this.ram *= 2; //Ram is always doubled
|
||||
}
|
||||
this.ram = Math.round(this.ram); //Handle any floating point precision issues
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateCoreUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const coreBaseCost = CONSTANTS.BaseCostForHacknetNodeCore;
|
||||
const mult = CONSTANTS.HacknetNodeUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
|
||||
++currentCores;
|
||||
}
|
||||
|
||||
totalCost *= Player.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseCoreUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateCoreUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Fail if we're already at max
|
||||
if (this.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the specified number of upgrades would exceed the max Cores, calculate
|
||||
//the max possible number of upgrades and use that
|
||||
if (this.cores + levels > CONSTANTS.HacknetNodeMaxCores) {
|
||||
var diff = Math.max(0, CONSTANTS.HacknetNodeMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + levels); //Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Saving and loading HackNets */
|
||||
HacknetNode.prototype.toJSON = function() {
|
||||
return Generic_toJSON("HacknetNode", this);
|
||||
}
|
||||
|
||||
HacknetNode.fromJSON = function(value) {
|
||||
return Generic_fromJSON(HacknetNode, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetNode = HacknetNode;
|
||||
|
||||
function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* END INTERACTIVE TUTORIAL */
|
||||
|
||||
var cost = getCostOfNextHacknetNode();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error("Cost is NaN");
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
//dialogBoxCreate("You cannot afford to purchase a Hacknet Node!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Auto generate a name for the node for now...TODO
|
||||
var numOwned = Player.hacknetNodes.length;
|
||||
var name = "hacknet-node-" + numOwned;
|
||||
var node = new HacknetNode(name);
|
||||
node.updateMoneyGainRate();
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(node);
|
||||
|
||||
if (routing.isOn(Page.HacknetNodes)) {
|
||||
displayHacknetNodesContent();
|
||||
}
|
||||
updateTotalHacknetProduction();
|
||||
return numOwned;
|
||||
}
|
||||
|
||||
//Calculates the total production from all HacknetNodes
|
||||
function updateTotalHacknetProduction() {
|
||||
var total = 0;
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
|
||||
}
|
||||
TotalHacknetNodeProduction = total;
|
||||
}
|
||||
|
||||
function getCostOfNextHacknetNode() {
|
||||
//Cost increases exponentially based on how many you own
|
||||
var numOwned = Player.hacknetNodes.length;
|
||||
var mult = CONSTANTS.HacknetNodePurchaseNextMult;
|
||||
return CONSTANTS.BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
var hacknetNodePurchaseMultiplier = 1;
|
||||
function updateHacknetNodesMultiplierButtons() {
|
||||
var mult1x = document.getElementById("hacknet-nodes-1x-multiplier");
|
||||
var mult5x = document.getElementById("hacknet-nodes-5x-multiplier");
|
||||
var mult10x = document.getElementById("hacknet-nodes-10x-multiplier");
|
||||
var multMax = document.getElementById("hacknet-nodes-max-multiplier");
|
||||
mult1x.setAttribute("class", "a-link-button");
|
||||
mult5x.setAttribute("class", "a-link-button");
|
||||
mult10x.setAttribute("class", "a-link-button");
|
||||
multMax.setAttribute("class", "a-link-button");
|
||||
if (Player.hacknetNodes.length == 0) {
|
||||
mult1x.setAttribute("class", "a-link-button-inactive");
|
||||
mult5x.setAttribute("class", "a-link-button-inactive");
|
||||
mult10x.setAttribute("class", "a-link-button-inactive");
|
||||
multMax.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 1) {
|
||||
mult1x.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 5) {
|
||||
mult5x.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 10) {
|
||||
mult10x.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
multMax.setAttribute("class", "a-link-button-inactive");
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node
|
||||
function getMaxNumberLevelUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var min = 1;
|
||||
var max = CONSTANTS.HacknetNodeMaxLevel - 1;
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxLevel - nodeObj.level;
|
||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
while (min <= max) {
|
||||
var curr = (min + max) / 2 | 0;
|
||||
if (curr != CONSTANTS.HacknetNodeMaxLevel &&
|
||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr)) &&
|
||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getMaxNumberRamUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const levelsToMax = Math.round(Math.log2(CONSTANTS.HacknetNodeMaxRam / nodeObj.ram));
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//We'll just loop until we find the max
|
||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getMaxNumberCoreUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var min = 1;
|
||||
var max = CONSTANTS.HacknetNodeMaxCores - 1;
|
||||
const levelsToMax = CONSTANTS.HacknetNodeMaxCores - nodeObj.cores;
|
||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != CONSTANTS.HacknetNodeMaxCores &&
|
||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr)) &&
|
||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Creates Hacknet Node DOM elements when the page is opened
|
||||
function displayHacknetNodesContent() {
|
||||
//Update Hacknet Nodes button
|
||||
var newPurchaseButton = clearEventListeners("hacknet-nodes-purchase-button");
|
||||
|
||||
newPurchaseButton.addEventListener("click", function() {
|
||||
purchaseHacknet();
|
||||
return false;
|
||||
});
|
||||
|
||||
//Handle Purchase multiplier buttons
|
||||
updateHacknetNodesMultiplierButtons();
|
||||
|
||||
//Remove all old hacknet Node DOM elements
|
||||
var hacknetNodesList = document.getElementById("hacknet-nodes-list");
|
||||
while (hacknetNodesList.firstChild) {
|
||||
hacknetNodesList.removeChild(hacknetNodesList.firstChild);
|
||||
}
|
||||
|
||||
//Then re-create them
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
createHacknetNodeDomElement(Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
updateTotalHacknetProduction();
|
||||
updateHacknetNodesContent();
|
||||
}
|
||||
|
||||
//Update information on all Hacknet Node DOM elements
|
||||
function updateHacknetNodesContent() {
|
||||
//Set purchase button to inactive if not enough money, and update its price display
|
||||
var cost = getCostOfNextHacknetNode();
|
||||
var purchaseButton = getElementById("hacknet-nodes-purchase-button");
|
||||
var formattedCost = numeralWrapper.formatMoney(cost);
|
||||
|
||||
updateText("hacknet-nodes-purchase-button", `Purchase Hacknet Node - ${formattedCost}`);
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
purchaseButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
purchaseButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
|
||||
//Update player's money
|
||||
updateText("hacknet-nodes-player-money", numeralWrapper.formatMoney(Player.money.toNumber()));
|
||||
updateText("hacknet-nodes-total-production", numeralWrapper.formatMoney(TotalHacknetNodeProduction) + " / sec");
|
||||
|
||||
//Update information in each owned hacknet node
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
updateHacknetNodeDomElement(Player.hacknetNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Creates a single Hacknet Node DOM element
|
||||
function createHacknetNodeDomElement(nodeObj) {
|
||||
var nodeName = nodeObj.name;
|
||||
|
||||
var nodeLevelContainer = createElement("div", {
|
||||
class: "hacknet-node-level-container row",
|
||||
innerHTML: "<p>Level:</p><span class=\"text upgradable-info\" id=\"hacknet-node-level-" + nodeName + "\"></span>"
|
||||
});
|
||||
|
||||
var nodeRamContainer = createElement("div", {
|
||||
class: "hacknet-node-ram-container row",
|
||||
innerHTML: "<p>RAM:</p><span class=\"text upgradable-info\" id=\"hacknet-node-ram-" + nodeName + "\"></span>"
|
||||
});
|
||||
|
||||
var nodeCoresContainer = createElement("div", {
|
||||
class: "hacknet-node-cores-container row",
|
||||
innerHTML: "<p>Cores:</p><span class=\"text upgradable-info\" id=\"hacknet-node-cores-" + nodeName + "\"><span>"
|
||||
})
|
||||
var containingDiv = createElement("div", {
|
||||
class: "hacknet-node-container",
|
||||
innerHTML: "<div class=\"hacknet-node-name-container row\">" +
|
||||
"<p>Node name:</p>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-name-" + nodeName + "\"></span>" +
|
||||
"</div>" +
|
||||
"<div class=\"hacknet-node-production-container row\">" +
|
||||
"<p>Production:</p>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-total-production-" + nodeName + "\"></span>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-production-rate-" + nodeName + "\"></span>" +
|
||||
"</div>"
|
||||
});
|
||||
containingDiv.appendChild(nodeLevelContainer);
|
||||
containingDiv.appendChild(nodeRamContainer);
|
||||
containingDiv.appendChild(nodeCoresContainer);
|
||||
|
||||
var listItem = createElement("li", {
|
||||
class: "hacknet-node"
|
||||
});
|
||||
listItem.appendChild(containingDiv);
|
||||
|
||||
//Upgrade buttons
|
||||
nodeLevelContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-level-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseLevelUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
nodeRamContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-ram-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberRamUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseRamUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
nodeCoresContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-core-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseCoreUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
document.getElementById("hacknet-nodes-list").appendChild(listItem);
|
||||
|
||||
//Set the text and stuff inside the DOM element
|
||||
updateHacknetNodeDomElement(nodeObj);
|
||||
}
|
||||
|
||||
//Updates information on a single hacknet node DOM element
|
||||
function updateHacknetNodeDomElement(nodeObj) {
|
||||
var nodeName = nodeObj.name;
|
||||
|
||||
updateText("hacknet-node-name-" + nodeName, nodeName);
|
||||
updateText("hacknet-node-total-production-" + nodeName, numeralWrapper.formatMoney(nodeObj.totalMoneyGenerated));
|
||||
updateText("hacknet-node-production-rate-" + nodeName, "(" + numeralWrapper.formatMoney(nodeObj.moneyGainRatePerSecond) + " / sec)");
|
||||
updateText("hacknet-node-level-" + nodeName, nodeObj.level);
|
||||
updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB");
|
||||
updateText("hacknet-node-cores-" + nodeName, nodeObj.cores);
|
||||
|
||||
//Upgrade level
|
||||
var upgradeLevelButton = getElementById("hacknet-node-upgrade-level-" + nodeName);
|
||||
|
||||
if (nodeObj.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
updateText("hacknet-node-upgrade-level-" + nodeName, "MAX LEVEL");
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
//Max
|
||||
multiplier = getMaxNumberLevelUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxLevel - nodeObj.level;
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
|
||||
var upgradeLevelCost = nodeObj.calculateLevelUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-level-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeLevelCost))
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
|
||||
//Upgrade RAM
|
||||
var upgradeRamButton = getElementById("hacknet-node-upgrade-ram-" + nodeName);
|
||||
|
||||
if (nodeObj.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
updateText("hacknet-node-upgrade-ram-" + nodeName, "MAX RAM");
|
||||
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
multiplier = getMaxNumberRamUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = Math.round(Math.log2(CONSTANTS.HacknetNodeMaxRam / nodeObj.ram));
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
|
||||
var upgradeRamCost = nodeObj.calculateRamUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-ram-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeRamCost));
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeRamButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
|
||||
//Upgrade Cores
|
||||
var upgradeCoreButton = getElementById("hacknet-node-upgrade-core-" + nodeName);
|
||||
|
||||
if (nodeObj.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
updateText("hacknet-node-upgrade-core-" + nodeName, "MAX CORES");
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
multiplier = getMaxNumberCoreUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxCores - nodeObj.cores;
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
var upgradeCoreCost = nodeObj.calculateCoreUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-core-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeCoreCost));
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function processAllHacknetNodeEarnings(numCycles) {
|
||||
var total = 0;
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function processSingleHacknetNodeEarnings(numCycles, nodeObj) {
|
||||
var cyclesPerSecond = 1000 / Engine._idleSpeed;
|
||||
var earningPerCycle = nodeObj.moneyGainRatePerSecond / cyclesPerSecond;
|
||||
if (isNaN(earningPerCycle)) {
|
||||
console.error("Hacknet Node '" + nodeObj.name + "' Calculated earnings is NaN");
|
||||
earningPerCycle = 0;
|
||||
}
|
||||
|
||||
var totalEarnings = numCycles * earningPerCycle;
|
||||
nodeObj.totalMoneyGenerated += totalEarnings;
|
||||
nodeObj.onlineTimeSeconds += (numCycles * (Engine._idleSpeed / 1000));
|
||||
Player.gainMoney(totalEarnings);
|
||||
Player.recordMoneySource(totalEarnings, "hacknetnode");
|
||||
return totalEarnings;
|
||||
}
|
||||
|
||||
function getHacknetNode(name) {
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (Player.hacknetNodes[i].name == name) {
|
||||
return Player.hacknetNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export {
|
||||
HacknetNode,
|
||||
displayHacknetNodesContent,
|
||||
getCostOfNextHacknetNode,
|
||||
getHacknetNode,
|
||||
getMaxNumberLevelUpgrades,
|
||||
hacknetNodesInit,
|
||||
processAllHacknetNodeEarnings,
|
||||
purchaseHacknet,
|
||||
updateHacknetNodesContent,
|
||||
updateHacknetNodesMultiplierButtons,
|
||||
updateTotalHacknetProduction
|
||||
};
|
@ -52,41 +52,81 @@ function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff)
|
||||
this.intExpGained = 0;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||
if (!this.clearanceLevel || isNaN(this.clearanceLevel) || !this.maxClearanceLevel ||isNaN(this.maxClearanceLevel)) return 1;
|
||||
return 2 * this.clearanceLevel / this.maxClearanceLevel;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.hackingExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedHackingExp = function() {
|
||||
if(!this.hackingExpGained || isNaN(this.hackingExpGained)) return 0;
|
||||
return Math.pow(this.hackingExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.strExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedStrengthExp = function() {
|
||||
if(!this.strExpGained || isNaN(this.strExpGained)) return 0;
|
||||
return Math.pow(this.strExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.defExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDefenseExp = function() {
|
||||
if(!this.defExpGained || isNaN(this.defExpGained)) return 0;
|
||||
return Math.pow(this.defExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.dexExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDexterityExp = function() {
|
||||
if(!this.dexExpGained || isNaN(this.dexExpGained)) return 0;
|
||||
return Math.pow(this.dexExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.agiExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedAgilityExp = function() {
|
||||
if(!this.agiExpGained || isNaN(this.agiExpGained)) return 0;
|
||||
return Math.pow(this.agiExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.chaExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedCharismaExp = function() {
|
||||
if(!this.chaExpGained || isNaN(this.chaExpGained)) return 0;
|
||||
return Math.pow(this.chaExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.intExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
|
||||
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
|
||||
return Math.pow(this.intExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
||||
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
|
||||
clearInfiltrationStatusText();
|
||||
@ -477,12 +517,12 @@ function updateInfiltrationLevelText(inst) {
|
||||
"Total value of stolen secrets<br>" +
|
||||
"Reputation: <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
|
||||
"Money: <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
|
||||
"Hack exp gained: " + formatNumber(inst.hackingExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Str exp gained: " + formatNumber(inst.strExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Def exp gained: " + formatNumber(inst.defExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Dex exp gained: " + formatNumber(inst.dexExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Agi exp gained: " + formatNumber(inst.agiExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Cha exp gained: " + formatNumber(inst.chaExpGained * expMultiplier, 3);
|
||||
"Hack exp gained: " + formatNumber(inst.calcGainedHackingExp(), 3) + "<br>" +
|
||||
"Str exp gained: " + formatNumber(inst.calcGainedStrengthExp(), 3) + "<br>" +
|
||||
"Def exp gained: " + formatNumber(inst.calcGainedDefenseExp(), 3) + "<br>" +
|
||||
"Dex exp gained: " + formatNumber(inst.calcGainedDexterityExp(), 3) + "<br>" +
|
||||
"Agi exp gained: " + formatNumber(inst.calcGainedAgilityExp(), 3) + "<br>" +
|
||||
"Cha exp gained: " + formatNumber(inst.calcGainedCharismaExp(), 3);
|
||||
/* eslint-enable no-irregular-whitespace */
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import {HacknetNode} from "./HacknetNode";
|
||||
import {NetscriptFunctions} from "./NetscriptFunctions";
|
||||
import {NetscriptPort} from "./NetscriptPort";
|
||||
|
||||
@ -56,37 +55,6 @@ Environment.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setArrayElement: function(name, idx, value) {
|
||||
if (!(idx instanceof Array)) {
|
||||
throw new Error("idx parameter is not an Array");
|
||||
}
|
||||
var scope = this.lookup(name);
|
||||
if (!scope && this.parent) {
|
||||
throw new Error("Undefined variable " + name);
|
||||
}
|
||||
var arr = (scope || this).vars[name];
|
||||
if (!(arr.constructor === Array || arr instanceof Array)) {
|
||||
throw new Error("Variable is not an array: " + name);
|
||||
}
|
||||
var res = arr;
|
||||
for (var iterator = 0; iterator < idx.length-1; ++iterator) {
|
||||
var i = idx[iterator];
|
||||
if (!(res instanceof Array) || i >= res.length) {
|
||||
throw new Error("Out-of-bounds array access");
|
||||
}
|
||||
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;
|
||||
},
|
||||
|
||||
//Creates (or overwrites) a variable in the current scope
|
||||
def: function(name, value) {
|
||||
return this.vars[name] = value;
|
||||
|
@ -196,7 +196,7 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = threads;
|
||||
server.runningScripts.push(runningScriptObj); //Push onto runningScripts
|
||||
server.runScript(runningScriptObj, Player); // Push onto runningScripts
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import { joinFaction,
|
||||
purchaseAugmentation } from "./Faction/FactionHelpers";
|
||||
import { FactionWorkType } from "./Faction/FactionWorkTypeEnum";
|
||||
import { getCostOfNextHacknetNode,
|
||||
purchaseHacknet } from "./HacknetNode";
|
||||
purchaseHacknet } from "./Hacknet/HacknetNode";
|
||||
import {Locations} from "./Locations";
|
||||
import { Message } from "./Message/Message";
|
||||
import { Messages } from "./Message/MessageHelpers";
|
||||
@ -279,27 +279,27 @@ function NetscriptFunctions(workerScript) {
|
||||
},
|
||||
upgradeLevel : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseLevelUpgrade(n);
|
||||
return node.purchaseLevelUpgrade(n, Player);
|
||||
},
|
||||
upgradeRam : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseRamUpgrade(n);
|
||||
return node.purchaseRamUpgrade(n, Player);
|
||||
},
|
||||
upgradeCore : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseCoreUpgrade(n);
|
||||
return node.purchaseCoreUpgrade(n, Player);
|
||||
},
|
||||
getLevelUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateLevelUpgradeCost(n);
|
||||
return node.calculateLevelUpgradeCost(n, Player);
|
||||
},
|
||||
getRamUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateRamUpgradeCost(n);
|
||||
return node.calculateRamUpgradeCost(n, Player);
|
||||
},
|
||||
getCoreUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateCoreUpgradeCost(n);
|
||||
return node.calculateCoreUpgradeCost(n, Player);
|
||||
}
|
||||
},
|
||||
sprintf : sprintf,
|
||||
@ -4976,9 +4976,9 @@ function NetscriptFunctions(workerScript) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sl = Player.sleeves[i];
|
||||
const sl = Player.sleeves[sleeveNumber];
|
||||
return {
|
||||
shock: sl.shock,
|
||||
shock: 100 - sl.shock,
|
||||
sync: sl.sync,
|
||||
hacking_skill: sl.hacking_skill,
|
||||
strength: sl.strength,
|
||||
|
@ -9,6 +9,8 @@ import { Sleeve } from "./Sleeve/Sleeve";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { HacknetNode } from "../Hacknet/HacknetNode";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile";
|
||||
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
||||
|
||||
@ -22,7 +24,7 @@ export interface IPlayer {
|
||||
corporation: any;
|
||||
currentServer: string;
|
||||
factions: string[];
|
||||
hacknetNodes: any[];
|
||||
hacknetNodes: (HacknetNode | HacknetServer)[];
|
||||
hasWseAccount: boolean;
|
||||
jobs: IMap<string>;
|
||||
karma: number;
|
||||
|
@ -96,7 +96,7 @@ export function createResleevesPage(p: IPlayer) {
|
||||
display: "inline-block",
|
||||
innerText: "Sort By: "
|
||||
});
|
||||
UIElems.sortSelector = createElement("select") as HTMLSelectElement;
|
||||
UIElems.sortSelector = createElement("select",{class:"dropdown"}) as HTMLSelectElement;
|
||||
|
||||
enum SortOption {
|
||||
Cost = "Cost",
|
||||
@ -309,7 +309,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
|
||||
elems.statsPanel.appendChild(elems.multipliersButton);
|
||||
|
||||
elems.augPanel = createElement("div", { class: "resleeve-panel", width: "50%" });
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector" }) as HTMLSelectElement;
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector dropdown" }) as HTMLSelectElement;
|
||||
elems.augDescription = createElement("p");
|
||||
for (let i = 0; i < resleeve.augmentations.length; ++i) {
|
||||
elems.augSelector.add(createOptionElement(resleeve.augmentations[i].name));
|
||||
|
@ -120,7 +120,7 @@ export class Sleeve extends Person {
|
||||
memory: number = 0;
|
||||
|
||||
/**
|
||||
* Sleeve shock. Number between 1 and 100
|
||||
* Sleeve shock. Number between 0 and 100
|
||||
* Trauma/shock that comes with being in a sleeve. Experience earned
|
||||
* is multipled by shock%. This gets applied before synchronization
|
||||
*
|
||||
|
@ -288,7 +288,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
}
|
||||
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
|
||||
elems.taskSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskSelector = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskSelector.add(createOptionElement("------"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Company"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Faction"));
|
||||
@ -297,8 +297,8 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
elems.taskSelector.add(createOptionElement("Workout at Gym"));
|
||||
elems.taskSelector.add(createOptionElement("Shock Recovery"));
|
||||
elems.taskSelector.add(createOptionElement("Synchronize"));
|
||||
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskDescription = createElement("p");
|
||||
elems.taskProgressBar = createElement("p");
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
|
@ -21,6 +21,7 @@ import { Faction } from "./Faction/Faction";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { displayFactionContent } from "./Faction/FactionHelpers";
|
||||
import {Gang, resetGangs} from "./Gang";
|
||||
import { HashManager } from "./Hacknet/HashManager";
|
||||
import {Locations} from "./Locations";
|
||||
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
|
||||
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
|
||||
@ -111,10 +112,13 @@ function PlayerObject() {
|
||||
// Company at which player is CURRENTLY working (only valid when the player is actively working)
|
||||
this.companyName = ""; // Name of Company. Must match a key value in Companies map
|
||||
|
||||
//Servers
|
||||
// Servers
|
||||
this.currentServer = ""; //IP address of Server currently being accessed through terminal
|
||||
this.purchasedServers = []; //IP Addresses of purchased servers
|
||||
|
||||
// Hacknet Nodes/Servers
|
||||
this.hacknetNodes = [];
|
||||
this.hashManager = new HashManager();
|
||||
|
||||
//Factions
|
||||
this.factions = []; //Names of all factions player has joined
|
||||
@ -1391,45 +1395,46 @@ PlayerObject.prototype.startClass = function(costMult, expMult, className) {
|
||||
//Find cost and exp gain per game cycle
|
||||
var cost = 0;
|
||||
var hackExp = 0, strExp = 0, defExp = 0, dexExp = 0, agiExp = 0, chaExp = 0;
|
||||
const hashManager = this.hashManager;
|
||||
switch (className) {
|
||||
case CONSTANTS.ClassStudyComputerScience:
|
||||
hackExp = baseStudyComputerScienceExp * expMult / gameCPS;
|
||||
hackExp = baseStudyComputerScienceExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassDataStructures:
|
||||
cost = CONSTANTS.ClassDataStructuresBaseCost * costMult / gameCPS;
|
||||
hackExp = baseDataStructuresExp * expMult / gameCPS;
|
||||
hackExp = baseDataStructuresExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassNetworks:
|
||||
cost = CONSTANTS.ClassNetworksBaseCost * costMult / gameCPS;
|
||||
hackExp = baseNetworksExp * expMult / gameCPS;
|
||||
hackExp = baseNetworksExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassAlgorithms:
|
||||
cost = CONSTANTS.ClassAlgorithmsBaseCost * costMult / gameCPS;
|
||||
hackExp = baseAlgorithmsExp * expMult / gameCPS;
|
||||
hackExp = baseAlgorithmsExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassManagement:
|
||||
cost = CONSTANTS.ClassManagementBaseCost * costMult / gameCPS;
|
||||
chaExp = baseManagementExp * expMult / gameCPS;
|
||||
chaExp = baseManagementExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassLeadership:
|
||||
cost = CONSTANTS.ClassLeadershipBaseCost * costMult / gameCPS;
|
||||
chaExp = baseLeadershipExp * expMult / gameCPS;
|
||||
chaExp = baseLeadershipExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassGymStrength:
|
||||
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
|
||||
strExp = baseGymExp * expMult / gameCPS;
|
||||
strExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult();
|
||||
break;
|
||||
case CONSTANTS.ClassGymDefense:
|
||||
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
|
||||
defExp = baseGymExp * expMult / gameCPS;
|
||||
defExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult();
|
||||
break;
|
||||
case CONSTANTS.ClassGymDexterity:
|
||||
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
|
||||
dexExp = baseGymExp * expMult / gameCPS;
|
||||
dexExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult();
|
||||
break;
|
||||
case CONSTANTS.ClassGymAgility:
|
||||
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
|
||||
agiExp = baseGymExp * expMult / gameCPS;
|
||||
agiExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult();
|
||||
break;
|
||||
default:
|
||||
throw new Error("ERR: Invalid/unrecognized class name");
|
||||
|
@ -213,9 +213,7 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
|
||||
var elemId = "bitnode-" + i.toString();
|
||||
var elem = clearEventListeners(elemId);
|
||||
if (elem == null) {return;}
|
||||
if (i === 1 || i === 2 || i === 3 || i === 4 || i === 5 ||
|
||||
i === 6 || i === 7 || i === 8 || i === 10 || i === 11 ||
|
||||
i === 12) {
|
||||
if (i >= 1 && i <= 12) {
|
||||
elem.addEventListener("click", function() {
|
||||
var bitNodeKey = "BitNode" + i;
|
||||
var bitNode = BitNodes[bitNodeKey];
|
||||
|
@ -10,7 +10,8 @@ import { processPassiveFactionRepGain } from "./Faction/FactionHelpers";
|
||||
import { loadFconf } from "./Fconf/Fconf";
|
||||
import { FconfSettings } from "./Fconf/FconfSettings";
|
||||
import {loadAllGangs, AllGangs} from "./Gang";
|
||||
import {processAllHacknetNodeEarnings} from "./HacknetNode";
|
||||
import { hasHacknetServers,
|
||||
processHacknetEarnings } from "./Hacknet/HacknetHelpers";
|
||||
import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers";
|
||||
import {Player, loadPlayer} from "./Player";
|
||||
import { loadAllRunningScripts } from "./Script/ScriptHelpers";
|
||||
@ -55,7 +56,6 @@ function BitburnerSaveObject() {
|
||||
this.FconfSettingsSave = "";
|
||||
this.VersionSave = "";
|
||||
this.AllGangsSave = "";
|
||||
this.CorporationResearchTreesSave = "";
|
||||
}
|
||||
|
||||
BitburnerSaveObject.prototype.getSaveString = function() {
|
||||
@ -491,7 +491,10 @@ function loadImportedGame(saveObj, saveString) {
|
||||
}
|
||||
|
||||
//Hacknet Nodes offline progress
|
||||
var offlineProductionFromHacknetNodes = processAllHacknetNodeEarnings(numCyclesOffline);
|
||||
var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline);
|
||||
const hacknetProdInfo = hasHacknetServers() ?
|
||||
`${numeralWrapper.format(offlineProductionFromHacknetNodes, "0.000a")} hashes` :
|
||||
`${numeralWrapper.formatMoney(offlineProductionFromHacknetNodes)}`;
|
||||
|
||||
//Passive faction rep gain offline
|
||||
processPassiveFactionRepGain(numCyclesOffline);
|
||||
@ -516,8 +519,8 @@ function loadImportedGame(saveObj, saveString) {
|
||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
dialogBoxCreate(`Offline for ${timeOfflineString}. While you were offline, your scripts ` +
|
||||
"generated <span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> and your Hacknet Nodes generated <span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(offlineProductionFromHacknetNodes) + "</span>");
|
||||
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> " +
|
||||
"and your Hacknet Nodes generated <span class='money-gold'>" + hacknetProdInfo + "</span>");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,8 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
|
||||
func = workerScript.env.vars.codingcontract[ref];
|
||||
} else if (ref in workerScript.env.vars.gang) {
|
||||
func = workerScript.env.vars.gang[ref];
|
||||
} else if (ref in workerScript.env.vars.sleeve) {
|
||||
func = workerScript.env.vars.sleeve[ref];
|
||||
} else {
|
||||
func = workerScript.env.get(ref);
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ class AceEditorWrapper extends ScriptEditor {
|
||||
});
|
||||
|
||||
//Get functions from namespaces
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang", "sleeve"];
|
||||
if (namespaces.includes(name)) {
|
||||
let namespace = fns[name];
|
||||
if (typeof namespace !== "object") {continue;}
|
||||
|
@ -182,7 +182,7 @@ class CodeMirrorEditorWrapper extends ScriptEditor {
|
||||
netscriptFns.push(name);
|
||||
|
||||
//Get functions from namespaces
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang", "sleeve"];
|
||||
if (namespaces.includes(name)) {
|
||||
let namespace = fnsObj[name];
|
||||
if (typeof namespace !== "object") {continue;}
|
||||
|
206
src/Server/BaseServer.ts
Normal file
206
src/Server/BaseServer.ts
Normal file
@ -0,0 +1,206 @@
|
||||
/**
|
||||
* Abstract Base Class for any Server object
|
||||
*/
|
||||
import { CodingContract } from "../CodingContracts";
|
||||
import { Message } from "../Message/Message";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { Script } from "../Script/Script";
|
||||
import { TextFile } from "../TextFile";
|
||||
|
||||
import { isScriptFilename } from "../Script/ScriptHelpersTS";
|
||||
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
organizationName?: string;
|
||||
}
|
||||
|
||||
export abstract class BaseServer {
|
||||
// Coding Contract files on this server
|
||||
contracts: CodingContract[] = [];
|
||||
|
||||
// How many CPU cores this server has. Maximum of 8.
|
||||
// Currently, this only affects hacking missions
|
||||
cpuCores: number = 1;
|
||||
|
||||
// Flag indicating whether the FTP port is open
|
||||
ftpPortOpen: boolean = false;
|
||||
|
||||
// Flag indicating whether player has admin/root access to this server
|
||||
hasAdminRights: boolean = false;
|
||||
|
||||
// Hostname. Must be unique
|
||||
hostname: string = "";
|
||||
|
||||
// Flag indicating whether HTTP Port is open
|
||||
httpPortOpen: boolean = false;
|
||||
|
||||
// IP Address. Must be unique
|
||||
ip: string = "";
|
||||
|
||||
// Flag indicating whether player is curently connected to this server
|
||||
isConnectedTo: boolean = false;
|
||||
|
||||
// RAM (GB) available on this server
|
||||
maxRam: number = 0;
|
||||
|
||||
// Message files AND Literature files on this Server
|
||||
// For Literature files, this array contains only the filename (string)
|
||||
// For Messages, it contains the actual Message object
|
||||
// TODO Separate literature files into its own property
|
||||
messages: (Message | string)[] = [];
|
||||
|
||||
// Name of company/faction/etc. that this server belongs to.
|
||||
// Optional, not applicable to all Servers
|
||||
organizationName: string = "";
|
||||
|
||||
// Programs on this servers. Contains only the names of the programs
|
||||
programs: string[] = [];
|
||||
|
||||
// RAM (GB) used. i.e. unavailable RAM
|
||||
ramUsed: number = 0;
|
||||
|
||||
// RunningScript files on this server
|
||||
runningScripts: RunningScript[] = [];
|
||||
|
||||
// Script files on this Server
|
||||
scripts: Script[] = [];
|
||||
|
||||
// Contains the IP Addresses of all servers that are immediately
|
||||
// reachable from this one
|
||||
serversOnNetwork: string[] = [];
|
||||
|
||||
// Flag indicating whether SMTP Port is open
|
||||
smtpPortOpen: boolean = false;
|
||||
|
||||
// Flag indicating whether SQL Port is open
|
||||
sqlPortOpen: boolean = false;
|
||||
|
||||
// Flag indicating whether the SSH Port is open
|
||||
sshPortOpen: boolean = false;
|
||||
|
||||
// Text files on this server
|
||||
textFiles: TextFile[] = [];
|
||||
|
||||
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
|
||||
this.ip = params.ip ? params.ip : createRandomIp();
|
||||
|
||||
this.hostname = params.hostname;
|
||||
this.organizationName = params.organizationName != null ? params.organizationName : "";
|
||||
this.isConnectedTo = params.isConnectedTo != null ? params.isConnectedTo : false;
|
||||
|
||||
//Access information
|
||||
this.hasAdminRights = params.adminRights != null ? params.adminRights : false;
|
||||
}
|
||||
|
||||
addContract(contract: CodingContract) {
|
||||
this.contracts.push(contract);
|
||||
}
|
||||
|
||||
getContract(contractName: string): CodingContract | null {
|
||||
for (const contract of this.contracts) {
|
||||
if (contract.fn === contractName) {
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Given the name of the script, returns the corresponding
|
||||
// script object on the server (if it exists)
|
||||
getScript(scriptName: string): Script | null {
|
||||
for (let i = 0; i < this.scripts.length; i++) {
|
||||
if (this.scripts[i].filename === scriptName) {
|
||||
return this.scripts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
removeContract(contract: CodingContract) {
|
||||
if (contract instanceof CodingContract) {
|
||||
this.contracts = this.contracts.filter((c) => {
|
||||
return c.fn !== contract.fn;
|
||||
});
|
||||
} else {
|
||||
this.contracts = this.contracts.filter((c) => {
|
||||
return c.fn !== contract;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a script is run on this server.
|
||||
* All this function does is add a RunningScript object to the
|
||||
* `runningScripts` array. It does NOT check whether the script actually can
|
||||
* be run.
|
||||
*/
|
||||
runScript(script: RunningScript): void {
|
||||
this.runningScripts.push(script);
|
||||
}
|
||||
|
||||
setMaxRam(ram: number): void {
|
||||
this.maxRam = ram;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to a script file
|
||||
* Overwrites existing files. Creates new files if the script does not eixst
|
||||
*/
|
||||
writeToScriptFile(fn: string, code: string) {
|
||||
var ret = {success: false, overwritten: false};
|
||||
if (!isScriptFilename(fn)) { return ret; }
|
||||
|
||||
//Check if the script already exists, and overwrite it if it does
|
||||
for (let i = 0; i < this.scripts.length; ++i) {
|
||||
if (fn === this.scripts[i].filename) {
|
||||
let script = this.scripts[i];
|
||||
script.code = code;
|
||||
script.updateRamUsage();
|
||||
script.module = "";
|
||||
ret.overwritten = true;
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise, create a new script
|
||||
const newScript = new Script();
|
||||
newScript.filename = fn;
|
||||
newScript.code = code;
|
||||
newScript.updateRamUsage();
|
||||
newScript.server = this.ip;
|
||||
this.scripts.push(newScript);
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write to a text file
|
||||
// Overwrites existing files. Creates new files if the text file does not exist
|
||||
writeToTextFile(fn: string, txt: string) {
|
||||
var ret = { success: false, overwritten: false };
|
||||
if (!fn.endsWith("txt")) { return ret; }
|
||||
|
||||
//Check if the text file already exists, and overwrite if it does
|
||||
for (let i = 0; i < this.textFiles.length; ++i) {
|
||||
if (this.textFiles[i].fn === fn) {
|
||||
ret.overwritten = true;
|
||||
this.textFiles[i].text = txt;
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise create a new text file
|
||||
var newFile = new TextFile(fn, txt);
|
||||
this.textFiles.push(newFile);
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
// Class representing a single generic Server
|
||||
// Class representing a single hackable Server
|
||||
import { BaseServer } from "./BaseServer";
|
||||
|
||||
// TODO This import is a circular import. Try to fix it in the future
|
||||
import { GetServerByHostname } from "./ServerHelpers";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CodingContract } from "../CodingContracts";
|
||||
import { Message } from "../Message/Message";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { Script } from "../Script/Script";
|
||||
import { isScriptFilename } from "../Script/ScriptHelpersTS";
|
||||
import { TextFile } from "../TextFile";
|
||||
|
||||
import { createRandomString } from "../utils/createRandomString";
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
@ -31,7 +27,7 @@ interface IConstructorParams {
|
||||
serverGrowth?: number;
|
||||
}
|
||||
|
||||
export class Server {
|
||||
export class Server extends BaseServer {
|
||||
// Initializes a Server Object from a JSON save state
|
||||
static fromJSON(value: any): Server {
|
||||
return Generic_fromJSON(Server, value.data);
|
||||
@ -41,47 +37,13 @@ export class Server {
|
||||
// (i.e. security level when the server was created)
|
||||
baseDifficulty: number = 1;
|
||||
|
||||
// Coding Contract files on this server
|
||||
contracts: CodingContract[] = [];
|
||||
|
||||
// How many CPU cores this server has. Maximum of 8.
|
||||
// Currently, this only affects hacking missions
|
||||
cpuCores: number = 1;
|
||||
|
||||
// Flag indicating whether the FTP port is open
|
||||
ftpPortOpen: boolean = false;
|
||||
|
||||
// Server Security Level
|
||||
hackDifficulty: number = 1;
|
||||
|
||||
// Flag indicating whether player has admin/root access to this server
|
||||
hasAdminRights: boolean = false;
|
||||
|
||||
// Hostname. Must be unique
|
||||
hostname: string = "";
|
||||
|
||||
// Flag indicating whether HTTP Port is open
|
||||
httpPortOpen: boolean = false;
|
||||
|
||||
// IP Address. Must be unique
|
||||
ip: string = "";
|
||||
|
||||
// Flag indicating whether player is curently connected to this server
|
||||
isConnectedTo: boolean = false;
|
||||
|
||||
// Flag indicating whether this server has been manually hacked (ie.
|
||||
// hacked through Terminal) by the player
|
||||
manuallyHacked: boolean = false;
|
||||
|
||||
// RAM (GB) available on this server
|
||||
maxRam: number = 0;
|
||||
|
||||
// Message files AND Literature files on this Server
|
||||
// For Literature files, this array contains only the filename (string)
|
||||
// For Messages, it contains the actual Message object
|
||||
// TODO Separate literature files into its own property
|
||||
messages: (Message | string)[] = [];
|
||||
|
||||
// Minimum server security level that this server can be weakened to
|
||||
minDifficulty: number = 1;
|
||||
|
||||
@ -97,67 +59,35 @@ export class Server {
|
||||
// How many ports are currently opened on the server
|
||||
openPortCount: number = 0;
|
||||
|
||||
// Name of company/faction/etc. that this server belongs to.
|
||||
// Optional, not applicable to all Servers
|
||||
organizationName: string = "";
|
||||
|
||||
// Programs on this servers. Contains only the names of the programs
|
||||
programs: string[] = [];
|
||||
|
||||
// Flag indicating wehther this is a purchased server
|
||||
purchasedByPlayer: boolean = false;
|
||||
|
||||
// RAM (GB) used. i.e. unavailable RAM
|
||||
ramUsed: number = 0;
|
||||
|
||||
// Hacking level required to hack this server
|
||||
requiredHackingSkill: number = 1;
|
||||
|
||||
// RunningScript files on this server
|
||||
runningScripts: RunningScript[] = [];
|
||||
|
||||
// Script files on this Server
|
||||
scripts: Script[] = [];
|
||||
|
||||
// Parameter that affects how effectively this server's money can
|
||||
// be increased using the grow() Netscript function
|
||||
serverGrowth: number = 1;
|
||||
|
||||
// Contains the IP Addresses of all servers that are immediately
|
||||
// reachable from this one
|
||||
serversOnNetwork: string[] = [];
|
||||
|
||||
// Flag indicating whether SMTP Port is open
|
||||
smtpPortOpen: boolean = false;
|
||||
|
||||
// Flag indicating whether SQL Port is open
|
||||
sqlPortOpen: boolean = false;
|
||||
|
||||
// Flag indicating whether the SSH Port is open
|
||||
sshPortOpen: boolean = false;
|
||||
|
||||
// Text files on this server
|
||||
textFiles: TextFile[] = [];
|
||||
|
||||
constructor(params: IConstructorParams={hostname: "", ip: createRandomIp() }) {
|
||||
/* Properties */
|
||||
//Connection information
|
||||
this.ip = params.ip ? params.ip : createRandomIp();
|
||||
super(params);
|
||||
|
||||
var hostname = params.hostname;
|
||||
var i = 0;
|
||||
var suffix = "";
|
||||
while (GetServerByHostname(hostname+suffix) != null) {
|
||||
//Server already exists
|
||||
suffix = "-" + i;
|
||||
++i;
|
||||
// "hacknet-node-X" hostnames are reserved for Hacknet Servers
|
||||
if (this.hostname.startsWith("hacknet-node-")) {
|
||||
this.hostname = createRandomString(10);
|
||||
}
|
||||
|
||||
// Validate hostname by ensuring there are no repeats
|
||||
if (GetServerByHostname(this.hostname) != null) {
|
||||
// Use a for loop to ensure that we don't get suck in an infinite loop somehow
|
||||
let hostname: string = this.hostname;
|
||||
for (let i = 0; i < 200; ++i) {
|
||||
hostname = `${this.hostname}-${i}`;
|
||||
if (GetServerByHostname(hostname) == null) { break; }
|
||||
}
|
||||
this.hostname = hostname;
|
||||
}
|
||||
this.hostname = hostname + suffix;
|
||||
this.organizationName = params.organizationName != null ? params.organizationName : "";
|
||||
this.isConnectedTo = params.isConnectedTo != null ? params.isConnectedTo : false;
|
||||
|
||||
//Access information
|
||||
this.hasAdminRights = params.adminRights != null ? params.adminRights : false;
|
||||
this.purchasedByPlayer = params.purchasedByPlayer != null ? params.purchasedByPlayer : false;
|
||||
|
||||
//RAM, CPU speed and Scripts
|
||||
@ -178,23 +108,9 @@ export class Server {
|
||||
this.numOpenPortsRequired = params.numOpenPortsRequired != null ? params.numOpenPortsRequired : 5;
|
||||
};
|
||||
|
||||
setMaxRam(ram: number): void {
|
||||
this.maxRam = ram;
|
||||
}
|
||||
|
||||
// Given the name of the script, returns the corresponding
|
||||
// script object on the server (if it exists)
|
||||
getScript(scriptName: string): Script | null {
|
||||
for (let i = 0; i < this.scripts.length; i++) {
|
||||
if (this.scripts[i].filename === scriptName) {
|
||||
return this.scripts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensures that the server's difficulty (server security) doesn't get too high
|
||||
/**
|
||||
* Ensures that the server's difficulty (server security) doesn't get too high
|
||||
*/
|
||||
capDifficulty(): void {
|
||||
if (this.hackDifficulty < this.minDifficulty) {this.hackDifficulty = this.minDifficulty;}
|
||||
if (this.hackDifficulty < 1) {this.hackDifficulty = 1;}
|
||||
@ -204,97 +120,54 @@ export class Server {
|
||||
if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;}
|
||||
}
|
||||
|
||||
// Strengthens a server's security level (difficulty) by the specified amount
|
||||
/**
|
||||
* Change this server's minimum security
|
||||
* @param n - Value by which to increase/decrease the server's minimum security
|
||||
* @param perc - Whether it should be changed by a percentage, or a flat value
|
||||
*/
|
||||
changeMinimumSecurity(n: number, perc: boolean=false): void {
|
||||
if (perc) {
|
||||
this.minDifficulty *= n;
|
||||
} else {
|
||||
this.minDifficulty += n;
|
||||
}
|
||||
|
||||
// Server security cannot go below 1
|
||||
this.minDifficulty = Math.max(1, this.minDifficulty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strengthens a server's security level (difficulty) by the specified amount
|
||||
*/
|
||||
fortify(amt: number): void {
|
||||
this.hackDifficulty += amt;
|
||||
this.capDifficulty();
|
||||
}
|
||||
|
||||
// Lowers the server's security level (difficulty) by the specified amount)
|
||||
/**
|
||||
* Change this server's maximum money
|
||||
* @param n - Value by which to change the server's maximum money
|
||||
* @param perc - Whether it should be changed by a percentage, or a flat value
|
||||
*/
|
||||
changeMaximumMoney(n: number, perc: boolean=false): void {
|
||||
if (perc) {
|
||||
this.moneyMax *= n;
|
||||
} else {
|
||||
this.moneyMax += n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowers the server's security level (difficulty) by the specified amount)
|
||||
*/
|
||||
weaken(amt: number): void {
|
||||
this.hackDifficulty -= (amt * BitNodeMultipliers.ServerWeakenRate);
|
||||
this.capDifficulty();
|
||||
}
|
||||
|
||||
// Write to a script file
|
||||
// Overwrites existing files. Creates new files if the script does not eixst
|
||||
writeToScriptFile(fn: string, code: string) {
|
||||
var ret = {success: false, overwritten: false};
|
||||
if (!isScriptFilename(fn)) { return ret; }
|
||||
|
||||
//Check if the script already exists, and overwrite it if it does
|
||||
for (let i = 0; i < this.scripts.length; ++i) {
|
||||
if (fn === this.scripts[i].filename) {
|
||||
let script = this.scripts[i];
|
||||
script.code = code;
|
||||
script.updateRamUsage();
|
||||
script.module = "";
|
||||
ret.overwritten = true;
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise, create a new script
|
||||
const newScript = new Script();
|
||||
newScript.filename = fn;
|
||||
newScript.code = code;
|
||||
newScript.updateRamUsage();
|
||||
newScript.server = this.ip;
|
||||
this.scripts.push(newScript);
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write to a text file
|
||||
// Overwrites existing files. Creates new files if the text file does not exist
|
||||
writeToTextFile(fn: string, txt: string) {
|
||||
var ret = { success: false, overwritten: false };
|
||||
if (!fn.endsWith("txt")) { return ret; }
|
||||
|
||||
//Check if the text file already exists, and overwrite if it does
|
||||
for (let i = 0; i < this.textFiles.length; ++i) {
|
||||
if (this.textFiles[i].fn === fn) {
|
||||
ret.overwritten = true;
|
||||
this.textFiles[i].text = txt;
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise create a new text file
|
||||
var newFile = new TextFile(fn, txt);
|
||||
this.textFiles.push(newFile);
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
addContract(contract: CodingContract) {
|
||||
this.contracts.push(contract);
|
||||
}
|
||||
|
||||
removeContract(contract: CodingContract) {
|
||||
if (contract instanceof CodingContract) {
|
||||
this.contracts = this.contracts.filter((c) => {
|
||||
return c.fn !== contract.fn;
|
||||
});
|
||||
} else {
|
||||
this.contracts = this.contracts.filter((c) => {
|
||||
return c.fn !== contract;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getContract(contractName: string) {
|
||||
for (const contract of this.contracts) {
|
||||
if (contract.fn === contractName) {
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
/**
|
||||
* Serialize the current object to a JSON save state
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("Server", this);
|
||||
}
|
||||
|
@ -175,6 +175,11 @@ let StockMarket = {} //Full name to stock object
|
||||
let StockSymbols = {} //Full name to symbol
|
||||
let SymbolToStockMap = {}; //Symbol to Stock object
|
||||
|
||||
let formatHelpData = {
|
||||
longestName: 0,
|
||||
longestSymbol: 0,
|
||||
};
|
||||
|
||||
function loadStockMarket(saveString) {
|
||||
if (saveString === "") {
|
||||
StockMarket = {};
|
||||
@ -220,6 +225,11 @@ function initStockSymbols() {
|
||||
StockSymbols["Catalyst Ventures"] = "CTYS";
|
||||
StockSymbols["Microdyne Technologies"] = "MDYN";
|
||||
StockSymbols["Titan Laboratories"] = "TITN";
|
||||
|
||||
for (const key in StockSymbols) {
|
||||
formatHelpData.longestName = key.length > formatHelpData.longestName ? key.length : formatHelpData.longestName;
|
||||
formatHelpData.longestSymbol = StockSymbols[key].length > formatHelpData.longestSymbol ? StockSymbols[key].length : formatHelpData.longestSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
function initStockMarket() {
|
||||
@ -1070,10 +1080,11 @@ function createStockTicker(stock) {
|
||||
return;
|
||||
}
|
||||
var tickerId = "stock-market-ticker-" + stock.symbol;
|
||||
var li = document.createElement("li"), hdr = document.createElement("button");
|
||||
var li = document.createElement("li"), hdr = document.createElement("button"), hdrpre = document.createElement("pre");;
|
||||
hdr.classList.add("accordion-header");
|
||||
hdr.setAttribute("id", tickerId + "-hdr");
|
||||
hdr.innerHTML = stock.name + " - " + stock.symbol + " - " + numeralWrapper.format(stock.price, '($0.000a)');
|
||||
hdrpre.textContent = stock.name + " - " + stock.symbol + " - " + numeralWrapper.format(stock.price, '($0.000a)');
|
||||
hdr.appendChild(hdrpre);
|
||||
|
||||
//Div for entire panel
|
||||
var stockDiv = document.createElement("div");
|
||||
@ -1082,8 +1093,8 @@ function createStockTicker(stock) {
|
||||
|
||||
/* Create panel DOM */
|
||||
var qtyInput = document.createElement("input"),
|
||||
longShortSelect = document.createElement("select"),
|
||||
orderTypeSelect = document.createElement("select"),
|
||||
longShortSelect = document.createElement("select", {class: "dropdown"}),
|
||||
orderTypeSelect = document.createElement("select", {class: "dropdown"}),
|
||||
buyButton = document.createElement("span"),
|
||||
sellButton = document.createElement("span"),
|
||||
buyMaxButton = document.createElement("span"),
|
||||
@ -1102,6 +1113,7 @@ function createStockTicker(stock) {
|
||||
" || (event.keyCode==46) )");
|
||||
|
||||
longShortSelect.classList.add("stock-market-input");
|
||||
longShortSelect.classList.add("dropdown");
|
||||
longShortSelect.setAttribute("id", tickerId + "-pos-selector");
|
||||
var longOpt = document.createElement("option");
|
||||
longOpt.text = "Long";
|
||||
@ -1113,6 +1125,7 @@ function createStockTicker(stock) {
|
||||
}
|
||||
|
||||
orderTypeSelect.classList.add("stock-market-input");
|
||||
orderTypeSelect.classList.add("dropdown");
|
||||
orderTypeSelect.setAttribute("id", tickerId + "-order-selector");
|
||||
var marketOpt = document.createElement("option");
|
||||
marketOpt.text = "Market Order";
|
||||
@ -1352,19 +1365,15 @@ function updateStockTicker(stock, increase) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
let hdrText = stock.name + " (" + stock.symbol + ") - " + numeralWrapper.format(stock.price, '($0.000a)');
|
||||
const stockPriceFormat = numeralWrapper.format(stock.price, '($0.000a)')
|
||||
let hdrText = `${stock.name}${" ".repeat(1 + formatHelpData.longestName - stock.name.length + (formatHelpData.longestSymbol-stock.symbol.length))}${stock.symbol} -${" ".repeat(10 - stockPriceFormat.length)}${stockPriceFormat}`;
|
||||
if (Player.has4SData) {
|
||||
hdrText += " - Volatility: " + numeralWrapper.format(stock.mv, '0,0.00') + "%" +
|
||||
" - Price Forecast: ";
|
||||
if (stock.b) {
|
||||
hdrText += "+".repeat(Math.floor(stock.otlkMag/10) + 1);
|
||||
} else {
|
||||
hdrText += "-".repeat(Math.floor(stock.otlkMag/10) + 1);
|
||||
}
|
||||
hdrText += ` - Volatility: ${numeralWrapper.format(stock.mv, '0,0.00')}% - Price Forecast: `;
|
||||
hdrText += (stock.b?"+":"-").repeat(Math.floor(stock.otlkMag/10) + 1);
|
||||
}
|
||||
hdr.innerText = hdrText;
|
||||
hdr.firstChild.textContent = hdrText;
|
||||
if (increase != null) {
|
||||
increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red";
|
||||
hdr.firstChild.style.color = increase ? "#66ff33" : "red";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import {calculateHackingChance,
|
||||
calculateHackingTime,
|
||||
calculateGrowTime,
|
||||
calculateWeakenTime} from "./Hacking";
|
||||
import { HacknetServer } from "./Hacknet/HacknetServer";
|
||||
import {TerminalHelpText, HelpTexts} from "./HelpText";
|
||||
import {iTutorialNextStep, iTutorialSteps,
|
||||
ITutorial} from "./InteractiveTutorial";
|
||||
@ -760,18 +761,19 @@ let Terminal = {
|
||||
finishAnalyze: function(cancelled = false) {
|
||||
if (cancelled == false) {
|
||||
let currServ = Player.getCurrentServer();
|
||||
const isHacknet = currServ instanceof HacknetServer;
|
||||
post(currServ.hostname + ": ");
|
||||
post("Organization name: " + currServ.organizationName);
|
||||
var rootAccess = "";
|
||||
if (currServ.hasAdminRights) {rootAccess = "YES";}
|
||||
else {rootAccess = "NO";}
|
||||
post("Root Access: " + rootAccess);
|
||||
post("Required hacking skill: " + currServ.requiredHackingSkill);
|
||||
if (!isHacknet) { post("Required hacking skill: " + currServ.requiredHackingSkill); }
|
||||
post("Server security level: " + numeralWrapper.format(currServ.hackDifficulty, '0.000a'));
|
||||
post("Chance to hack: " + numeralWrapper.format(calculateHackingChance(currServ), '0.00%'));
|
||||
post("Time to hack: " + numeralWrapper.format(calculateHackingTime(currServ), '0.000') + " seconds");
|
||||
post("Total money available on server: " + numeralWrapper.format(currServ.moneyAvailable, '$0,0.00'));
|
||||
post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired);
|
||||
if (!isHacknet) { post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired); }
|
||||
|
||||
if (currServ.sshPortOpen) {
|
||||
post("SSH port: Open")
|
||||
@ -2240,7 +2242,7 @@ let Terminal = {
|
||||
post("May take a few seconds to start up the process...");
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = numThreads;
|
||||
server.runningScripts.push(runningScriptObj);
|
||||
server.runScript(runningScriptObj, Player);
|
||||
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
return;
|
||||
|
@ -30,8 +30,10 @@ import { FconfSettings } from "./Fconf/FconfSetti
|
||||
import {displayLocationContent,
|
||||
initLocationButtons} from "./Location";
|
||||
import {Locations} from "./Locations";
|
||||
import {displayHacknetNodesContent, processAllHacknetNodeEarnings,
|
||||
updateHacknetNodesContent} from "./HacknetNode";
|
||||
import { hasHacknetServers,
|
||||
renderHacknetNodesUI,
|
||||
clearHacknetNodesUI,
|
||||
processHacknetEarnings } from "./Hacknet/HacknetHelpers";
|
||||
import {iTutorialStart} from "./InteractiveTutorial";
|
||||
import {initLiterature} from "./Literature";
|
||||
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
|
||||
@ -109,6 +111,7 @@ import "../css/mainmenu.scss";
|
||||
import "../css/characteroverview.scss";
|
||||
import "../css/terminal.scss";
|
||||
import "../css/scripteditor.scss";
|
||||
import "../css/hacknetnodes.scss";
|
||||
import "../css/menupages.scss";
|
||||
import "../css/redpill.scss";
|
||||
import "../css/stockmarket.scss";
|
||||
@ -124,6 +127,8 @@ import "../css/gang.scss";
|
||||
import "../css/sleeves.scss";
|
||||
import "../css/resleeving.scss";
|
||||
import "../css/treant.css";
|
||||
import "../css/grid.min.css";
|
||||
import "../css/dev-menu.css";
|
||||
|
||||
|
||||
/* Shortcuts to navigate through the game
|
||||
@ -292,8 +297,8 @@ const Engine = {
|
||||
loadHacknetNodesContent: function() {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.hacknetNodesContent.style.display = "block";
|
||||
displayHacknetNodesContent();
|
||||
routing.navigateTo(Page.HacknetNodes);
|
||||
renderHacknetNodesUI();
|
||||
MainMenuLinks.HacknetNodes.classList.add("active");
|
||||
},
|
||||
|
||||
@ -504,7 +509,7 @@ const Engine = {
|
||||
Engine.Display.characterContent.style.display = "none";
|
||||
Engine.Display.scriptEditorContent.style.display = "none";
|
||||
Engine.Display.activeScriptsContent.style.display = "none";
|
||||
Engine.Display.hacknetNodesContent.style.display = "none";
|
||||
clearHacknetNodesUI();
|
||||
Engine.Display.worldContent.style.display = "none";
|
||||
Engine.Display.createProgramContent.style.display = "none";
|
||||
Engine.Display.factionsContent.style.display = "none";
|
||||
@ -868,7 +873,7 @@ const Engine = {
|
||||
updateOnlineScriptTimes(numCycles);
|
||||
|
||||
//Hacknet Nodes
|
||||
processAllHacknetNodeEarnings(numCycles);
|
||||
processHacknetEarnings(numCycles);
|
||||
},
|
||||
|
||||
//Counters for the main event loop. Represent the number of game cycles are required
|
||||
@ -930,7 +935,7 @@ const Engine = {
|
||||
if (Engine.Counters.updateDisplays <= 0) {
|
||||
Engine.displayCharacterOverviewInfo();
|
||||
if (routing.isOn(Page.HacknetNodes)) {
|
||||
updateHacknetNodesContent();
|
||||
renderHacknetNodesUI();
|
||||
} else if (routing.isOn(Page.CreateProgram)) {
|
||||
displayCreateProgramContent();
|
||||
} else if (routing.isOn(Page.Sleeves)) {
|
||||
@ -1181,7 +1186,10 @@ const Engine = {
|
||||
}
|
||||
|
||||
//Hacknet Nodes offline progress
|
||||
var offlineProductionFromHacknetNodes = processAllHacknetNodeEarnings(numCyclesOffline);
|
||||
var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline);
|
||||
const hacknetProdInfo = hasHacknetServers() ?
|
||||
`${numeralWrapper.format(offlineProductionFromHacknetNodes, "0.000a")} hashes` :
|
||||
`${numeralWrapper.formatMoney(offlineProductionFromHacknetNodes)}`;
|
||||
|
||||
//Passive faction rep gain offline
|
||||
processPassiveFactionRepGain(numCyclesOffline);
|
||||
@ -1235,8 +1243,8 @@ const Engine = {
|
||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
dialogBoxCreate(`Offline for ${timeOfflineString}. While you were offline, your scripts ` +
|
||||
"generated <span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> and your Hacknet Nodes generated <span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(offlineProductionFromHacknetNodes) + "</span>");
|
||||
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> " +
|
||||
"and your Hacknet Nodes generated <span class='money-gold'>" + hacknetProdInfo + "</span>");
|
||||
//Close main menu accordions for loaded game
|
||||
var visibleMenuTabs = [terminal, createScript, activeScripts, stats,
|
||||
hacknetnodes, city, tutorial, options, dev];
|
||||
|
@ -133,7 +133,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<h1 style="color:white;"> Script Editor Options </h1>
|
||||
<fieldset>
|
||||
<label for="script-editor-option-editor">Editor</label>
|
||||
<select id="script-editor-option-editor">
|
||||
<select id="script-editor-option-editor" class="dropdown">
|
||||
<option value="Ace">Ace</option>
|
||||
<option value="CodeMirror">CodeMirror</option>
|
||||
</select>
|
||||
@ -141,12 +141,12 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-theme">Theme</label>
|
||||
<select id="script-editor-option-theme"></select>
|
||||
<select id="script-editor-option-theme" class="dropdown"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-keybinding">Key Binding</label>
|
||||
<select id="script-editor-option-keybinding"></select>
|
||||
<select id="script-editor-option-keybinding" class="dropdown"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
@ -203,35 +203,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
|
||||
<!-- Hacknet Nodes -->
|
||||
<div id="hacknet-nodes-container" class="generic-menupage-container">
|
||||
<h1 id="hacknet-nodes-title"> Hacknet Nodes </h1>
|
||||
<p id="hacknet-nodes-text" class="menu-page-text">
|
||||
The Hacknet is a global, decentralized network of machines. It is used by hackers all around
|
||||
the world to anonymously share computing power and perform distributed cyberattacks without the
|
||||
fear of being traced.
|
||||
<br /><br />
|
||||
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
|
||||
resources to the Hacknet network. This allows you to take a small percentage of profits
|
||||
from hacks performed on the network. Essentially, you are renting out your Node's computing power.
|
||||
<br /><br />
|
||||
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
|
||||
in order to increase its computing power and thereby increase the profit you earn from it.
|
||||
</p>
|
||||
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
|
||||
<br />
|
||||
<div id="hacknet-nodes-money-multipliers-div">
|
||||
<p id="hacknet-nodes-money">
|
||||
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br />
|
||||
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
|
||||
</p>
|
||||
<span id="hacknet-nodes-multipliers">
|
||||
<a id="hacknet-nodes-1x-multiplier" class="a-link-button-inactive"> x1 </a>
|
||||
<a id="hacknet-nodes-5x-multiplier" class="a-link-button"> x5 </a>
|
||||
<a id="hacknet-nodes-10x-multiplier" class="a-link-button"> x10 </a>
|
||||
<a id="hacknet-nodes-max-multiplier" class="a-link-button"> MAX </a>
|
||||
</span>
|
||||
</div>
|
||||
<ul id="hacknet-nodes-list">
|
||||
</ul>
|
||||
<!-- React Component -->
|
||||
</div>
|
||||
|
||||
<!-- World -->
|
||||
@ -702,7 +674,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
|
||||
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
|
||||
<br /><br />
|
||||
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
||||
<input id="stock-market-watchlist-filter" class="text-input" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
||||
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
|
||||
<ul id="stock-market-list" style="list-style:none;">
|
||||
</ul>
|
||||
@ -757,7 +729,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<p id="infiltration-box-text"> </p>
|
||||
|
||||
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br /><br />
|
||||
<select id="infiltration-faction-select"> </select> <br />
|
||||
<select id="infiltration-faction-select" class="dropdown"> </select> <br />
|
||||
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
|
||||
|
||||
</div>
|
||||
@ -944,7 +916,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
Sets the locale for displaying numbers. Defaults to 'en'
|
||||
</span>
|
||||
</label>
|
||||
<select name="settingsLocale" id="settingsLocale">
|
||||
<select name="settingsLocale" id="settingsLocale" class="dropdown">
|
||||
<option value="en">en</option>
|
||||
<option value="bg">bg</option>
|
||||
<option value="cs">cs</option>
|
||||
|
65
src/ui/React/ServerDropdown.jsx
Normal file
65
src/ui/React/ServerDropdown.jsx
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Creates a dropdown (select HTML element) with server hostnames as options
|
||||
*
|
||||
* Configurable to only contain certain types of servers
|
||||
*/
|
||||
import React from "react";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
|
||||
import { HacknetServer } from "../../Hacknet/HacknetServer";
|
||||
|
||||
// TODO make this an enum when this gets converted to TypeScript
|
||||
export const ServerType = {
|
||||
All: 0,
|
||||
Foreign: 1, // Hackable, non-owned servers
|
||||
Owned: 2, // Home Computer, Purchased Servers, and Hacknet Servers
|
||||
Purchased: 3, // Everything from Owned except home computer
|
||||
}
|
||||
|
||||
export class ServerDropdown extends React.Component {
|
||||
/**
|
||||
* Checks if the server should be shown in the dropdown menu, based on the
|
||||
* 'serverType' property
|
||||
*/
|
||||
isValidServer(s) {
|
||||
const type = this.props.serverType;
|
||||
switch (type) {
|
||||
case ServerType.All:
|
||||
return true;
|
||||
case ServerType.Foreign:
|
||||
return (s.hostname !== "home" && !s.purchasedByPlayer);
|
||||
case ServerType.Owned:
|
||||
return s.purchasedByPlayer || (s instanceof HacknetServer) || s.hostname === "home";
|
||||
case ServerType.Purchased:
|
||||
return s.purchasedByPlayer || (s instanceof HacknetServer);
|
||||
default:
|
||||
console.warn(`Invalid ServerType specified for ServerDropdown component: ${type}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a Server object, creates a Option element
|
||||
*/
|
||||
renderOption(s) {
|
||||
return (
|
||||
<option key={s.hostname} value={s.hostname}>{s.hostname}</option>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const servers = [];
|
||||
for (const serverName in AllServers) {
|
||||
const server = AllServers[serverName];
|
||||
if (this.isValidServer(server)) {
|
||||
servers.push(this.renderOption(server));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<select className={"dropdown"} onChange={this.props.onChange} style={this.props.style}>
|
||||
{servers}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
}
|
56
src/ui/React/createPopup.ts
Normal file
56
src/ui/React/createPopup.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Create a pop-up dialog box using React.
|
||||
*
|
||||
* Calling this function with the same ID and React Root Component will trigger a re-render
|
||||
*
|
||||
* @param id The (hopefully) unique identifier for the popup container
|
||||
* @param rootComponent Root React Component
|
||||
*/
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
|
||||
|
||||
type ReactComponent = new(...args: any[]) => React.Component<any, any>;
|
||||
|
||||
export function createPopup(id: string, rootComponent: ReactComponent, props: object): HTMLElement {
|
||||
let container = document.getElementById(id);
|
||||
let content = document.getElementById(`${id}-content`);
|
||||
if (container == null || content == null) {
|
||||
container = createElement("div", {
|
||||
class: "popup-box-container",
|
||||
display: "flex",
|
||||
id: id,
|
||||
});
|
||||
|
||||
content = createElement("div", {
|
||||
class: "popup-box-content",
|
||||
id: `${id}-content`,
|
||||
});
|
||||
|
||||
container.appendChild(content);
|
||||
|
||||
try {
|
||||
document.getElementById("entire-game-container")!.appendChild(container);
|
||||
} catch(e) {
|
||||
console.error(`Exception caught when creating popup: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(React.createElement(rootComponent, props), content);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a popup created with the createPopup() function above
|
||||
*/
|
||||
export function removePopup(id: string): void {
|
||||
let content = document.getElementById(`${id}-content`);
|
||||
if (content == null) { return; }
|
||||
|
||||
ReactDOM.unmountComponentAtNode(content);
|
||||
|
||||
removeElementById(id);
|
||||
}
|
12
src/utils/createRandomString.ts
Normal file
12
src/utils/createRandomString.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// Function that generates a random gibberish string of length n
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
export function createRandomString(n: number): string {
|
||||
let str: string = "";
|
||||
|
||||
for (let i = 0; i < n; ++i) {
|
||||
str += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
@ -30,22 +30,21 @@ function infiltrationSetText(txt) {
|
||||
//ram argument is in GB
|
||||
function infiltrationBoxCreate(inst) {
|
||||
//Gain exp
|
||||
var expMultiplier = 2 * inst.clearanceLevel / inst.maxClearanceLevel;
|
||||
Player.gainHackingExp(inst.hackingExpGained * expMultiplier);
|
||||
Player.gainStrengthExp(inst.strExpGained * expMultiplier);
|
||||
Player.gainDefenseExp(inst.defExpGained * expMultiplier);
|
||||
Player.gainDexterityExp(inst.dexExpGained * expMultiplier);
|
||||
Player.gainAgilityExp(inst.agiExpGained * expMultiplier);
|
||||
Player.gainCharismaExp(inst.chaExpGained * expMultiplier);
|
||||
Player.gainIntelligenceExp(inst.intExpGained * expMultiplier);
|
||||
Player.gainHackingExp(inst.calcGainedHackingExp());
|
||||
Player.gainStrengthExp(inst.calcGainedStrengthExp());
|
||||
Player.gainDefenseExp(inst.calcGainedDefenseExp());
|
||||
Player.gainDexterityExp(inst.calcGainedDexterityExp());
|
||||
Player.gainAgilityExp(inst.calcGainedAgilityExp());
|
||||
Player.gainCharismaExp(inst.calcGainedCharismaExp());
|
||||
Player.gainIntelligenceExp(inst.calcGainedIntelligenceExp());
|
||||
|
||||
const expGainText = ["You gained:",
|
||||
`${formatNumber(inst.hackingExpGained * expMultiplier, 3)} hacking exp`,
|
||||
`${formatNumber(inst.strExpGained * expMultiplier, 3)} str exp`,
|
||||
`${formatNumber(inst.defExpGained * expMultiplier, 3)} def exp`,
|
||||
`${formatNumber(inst.dexExpGained * expMultiplier, 3)} dex exp`,
|
||||
`${formatNumber(inst.agiExpGained * expMultiplier, 3)} agi exp`,
|
||||
`${formatNumber(inst.chaExpGained * expMultiplier, 3)} cha exp`].join("\n");
|
||||
`${formatNumber(inst.calcGainedHackingExp(), 3)} hacking exp`,
|
||||
`${formatNumber(inst.calcGainedStrengthExp(), 3)} str exp`,
|
||||
`${formatNumber(inst.calcGainedDefenseExp(), 3)} def exp`,
|
||||
`${formatNumber(inst.calcGainedDexterityExp(), 3)} dex exp`,
|
||||
`${formatNumber(inst.calcGainedAgilityExp(), 3)} agi exp`,
|
||||
`${formatNumber(inst.calcGainedCharismaExp(), 3)} cha exp`].join("\n");
|
||||
|
||||
var totalValue = 0;
|
||||
for (var i = 0; i < inst.secretsStolen.length; ++i) {
|
||||
|
Loading…
Reference in New Issue
Block a user