mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-17 14:59:16 +02:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcb198220d | ||
|
|
bf1af6a68c | ||
|
|
3dd2975c61 | ||
|
|
33f1e0cb3c | ||
|
|
7514f63dcd | ||
|
|
b6ff73391d | ||
|
|
369ea8d381 | ||
|
|
a7296c512c | ||
|
|
8f70817c10 | ||
|
|
7417fb6ef8 | ||
|
|
d044739f1c | ||
|
|
3d1684f825 | ||
|
|
f6af9e94ab | ||
|
|
215cf59e0b | ||
|
|
0d14cd6e7e | ||
|
|
98a04e4932 | ||
|
|
8d33c5b571 | ||
|
|
221b81d802 | ||
|
|
df89cc5002 | ||
|
|
3b6b37f8a6 | ||
|
|
cf2acb8844 | ||
|
|
2e9b028174 | ||
|
|
c56645c794 | ||
|
|
3ce2e83dd8 | ||
|
|
3241945452 | ||
|
|
fb857642e8 | ||
|
|
cc0e6548ff | ||
|
|
6c3c569a44 | ||
|
|
b5ebbba43d | ||
|
|
bf9b837e31 | ||
|
|
7f88ade30e | ||
|
|
36499ae9f2 | ||
|
|
4b95ba9ed1 | ||
|
|
804e4c23e3 | ||
|
|
b6b6d8e9fa | ||
|
|
c566c838be | ||
|
|
51d9274626 | ||
|
|
c8b478c208 | ||
|
|
18a3f061b4 | ||
|
|
3f8b9e4a32 | ||
|
|
e63ad76701 | ||
|
|
cb66ad9628 | ||
|
|
971bfbada4 | ||
|
|
b646c15521 | ||
|
|
92f7d12c0e | ||
|
|
7172f4e527 | ||
|
|
5592a8bc96 | ||
|
|
c4cb7daac5 | ||
|
|
75bc34208c | ||
|
|
83fc4d81b2 | ||
|
|
3cf18f100a | ||
|
|
8fbb072596 | ||
|
|
ea7f0752cb | ||
|
|
0f8f572519 | ||
|
|
52b6defebd | ||
|
|
34d749809a | ||
|
|
2ce4af2498 | ||
|
|
227bcf146e | ||
|
|
139a5add20 | ||
|
|
3a61a5cfa1 | ||
|
|
96db360a36 |
@@ -1,6 +1,7 @@
|
|||||||
# Bitburner
|
# Bitburner
|
||||||
Bitburner is a cyberpunk hacking-themed incremental game. The game can be
|
Bitburner is a programming-based [incremental game](https://en.wikipedia.org/wiki/Incremental_game)
|
||||||
played at https://danielyxie.github.io/bitburner.
|
that revolves around hacking and cyberpunk themes.
|
||||||
|
The game can be played at https://danielyxie.github.io/bitburner.
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
The game's official documentation can be found on [Read The
|
The game's official documentation can be found on [Read The
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.a-link-button-inactive,
|
.a-link-button-inactive,
|
||||||
|
.std-button-disabled,
|
||||||
.std-button:disabled {
|
.std-button:disabled {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@import "theme";
|
@import "theme";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Styling for the Character Overview Panel (top-right)
|
* Styling for the Character Overview Panel (top-right panel)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#character-overview-wrapper {
|
#character-overview-wrapper {
|
||||||
|
|||||||
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 */
|
||||||
#world-container {
|
#world-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
/* Pop-up boxes */
|
/* Pop-up boxes */
|
||||||
.popup-box-container {
|
.popup-box-container {
|
||||||
display: none; /* Hidden by default */
|
display: none; /* Initially hidden */
|
||||||
position: fixed; /* Stay in place */
|
position: fixed; /* Stay in place */
|
||||||
z-index: 10; /* Sit on top */
|
z-index: 10; /* Sit on top */
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@@ -68,13 +68,14 @@
|
|||||||
@include boxShadow($boxShadowArgs);
|
@include boxShadow($boxShadowArgs);
|
||||||
|
|
||||||
background-color: #555;
|
background-color: #555;
|
||||||
|
border: 2px solid var(--my-highlight-color);
|
||||||
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: center;
|
float: center;
|
||||||
resize: none;
|
|
||||||
color: #fff;
|
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border: 2px solid var(--my-highlight-color);
|
resize: none;
|
||||||
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#script-editor-status {
|
#script-editor-status {
|
||||||
|
|||||||
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
132
dist/engine.css
vendored
132
dist/engine.css
vendored
@@ -486,6 +486,7 @@ button {
|
|||||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
|
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
|
||||||
|
|
||||||
.a-link-button-inactive,
|
.a-link-button-inactive,
|
||||||
|
.std-button-disabled,
|
||||||
.std-button:disabled {
|
.std-button:disabled {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
@@ -501,11 +502,15 @@ button {
|
|||||||
.a-link-button-inactive:hover .tooltiptext,
|
.a-link-button-inactive:hover .tooltiptext,
|
||||||
.a-link-button-inactive:hover .tooltiptexthigh,
|
.a-link-button-inactive:hover .tooltiptexthigh,
|
||||||
.a-link-button-inactive:hover .tooltiptextleft,
|
.a-link-button-inactive:hover .tooltiptextleft,
|
||||||
|
.std-button-disabled:hover .tooltiptext,
|
||||||
|
.std-button-disabled:hover .tooltiptexthigh,
|
||||||
|
.std-button-disabled:hover .tooltiptextleft,
|
||||||
.std-button:disabled:hover .tooltiptext,
|
.std-button:disabled:hover .tooltiptext,
|
||||||
.std-button:disabled:hover .tooltiptexthigh,
|
.std-button:disabled:hover .tooltiptexthigh,
|
||||||
.std-button:disabled:hover .tooltiptextleft {
|
.std-button:disabled:hover .tooltiptextleft {
|
||||||
visibility: visible; }
|
visibility: visible; }
|
||||||
.a-link-button-inactive:active,
|
.a-link-button-inactive:active,
|
||||||
|
.std-button-disabled:active,
|
||||||
.std-button:disabled:active {
|
.std-button:disabled:active {
|
||||||
pointer-events: none; }
|
pointer-events: none; }
|
||||||
|
|
||||||
@@ -671,7 +676,7 @@ button {
|
|||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
/**
|
/**
|
||||||
* Styling for the Character Overview Panel (top-right)
|
* Styling for the Character Overview Panel (top-right panel)
|
||||||
*/
|
*/
|
||||||
#character-overview-wrapper {
|
#character-overview-wrapper {
|
||||||
position: relative; }
|
position: relative; }
|
||||||
@@ -881,13 +886,14 @@ button {
|
|||||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||||
background-color: #555;
|
background-color: #555;
|
||||||
|
border: 2px solid var(--my-highlight-color);
|
||||||
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: center;
|
float: center;
|
||||||
resize: none;
|
|
||||||
color: #fff;
|
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border: 2px solid var(--my-highlight-color); }
|
resize: none;
|
||||||
|
width: 60%; }
|
||||||
|
|
||||||
#script-editor-status {
|
#script-editor-status {
|
||||||
float: left;
|
float: left;
|
||||||
@@ -927,6 +933,64 @@ button {
|
|||||||
|
|
||||||
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
|
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
|
||||||
|
|
||||||
|
/* COLORS */
|
||||||
|
/* Attributes */
|
||||||
|
/**
|
||||||
|
* 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-nodes-container li.hacknet-node {
|
||||||
|
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||||
|
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||||
|
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; }
|
||||||
|
.hacknet-node-container .row {
|
||||||
|
display: table-row;
|
||||||
|
height: 30px; }
|
||||||
|
.hacknet-node-container .row p {
|
||||||
|
display: table-cell; }
|
||||||
|
.hacknet-node-container .upgradable-info {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 4px;
|
||||||
|
/* Don't want the vertical margin/padding, just left & right */
|
||||||
|
padding: 0 4px;
|
||||||
|
width: 64px; }
|
||||||
|
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
||||||
@@ -1044,64 +1108,6 @@ button {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
margin-left: 5%; }
|
margin-left: 5%; }
|
||||||
|
|
||||||
/* 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-nodes-container li.hacknet-node {
|
|
||||||
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
|
||||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
|
||||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
|
||||||
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; }
|
|
||||||
.hacknet-node-container .row {
|
|
||||||
display: table-row;
|
|
||||||
height: 30px; }
|
|
||||||
.hacknet-node-container .row p {
|
|
||||||
display: table-cell; }
|
|
||||||
.hacknet-node-container .upgradable-info {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 4px;
|
|
||||||
/* Don't want the vertical margin/padding, just left & right */
|
|
||||||
padding: 0 4px;
|
|
||||||
width: 64px; }
|
|
||||||
|
|
||||||
.menu-page-text {
|
|
||||||
width: 70vw; }
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container {
|
#world-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1402,7 +1408,7 @@ button {
|
|||||||
/* Pop-up boxes */
|
/* Pop-up boxes */
|
||||||
.popup-box-container {
|
.popup-box-container {
|
||||||
display: none;
|
display: none;
|
||||||
/* Hidden by default */
|
/* Initially hidden */
|
||||||
position: fixed;
|
position: fixed;
|
||||||
/* Stay in place */
|
/* Stay in place */
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|||||||
130
dist/vendor.bundle.js
vendored
130
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@@ -80,6 +80,16 @@ when you normally install Augmentations.
|
|||||||
The cost of purchasing an Augmentation for a Duplicate Sleeve is **not** affected
|
The cost of purchasing an Augmentation for a Duplicate Sleeve is **not** affected
|
||||||
by how many Augmentations you have purchased for yourself, and vice versa.
|
by how many Augmentations you have purchased for yourself, and vice versa.
|
||||||
|
|
||||||
|
Memory
|
||||||
|
~~~~~~
|
||||||
|
Sleeve memory dictates what a sleeve's synchronization will be when its reset by
|
||||||
|
switching BitNodes. For example, if a sleeve has a memory of 10, then when you
|
||||||
|
switch BitNodes its synchronization will initially be set to 10, rather than 1.
|
||||||
|
|
||||||
|
Memory can only be increased by purchasing upgrades from The Covenant.
|
||||||
|
It is a persistent stat, meaning it never gets reset back to 1.
|
||||||
|
The maximum possible value for a sleeve's memory is 100.
|
||||||
|
|
||||||
Re-sleeving
|
Re-sleeving
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ solutions. Some may be numbers, others may be strings or arrays.
|
|||||||
If a contract asks for a specific solution format, then
|
If a contract asks for a specific solution format, then
|
||||||
use that. Otherwise, follow these rules when submitting solutions:
|
use that. Otherwise, follow these rules when submitting solutions:
|
||||||
|
|
||||||
* String-type solutions should not have quotation marks surrounding
|
* String-type solutions should **not** have quotation marks surrounding
|
||||||
the string (unless specifically asked for). Only quotation
|
the string (unless specifically asked for). Only quotation
|
||||||
marks that are part of the actual string solution should be included.
|
marks that are part of the actual string solution should be included.
|
||||||
* Array-type solutions should be submitted with each element
|
* Array-type solutions should be submitted with each element
|
||||||
|
|||||||
@@ -16,6 +16,85 @@ the terminal and enter::
|
|||||||
|
|
||||||
nano .fconf
|
nano .fconf
|
||||||
|
|
||||||
|
|
||||||
|
.. _terminal_filesystem:
|
||||||
|
|
||||||
|
Filesystem (Directories)
|
||||||
|
------------------------
|
||||||
|
The Terminal contains a **very** basic filesystem that allows you to store and
|
||||||
|
organize your files into different directories. Note that this is **not** a true
|
||||||
|
filesystem implementation. Instead, it is done almost entirely using string manipulation.
|
||||||
|
For this reason, many of the nice & useful features you'd find in a real
|
||||||
|
filesystem do not exist.
|
||||||
|
|
||||||
|
Here are the Terminal commands you'll commonly use when dealing with the filesystem.
|
||||||
|
|
||||||
|
* :ref:`ls_terminal_command`
|
||||||
|
* :ref:`cd_terminal_command`
|
||||||
|
* :ref:`mv_terminal_command`
|
||||||
|
|
||||||
|
Directories
|
||||||
|
^^^^^^^^^^^
|
||||||
|
In order to create a directory, simply name a file using a full absolute Linux-style path::
|
||||||
|
|
||||||
|
/scripts/myScript.js
|
||||||
|
|
||||||
|
This will automatically create a "directory" called :code:`scripts`. This will also work
|
||||||
|
for subdirectories::
|
||||||
|
|
||||||
|
/scripts/hacking/helpers/myHelperScripts.script
|
||||||
|
|
||||||
|
Files in the root directory do not need to begin with a forward slash::
|
||||||
|
|
||||||
|
thisIsAFileInTheRootDirectory.txt
|
||||||
|
|
||||||
|
Note that there is no way to manually create or remove directories. The creation and
|
||||||
|
deletion of directories is automatically handled as you name/rename/delete
|
||||||
|
files.
|
||||||
|
|
||||||
|
Absolute vs Relative Paths
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Many Terminal commands accept absolute both absolute and relative paths for specifying a
|
||||||
|
file.
|
||||||
|
|
||||||
|
An absolute path specifies the location of the file from the root directory (/).
|
||||||
|
Any path that begins with the forward slash is an absolute path::
|
||||||
|
|
||||||
|
$ nano /scripts/myScript.js
|
||||||
|
$ cat /serverList.txt
|
||||||
|
|
||||||
|
A relative path specifies the location of the file relative to the current working directory.
|
||||||
|
Any path that does **not** begin with a forward slash is a relative path. Note that the
|
||||||
|
Linux-style dot symbols will work for relative paths::
|
||||||
|
|
||||||
|
. (a single dot) - represents the current directory
|
||||||
|
.. (two dots) - represents the parent directory
|
||||||
|
|
||||||
|
$ cd ..
|
||||||
|
$ nano ../scripts/myScript.js
|
||||||
|
$ nano ../../helper.js
|
||||||
|
|
||||||
|
Netscript
|
||||||
|
^^^^^^^^^
|
||||||
|
Note that in order to reference a file, :ref:`netscript` functions require the
|
||||||
|
**full** absolute file path. For example
|
||||||
|
|
||||||
|
.. code:: javascript
|
||||||
|
|
||||||
|
run("/scripts/hacking/helpers.myHelperScripts.script");
|
||||||
|
rm("/logs/myHackingLogs.txt");
|
||||||
|
rm("thisIsAFileInTheRootDirectory.txt");
|
||||||
|
|
||||||
|
.. note:: A full file path **must** begin with a forward slash (/) if that file
|
||||||
|
is not in the root directory.
|
||||||
|
|
||||||
|
Missing Features
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
These features that are typically in Linux filesystems have not yet been added to the game:
|
||||||
|
|
||||||
|
* Tab autocompletion does not work with relative paths
|
||||||
|
* :code:`mv` only accepts full filepaths for the destination argument. It does not accept directories
|
||||||
|
|
||||||
Commands
|
Commands
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@@ -98,6 +177,25 @@ Display a message (.msg), literature (.lit), or text (.txt) file::
|
|||||||
$ cat foo.lit
|
$ cat foo.lit
|
||||||
$ cat servers.txt
|
$ cat servers.txt
|
||||||
|
|
||||||
|
.. _cd_terminal_command:
|
||||||
|
|
||||||
|
cd
|
||||||
|
^^
|
||||||
|
|
||||||
|
$ cd [dir]
|
||||||
|
|
||||||
|
Change to the specified directory.
|
||||||
|
|
||||||
|
See :ref:`terminal_filesystem` for details on directories.
|
||||||
|
|
||||||
|
Note that this command works even for directories that don't exist. If you change
|
||||||
|
to a directory that doesn't exist, it will not be created. A directory is only created
|
||||||
|
once there is a file in it::
|
||||||
|
|
||||||
|
$ cd scripts/hacking
|
||||||
|
$ cd /logs
|
||||||
|
$ cd ..
|
||||||
|
|
||||||
check
|
check
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
@@ -234,27 +332,35 @@ killall
|
|||||||
|
|
||||||
Kills all scripts on the current server.
|
Kills all scripts on the current server.
|
||||||
|
|
||||||
|
.. _ls_terminal_command:
|
||||||
|
|
||||||
ls
|
ls
|
||||||
^^
|
^^
|
||||||
|
|
||||||
$ ls [| grep pattern]
|
$ ls [dir] [| grep pattern]
|
||||||
|
|
||||||
Prints files on the current server to the Terminal screen.
|
Prints files and directories on the current server to the Terminal screen.
|
||||||
|
|
||||||
If this command is run with no arguments, then it prints all files on the current
|
If this command is run with no arguments, then it prints all files and directories on the current
|
||||||
server to the Terminal screen. The files will be displayed in alphabetical
|
server to the Terminal screen. Directories will be printed first in alphabetical order,
|
||||||
order.
|
followed by the files (also in alphabetical order).
|
||||||
|
|
||||||
The '| grep pattern' is an optional parameter that can be used to only display files
|
The :code:`dir` optional parameter allows you to specify the directory for which to display
|
||||||
whose filenames match the specified pattern. For example, if you wanted to only display
|
files.
|
||||||
files with the .script extension, you could use::
|
|
||||||
|
|
||||||
|
The :code:`| grep pattern` optional parameter allows you to only display files and directories
|
||||||
|
with a certain pattern in their names.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
// List files/directories with the '.script' extension in the current directory
|
||||||
$ ls | grep .script
|
$ ls | grep .script
|
||||||
|
|
||||||
Alternatively, if you wanted to display all files with the word *purchase* in the filename,
|
// List files/directories with the '.js' extension in the root directory
|
||||||
you could use::
|
$ ls / | grep .js
|
||||||
|
|
||||||
$ ls | grep purchase
|
// List files/directories with the word 'purchase' in the name, in the :code:`scripts` directory
|
||||||
|
$ ls scripts | grep purchase
|
||||||
|
|
||||||
|
|
||||||
lscpu
|
lscpu
|
||||||
@@ -282,6 +388,28 @@ The first example above will print the amount of RAM needed to run 'foo.script'
|
|||||||
with a single thread. The second example above will print the amount of RAM needed
|
with a single thread. The second example above will print the amount of RAM needed
|
||||||
to run 'foo.script' with 50 threads.
|
to run 'foo.script' with 50 threads.
|
||||||
|
|
||||||
|
.. _mv_terminal_command:
|
||||||
|
|
||||||
|
mv
|
||||||
|
^^
|
||||||
|
|
||||||
|
$ mv [source] [destination]
|
||||||
|
|
||||||
|
Move the source file to the specified destination in the filesystem.
|
||||||
|
See :ref:`terminal_filesystem` for more details about the Terminal's filesystem.
|
||||||
|
This command only works for scripts and text files (.txt). It cannot, however, be used
|
||||||
|
to convert from script to text file, or vice versa.
|
||||||
|
|
||||||
|
This function can also be used to rename files.
|
||||||
|
|
||||||
|
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
|
||||||
|
full filepath. It cannot be a directory.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
$ mv hacking.script scripts/hacking.script
|
||||||
|
$ mv myScript.js myOldScript.js
|
||||||
|
|
||||||
nano
|
nano
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,50 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
v0.46.2 - 4/14/2019
|
||||||
|
-------------------
|
||||||
|
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
|
||||||
|
* (Karma is a hidden stat and is lowered by committing crimes)
|
||||||
|
|
||||||
|
* Gang changes:
|
||||||
|
* Bug Fix: Gangs can no longer clash with themselve
|
||||||
|
* Bug Fix: Winning against another gang should properly reduce their power
|
||||||
|
|
||||||
|
* Bug Fix: Terminal 'wget' command now works properly
|
||||||
|
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
|
||||||
|
* Bug Fix: Fixed button for creating Corporations
|
||||||
|
|
||||||
|
v0.46.1 - 4/12/2019
|
||||||
|
-------------------
|
||||||
|
* Added a very rudimentary directory system to the Terminal
|
||||||
|
* Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
|
||||||
|
|
||||||
|
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
|
||||||
|
* 'Generate Coding Contract' hash upgrade is now more expensive
|
||||||
|
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
|
||||||
|
* The cost of selling hashes for money no longer increases each time
|
||||||
|
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
|
||||||
|
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
|
||||||
|
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
|
||||||
|
|
||||||
|
v0.46.0 - 4/3/2019
|
||||||
|
------------------
|
||||||
|
* Added BitNode-9: Hacktocracy
|
||||||
|
* Changed BitNode-11's multipliers to make it slightly harder overall
|
||||||
|
* Source-File 11 is now slightly stronger
|
||||||
|
* Added several functions to Netscript Sleeve API for buying Sleeve augmentations (by hydroflame)
|
||||||
|
* Added a new stat for Duplicate Sleeves: Memory
|
||||||
|
* Increase baseline experience earned from Infiltration, but it now gives diminishing returns (on exp) as you get to higher difficulties/levels
|
||||||
|
* In Bladeburner, stamina gained from Hyperbolic Regeneration Chamber is now a percentage of your max stamina
|
||||||
|
|
||||||
|
* Corporation Changes:
|
||||||
|
* 'Demand' value of products decreases more slowly
|
||||||
|
* Bug Fix: Fixed a Corporation issue that broke the Market-TA2 Research
|
||||||
|
* Bug Fix: Issuing New Shares now works properly
|
||||||
|
|
||||||
|
* Bug Fix: Money Statistics tracker was incorrectly recording profits when selling stocks manually
|
||||||
|
* Bug Fix: Fixed an issue with the job requirement tooltip for security jobs
|
||||||
|
|
||||||
v0.45.1 - 3/23/2019
|
v0.45.1 - 3/23/2019
|
||||||
-------------------
|
-------------------
|
||||||
* Added two new Corporation Researches
|
* Added two new Corporation Researches
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.45'
|
version = '0.46'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.45.0'
|
release = '0.46.2'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ Guides & Tips
|
|||||||
=============
|
=============
|
||||||
|
|
||||||
Getting Started Guide for Intermediate Programmers
|
Getting Started Guide for Intermediate Programmers
|
||||||
What BitNode should I do?
|
|
||||||
Beginners FAQ
|
Beginners FAQ
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
|
|
||||||
Getting Started Guide for Beginner Programmers <guidesandtips/gettingstartedguideforbeginnerprogrammers>
|
Getting Started Guide for Beginner Programmers <guidesandtips/gettingstartedguideforbeginnerprogrammers>
|
||||||
|
What BitNode should I do?<guidesandtips/recommendedbitnodeorder>
|
||||||
|
|||||||
490
doc/source/guidesandtips/recommendedbitnodeorder.rst
Normal file
490
doc/source/guidesandtips/recommendedbitnodeorder.rst
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
What BitNode should I do?
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers regarding the game's story/plot-line.
|
||||||
|
|
||||||
|
After destroying their first :ref:`BitNode <gameplay_bitnodes>`, many players
|
||||||
|
wonder which BitNode they should tackle next. This guide hopefully helps answer
|
||||||
|
that question.
|
||||||
|
|
||||||
|
Overview of each BitNode
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
BitNode-1: Source Genesis
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
The first BitNode created by the Enders to imprison the minds of humans. It became
|
||||||
|
the prototype and testing-grounds for all of the BitNodes that followed.
|
||||||
|
This is the first BitNode that you play through. It has no special
|
||||||
|
modifications or mechanics.
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File lets the player start with 32GB of RAM on his/her home computer when
|
||||||
|
entering a new BitNode, and also increases all of the player's multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 16%
|
||||||
|
* Level 2: 24%
|
||||||
|
* Level 3: 28%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
The easiest BitNode
|
||||||
|
|
||||||
|
BitNode-2: Rise of the Underworld
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
Organized crime groups quickly filled the void of power left behind from the collapse of
|
||||||
|
Western government in the 2050s. As society and civlization broke down, people quickly
|
||||||
|
succumbed to the innate human impulse of evil and savagery. The organized crime
|
||||||
|
factions quickly rose to the top of the modern world.
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
|
||||||
|
* Your hacking level is reduced by 20%
|
||||||
|
* The growth rate and maximum amount of money available on servers are significantly decreased
|
||||||
|
* The amount of money gained from crimes and Infiltration is tripled
|
||||||
|
* Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead,
|
||||||
|
NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs
|
||||||
|
will earn the player money and reputation with the corresponding Faction
|
||||||
|
* Every Augmentation in the game will be available through the Factions listed above
|
||||||
|
* For every Faction NOT listed above, reputation gains are halved
|
||||||
|
* You will no longer gain passive reputation with Factions
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File allows you to form gangs in other BitNodes once your karma decreases to a certain value.
|
||||||
|
It also increases the player's crime success rate, crime money, and charisma multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 24%
|
||||||
|
* Level 2: 36%
|
||||||
|
* Level 3: 42%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Fairly easy, as hacking is still very profitable and the costs of various purchases/upgrades
|
||||||
|
is not increased. The gang mechanic may seem strange as its very different from anything
|
||||||
|
else, but it can be very powerful once you get the hang of it.
|
||||||
|
|
||||||
|
BitNode-3: Corporatocracy
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
Our greatest illusion is that a healthy society can revolve around a
|
||||||
|
single-minded pursuit of wealth.
|
||||||
|
Sometime in the early 21st century economic and political globalization turned
|
||||||
|
the world into a corporatocracy, and it never looked back. Now, the privileged
|
||||||
|
elite will happily bankrupt their own countrymen, decimate their own community,
|
||||||
|
and evict their neighbors from houses in their desperate bid to increase their wealth.
|
||||||
|
In this BitNode you can create and manage your own corporation. Running a successful corporation
|
||||||
|
has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore:
|
||||||
|
|
||||||
|
* The price and reputation cost of all Augmentations is tripled
|
||||||
|
* The starting and maximum amount of money on servers is reduced by 75%
|
||||||
|
* Server growth rate is reduced by 80%
|
||||||
|
* You now only need 75 favour with a faction in order to donate to it, rather than 150
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File lets you create corporations on other BitNodes (although
|
||||||
|
some BitNodes will disable this mechanic). This Source-File also increases your
|
||||||
|
charisma and company salary multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 8%
|
||||||
|
* Level 2: 12%
|
||||||
|
* Level 3: 14%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Somewhat-steep learning curve as you learn how to use and manage Corporations. Afterwards,
|
||||||
|
however, the BitNode is easy as Corporations can be very profitable.
|
||||||
|
|
||||||
|
BitNode-4: The Singularity
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
The Singularity has arrived. The human race is gone, replaced by artificially superintelligent
|
||||||
|
beings that are more machine than man.
|
||||||
|
|
||||||
|
In this BitNode, progressing is significantly harder:
|
||||||
|
|
||||||
|
* Experience gain rates for all stats are reduced.
|
||||||
|
* Most methods of earning money will now give significantly less.
|
||||||
|
|
||||||
|
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions.
|
||||||
|
These functions allow you to control most aspects of the game through scripts, including
|
||||||
|
working for factions/companies, purchasing/installing Augmentations, and creating programs.
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File lets you access and use the Singularity Functions in other BitNodes.
|
||||||
|
Each level of this Source-File will open up more Singularity Functions that you can use.
|
||||||
|
|
||||||
|
Difficulty:
|
||||||
|
Depending on what Source-Files you have unlocked before attempting this BitNode,
|
||||||
|
it can range from easy to moderate.
|
||||||
|
|
||||||
|
BitNode-5: Artificial Intelligence
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
They said it couldn't be done. They said the human brain,
|
||||||
|
along with its consciousness and intelligence, couldn't be replicated. They said the complexity
|
||||||
|
of the brain results from unpredictable, nonlinear interactions that couldn't be modeled
|
||||||
|
by 1's and 0's. They were wrong.
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
|
||||||
|
* The base security level of servers is doubled
|
||||||
|
* The starting money on servers is halved, but the maximum money remains the same
|
||||||
|
* Most methods of earning money now give significantly less
|
||||||
|
* Infiltration gives 50% more reputation and money
|
||||||
|
* Corporations have 50% lower valuations and are therefore less profitable
|
||||||
|
* Augmentations are more expensive
|
||||||
|
* Hacking experience gain rates are reduced
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File grants you a special new stat called Intelligence.
|
||||||
|
|
||||||
|
Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However
|
||||||
|
gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know
|
||||||
|
when you gain experience and how much). Higher Intelligence levels will boost your production for many actions
|
||||||
|
in the game.
|
||||||
|
|
||||||
|
In addition, this Source-File will unlock the :js:func:`getBitNodeMultipliers()` Netscript function,
|
||||||
|
and will also raise all of your hacking-related multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 8%
|
||||||
|
* Level 2: 12%
|
||||||
|
* Level 3: 14%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Depending on what Source-Files you have unlocked before attempting this BitNode, it
|
||||||
|
can range from easy to moderate.
|
||||||
|
|
||||||
|
BitNode-6: Bladeburners
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic
|
||||||
|
androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation
|
||||||
|
of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was
|
||||||
|
the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent
|
||||||
|
than the humans that had created them.
|
||||||
|
|
||||||
|
In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides
|
||||||
|
a new mechanic for progression. Furthermore:
|
||||||
|
|
||||||
|
* Hacking and Hacknet Nodes will be less profitable
|
||||||
|
* Your hacking level is reduced by 65%
|
||||||
|
* Hacking experience gain from scripts is reduced by 75%
|
||||||
|
* Corporations have 80% lower valuations and are therefore less profitable
|
||||||
|
* Working for companies is 50% less profitable
|
||||||
|
* Crimes and Infiltration are 25% less profitable
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File allows you to access the NSA's Bladeburner Division in other
|
||||||
|
BitNodes. In addition, this Source-File will raise both the level and experience
|
||||||
|
gain rate of all your combat stats by:
|
||||||
|
|
||||||
|
* Level 1: 8%
|
||||||
|
* Level 2: 12%
|
||||||
|
* Level 3: 14%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Initially difficult due to the fact that hacking is no longer profitable and you have
|
||||||
|
to learn a new mechanic. After you get the hang of the Bladeburner mechanic, however,
|
||||||
|
it becomes moderately easy.
|
||||||
|
|
||||||
|
BitNode-7: Bladeburners 2079
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated
|
||||||
|
as part of the AI design team for advanced synthetic androids, or Synthoids for short. You helped
|
||||||
|
achieve a major technological breakthrough in the sixth generation of the company's Synthoid
|
||||||
|
design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was the first
|
||||||
|
sentient AI ever created. This resulted in Synthoid models that were stronger, faster,
|
||||||
|
and more intelligent than the humans that had created them.
|
||||||
|
|
||||||
|
In this BitNode you will be able to access the Bladeburner API, which allows you to access
|
||||||
|
Bladeburner functionality through Netscript. Furthermore:
|
||||||
|
|
||||||
|
* The rank you gain from Bladeburner contracts/operations is reduced by 40%
|
||||||
|
* Bladeburner skills cost twice as many skill points
|
||||||
|
* Augmentations are 3x more expensive
|
||||||
|
* Hacking and Hacknet Nodes will be significantly less profitable
|
||||||
|
* Your hacking level is reduced by 65%
|
||||||
|
* Hacking experience gain from scripts is reduced by 75%
|
||||||
|
* Corporations have 80% lower valuations and are therefore less profitable
|
||||||
|
* Working for companies is 50% less profitable
|
||||||
|
* Crimes and Infiltration are 25% less profitable
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File allows you to access the Bladeburner Netscript API in other
|
||||||
|
BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 8%
|
||||||
|
* Level 2: 12%
|
||||||
|
* Level 3: 14%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Slightly more difficult than BitNode-6. However, you will be able to automate more
|
||||||
|
aspects of the Bladeburner feature, which means it will be more passive.
|
||||||
|
|
||||||
|
BitNode-8: Ghost of Wall Street
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
|
||||||
|
* You start with $250 million
|
||||||
|
* The only way to earn money is by trading on the stock market
|
||||||
|
* You start with a WSE membership and access to the TIX API
|
||||||
|
* You are able to short stocks and place different types of orders (limit/stop)
|
||||||
|
* You can immediately donate to factions to gain reputation
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File grants the following benefits:
|
||||||
|
|
||||||
|
* Level 1: Permanent access to WSE and TIX API
|
||||||
|
* Level 2: Ability to short stocks in other BitNodes
|
||||||
|
* Level 3: Ability to use limit/stop orders in other BitNodes
|
||||||
|
|
||||||
|
This Source-File also increases your hacking growth multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 12%
|
||||||
|
* Level 2: 18%
|
||||||
|
* Level 3: 21%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Very difficult until you unlock the Four Sigma (4S) Market Data API. After you
|
||||||
|
unlock the API however, it becomes moderately easy.
|
||||||
|
|
||||||
|
BitNode-9: Hacktocracy
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
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, a global, decentralized network used for nefarious
|
||||||
|
purposes. Fulcrum quickly abandoned the project and dissociated themselves from it.
|
||||||
|
|
||||||
|
This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate
|
||||||
|
hashes, which can be spent on a variety of different upgrades.
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
* Your stats are significantly decreased
|
||||||
|
* You cannnot purchase additional servers
|
||||||
|
* Hacking is significantly less profitable
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File grants the following benefits:
|
||||||
|
|
||||||
|
* Level 1: Permanently unlocks the Hacknet Server in other BitNodes
|
||||||
|
* Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
|
||||||
|
* Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
|
||||||
|
|
||||||
|
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT
|
||||||
|
when installing Augmentation
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Hard
|
||||||
|
|
||||||
|
BitNode-10: Digital Carbon
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
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
|
||||||
|
or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves'
|
||||||
|
for the human consciousness. Mankind had finally achieved immortality - at least for those
|
||||||
|
that could afford it.
|
||||||
|
|
||||||
|
This BitNode unlocks Sleeve technology. Sleeve technology allows you to:
|
||||||
|
|
||||||
|
1. Re-sleeve: Purchase and transfer your consciousness into a new body
|
||||||
|
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
* Your stats are significantly decreased
|
||||||
|
* All methods of gaining money are half as profitable (except Stock Market)
|
||||||
|
* Purchased servers are more expensive, have less max RAM, and a lower maximum limit
|
||||||
|
* Augmentations are 5x as expensive and require twice as much reputation
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
This Source-File unlocks Sleeve technology in other BitNodes.
|
||||||
|
Each level of this Source-File also grants you a Duplicate Sleeve
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Hard
|
||||||
|
|
||||||
|
BitNode-11: The Big Crash
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period
|
||||||
|
of disorder that eventually lead to the governmental reformation of many global superpowers, most notably
|
||||||
|
the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.
|
||||||
|
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers
|
||||||
|
were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as
|
||||||
|
governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.
|
||||||
|
|
||||||
|
In this BitNode:
|
||||||
|
|
||||||
|
* Your hacking stat and experience gain are halved
|
||||||
|
* The starting and maximum amount of money available on servers is significantly decreased
|
||||||
|
* The growth rate of servers is significantly reduced
|
||||||
|
* Weakening a server is twice as effective
|
||||||
|
* Company wages are decreased by 50%
|
||||||
|
* Corporation valuations are 99% lower and are therefore significantly less profitable
|
||||||
|
* Hacknet Node production is significantly decreased
|
||||||
|
* Crime and Infiltration are more lucrative
|
||||||
|
* Augmentations are twice as expensive
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: 3
|
||||||
|
|
||||||
|
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will
|
||||||
|
upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH
|
||||||
|
the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain).
|
||||||
|
This Source-File also increases the player's company salary and reputation gain multipliers by:
|
||||||
|
|
||||||
|
* Level 1: 32%
|
||||||
|
* Level 2: 48%
|
||||||
|
* Level 3: 56%
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Hard
|
||||||
|
|
||||||
|
BitNode-12: The Recursion
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Description
|
||||||
|
Every time this BitNode is destroyed, it becomes slightly harder.
|
||||||
|
|
||||||
|
Source-File
|
||||||
|
:Max Level: Infinity
|
||||||
|
|
||||||
|
Each level of Source-File 12 will increase all of your multipliers by 1%. This effect
|
||||||
|
is multiplicative with itself. In other words, level N of this Source-File will result
|
||||||
|
in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)
|
||||||
|
|
||||||
|
Difficulty
|
||||||
|
Initially very easy, but then it (obviously) becomes harder as you continue to do it.
|
||||||
|
|
||||||
|
Recommended BitNodes
|
||||||
|
--------------------
|
||||||
|
As a player, you are not forced to tackle the BitNodes in any particular order. You are
|
||||||
|
free to choose whichever ones you want. The "best" order can vary between players,
|
||||||
|
depending on what you like to do any what kind of player you are. In general, here
|
||||||
|
are the recommended BitNodes for different things:
|
||||||
|
|
||||||
|
For fast progression
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
.. note:: This does not recommend the absolute fastest path, as I don't know what
|
||||||
|
exactly the fastest path is. But it does recommend the BitNodes that are
|
||||||
|
commonly considered to be optimal by players.
|
||||||
|
|
||||||
|
1. Repeat **BitNode-1: Source Genesis** until you max out its Source-File. Its Source-File
|
||||||
|
is extremely powerful, as it raises all multipliers by a significant amount.
|
||||||
|
|
||||||
|
2. Repeat **BitNode-12: The Recursion** several times. This BitNode will be extremely easy the
|
||||||
|
first few times you tackle it, and its Source-File raises all multipliers. Furthermore,
|
||||||
|
its effect stacks multiplicatively with itself and other Source-Files/Augmentations,
|
||||||
|
which gets better as time goes on
|
||||||
|
|
||||||
|
3. Do **BitNode-5: Artificial Intelligence** once or twice. The intelligence stat it unlocks
|
||||||
|
will gradually build up as you continue to play the game, and will be helpful
|
||||||
|
in the future. The Source-File also provides hacking multipliers, which are
|
||||||
|
strong because hacking is typically one of the best ways of earning money.
|
||||||
|
|
||||||
|
4. (Optional) Consider doing **BitNode-4: The Singularity**. Its Source-File does not directly make you
|
||||||
|
more powerful in any way, but it does unlock :ref:`netscript_singularityfunctions` which
|
||||||
|
let you automate significantly more aspects of the game.
|
||||||
|
|
||||||
|
5. Do **BitNode-3: Corporatocracy** once to unlock the Corporation mechanic. This mechanic
|
||||||
|
has high profit potential.
|
||||||
|
|
||||||
|
6. Do **BitNode-6: Bladeburners** once to unlock the Bladeburners mechanic. The Bladeburner
|
||||||
|
mechanic is useful for some of the future BitNodes (such as 9 and 10).
|
||||||
|
|
||||||
|
7. Do **BitNode-9: Hacktocracy** to unlock the Hacknet Server mechanic. You can
|
||||||
|
consider repeating it as well, as its Level 2 and 3 effects are pretty helpful as well.
|
||||||
|
|
||||||
|
.. todo:: To be continued as more BitNodes get added
|
||||||
|
|
||||||
|
For the strongest Source-Files
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Note that the strongest Source-Files are typically rewarded by the hardest BitNodes.
|
||||||
|
|
||||||
|
The strongest Source-File is that from **BitNode-1: Source Genesis**, as it raises
|
||||||
|
all multipliers by a significant amount.
|
||||||
|
|
||||||
|
Similarly, the Source-File from **BitNode-12: The Recursion** is also very strong
|
||||||
|
because it raises all multipliers. Each level of Source-File 12 is fairly weak,
|
||||||
|
but its effectiveness gets better over time since the effects of Source-Files and
|
||||||
|
Augmentations are multiplicative with each other.
|
||||||
|
|
||||||
|
The Source-File from **BitNode-9: Hacktocracy** is good because it unlocks the Hacknet
|
||||||
|
Server mechanic. The Hacknet Server mechanic causes Hacknet Nodes to produce a new
|
||||||
|
currency called *hashes*, rather than money. *Hashes* can be spent on powerful upgrades
|
||||||
|
that benefit your hacking, Corporation, Bladeburner, etc.
|
||||||
|
|
||||||
|
The Duplicate Sleeves granted by the Source-File from **BitNode-10: Digital Carbon**
|
||||||
|
are strong, but only after you have several of them and have spent some time/money upgrading
|
||||||
|
them.
|
||||||
|
|
||||||
|
For more scripting/hacking
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
**BitNode-4: The Singularity** unlocks the :ref:`netscript_singularityfunctions`, which
|
||||||
|
can be used to automate many different aspects of the game, including working for factions/companies,
|
||||||
|
purchasing & installing Augmentations, and creating programs
|
||||||
|
|
||||||
|
**BitNode-6** and **BitNode-7** unlock Bladeburner and its corresponding
|
||||||
|
:ref:`Netscript API <netscript_bladeburnerapi>`. This allows you to automate an entire
|
||||||
|
new mechanic.
|
||||||
|
|
||||||
|
**BitNode-2: Rise of the Underworld** also unlocks a new mechanic and Netscript API for automating
|
||||||
|
it (the Gang mechanic). However, it is not as interesting as Bladeburner (in my opinion)
|
||||||
|
|
||||||
|
**BitNode-9: Hacktocracy** unlocks the Hacknet Server mechanic and several new
|
||||||
|
functions in the :ref:`Hacknet Node API <netscript_hacknetnodeapi>` for using it.
|
||||||
|
|
||||||
|
For new mechanics
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
**BitNode-2: Rise of the Underworld** unlocks a new mechanic in which you can
|
||||||
|
manage a gang. Gangs earn you money and can be very profitable once they get large
|
||||||
|
and powerful. The biggest benefit of gangs, however, is that they make all
|
||||||
|
Augmentations available to you through their corresponding faction.
|
||||||
|
|
||||||
|
**BitNode-3: Corporatocracy** unlocks a new mechanic in which you can manage a
|
||||||
|
corporation. You can earn money through Corporations by selling your stocks, or by
|
||||||
|
configuring your corporation to pay dividends to shareholders. If your Corporation
|
||||||
|
gets big enough, it can also bribe factions in exchange for faction reputation.
|
||||||
|
|
||||||
|
**BitNode-6: Bladeburners** unlocks a new mechanic that centers around combat rather
|
||||||
|
than hacking. The main benefit of the Bladeburner mechanic is that it offers a new
|
||||||
|
method of destroying a BitNode.
|
||||||
|
|
||||||
|
**BitNode-9: Hacktocracy** unlocks the Hacknet Server, which is an upgraded version of a
|
||||||
|
Hacknet Node. The Hacknet Server generates a computational unit called a *hash*. *Hashes*
|
||||||
|
can be spent on a variety of different upgrades that can benefit your hacking,
|
||||||
|
Corporation, Bladeburner progress, and more. It transforms the Hacknet Node from a
|
||||||
|
simple money-generator to a more interesting mechanic.
|
||||||
|
|
||||||
|
**BitNode-10: Digital Carbon** unlocks two new mechanics: Re-Sleeving and
|
||||||
|
Duplicate Sleeves.
|
||||||
|
|
||||||
|
For a Challenge
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
In general, the higher BitNodes are more difficult than the lower ones.
|
||||||
|
**BitNode-12: The Recursion** is an obvious exception as it gets progressively harder.
|
||||||
|
|
||||||
|
**BitNode-8: Ghost of Wall Street** provides a unique challenge as the only method
|
||||||
|
of earning money in that BitNode is through trading at the stock market.
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
|
|
||||||
Welcome to Bitburner's documentation!
|
Welcome to Bitburner's documentation!
|
||||||
=====================================
|
=====================================
|
||||||
Bitburner is a cyberpunk-themed `incremental game <https://en.wikipedia.org/wiki/Incremental_game>`_ that is currently in the
|
Bitburner is a programming-based `incremental game <https://en.wikipedia.org/wiki/Incremental_game>`_
|
||||||
early beta stage of development. The game `can be played here <https://danielyxie.github.io/bitburner/>`_.
|
that revolves around hacking and cyberpunk themes. The game is currently in the
|
||||||
|
early beta stage of development. It `can be played here <https://danielyxie.github.io/bitburner/>`_.
|
||||||
|
|
||||||
What is Bitburner?
|
What is Bitburner?
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
getScriptRam() Netscript Function
|
getScriptRam() Netscript Function
|
||||||
===========================
|
=================================
|
||||||
|
|
||||||
.. js:function:: getScriptRam(scriptname[, hostname/ip])
|
.. js:function:: getScriptRam(scriptname[, hostname/ip])
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ recruitMember() Netscript Function
|
|||||||
Attempt to recruit a new gang member.
|
Attempt to recruit a new gang member.
|
||||||
|
|
||||||
Possible reasons for failure:
|
Possible reasons for failure:
|
||||||
|
|
||||||
* Cannot currently recruit a new member
|
* Cannot currently recruit a new member
|
||||||
* There already exists a member with the specified name
|
* There already exists a member with the specified name
|
||||||
|
|
||||||
|
|||||||
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
getCacheUpgradeCost() Netscript Function
|
||||||
|
========================================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
|
.. js:function:: getCacheUpgradeCost(i, n)
|
||||||
|
|
||||||
|
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||||
|
:param number n: Number of times to upgrade cache. Must be positive. Rounded to nearest integer
|
||||||
|
|
||||||
|
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||||
|
a Hacknet Node).
|
||||||
|
|
||||||
|
Returns the cost of upgrading the cache level of the specified Hacknet Server by *n*.
|
||||||
|
|
||||||
|
If an invalid value for *n* is provided, then this function returns 0. If the
|
||||||
|
specified Hacknet Server is already at the max cache level, then Infinity is returned.
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
getNodeStats() Netscript Function
|
getNodeStats() Netscript Function
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
.. js:function:: getNodeStats(i)
|
.. js:function:: getNodeStats(i)
|
||||||
|
|
||||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||||
@@ -12,7 +14,12 @@ getNodeStats() Netscript Function
|
|||||||
level: Node's level,
|
level: Node's level,
|
||||||
ram: Node's RAM,
|
ram: Node's RAM,
|
||||||
cores: Node's number of cores,
|
cores: Node's number of cores,
|
||||||
production: Node's money earned per second,
|
cache: Cache level. Only applicable for Hacknet Servers
|
||||||
|
production: Node's production per second
|
||||||
timeOnline: Number of seconds since Node has been purchased,
|
timeOnline: Number of seconds since Node has been purchased,
|
||||||
totalProduction: Total number of money Node has produced
|
totalProduction: Total amount that the Node has produced
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. note:: Note that for Hacknet Nodes, production refers to the amount of money the node generates.
|
||||||
|
For Hacknet Servers (the upgraded version of Hacknet Nodes), production refers to the amount
|
||||||
|
of hashes the node generates.
|
||||||
|
|||||||
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
hashCost() Netscript Function
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
|
.. js:function:: hashCost(upgName)
|
||||||
|
|
||||||
|
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
||||||
|
|
||||||
|
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||||
|
of a Hacknet Node).
|
||||||
|
|
||||||
|
Returns the number of hashes required for the specified upgrade. The name of the
|
||||||
|
upgrade must be an exact match.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: javascript
|
||||||
|
|
||||||
|
var upgradeName = "Sell for Corporation Funds";
|
||||||
|
if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||||
|
hacknet.spendHashes(upgName);
|
||||||
|
}
|
||||||
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
numHashes() Netscript Function
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
|
.. js:function:: numHashes()
|
||||||
|
|
||||||
|
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||||
|
of a Hacknet Node).
|
||||||
|
|
||||||
|
Returns the number of hashes you have
|
||||||
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
spendHashes() Netscript Function
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
|
.. js:function:: spendHashes(upgName, upgTarget)
|
||||||
|
|
||||||
|
:param string upgName: Name of upgrade to spend hashes on. Must be an exact match
|
||||||
|
:param string upgTarget: Object to which upgrade applies. Required for certain upgrades
|
||||||
|
|
||||||
|
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||||
|
of a Hacknet Node).
|
||||||
|
|
||||||
|
Spend the hashes generated by your Hacknet Servers on an upgrade. Returns a boolean value -
|
||||||
|
true if the upgrade is successfully purchased, and false otherwise.
|
||||||
|
|
||||||
|
The name of the upgrade must be an exact match. The :code:`upgTarget` argument is used
|
||||||
|
for upgrades such as :code:`Reduce Minimum Security`, which applies to a specific server.
|
||||||
|
In this case, the :code:`upgTarget` argument must be the hostname of the server.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: javascript
|
||||||
|
|
||||||
|
hacknet.spendHashes("Sell for Corporation Funds");
|
||||||
|
hacknet.spendHashes("Increase Maximum Money", "foodnstuff");
|
||||||
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
upgradeCache() Netscript Function
|
||||||
|
=================================
|
||||||
|
|
||||||
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
|
.. js:function:: upgradeCache(i, n)
|
||||||
|
|
||||||
|
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||||
|
:param number n: Number of cache levels to purchase. Must be positive. Rounded to nearest integer
|
||||||
|
|
||||||
|
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||||
|
a Hacknet Node).
|
||||||
|
|
||||||
|
Tries to upgrade the specified Hacknet Server's cache *n* times.
|
||||||
|
|
||||||
|
Returns true if it successfully upgrades the Server's cache *n* times, or if
|
||||||
|
it purchases some positive amount and the Server reaches its max cache level.
|
||||||
|
|
||||||
|
Returns false otherwise.
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
Netscript Hacknet Node API
|
Netscript Hacknet Node API
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
.. warning:: Not all functions in the Hacknet Node API are immediately available.
|
||||||
|
For this reason, the documentation for this API may contains spoilers
|
||||||
|
for the game.
|
||||||
|
|
||||||
Netscript provides the following API for accessing and upgrading your Hacknet Nodes
|
Netscript provides the following API for accessing and upgrading your Hacknet Nodes
|
||||||
through scripts.
|
through scripts.
|
||||||
|
|
||||||
@@ -31,9 +35,14 @@ In :ref:`netscriptjs`::
|
|||||||
upgradeLevel() <hacknetnodeapi/upgradeLevel>
|
upgradeLevel() <hacknetnodeapi/upgradeLevel>
|
||||||
upgradeRam() <hacknetnodeapi/upgradeRam>
|
upgradeRam() <hacknetnodeapi/upgradeRam>
|
||||||
upgradeCore() <hacknetnodeapi/upgradeCore>
|
upgradeCore() <hacknetnodeapi/upgradeCore>
|
||||||
|
upgradeCache() <hacknetnodeapi/upgradeCache>
|
||||||
getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost>
|
getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost>
|
||||||
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
|
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
|
||||||
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
|
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
|
||||||
|
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
|
||||||
|
numHashes() <hacknetnodeapi/numHashes>
|
||||||
|
hashCost() <hacknetnodeapi/hashCost>
|
||||||
|
spendHashes() <hacknetnodeapi/spendHashes>
|
||||||
|
|
||||||
.. _netscript_hacknetnodeapi_referencingahacknetnode:
|
.. _netscript_hacknetnodeapi_referencingahacknetnode:
|
||||||
|
|
||||||
@@ -68,23 +77,25 @@ The following is an example of one way a script can be used to automate the
|
|||||||
purchasing and upgrading of Hacknet Nodes.
|
purchasing and upgrading of Hacknet Nodes.
|
||||||
|
|
||||||
This script attempts to purchase Hacknet Nodes until the player has a total of 8. Then
|
This script attempts to purchase Hacknet Nodes until the player has a total of 8. Then
|
||||||
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores::
|
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores
|
||||||
|
|
||||||
|
.. code:: javascript
|
||||||
|
|
||||||
function myMoney() {
|
function myMoney() {
|
||||||
return getServerMoneyAvailable("home");() <hacknetnodeapi/ return getServerMoneyAvailable("home");>
|
return getServerMoneyAvailable("home");
|
||||||
}
|
}
|
||||||
}() <hacknetnodeapi/>
|
|
||||||
disableLog("getServerMoneyAvailable");
|
disableLog("getServerMoneyAvailable");
|
||||||
disableLog("sleep");
|
disableLog("sleep");
|
||||||
|
|
||||||
cnt = 8;
|
var cnt = 8;
|
||||||
|
|
||||||
while(hacknet.numNodes() < cnt) {
|
while(hacknet.numNodes() < cnt) {
|
||||||
res = hacknet.purchaseNode();
|
res = hacknet.purchaseNode();
|
||||||
print("Purchased hacknet Node with index " + res);
|
print("Purchased hacknet Node with index " + res);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
for (var i = 0; i < cnt; i++) {
|
||||||
while (hacknet.getNodeStats(i).level <= 80) {
|
while (hacknet.getNodeStats(i).level <= 80) {
|
||||||
var cost = hacknet.getLevelUpgradeCost(i, 10);
|
var cost = hacknet.getLevelUpgradeCost(i, 10);
|
||||||
while (myMoney() < cost) {
|
while (myMoney() < cost) {
|
||||||
@@ -95,9 +106,9 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
print("All nodes upgrade to level 80");
|
print("All nodes upgraded to level 80");
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
for (var i = 0; i < cnt; i++) {
|
||||||
while (hacknet.getNodeStats(i).ram < 16) {
|
while (hacknet.getNodeStats(i).ram < 16) {
|
||||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
var cost = hacknet.getRamUpgradeCost(i, 2);
|
||||||
while (myMoney() < cost) {
|
while (myMoney() < cost) {
|
||||||
@@ -108,43 +119,4 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
print("All nodes upgrade to 16GB RAM");
|
print("All nodes upgraded to 16GB RAM");
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
|
||||||
while (hacknet.getNodeStats(i).level <= 140) {
|
|
||||||
var cost = hacknet.getLevelUpgradeCost(i, 5);
|
|
||||||
while (myMoney() < cost) {
|
|
||||||
print("Need $" + cost + " . Have $" + myMoney());
|
|
||||||
sleep(3000);
|
|
||||||
}
|
|
||||||
res = hacknet.upgradeLevel(i, 5);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
print("All nodes upgrade to level 140");
|
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
|
||||||
while (hacknet.getNodeStats(i).ram < 64) {
|
|
||||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
|
||||||
while (myMoney() < cost) {
|
|
||||||
print("Need $" + cost + " . Have $" + myMoney());
|
|
||||||
sleep(3000);
|
|
||||||
}
|
|
||||||
res = hacknet.upgradeRam(i, 2);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
print("All nodes upgrade to 64GB RAM (MAX)");
|
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
|
||||||
while (hacknetnodes.getNodeStatsi(i).cores < 8) {
|
|
||||||
var cost = hacknet.getCoreUpgradeCost(7);
|
|
||||||
while (myMoney() < cost) {
|
|
||||||
print("Need $" + cost + " . Have $" + myMoney());
|
|
||||||
sleep(3000);
|
|
||||||
}
|
|
||||||
res = hacknet.upgradeCore(i, 7);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
print("All nodes upgrade to 8 cores");
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ In :ref:`netscriptjs`::
|
|||||||
setToUniversityCourse() <sleeveapi/setToUniversityCourse>
|
setToUniversityCourse() <sleeveapi/setToUniversityCourse>
|
||||||
setToGymWorkout() <sleeveapi/setToGymWorkout>
|
setToGymWorkout() <sleeveapi/setToGymWorkout>
|
||||||
travel() <sleeveapi/travel>
|
travel() <sleeveapi/travel>
|
||||||
|
getSleeveAugmentations() <sleeveapi/getSleeveAugmentations>
|
||||||
|
getSleevePurchasableAugs() <sleeveapi/getSleevePurchasableAugs>
|
||||||
|
purchaseSleeveAug() <sleeveapi/purchaseSleeveAug>
|
||||||
|
|
||||||
.. _netscript_sleeveapi_referencingaduplicatesleeve:
|
.. _netscript_sleeveapi_referencingaduplicatesleeve:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
getSleeveAugmentations() Netscript Function
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. js:function:: getSleeveAugmentations(sleeveNumber)
|
||||||
|
|
||||||
|
:param int sleeveNumber: Index of the sleeve to retrieve augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||||
|
|
||||||
|
Return a list of augmentation names that this sleeve has installed.
|
||||||
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
getSleevePurchasableAugs() Netscript Function
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
.. js:function:: getSleevePurchasableAugs(sleeveNumber)
|
||||||
|
|
||||||
|
:param int sleeveNumber: Index of the sleeve to retrieve purchasable augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||||
|
|
||||||
|
Return a list of augmentations that the player can buy for this sleeve.
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: string, // augmentation name
|
||||||
|
cost: number, // augmentation cost
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -10,8 +10,8 @@ getSleeveStats() Netscript Function
|
|||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
{
|
{
|
||||||
shock: current shock of the sleeve [0-1],
|
shock: current shock of the sleeve [0-100],
|
||||||
sync: current sync of the sleeve [0-1],
|
sync: current sync of the sleeve [0-100],
|
||||||
hacking_skill: current hacking skill of the sleeve,
|
hacking_skill: current hacking skill of the sleeve,
|
||||||
strength: current strength of the sleeve,
|
strength: current strength of the sleeve,
|
||||||
defense: current defense of the sleeve,
|
defense: current defense of the sleeve,
|
||||||
|
|||||||
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
purchaseSleeveAug() Netscript Function
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. js:function:: purchaseSleeveAug(sleeveNumber, augName)
|
||||||
|
|
||||||
|
:param int sleeveNumber: Index of the sleeve to buy an aug for. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||||
|
:param string augName: Name of the aug to buy. Must be an exact match
|
||||||
|
|
||||||
|
Return true if the aug was purchased and installed on the sleeve.
|
||||||
395
index.html
395
index.html
@@ -115,7 +115,7 @@
|
|||||||
|
|
||||||
<div id="script-editor-filename-wrapper">
|
<div id="script-editor-filename-wrapper">
|
||||||
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
|
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
|
||||||
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
|
<input id="script-editor-filename" type="text" maxlength="100" tabindex="1"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="ace-editor"></div>
|
<div id="ace-editor"></div>
|
||||||
@@ -201,257 +201,9 @@
|
|||||||
|
|
||||||
<!-- Hacknet Nodes -->
|
<!-- Hacknet Nodes -->
|
||||||
<div id="hacknet-nodes-container" class="generic-menupage-container">
|
<div id="hacknet-nodes-container" class="generic-menupage-container">
|
||||||
<h1 id="hacknet-nodes-title"> Hacknet Nodes </h1>
|
<!-- React Component -->
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- World -->
|
|
||||||
<div id="world-container" class="generic-menupage-container">
|
|
||||||
<h2 id="world-city-name"> </h2>
|
|
||||||
<p id="world-city-desc"> </p>
|
|
||||||
<ul id="aevum-locations-list">
|
|
||||||
<li id="aevum-travelagency-li">
|
|
||||||
<a id="aevum-travelagency" class="a-link-button"> Travel Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-hospital-li">
|
|
||||||
<a id="aevum-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-summituniversity-li">
|
|
||||||
<a id="aevum-summituniversity" class="a-link-button"> Summit University </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-ecorp-li">
|
|
||||||
<a id="aevum-ecorp" class="a-link-button"> ECorp </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-bachmanandassociates-li">
|
|
||||||
<a id="aevum-bachmanandassociates" class="a-link-button"> Bachman & Associates</a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-clarkeincorporated-li">
|
|
||||||
<a id="aevum-clarkeincorporated" class="a-link-button"> Clarke Incorporated </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-fulcrumtechnologies-li">
|
|
||||||
<a id="aevum-fulcrumtechnologies" class="a-link-button"> Fulcrum Technologies </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-aerocorp-li">
|
|
||||||
<a id="aevum-aerocorp" class="a-link-button"> AeroCorp </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-galacticcybersystems-li">
|
|
||||||
<a id="aevum-galacticcybersystems" class="a-link-button"> Galactic Cybersystems </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-watchdogsecurity-li">
|
|
||||||
<a id="aevum-watchdogsecurity" class="a-link-button">Watchdog Security </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-rhoconstruction-li">
|
|
||||||
<a id="aevum-rhoconstruction" class="a-link-button">Rho Construction </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-aevumpolice-li">
|
|
||||||
<a id="aevum-aevumpolice" class="a-link-button">Aevum Police</a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-netlinktechnologies-li">
|
|
||||||
<a id="aevum-netlinktechnologies" class="a-link-button">NetLink Technologies</a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-crushfitnessgym-li">
|
|
||||||
<a id="aevum-crushfitnessgym" class="a-link-button">Crush Fitness Gym </a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-snapfitnessgym-li">
|
|
||||||
<a id="aevum-snapfitnessgym" class="a-link-button">Snap Fitness Gym</a>
|
|
||||||
</li>
|
|
||||||
<li id="aevum-slums-li">
|
|
||||||
<a id="aevum-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="chongqing-locations-list">
|
|
||||||
<li id="chongqing-travelagency-li">
|
|
||||||
<a id="chongqing-travelagency" class="a-link-button"> Travel Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="chongqing-hospital-li">
|
|
||||||
<a id="chongqing-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="chonqging-kuaigonginternational-li">
|
|
||||||
<a id="chongqing-kuaigonginternational" class="a-link-button">KuaiGong International </a>
|
|
||||||
</li>
|
|
||||||
<li id="chongqing-solarisspacesystems-li">
|
|
||||||
<a id="chongqing-solarisspacesystems" class="a-link-button">Solaris Space Systems</a>
|
|
||||||
</li>
|
|
||||||
<li id="chongqing-slums-li">
|
|
||||||
<a id="chongqing-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="sector12-locations-list">
|
|
||||||
<li id="sector12-travelagency-li">
|
|
||||||
<a id="sector12-travelagency" class="a-link-button">Travel Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-hospital-li">
|
|
||||||
<a id="sector12-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-rothmanuniversity-li">
|
|
||||||
<a id="sector12-rothmanuniversity" class="a-link-button"> Rothman University</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-megacorp-li">
|
|
||||||
<a id="sector12-megacorp" class="a-link-button">MegaCorp</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-bladeindustries-li">
|
|
||||||
<a id="sector12-bladeindustries" class="a-link-button"> Blade Industries</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-foursigma-li">
|
|
||||||
<a id="sector12-foursigma" class="a-link-button">Four Sigma</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-icarusmicrosystems-li">
|
|
||||||
<a id="sector12-icarusmicrosystems" class="a-link-button"> Icarus Microsystems</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-universalenergy-li">
|
|
||||||
<a id="sector12-universalenergy" class="a-link-button">Universal Energy </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-deltaone-li">
|
|
||||||
<a id="sector12-deltaone" class="a-link-button">DeltaOne </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-cia-li">
|
|
||||||
<a id="sector12-cia" class="a-link-button">Central Intelligence Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-nsa-li">
|
|
||||||
<a id="sector12-nsa" class="a-link-button">National Security Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-alphaenterprises-li">
|
|
||||||
<a id="sector12-alphaenterprises" class="a-link-button">Alpha Enterprises</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-carmichaelsecurity-li">
|
|
||||||
<a id="sector12-carmichaelsecurity" class="a-link-button"> Carmichael Security</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-foodnstuff-li">
|
|
||||||
<a id="sector12-foodnstuff" class="a-link-button">FoodNStuff</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-joesguns-li">
|
|
||||||
<a id="sector12-joesguns" class="a-link-button"> Joe's Guns</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-irongym-li">
|
|
||||||
<a id="sector12-irongym" class="a-link-button">Iron Gym </a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-powerhousegym-li">
|
|
||||||
<a id="sector12-powerhousegym" class="a-link-button">Powerhouse Gym</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-slums-li">
|
|
||||||
<a id="sector12-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
<li id="sector12-cityhall-li">
|
|
||||||
<a id="sector12-cityhall" class="a-link-button">City Hall</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="newtokyo-locations-list">
|
|
||||||
<li id="newtokyo-travelagency-li">
|
|
||||||
<a id="newtokyo-travelagency" class="a-link-button"> Travel Agency</a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-hospital-li">
|
|
||||||
<a id="newtokyo-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-defcomm-li">
|
|
||||||
<a id="newtokyo-defcomm" class="a-link-button"> DefComm</a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-vitalife-li">
|
|
||||||
<a id="newtokyo-vitalife" class="a-link-button">VitaLife </a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-globalpharmaceuticals-li">
|
|
||||||
<a id="newtokyo-globalpharmaceuticals" class="a-link-button">Global Pharmaceuticals</a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-noodlebar-li">
|
|
||||||
<a id="newtokyo-noodlebar" class="a-link-button">Noodle Bar </a>
|
|
||||||
</li>
|
|
||||||
<li id="newtokyo-slums-li">
|
|
||||||
<a id="newtokyo-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="ishima-locations-list">
|
|
||||||
<li id="ishima-travelagency-li">
|
|
||||||
<a id="ishima-travelagency" class="a-link-button">Travel Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="ishima-hospital-li">
|
|
||||||
<a id="ishima-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="ishima-stormtechnologies-li">
|
|
||||||
<a id="ishima-stormtechnologies" class="a-link-button">Storm Technologies</a>
|
|
||||||
</li>
|
|
||||||
<li id="ishima-novamedical-li">
|
|
||||||
<a id="ishima-novamedical" class="a-link-button">Nova Medical</a>
|
|
||||||
</li>
|
|
||||||
<li id="ishima-omegasoftware-li">
|
|
||||||
<a id="ishima-omegasoftware" class="a-link-button">Omega Software </a>
|
|
||||||
</li>
|
|
||||||
<li id="ishima-slums-li">
|
|
||||||
<a id="ishima-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="volhaven-locations-list">
|
|
||||||
<li id="volhaven-travelagency-li">
|
|
||||||
<a id="volhaven-travelagency" class="a-link-button">Travel Agency </a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-hospital-li">
|
|
||||||
<a id="volhaven-hospital" class="a-link-button">Hospital</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-zbinstituteoftechnology-li">
|
|
||||||
<a id="volhaven-zbinstituteoftechnology" class="a-link-button">ZB Insitute of Technology</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-omnitekincorporated-li">
|
|
||||||
<a id="volhaven-omnitekincorporated" class="a-link-button">OmniTek Incorporated </a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-nwo-li">
|
|
||||||
<a id="volhaven-nwo" class="a-link-button">NWO</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-helislabs-li">
|
|
||||||
<a id="volhaven-helioslabs" class="a-link-button">Helios Labs</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-omniacybersystems-li">
|
|
||||||
<a id="volhaven-omniacybersystems" class="a-link-button">Omnia Cybersystems</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-lexocorp-li">
|
|
||||||
<a id="volhaven-lexocorp" class="a-link-button">LexoCorp</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-syscoresecurities-li">
|
|
||||||
<a id="volhaven-syscoresecurities" class="a-link-button">SysCore Securities</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-computek-li">
|
|
||||||
<a id="volhaven-computek" class="a-link-button">CompuTek</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-milleniumfitnessgym-li">
|
|
||||||
<a id="volhaven-milleniumfitnessgym" class="a-link-button">Millenium Fitness Gym</a>
|
|
||||||
</li>
|
|
||||||
<li id="volhaven-slums-li">
|
|
||||||
<a id="volhaven-slums" class="a-link-button">The Slums</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="generic-locations-list"></ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Create a program(executable) -->
|
<!-- Create a program(executable) -->
|
||||||
<div id="create-program-container" class="generic-menupage-container">
|
<div id="create-program-container" class="generic-menupage-container">
|
||||||
<p id="create-program-page-text">
|
<p id="create-program-page-text">
|
||||||
@@ -469,8 +221,6 @@
|
|||||||
<!-- Single Faction info (when you select a faction from the Factions menu) -->
|
<!-- Single Faction info (when you select a faction from the Factions menu) -->
|
||||||
<div id="faction-container" class="generic-menupage-container"></div>
|
<div id="faction-container" class="generic-menupage-container"></div>
|
||||||
|
|
||||||
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
|
|
||||||
|
|
||||||
<!-- Augmentations -->
|
<!-- Augmentations -->
|
||||||
<div id="augmentations-container" class="generic-menupage-container"></div>
|
<div id="augmentations-container" class="generic-menupage-container"></div>
|
||||||
|
|
||||||
@@ -478,150 +228,31 @@
|
|||||||
<div id="tutorial-container" class="generic-menupage-container">
|
<div id="tutorial-container" class="generic-menupage-container">
|
||||||
<h1> Tutorial (AKA Links to Documentation) </h1>
|
<h1> Tutorial (AKA Links to Documentation) </h1>
|
||||||
<a id="tutorial-getting-started-link" class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html">
|
<a id="tutorial-getting-started-link" class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html">
|
||||||
Getting Started
|
Getting Started</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html">
|
||||||
Servers & Networking
|
Servers & Networking</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html">
|
||||||
Hacking
|
Hacking</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html">
|
||||||
Scripts
|
Scripts</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
|
||||||
Netscript Programming Language
|
Netscript Programming Language</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html">
|
||||||
Traveling
|
Traveling</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html">
|
||||||
Companies
|
Companies</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html">
|
||||||
Infiltration
|
Infiltration</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html">
|
||||||
Factions
|
Factions</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html">
|
||||||
Augmentations
|
Augmentations</a><br><br>
|
||||||
</a><br><br>
|
|
||||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
|
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
|
||||||
Keyboard Shortcuts
|
Keyboard Shortcuts</a>
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Location (visiting a location in World) -->
|
<!-- Location (visiting a location in World) -->
|
||||||
<div id="location-container" class="generic-menupage-container">
|
<div id="location-container" class="generic-menupage-container">
|
||||||
<a id="location-return-to-world-button" class="a-link-button"> Return to World </a>
|
|
||||||
<h1 id="location-name"></h1>
|
|
||||||
<p id="location-info"> </p>
|
|
||||||
|
|
||||||
<p id="location-job-title"> </p>
|
|
||||||
<p id="location-text-divider-1"> --------------- </p>
|
|
||||||
<p id="location-job-reputation" class="tooltip"> </p>
|
|
||||||
<p id="location-text-divider-2"> --------------- </p>
|
|
||||||
<p id="location-company-favor" class="tooltip"> </p>
|
|
||||||
<p id="location-text-divider-3"> --------------- </p>
|
|
||||||
|
|
||||||
<!-- Jobs/Work at a company -->
|
|
||||||
<a id="location-software-job" class="a-link-button tooltip"> Apply for Software Job</a>
|
|
||||||
<a id="location-software-consultant-job" class="a-link-button tooltip"> Apply for Software Consultant Job</a>
|
|
||||||
<a id="location-it-job" class="a-link-button tooltip"> Apply for IT Job </a>
|
|
||||||
<a id="location-security-engineer-job" class="a-link-button tooltip"> Apply for Security Engineer Job</a>
|
|
||||||
<a id="location-network-engineer-job" class="a-link-button tooltip"> Apply for Network Engineer Job</a>
|
|
||||||
<a id="location-business-job" class="a-link-button tooltip"> Apply for Business Job</a>
|
|
||||||
<a id="location-business-consultant-job" class="a-link-button tooltip"> Apply for Business Consultant Job </a>
|
|
||||||
<a id="location-security-job" class="a-link-button tooltip"> Apply for Security Job</a>
|
|
||||||
<a id="location-agent-job" class="a-link-button tooltip"> Apply to be an Agent</a>
|
|
||||||
<a id="location-employee-job" class="a-link-button tooltip"> Apply to be an Employee </a>
|
|
||||||
<a id="location-parttime-employee-job" class="a-link-button tooltip"> Apply to be a Part-time Employee </a>
|
|
||||||
<a id="location-waiter-job" class="a-link-button tooltip"> Apply to be a Waiter</a>
|
|
||||||
<a id="location-parttime-waiter-job" class="a-link-button tooltip"> Apply to be a Part-time Waiter</a>
|
|
||||||
|
|
||||||
<a id="location-work" class="a-link-button"> Work </a>
|
|
||||||
|
|
||||||
<!-- Gym -->
|
|
||||||
<a id="location-gym-train-str" class="a-link-button">Train Strength</a>
|
|
||||||
<a id="location-gym-train-def" class="a-link-button">Train Defense </a>
|
|
||||||
<a id="location-gym-train-dex" class="a-link-button">Train Dexterity</a>
|
|
||||||
<a id="location-gym-train-agi" class="a-link-button">Train Agility</a>
|
|
||||||
|
|
||||||
<!-- Study/Take classes at a university -->
|
|
||||||
<a id="location-study-computer-science" class="a-link-button">Study Computer Science (free)</a>
|
|
||||||
<a id="location-data-structures-class" class="a-link-button">Take Data Structures course</a>
|
|
||||||
<a id="location-networks-class" class="a-link-button">Take Networks course</a>
|
|
||||||
<a id="location-algorithms-class" class="a-link-button">Take Algorithms course</a>
|
|
||||||
<a id="location-management-class" class="a-link-button">Take Management course</a>
|
|
||||||
<a id="location-leadership-class" class="a-link-button">Take Leadership course</a>
|
|
||||||
|
|
||||||
<!-- Purchase servers -->
|
|
||||||
<a id="location-purchase-2gb" class="a-link-button"> Purchase 2GB Server - $150,000</a>
|
|
||||||
<a id="location-purchase-4gb" class="a-link-button"> Purchase 4GB Server - $300,000</a>
|
|
||||||
<a id="location-purchase-8gb" class="a-link-button"> Purchase 8GB Server - $600,000</a>
|
|
||||||
<a id="location-purchase-16gb" class="a-link-button"> Purchase 16GB Server - $1,200,000</a>
|
|
||||||
<a id="location-purchase-32gb" class="a-link-button"> Purchase 32GB Server - $2,400,000</a>
|
|
||||||
<a id="location-purchase-64gb" class="a-link-button"> Purchase 64GB Server - $4,800,000</a>
|
|
||||||
<a id="location-purchase-128gb" class="a-link-button"> Purchase 128GB Server - $9,600,000</a>
|
|
||||||
<a id="location-purchase-256gb" class="a-link-button"> Purchase 256GB Server - $19,200,000</a>
|
|
||||||
<a id="location-purchase-512gb" class="a-link-button"> Purchase 512GB Server - $38,400,000</a>
|
|
||||||
<a id="location-purchase-1tb" class="a-link-button"> Purchase 1TB Server - $75,000,000</a>
|
|
||||||
<a id="location-purchase-tor" class="a-link-button"> Purchase TOR Router - $100,000</a>
|
|
||||||
<a id="location-purchase-home-ram" class="a-link-button"> Purchase additional RAM for Home computer </a>
|
|
||||||
<a id="location-purchase-home-cores" class="a-link-button"> Purchase additional Core for Home computer </a>
|
|
||||||
|
|
||||||
<!-- Infiltrate -->
|
|
||||||
<a id="location-infiltrate" class="a-link-button tooltip"> Infiltrate Company
|
|
||||||
<span class="tooltiptext">
|
|
||||||
Infiltrate this company's facility to try and steal their classified secrets!
|
|
||||||
Warning: You may end up hospitalized if you are unsuccessful!
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Hospital -->
|
|
||||||
<a id="location-hospital-treatment" class="a-link-button"> Get Treatment for Wounds </a>
|
|
||||||
|
|
||||||
<!-- Travel agency -->
|
|
||||||
<p id="location-travel-agency-text">
|
|
||||||
From here, you can travel to any other city! A ticket costs $200,000.
|
|
||||||
</p>
|
|
||||||
<a id="location-travel-to-aevum" class="a-link-button"> Travel to Aevum </a>
|
|
||||||
<a id="location-travel-to-chongqing" class="a-link-button"> Travel to Chongqing</a>
|
|
||||||
<a id="location-travel-to-sector12" class="a-link-button"> Travel to Sector-12</a>
|
|
||||||
<a id="location-travel-to-newtokyo" class="a-link-button"> Travel to New Tokyo</a>
|
|
||||||
<a id="location-travel-to-ishima" class="a-link-button"> Travel to Ishima</a>
|
|
||||||
<a id="location-travel-to-volhaven" class="a-link-button"> Travel to Volhaven</a>
|
|
||||||
|
|
||||||
<!-- Slums -->
|
|
||||||
<p id="location-slums-description">
|
|
||||||
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
|
|
||||||
other shadowy entities. The city's government and police have neglected this area for years...
|
|
||||||
<br/><br/><br/>
|
|
||||||
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
|
|
||||||
successful. Your chance at successfully committing a crime is determined by your stats.
|
|
||||||
</p>
|
|
||||||
<a class="a-link-button tooltip" id="location-slums-shoplift"> Shoplift </a>
|
|
||||||
<a id="location-slums-rob-store" class="a-link-button tooltip"> Rob a store </a>
|
|
||||||
<a id="location-slums-mug" class="a-link-button tooltip"> Mug someone </a>
|
|
||||||
<a id="location-slums-larceny" class="a-link-button tooltip"> Commit Larceny </a>
|
|
||||||
<a id="location-slums-deal-drugs" class="a-link-button tooltip"> Deal Drugs </a>
|
|
||||||
<a id="location-slums-bond-forgery" class="a-link-button tooltip">Bond Forgery</a>
|
|
||||||
<a id="location-slums-traffic-arms" class="a-link-button tooltip">Traffick Illegal Arms</a>
|
|
||||||
<a id="location-slums-homicide" class="a-link-button tooltip">Homicide</a>
|
|
||||||
<a id="location-slums-gta" class="a-link-button tooltip"> Grand Theft Auto </a>
|
|
||||||
<a id="location-slums-kidnap" class="a-link-button tooltip"> Kidnap and Ransom </a>
|
|
||||||
<a id="location-slums-assassinate" class="a-link-button tooltip"> Assassinate </a>
|
|
||||||
<a id="location-slums-heist" class="a-link-button tooltip"> Heist </a>
|
|
||||||
|
|
||||||
<!-- City Hall -->
|
|
||||||
<a id="location-cityhall-create-corporation" class="a-link-button">Create a Corporation</a>
|
|
||||||
|
|
||||||
<!-- Bladeburner @ NSA -->
|
|
||||||
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
|
|
||||||
|
|
||||||
<!-- Re-sleeving @ VitaLife -->
|
|
||||||
<a id="location-vitalife-resleeve" class="a-link-button">Re-Sleeve</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="infiltration-container" class="generic-menupage-container">
|
<div id="infiltration-container" class="generic-menupage-container">
|
||||||
|
|||||||
1090
package-lock.json
generated
1090
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@
|
|||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"js-beautify": "^1.5.10",
|
"js-beautify": "^1.5.10",
|
||||||
"json5": "^1.0.1",
|
"json5": "^1.0.1",
|
||||||
"less": "^3.0.4",
|
"less": "^3.9.0",
|
||||||
"less-loader": "^4.1.0",
|
"less-loader": "^4.1.0",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"mini-css-extract-plugin": "^0.4.1",
|
"mini-css-extract-plugin": "^0.4.1",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"stylelint": "^9.2.1",
|
"stylelint": "^9.2.1",
|
||||||
"stylelint-declaration-use-variable": "^1.6.1",
|
"stylelint-declaration-use-variable": "^1.6.1",
|
||||||
"stylelint-order": "^0.8.1",
|
"stylelint-order": "^0.8.1",
|
||||||
"ts-loader": "^4.4.1",
|
"ts-loader": "^4.5.0",
|
||||||
"tslint": "^5.10.0",
|
"tslint": "^5.10.0",
|
||||||
"typescript": "^2.9.2",
|
"typescript": "^2.9.2",
|
||||||
"uglify-es": "^3.3.9",
|
"uglify-es": "^3.3.9",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"webpack": "^4.12.0",
|
"webpack": "^4.12.0",
|
||||||
"webpack-cli": "^3.0.4",
|
"webpack-cli": "^3.0.4",
|
||||||
"webpack-dev-middleware": "^3.1.3",
|
"webpack-dev-middleware": "^3.1.3",
|
||||||
"webpack-dev-server": "^3.1.4",
|
"webpack-dev-server": "^3.2.1",
|
||||||
"worker-loader": "^2.0.0"
|
"worker-loader": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -114,5 +114,5 @@
|
|||||||
"watch": "webpack --watch --mode production",
|
"watch": "webpack --watch --mode production",
|
||||||
"watch:dev": "webpack --watch --mode development"
|
"watch:dev": "webpack --watch --mode development"
|
||||||
},
|
},
|
||||||
"version": "0.45.0"
|
"version": "0.46.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,38 @@
|
|||||||
import {workerScripts,
|
// TODO - Convert this to React
|
||||||
killWorkerScript} from "./NetscriptWorker";
|
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { getServer } from "./Server/ServerHelpers";
|
import { getServer } from "./Server/ServerHelpers";
|
||||||
import {numeralWrapper} from "./ui/numeralFormat";
|
|
||||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
|
||||||
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
|
|
||||||
import {arrayToString} from "../utils/helpers/arrayToString";
|
|
||||||
import {createElement} from "../utils/uiHelpers/createElement";
|
|
||||||
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
|
|
||||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
|
||||||
import {getElementById} from "../utils/uiHelpers/getElementById";
|
|
||||||
import {logBoxCreate} from "../utils/LogBox";
|
|
||||||
import {formatNumber,
|
|
||||||
convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
|
||||||
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
|
|
||||||
import {removeElement} from "../utils/uiHelpers/removeElement";
|
|
||||||
import {roundToTwo} from "../utils/helpers/roundToTwo";
|
|
||||||
import {Page, routing} from "./ui/navigationTracking";
|
|
||||||
|
|
||||||
/* {
|
import { Page, routing } from "./ui/navigationTracking";
|
||||||
* serverName: {
|
import { numeralWrapper } from "./ui/numeralFormat";
|
||||||
* header: Server Header Element
|
|
||||||
* panel: Server Panel List (ul) element
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
* scripts: {
|
import { logBoxCreate } from "../utils/LogBox";
|
||||||
* script id: Ref to Script information
|
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||||
* }
|
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||||
* }
|
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
|
||||||
* ...
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
|
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||||
|
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
||||||
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
|
import { getElementById } from "../utils/uiHelpers/getElementById";
|
||||||
|
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||||
|
import { removeElement } from "../utils/uiHelpers/removeElement";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* serverName: {
|
||||||
|
* header: Server Header Element
|
||||||
|
* panel: Server Panel List (ul) element
|
||||||
|
* scripts: {
|
||||||
|
* script id: Ref to Script information
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ...
|
||||||
*/
|
*/
|
||||||
let ActiveScriptsUI = {};
|
const ActiveScriptsUI = {};
|
||||||
let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements
|
const ActiveScriptsTasks = []; // Sequentially schedule the creation/deletion of UI elements
|
||||||
|
|
||||||
const getHeaderHtml = (server) => {
|
const getHeaderHtml = (server) => {
|
||||||
// TODO: calculate the longest hostname length rather than hard coding it
|
// TODO: calculate the longest hostname length rather than hard coding it
|
||||||
@@ -48,7 +51,7 @@ const updateHeaderHtml = (server) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert it to a string, as that's how it's stored it will come out of the data attributes
|
// Convert it to a string, as that's how it's stored it will come out of the data attributes
|
||||||
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
|
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
|
||||||
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
||||||
accordion.header.dataset.ramPercentage = ramPercentage;
|
accordion.header.dataset.ramPercentage = ramPercentage;
|
||||||
@@ -81,16 +84,18 @@ function createActiveScriptsServerPanel(server) {
|
|||||||
header: hdr,
|
header: hdr,
|
||||||
panel: panel,
|
panel: panel,
|
||||||
panelList: panelScriptList,
|
panelList: panelScriptList,
|
||||||
scripts: {}, //Holds references to li elements for each active script
|
scripts: {}, // Holds references to li elements for each active script
|
||||||
scriptHdrs: {}, //Holds references to header elements for each active script
|
scriptHdrs: {}, // Holds references to header elements for each active script
|
||||||
scriptStats: {} //Holds references to the p elements containing text for each active script
|
scriptStats: {}, // Holds references to the p elements containing text for each active script
|
||||||
};
|
};
|
||||||
|
|
||||||
return li;
|
return li;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Deletes the info for a particular server (Dropdown header + Panel with all info)
|
/**
|
||||||
//in the Active Scripts page if it exists
|
* Deletes the info for a particular server (Dropdown header + Panel with all info)
|
||||||
|
* in the Active Scripts page if it exists
|
||||||
|
*/
|
||||||
function deleteActiveScriptsServerPanel(server) {
|
function deleteActiveScriptsServerPanel(server) {
|
||||||
let hostname = server.hostname;
|
let hostname = server.hostname;
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
if (ActiveScriptsUI[hostname] == null) {
|
||||||
@@ -98,9 +103,9 @@ function deleteActiveScriptsServerPanel(server) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make sure it's empty
|
// Make sure it's empty
|
||||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
|
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
|
||||||
console.log("WARNING: Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +117,7 @@ function deleteActiveScriptsServerPanel(server) {
|
|||||||
function addActiveScriptsItem(workerscript) {
|
function addActiveScriptsItem(workerscript) {
|
||||||
var server = getServer(workerscript.serverIp);
|
var server = getServer(workerscript.serverIp);
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
console.log("ERROR: Invalid server IP for workerscript in addActiveScriptsItem()");
|
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let hostname = server.hostname;
|
let hostname = server.hostname;
|
||||||
@@ -122,7 +127,7 @@ function addActiveScriptsItem(workerscript) {
|
|||||||
createActiveScriptsServerPanel(server);
|
createActiveScriptsServerPanel(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create the unique identifier (key) for this script
|
// Create the unique identifier (key) for this script
|
||||||
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
for (var i = 0; i < workerscript.args.length; ++i) {
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
itemNameArray.push(String(workerscript.args[i]));
|
||||||
@@ -139,8 +144,10 @@ function addActiveScriptsItem(workerscript) {
|
|||||||
panel.classList.remove("accordion-panel");
|
panel.classList.remove("accordion-panel");
|
||||||
panel.classList.add("active-scripts-script-panel");
|
panel.classList.add("active-scripts-script-panel");
|
||||||
|
|
||||||
//Handle the constant elements on the panel that don't change after creation
|
/**
|
||||||
//Threads, args, kill/log button
|
* Handle the constant elements on the panel that don't change after creation:
|
||||||
|
* Threads, args, kill/log button
|
||||||
|
*/
|
||||||
panel.appendChild(createElement("p", {
|
panel.appendChild(createElement("p", {
|
||||||
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
|
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
|
||||||
"Args: " + arrayToString(workerscript.args)
|
"Args: " + arrayToString(workerscript.args)
|
||||||
@@ -173,7 +180,7 @@ function addActiveScriptsItem(workerscript) {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Append element to list
|
// Append element to list
|
||||||
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
||||||
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
||||||
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
||||||
@@ -218,11 +225,13 @@ function deleteActiveScriptsItem(workerscript) {
|
|||||||
}.bind(null, workerscript));
|
}.bind(null, workerscript));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update the ActiveScriptsItems array
|
|
||||||
function updateActiveScriptsItems(maxTasks=150) {
|
function updateActiveScriptsItems(maxTasks=150) {
|
||||||
//Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
|
/**
|
||||||
//We'll limit this to 150 at a time in case someone decides to start a bunch of scripts all at once...
|
* Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
|
||||||
let numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
* We'll limit this to 150 at a time for performance (in case someone decides to start a
|
||||||
|
* bunch of scripts all at once...)
|
||||||
|
*/
|
||||||
|
const numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
||||||
for (let i = 0; i < numTasks; ++i) {
|
for (let i = 0; i < numTasks; ++i) {
|
||||||
let task = ActiveScriptsTasks.shift();
|
let task = ActiveScriptsTasks.shift();
|
||||||
try {
|
try {
|
||||||
@@ -233,8 +242,8 @@ function updateActiveScriptsItems(maxTasks=150) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!routing.isOn(Page.ActiveScripts)) {return;}
|
if (!routing.isOn(Page.ActiveScripts)) { return; }
|
||||||
var total = 0;
|
let total = 0;
|
||||||
for (var i = 0; i < workerScripts.length; ++i) {
|
for (var i = 0; i < workerScripts.length; ++i) {
|
||||||
try {
|
try {
|
||||||
total += updateActiveScriptsItemContent(workerScripts[i]);
|
total += updateActiveScriptsItemContent(workerScripts[i]);
|
||||||
@@ -246,10 +255,10 @@ function updateActiveScriptsItems(maxTasks=150) {
|
|||||||
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
|
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
|
||||||
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
|
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
|
||||||
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
|
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Updates the content of the given item in the Active Scripts list
|
|
||||||
function updateActiveScriptsItemContent(workerscript) {
|
function updateActiveScriptsItemContent(workerscript) {
|
||||||
var server = getServer(workerscript.serverIp);
|
var server = getServer(workerscript.serverIp);
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
@@ -258,7 +267,7 @@ function updateActiveScriptsItemContent(workerscript) {
|
|||||||
}
|
}
|
||||||
let hostname = server.hostname;
|
let hostname = server.hostname;
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
if (ActiveScriptsUI[hostname] == null) {
|
||||||
return; //Hasn't been created yet. We'll skip it
|
return; // Hasn't been created yet. We'll skip it
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHeaderHtml(server);
|
updateHeaderHtml(server);
|
||||||
@@ -270,11 +279,11 @@ function updateActiveScriptsItemContent(workerscript) {
|
|||||||
var itemName = itemNameArray.join("-");
|
var itemName = itemNameArray.join("-");
|
||||||
|
|
||||||
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
||||||
return; //Hasn't been fully added yet. We'll skip it
|
return; // Hasn't been fully added yet. We'll skip it
|
||||||
}
|
}
|
||||||
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
||||||
|
|
||||||
//Update the text if necessary. This fn returns the online $/s production
|
// Update the text if necessary. This fn returns the online $/s production
|
||||||
return updateActiveScriptsText(workerscript, item, itemName);
|
return updateActiveScriptsText(workerscript, item, itemName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +302,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
|||||||
updateHeaderHtml(server);
|
updateHeaderHtml(server);
|
||||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
||||||
|
|
||||||
//Only update if the item is visible
|
// Only update if the item is visible
|
||||||
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
||||||
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
||||||
|
|
||||||
@@ -302,7 +311,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
|||||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
||||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
||||||
|
|
||||||
//Online
|
// Online
|
||||||
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
|
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
|
||||||
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, " ");
|
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, " ");
|
||||||
|
|
||||||
@@ -310,7 +319,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
|||||||
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
||||||
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||||
|
|
||||||
//Offline
|
// Offline
|
||||||
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
|
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
|
||||||
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ");
|
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ");
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import {post} from "./ui/postToTerminal";
|
import { IMap } from "./types";
|
||||||
|
import { post } from "./ui/postToTerminal";
|
||||||
|
|
||||||
let Aliases = {};
|
export let Aliases: IMap<string> = {};
|
||||||
let GlobalAliases = {};
|
export let GlobalAliases: IMap<string> = {};
|
||||||
|
|
||||||
function loadAliases(saveString) {
|
export function loadAliases(saveString: string): void {
|
||||||
if (saveString === "") {
|
if (saveString === "") {
|
||||||
Aliases = {};
|
Aliases = {};
|
||||||
} else {
|
} else {
|
||||||
@@ -11,7 +12,7 @@ function loadAliases(saveString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadGlobalAliases(saveString) {
|
export function loadGlobalAliases(saveString: string): void {
|
||||||
if (saveString === "") {
|
if (saveString === "") {
|
||||||
GlobalAliases = {};
|
GlobalAliases = {};
|
||||||
} else {
|
} else {
|
||||||
@@ -19,8 +20,8 @@ function loadGlobalAliases(saveString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Print all aliases to terminal
|
// Prints all aliases to terminal
|
||||||
function printAliases() {
|
export function printAliases(): void {
|
||||||
for (var name in Aliases) {
|
for (var name in Aliases) {
|
||||||
if (Aliases.hasOwnProperty(name)) {
|
if (Aliases.hasOwnProperty(name)) {
|
||||||
post("alias " + name + "=" + Aliases[name]);
|
post("alias " + name + "=" + Aliases[name]);
|
||||||
@@ -33,8 +34,8 @@ function printAliases() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//True if successful, false otherwise
|
// Returns true if successful, false otherwise
|
||||||
function parseAliasDeclaration(dec,global=false) {
|
export function parseAliasDeclaration(dec: string, global: boolean=false) {
|
||||||
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
||||||
var matches = dec.match(re);
|
var matches = dec.match(re);
|
||||||
if (matches == null || matches.length != 3) {return false;}
|
if (matches == null || matches.length != 3) {return false;}
|
||||||
@@ -46,50 +47,55 @@ function parseAliasDeclaration(dec,global=false) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAlias(name, value) {
|
function addAlias(name: string, value: string): void {
|
||||||
if (name in GlobalAliases){
|
if (name in GlobalAliases) {
|
||||||
delete GlobalAliases[name];
|
delete GlobalAliases[name];
|
||||||
}
|
}
|
||||||
Aliases[name] = value;
|
Aliases[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGlobalAlias(name, value) {
|
function addGlobalAlias(name: string, value: string): void {
|
||||||
if (name in Aliases){
|
if (name in Aliases){
|
||||||
delete Aliases[name];
|
delete Aliases[name];
|
||||||
}
|
}
|
||||||
GlobalAliases[name] = value;
|
GlobalAliases[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlias(name) {
|
function getAlias(name: string): string | null {
|
||||||
if (Aliases.hasOwnProperty(name)) {
|
if (Aliases.hasOwnProperty(name)) {
|
||||||
return Aliases[name];
|
return Aliases[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGlobalAlias(name) {
|
function getGlobalAlias(name: string): string | null {
|
||||||
if (GlobalAliases.hasOwnProperty(name)) {
|
if (GlobalAliases.hasOwnProperty(name)) {
|
||||||
return GlobalAliases[name];
|
return GlobalAliases[name];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeAlias(name) {
|
export function removeAlias(name: string): boolean {
|
||||||
if (Aliases.hasOwnProperty(name)) {
|
if (Aliases.hasOwnProperty(name)) {
|
||||||
delete Aliases[name];
|
delete Aliases[name];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GlobalAliases.hasOwnProperty(name)) {
|
if (GlobalAliases.hasOwnProperty(name)) {
|
||||||
delete GlobalAliases[name];
|
delete GlobalAliases[name];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns the original string with any aliases substituted in
|
/**
|
||||||
//Aliases only applied to "whole words", one level deep
|
* Returns the original string with any aliases substituted in.
|
||||||
function substituteAliases(origCommand) {
|
* Aliases are only applied to "whole words", one level deep
|
||||||
var commandArray = origCommand.split(" ");
|
*/
|
||||||
|
export function substituteAliases(origCommand: string): string {
|
||||||
|
const commandArray = origCommand.split(" ");
|
||||||
if (commandArray.length > 0){
|
if (commandArray.length > 0){
|
||||||
// For the unalias command, dont substite
|
// For the unalias command, dont substite
|
||||||
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
||||||
@@ -112,6 +118,3 @@ function substituteAliases(origCommand) {
|
|||||||
}
|
}
|
||||||
return commandArray.join(" ");
|
return commandArray.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Aliases, GlobalAliases, printAliases, parseAliasDeclaration,
|
|
||||||
removeAlias, substituteAliases, loadAliases, loadGlobalAliases};
|
|
||||||
@@ -10,6 +10,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
|
|||||||
|
|
||||||
interface IConstructorParams {
|
interface IConstructorParams {
|
||||||
info: string;
|
info: string;
|
||||||
|
isSpecial?: boolean;
|
||||||
moneyCost: number;
|
moneyCost: number;
|
||||||
name: string;
|
name: string;
|
||||||
prereqs?: string[];
|
prereqs?: string[];
|
||||||
@@ -62,6 +63,9 @@ export class Augmentation {
|
|||||||
// Description of what this Aug is and what it does
|
// Description of what this Aug is and what it does
|
||||||
info: string = "";
|
info: string = "";
|
||||||
|
|
||||||
|
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
|
||||||
|
isSpecial: boolean = false;
|
||||||
|
|
||||||
// Augmentation level - for repeatable Augs like NeuroFlux Governor
|
// Augmentation level - for repeatable Augs like NeuroFlux Governor
|
||||||
level: number = 0;
|
level: number = 0;
|
||||||
|
|
||||||
@@ -90,6 +94,10 @@ export class Augmentation {
|
|||||||
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||||
this.startingCost = this.baseCost;
|
this.startingCost = this.baseCost;
|
||||||
|
|
||||||
|
if (params.isSpecial) {
|
||||||
|
this.isSpecial = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.level = 0;
|
this.level = 0;
|
||||||
|
|
||||||
// Set multipliers
|
// Set multipliers
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@ export let AugmentationNames: IMap<string> = {
|
|||||||
PCDNINeuralNetwork: "PC Direct-Neural Interface NeuroNet Injector",
|
PCDNINeuralNetwork: "PC Direct-Neural Interface NeuroNet Injector",
|
||||||
ADRPheromone1: "ADR-V1 Pheromone Gene",
|
ADRPheromone1: "ADR-V1 Pheromone Gene",
|
||||||
ADRPheromone2: "ADR-V2 Pheromone Gene",
|
ADRPheromone2: "ADR-V2 Pheromone Gene",
|
||||||
|
ShadowsSimulacrum: "The Shadow's Simulacrum",
|
||||||
HacknetNodeCPUUpload: "Hacknet Node CPU Architecture Neural-Upload",
|
HacknetNodeCPUUpload: "Hacknet Node CPU Architecture Neural-Upload",
|
||||||
HacknetNodeCacheUpload: "Hacknet Node Cache Architecture Neural-Upload",
|
HacknetNodeCacheUpload: "Hacknet Node Cache Architecture Neural-Upload",
|
||||||
HacknetNodeNICUpload: "Hacknet Node NIC Architecture Neural-Upload",
|
HacknetNodeNICUpload: "Hacknet Node NIC Architecture Neural-Upload",
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ export function initBitNodes() {
|
|||||||
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
||||||
"You will no longer gain passive reputation with Factions<br><br>" +
|
"You will no longer gain passive reputation with Factions<br><br>" +
|
||||||
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File increases the player's crime success rate, " +
|
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
|
||||||
"crime money, and charisma multipliers by:<br><br>" +
|
"once your karma decreases to a certain value. " +
|
||||||
|
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||||
"Level 1: 24%<br>" +
|
"Level 1: 24%<br>" +
|
||||||
"Level 2: 36%<br>" +
|
"Level 2: 36%<br>" +
|
||||||
"Level 3: 42%");
|
"Level 3: 42%");
|
||||||
@@ -81,7 +82,8 @@ export function initBitNodes() {
|
|||||||
"Level 1: 8%<br>" +
|
"Level 1: 8%<br>" +
|
||||||
"Level 2: 12%<br>" +
|
"Level 2: 12%<br>" +
|
||||||
"Level 3: 14%");
|
"Level 3: 14%");
|
||||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine", "The Singularity has arrived. The human race is gone, replaced " +
|
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
|
||||||
|
"The Singularity has arrived. The human race is gone, replaced " +
|
||||||
"by artificially superintelligent beings that are more machine than man. <br><br>" +
|
"by artificially superintelligent beings that are more machine than man. <br><br>" +
|
||||||
"In this BitNode, progressing is significantly harder. Experience gain rates " +
|
"In this BitNode, progressing is significantly harder. Experience gain rates " +
|
||||||
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
|
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
|
||||||
@@ -92,7 +94,8 @@ export function initBitNodes() {
|
|||||||
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
||||||
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
||||||
"that you can use.");
|
"that you can use.");
|
||||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman", "They said it couldn't be done. They said the human brain, " +
|
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
|
||||||
|
"They said it couldn't be done. They said the human brain, " +
|
||||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||||
"by 1's and 0's. They were wrong.<br><br>" +
|
"by 1's and 0's. They were wrong.<br><br>" +
|
||||||
@@ -173,7 +176,24 @@ export function initBitNodes() {
|
|||||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||||
"This Source-File also increases your hacking growth multipliers by: " +
|
"This Source-File also increases your hacking growth multipliers by: " +
|
||||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
"<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, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||||
|
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||||
|
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||||
|
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||||
|
"In this BitNode:<br><br>" +
|
||||||
|
"Your stats are significantly decreased<br>" +
|
||||||
|
"You cannnot purchase additional servers<br>" +
|
||||||
|
"Hacking is significantly less profitable<br><br>" +
|
||||||
|
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||||
|
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||||
|
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||||
|
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||||
|
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||||
|
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||||
|
"when installing Augmentations)");
|
||||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
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 " +
|
"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 " +
|
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||||
@@ -183,7 +203,7 @@ export function initBitNodes() {
|
|||||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||||
"In this BitNode:<br><br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"Your stats are significantly decreased.<br>" +
|
"Your stats are significantly decreased<br>" +
|
||||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||||
@@ -198,8 +218,9 @@ export function initBitNodes() {
|
|||||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||||
"In this BitNode:<br><br>" +
|
"In this BitNode:<br><br>" +
|
||||||
|
"Your hacking stat and experience gain are halved<br>" +
|
||||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||||
"The growth rate of servers is halved<br>" +
|
"The growth rate of servers is significantly reduced<br>" +
|
||||||
"Weakening a server is twice as effective<br>" +
|
"Weakening a server is twice as effective<br>" +
|
||||||
"Company wages are decreased by 50%<br>" +
|
"Company wages are decreased by 50%<br>" +
|
||||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||||
@@ -210,9 +231,9 @@ export function initBitNodes() {
|
|||||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||||
"Level 1: 24%<br>" +
|
"Level 1: 32%<br>" +
|
||||||
"Level 2: 36%<br>" +
|
"Level 2: 48%<br>" +
|
||||||
"Level 3: 42%");
|
"Level 3: 56%");
|
||||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||||
"To iterate is human, to recurse divine.<br><br>" +
|
"To iterate is human, to recurse divine.<br><br>" +
|
||||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||||
@@ -245,9 +266,9 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (p.bitNodeN) {
|
switch (p.bitNodeN) {
|
||||||
case 1: //Source Genesis (every multiplier is 1)
|
case 1: // Source Genesis (every multiplier is 1)
|
||||||
break;
|
break;
|
||||||
case 2: //Rise of the Underworld
|
case 2: // Rise of the Underworld
|
||||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||||
BitNodeMultipliers.ServerGrowthRate = 0.8;
|
BitNodeMultipliers.ServerGrowthRate = 0.8;
|
||||||
BitNodeMultipliers.ServerMaxMoney = 0.2;
|
BitNodeMultipliers.ServerMaxMoney = 0.2;
|
||||||
@@ -257,7 +278,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||||
break;
|
break;
|
||||||
case 3: //Corporatocracy
|
case 3: // Corporatocracy
|
||||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||||
BitNodeMultipliers.RepToDonateToFaction = 0.5;
|
BitNodeMultipliers.RepToDonateToFaction = 0.5;
|
||||||
BitNodeMultipliers.AugmentationRepCost = 3;
|
BitNodeMultipliers.AugmentationRepCost = 3;
|
||||||
@@ -272,7 +293,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||||
break;
|
break;
|
||||||
case 4: //The Singularity
|
case 4: // The Singularity
|
||||||
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||||
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||||
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
||||||
@@ -286,7 +307,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||||
break;
|
break;
|
||||||
case 5: //Artificial intelligence
|
case 5: // Artificial intelligence
|
||||||
BitNodeMultipliers.ServerMaxMoney = 2;
|
BitNodeMultipliers.ServerMaxMoney = 2;
|
||||||
BitNodeMultipliers.ServerStartingSecurity = 2;
|
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||||
@@ -299,7 +320,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.HackExpGain = 0.5;
|
BitNodeMultipliers.HackExpGain = 0.5;
|
||||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||||
break;
|
break;
|
||||||
case 6: //Bladeburner
|
case 6: // Bladeburner
|
||||||
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
||||||
BitNodeMultipliers.ServerMaxMoney = 0.4;
|
BitNodeMultipliers.ServerMaxMoney = 0.4;
|
||||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||||
@@ -314,7 +335,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.HackExpGain = 0.25;
|
BitNodeMultipliers.HackExpGain = 0.25;
|
||||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||||
break;
|
break;
|
||||||
case 7: //Bladeburner 2079
|
case 7: // Bladeburner 2079
|
||||||
BitNodeMultipliers.BladeburnerRank = 0.6;
|
BitNodeMultipliers.BladeburnerRank = 0.6;
|
||||||
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
||||||
BitNodeMultipliers.AugmentationMoneyCost = 3;
|
BitNodeMultipliers.AugmentationMoneyCost = 3;
|
||||||
@@ -334,7 +355,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||||
break;
|
break;
|
||||||
case 8: //Ghost of Wall Street
|
case 8: // Ghost of Wall Street
|
||||||
BitNodeMultipliers.ScriptHackMoney = 0;
|
BitNodeMultipliers.ScriptHackMoney = 0;
|
||||||
BitNodeMultipliers.ManualHackMoney = 0;
|
BitNodeMultipliers.ManualHackMoney = 0;
|
||||||
BitNodeMultipliers.CompanyWorkMoney = 0;
|
BitNodeMultipliers.CompanyWorkMoney = 0;
|
||||||
@@ -345,6 +366,27 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.CorporationValuation = 0;
|
BitNodeMultipliers.CorporationValuation = 0;
|
||||||
BitNodeMultipliers.CodingContractMoney = 0;
|
BitNodeMultipliers.CodingContractMoney = 0;
|
||||||
break;
|
break;
|
||||||
|
case 9: // Hacktocracy
|
||||||
|
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
|
||||||
|
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 = 5;
|
||||||
|
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||||
|
BitNodeMultipliers.ScriptHackMoney = 0.1;
|
||||||
|
BitNodeMultipliers.HackExpGain = 0.05;
|
||||||
|
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||||
|
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||||
|
BitNodeMultipliers.ServerStartingSecurity = 2.5;
|
||||||
|
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||||
|
BitNodeMultipliers.FourSigmaMarketDataCost = 5;
|
||||||
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||||
|
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||||
|
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||||
|
break;
|
||||||
case 10: // Digital Carbon
|
case 10: // Digital Carbon
|
||||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
||||||
@@ -369,9 +411,11 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||||
break;
|
break;
|
||||||
case 11: //The Big Crash
|
case 11: //The Big Crash
|
||||||
|
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
|
||||||
|
BitNodeMultipliers.HackExpGain = 0.5;
|
||||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||||
BitNodeMultipliers.ServerGrowthRate = 0.5;
|
BitNodeMultipliers.ServerGrowthRate = 0.2;
|
||||||
BitNodeMultipliers.ServerWeakenRate = 2;
|
BitNodeMultipliers.ServerWeakenRate = 2;
|
||||||
BitNodeMultipliers.CrimeMoney = 3;
|
BitNodeMultipliers.CrimeMoney = 3;
|
||||||
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
||||||
@@ -379,8 +423,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||||
BitNodeMultipliers.InfiltrationMoney = 2.5;
|
BitNodeMultipliers.InfiltrationMoney = 2.5;
|
||||||
BitNodeMultipliers.InfiltrationRep = 2.5;
|
BitNodeMultipliers.InfiltrationRep = 2.5;
|
||||||
BitNodeMultipliers.CorporationValuation = 0.01;
|
BitNodeMultipliers.CorporationValuation = 0.1;
|
||||||
BitNodeMultipliers.CodingContractMoney = 0.5;
|
BitNodeMultipliers.CodingContractMoney = 0.25;
|
||||||
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
||||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -120,7 +120,8 @@ interface IBitNodeMultipliers {
|
|||||||
HackingLevelMultiplier: number;
|
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;
|
HacknetNodeMoney: number;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,35 @@
|
|||||||
import {Engine} from "./engine";
|
|
||||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
|
||||||
|
|
||||||
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
|
import { Engine } from "./engine";
|
||||||
import {createElement} from "../utils/uiHelpers/createElement";
|
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
|
||||||
import {isString} from "../utils/helpers/isString";
|
|
||||||
|
|
||||||
var cinematicTextFlag = false;
|
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||||
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
|
import { isString } from "../utils/helpers/isString";
|
||||||
|
|
||||||
//Lines must be an array of strings
|
export let cinematicTextFlag = false;
|
||||||
function writeCinematicText(lines) {
|
|
||||||
|
/**
|
||||||
|
* Print a message using a hacking-style "typing" effect.
|
||||||
|
* Note that this clears the UI so that the text from this is the only thing visible.
|
||||||
|
*
|
||||||
|
* @param lines {string[]} Array of strings to print, where each element is a separate line
|
||||||
|
*/
|
||||||
|
export function writeCinematicText(lines) {
|
||||||
cinematicTextFlag = true;
|
cinematicTextFlag = true;
|
||||||
|
|
||||||
if (lines.constructor !== Array) {
|
if (lines.constructor !== Array) {
|
||||||
throw new Error("Invalid non-array argument passed into writeCinematicText()");
|
throw new Error("Invalid non-array argument passed into writeCinematicText()");
|
||||||
}
|
}
|
||||||
|
|
||||||
//We'll reuse the 'Red Pill' content
|
// Reuse the 'Red Pill' content
|
||||||
Engine.loadCinematicTextContent();
|
Engine.loadCinematicTextContent();
|
||||||
var container = document.getElementById("cinematic-text-container");
|
const container = document.getElementById("cinematic-text-container");
|
||||||
container.style.width = "75%";
|
container.style.width = "75%";
|
||||||
if (container == null) {throw new Error("Could not find cinematic-text-container for writeCinematicText()");}
|
if (container == null) {throw new Error("Could not find cinematic-text-container for writeCinematicText()");}
|
||||||
removeChildrenFromElement(container);
|
removeChildrenFromElement(container);
|
||||||
|
|
||||||
for (var i = 0; i < lines.length; ++i) {
|
for (let i = 0; i < lines.length; ++i) {
|
||||||
if (!isString(lines[i])) {
|
if (!isString(lines[i])) {
|
||||||
throw new Error("Invalid non-string element in 'lines' argument. writeCinematicText() failed");
|
throw new Error("Invalid non-string element in 'lines' argument. writeCinematicText() failed");
|
||||||
}
|
}
|
||||||
@@ -45,11 +51,11 @@ function writeCinematicTextRecurse(lines, lineNumber=0) {
|
|||||||
|
|
||||||
function writeCinematicTextLine(line) {
|
function writeCinematicTextLine(line) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var container = document.getElementById("cinematic-text-container");
|
const container = document.getElementById("cinematic-text-container");
|
||||||
var pElem = document.createElement("p");
|
const pElem = document.createElement("p");
|
||||||
container.appendChild(pElem);
|
container.appendChild(pElem);
|
||||||
|
|
||||||
var promise = writeCinematicTextLetter(pElem, line, 0);
|
const promise = writeCinematicTextLetter(pElem, line, 0);
|
||||||
promise.then(function(res) {
|
promise.then(function(res) {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}, function(e) {
|
}, function(e) {
|
||||||
@@ -61,14 +67,15 @@ function writeCinematicTextLine(line) {
|
|||||||
function writeCinematicTextLetter(pElem, line, i=0) {
|
function writeCinematicTextLetter(pElem, line, i=0) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
setTimeoutRef(function() {
|
setTimeoutRef(function() {
|
||||||
|
const textToShow = line.substring(0, i);
|
||||||
|
|
||||||
if (i >= line.length) {
|
if (i >= line.length) {
|
||||||
var textToShow = line.substring(0, i);
|
|
||||||
pElem.innerHTML = textToShow;
|
pElem.innerHTML = textToShow;
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
}
|
}
|
||||||
var textToShow = line.substring(0, i);
|
|
||||||
pElem.innerHTML = textToShow + "<span class='typed-cursor'> █ </span>";
|
pElem.innerHTML = textToShow + "<span class='typed-cursor'> █ </span>";
|
||||||
var promise = writeCinematicTextLetter(pElem, line, i+1);
|
const promise = writeCinematicTextLetter(pElem, line, i+1);
|
||||||
promise.then(function(res) {
|
promise.then(function(res) {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}, function(e) {
|
}, function(e) {
|
||||||
@@ -96,5 +103,3 @@ function cinematicTextEnd() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export {cinematicTextFlag, writeCinematicText};
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { CodingContract,
|
import {
|
||||||
CodingContractRewardType,
|
CodingContract,
|
||||||
CodingContractTypes } from "./CodingContracts";
|
CodingContractRewardType,
|
||||||
import { Factions } from "./Faction/Factions";
|
CodingContractTypes
|
||||||
import { Player } from "./Player";
|
} from "./CodingContracts";
|
||||||
import { AllServers } from "./Server/AllServers";
|
import { Factions } from "./Faction/Factions";
|
||||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
import { Player } from "./Player";
|
||||||
|
import { AllServers } from "./Server/AllServers";
|
||||||
|
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||||
|
|
||||||
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
|
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
|
||||||
|
|
||||||
export function generateRandomContract() {
|
export function generateRandomContract() {
|
||||||
// First select a random problem type
|
// First select a random problem type
|
||||||
@@ -127,14 +130,15 @@ function getRandomReward() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
switch (reward.type) {
|
switch (reward.type) {
|
||||||
case CodingContractRewardType.FactionReputation:
|
case CodingContractRewardType.FactionReputation: {
|
||||||
// Get a random faction that player is a part of. That
|
// Get a random faction that player is a part of. That
|
||||||
// faction must allow hacking contracts
|
// faction must allow hacking contracts
|
||||||
var numFactions = factionsThatAllowHacking.length;
|
var numFactions = factionsThatAllowHacking.length;
|
||||||
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
|
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
|
||||||
reward.name = randFaction;
|
reward.name = randFaction;
|
||||||
break;
|
break;
|
||||||
case CodingContractRewardType.CompanyReputation:
|
}
|
||||||
|
case CodingContractRewardType.CompanyReputation: {
|
||||||
const allJobs = Object.keys(Player.jobs);
|
const allJobs = Object.keys(Player.jobs);
|
||||||
if (allJobs.length > 0) {
|
if (allJobs.length > 0) {
|
||||||
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
|
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
|
||||||
@@ -142,6 +146,7 @@ function getRandomReward() {
|
|||||||
reward.type = CodingContractRewardType.Money;
|
reward.type = CodingContractRewardType.Money;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import { codingContractTypesMetadata,
|
import {
|
||||||
DescriptionFunc,
|
codingContractTypesMetadata,
|
||||||
GeneratorFunc,
|
DescriptionFunc,
|
||||||
SolverFunc } from "./data/codingcontracttypes";
|
GeneratorFunc,
|
||||||
|
SolverFunc
|
||||||
|
} from "./data/codingcontracttypes";
|
||||||
|
|
||||||
import { IMap } from "./types";
|
import { IMap } from "./types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import {
|
||||||
|
Generic_fromJSON,
|
||||||
|
Generic_toJSON,
|
||||||
|
Reviver
|
||||||
|
} from "../utils/JSONReviver";
|
||||||
import { KEY } from "../utils/helpers/keyCodes";
|
import { KEY } from "../utils/helpers/keyCodes";
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||||
@@ -13,6 +19,7 @@ import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
||||||
|
|
||||||
/* Represents different types of problems that a Coding Contract can have */
|
/* Represents different types of problems that a Coding Contract can have */
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
|
||||||
import { CompanyPosition } from "./CompanyPosition";
|
import { CompanyPosition } from "./CompanyPosition";
|
||||||
|
import * as posNames from "./data/companypositionnames";
|
||||||
|
|
||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
|
||||||
export interface IConstructorParams {
|
export interface IConstructorParams {
|
||||||
name: string;
|
name: string;
|
||||||
info: string;
|
info: string;
|
||||||
@@ -93,6 +96,43 @@ export class Company {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasAgentPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.AgentCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasBusinessConsultantPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.BusinessConsultantCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasBusinessPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.BusinessCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasEmployeePositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.MiscCompanyPositions[1]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasITPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.ITCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSecurityPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.SecurityCompanyPositions[2]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSoftwareConsultantPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.SoftwareConsultantCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSoftwarePositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.SoftwareCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasWaiterPositions(): boolean {
|
||||||
|
return (this.companyPositions[posNames.MiscCompanyPositions[0]] != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
gainFavor(): void {
|
gainFavor(): void {
|
||||||
if (this.favor == null) { this.favor = 0; }
|
if (this.favor == null) { this.favor = 0; }
|
||||||
if (this.rolloverRep == null) { this.rolloverRep = 0; }
|
if (this.rolloverRep == null) { this.rolloverRep = 0; }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
import * as names from "./data/CompanyPositionNames";
|
import * as names from "./data/companypositionnames";
|
||||||
|
|
||||||
/* tslint:disable:completed-docs */
|
/* tslint:disable:completed-docs */
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { CompanyPosition } from "./CompanyPosition";
|
import { CompanyPosition } from "./CompanyPosition";
|
||||||
import { CompanyPositions } from "./CompanyPositions";
|
import { CompanyPositions } from "./CompanyPositions";
|
||||||
|
|
||||||
export function getNextCompanyPosition(currPos: CompanyPosition | null): CompanyPosition | null {
|
export function getNextCompanyPositionHelper(currPos: CompanyPosition | null): CompanyPosition | null {
|
||||||
if (currPos == null) { return null; }
|
if (currPos == null) { return null; }
|
||||||
|
|
||||||
const nextPosName: string | null = currPos.nextPosition;
|
const nextPosName: string | null = currPos.nextPosition;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { IConstructorParams } from "../Company";
|
import * as posNames from "./companypositionnames";
|
||||||
import { Locations } from "../../Locations";
|
import { IConstructorParams } from "../Company";
|
||||||
import * as posNames from "./CompanyPositionNames";
|
|
||||||
import { IMap } from "../../types";
|
import { IMap } from "../../types";
|
||||||
|
import { LocationName } from "../../Locations/data/LocationNames";
|
||||||
|
|
||||||
// Create Objects containing Company Positions by category
|
// Create Objects containing Company Positions by category
|
||||||
// Will help in metadata construction later
|
// Will help in metadata construction later
|
||||||
@@ -89,7 +90,7 @@ CEOOnly[posNames.BusinessCompanyPositions[5]] = true;
|
|||||||
// Metadata
|
// Metadata
|
||||||
export const companiesMetadata: IConstructorParams[] = [
|
export const companiesMetadata: IConstructorParams[] = [
|
||||||
{
|
{
|
||||||
name: Locations.AevumECorp,
|
name: LocationName.AevumECorp,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -101,7 +102,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 249,
|
jobStatReqOffset: 249,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12MegaCorp,
|
name: LocationName.Sector12MegaCorp,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -113,7 +114,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 249,
|
jobStatReqOffset: 249,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumBachmanAndAssociates,
|
name: LocationName.AevumBachmanAndAssociates,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -125,7 +126,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12BladeIndustries,
|
name: LocationName.Sector12BladeIndustries,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -137,7 +138,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenNWO,
|
name: LocationName.VolhavenNWO,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -149,7 +150,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 249,
|
jobStatReqOffset: 249,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumClarkeIncorporated,
|
name: LocationName.AevumClarkeIncorporated,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -161,7 +162,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenOmniTekIncorporated,
|
name: LocationName.VolhavenOmniTekIncorporated,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -173,7 +174,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12FourSigma,
|
name: LocationName.Sector12FourSigma,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -185,7 +186,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.ChongqingKuaiGongInternational,
|
name: LocationName.ChongqingKuaiGongInternational,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -197,7 +198,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumFulcrumTechnologies,
|
name: LocationName.AevumFulcrumTechnologies,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -208,7 +209,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.IshimaStormTechnologies,
|
name: LocationName.IshimaStormTechnologies,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -220,7 +221,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.NewTokyoDefComm,
|
name: LocationName.NewTokyoDefComm,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -232,7 +233,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenHeliosLabs,
|
name: LocationName.VolhavenHeliosLabs,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -244,7 +245,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.NewTokyoVitaLife,
|
name: LocationName.NewTokyoVitaLife,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -256,7 +257,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12IcarusMicrosystems,
|
name: LocationName.Sector12IcarusMicrosystems,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -268,7 +269,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12UniversalEnergy,
|
name: LocationName.Sector12UniversalEnergy,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -280,7 +281,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumGalacticCybersystems,
|
name: LocationName.AevumGalacticCybersystems,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -292,7 +293,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumAeroCorp,
|
name: LocationName.AevumAeroCorp,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -305,7 +306,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenOmniaCybersystems,
|
name: LocationName.VolhavenOmniaCybersystems,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -318,7 +319,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.ChongqingSolarisSpaceSystems,
|
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -331,7 +332,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12DeltaOne,
|
name: LocationName.Sector12DeltaOne,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
CEOOnly,
|
CEOOnly,
|
||||||
@@ -344,7 +345,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.NewTokyoGlobalPharmaceuticals,
|
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -357,7 +358,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 224,
|
jobStatReqOffset: 224,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.IshimaNovaMedical,
|
name: LocationName.IshimaNovaMedical,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -370,7 +371,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 199,
|
jobStatReqOffset: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12CIA,
|
name: LocationName.Sector12CIA,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
SoftwarePositionsUpToHeadOfEngineering,
|
SoftwarePositionsUpToHeadOfEngineering,
|
||||||
@@ -385,7 +386,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 149,
|
jobStatReqOffset: 149,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12NSA,
|
name: LocationName.Sector12NSA,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
SoftwarePositionsUpToHeadOfEngineering,
|
SoftwarePositionsUpToHeadOfEngineering,
|
||||||
@@ -400,7 +401,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 149,
|
jobStatReqOffset: 149,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumWatchdogSecurity,
|
name: LocationName.AevumWatchdogSecurity,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
SoftwarePositionsUpToHeadOfEngineering,
|
SoftwarePositionsUpToHeadOfEngineering,
|
||||||
@@ -415,7 +416,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 124,
|
jobStatReqOffset: 124,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenLexoCorp,
|
name: LocationName.VolhavenLexoCorp,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -428,7 +429,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 99,
|
jobStatReqOffset: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumRhoConstruction,
|
name: LocationName.AevumRhoConstruction,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
SoftwarePositionsUpToLeadDeveloper,
|
SoftwarePositionsUpToLeadDeveloper,
|
||||||
@@ -439,7 +440,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 49,
|
jobStatReqOffset: 49,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12AlphaEnterprises,
|
name: LocationName.Sector12AlphaEnterprises,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
SoftwarePositionsUpToLeadDeveloper,
|
SoftwarePositionsUpToLeadDeveloper,
|
||||||
@@ -451,7 +452,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 99,
|
jobStatReqOffset: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumPolice,
|
name: LocationName.AevumPolice,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllSecurityPositions,
|
AllSecurityPositions,
|
||||||
@@ -462,7 +463,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 99,
|
jobStatReqOffset: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenSysCoreSecurities,
|
name: LocationName.VolhavenSysCoreSecurities,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions
|
AllTechnologyPositions
|
||||||
@@ -472,7 +473,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 124,
|
jobStatReqOffset: 124,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.VolhavenCompuTek,
|
name: LocationName.VolhavenCompuTek,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions
|
AllTechnologyPositions
|
||||||
@@ -482,7 +483,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 74,
|
jobStatReqOffset: 74,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.AevumNetLinkTechnologies,
|
name: LocationName.AevumNetLinkTechnologies,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions
|
AllTechnologyPositions
|
||||||
@@ -492,7 +493,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 99,
|
jobStatReqOffset: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12CarmichaelSecurity,
|
name: LocationName.Sector12CarmichaelSecurity,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllTechnologyPositions,
|
AllTechnologyPositions,
|
||||||
@@ -505,7 +506,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 74,
|
jobStatReqOffset: 74,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12FoodNStuff,
|
name: LocationName.Sector12FoodNStuff,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
EmployeeOnly, PartTimeEmployeeOnly
|
EmployeeOnly, PartTimeEmployeeOnly
|
||||||
@@ -515,7 +516,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 0,
|
jobStatReqOffset: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.Sector12JoesGuns,
|
name: LocationName.Sector12JoesGuns,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
EmployeeOnly, PartTimeEmployeeOnly
|
EmployeeOnly, PartTimeEmployeeOnly
|
||||||
@@ -525,7 +526,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 0,
|
jobStatReqOffset: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.IshimaOmegaSoftware,
|
name: LocationName.IshimaOmegaSoftware,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
AllSoftwarePositions,
|
AllSoftwarePositions,
|
||||||
@@ -537,7 +538,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
|||||||
jobStatReqOffset: 49,
|
jobStatReqOffset: 49,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Locations.NewTokyoNoodleBar,
|
name: LocationName.NewTokyoNoodleBar,
|
||||||
info: "",
|
info: "",
|
||||||
companyPositions: Object.assign({},
|
companyPositions: Object.assign({},
|
||||||
WaiterOnly, PartTimeWaiterOnly
|
WaiterOnly, PartTimeWaiterOnly
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Metadata used for constructing Company Positions
|
// Metadata used for constructing Company Positions
|
||||||
import { IConstructorParams } from "../CompanyPosition";
|
import { IConstructorParams } from "../CompanyPosition";
|
||||||
import * as posNames from "./CompanyPositionNames";
|
import * as posNames from "./companypositionnames";
|
||||||
|
|
||||||
export const companyPositionMetadata: IConstructorParams[] = [
|
export const companyPositionMetadata: IConstructorParams[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
277
src/Constants.ts
277
src/Constants.ts
@@ -1,110 +1,100 @@
|
|||||||
import {IMap} from "./types";
|
/**
|
||||||
|
* Generic Game Constants
|
||||||
|
*
|
||||||
|
* Constants for specific mechanics or features will NOT be here.
|
||||||
|
*/
|
||||||
|
import { IMap } from "./types";
|
||||||
|
|
||||||
export let CONSTANTS: IMap<any> = {
|
export let CONSTANTS: IMap<any> = {
|
||||||
Version: "0.45.1",
|
Version: "0.46.3",
|
||||||
|
|
||||||
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||||
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||||
//the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
|
* the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
|
||||||
|
*/
|
||||||
MaxSkillLevel: 975,
|
MaxSkillLevel: 975,
|
||||||
|
|
||||||
//Milliseconds per game cycle
|
// Milliseconds per game cycle
|
||||||
MilliPerCycle: 200,
|
MilliPerCycle: 200,
|
||||||
|
|
||||||
//How much reputation is needed to join a megacorporation's faction
|
// How much reputation is needed to join a megacorporation's faction
|
||||||
CorpFactionRepRequirement: 200e3,
|
CorpFactionRepRequirement: 200e3,
|
||||||
|
|
||||||
/* Base costs */
|
// Base RAM costs
|
||||||
BaseCostFor1GBOfRamHome: 32000,
|
BaseCostFor1GBOfRamHome: 32000,
|
||||||
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
||||||
BaseCostFor1GBOfRamHacknetNode: 30000,
|
|
||||||
|
|
||||||
|
// Cost to travel to another city
|
||||||
TravelCost: 200e3,
|
TravelCost: 200e3,
|
||||||
|
|
||||||
BaseCostForHacknetNode: 1000,
|
// Faction and Company favor-related things
|
||||||
BaseCostForHacknetNodeCore: 500000,
|
BaseFavorToDonate: 150,
|
||||||
|
DonateMoneyToRepDivisor: 1e6,
|
||||||
/* 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,
|
|
||||||
FactionReputationToFavorBase: 500,
|
FactionReputationToFavorBase: 500,
|
||||||
FactionReputationToFavorMult: 1.02,
|
FactionReputationToFavorMult: 1.02,
|
||||||
CompanyReputationToFavorBase: 500,
|
CompanyReputationToFavorBase: 500,
|
||||||
CompanyReputationToFavorMult: 1.02,
|
CompanyReputationToFavorMult: 1.02,
|
||||||
|
|
||||||
/* Augmentation */
|
// NeuroFlux Governor Augmentation cost multiplier
|
||||||
//NeuroFlux Governor cost multiplier as you level up
|
|
||||||
NeuroFluxGovernorLevelMult: 1.14,
|
NeuroFluxGovernorLevelMult: 1.14,
|
||||||
|
|
||||||
/* Netscript Constants */
|
// RAM Costs for Netscript functions
|
||||||
//RAM Costs for different commands
|
ScriptBaseRamCost: 1.6,
|
||||||
ScriptBaseRamCost: 1.6,
|
ScriptDomRamCost: 25,
|
||||||
ScriptDomRamCost: 25,
|
ScriptWhileRamCost: 0,
|
||||||
ScriptWhileRamCost: 0,
|
ScriptForRamCost: 0,
|
||||||
ScriptForRamCost: 0,
|
ScriptIfRamCost: 0,
|
||||||
ScriptIfRamCost: 0,
|
ScriptHackRamCost: 0.1,
|
||||||
ScriptHackRamCost: 0.1,
|
ScriptHackAnalyzeRamCost: 1,
|
||||||
ScriptHackAnalyzeRamCost: 1,
|
ScriptGrowRamCost: 0.15,
|
||||||
ScriptGrowRamCost: 0.15,
|
ScriptGrowthAnalyzeRamCost: 1,
|
||||||
ScriptGrowthAnalyzeRamCost: 1,
|
ScriptWeakenRamCost: 0.15,
|
||||||
ScriptWeakenRamCost: 0.15,
|
ScriptScanRamCost: 0.2,
|
||||||
ScriptScanRamCost: 0.2,
|
ScriptPortProgramRamCost: 0.05,
|
||||||
ScriptPortProgramRamCost: 0.05,
|
ScriptRunRamCost: 1.0,
|
||||||
ScriptRunRamCost: 1.0,
|
ScriptExecRamCost: 1.3,
|
||||||
ScriptExecRamCost: 1.3,
|
ScriptSpawnRamCost: 2.0,
|
||||||
ScriptSpawnRamCost: 2.0,
|
ScriptScpRamCost: 0.6,
|
||||||
ScriptScpRamCost: 0.6,
|
ScriptKillRamCost: 0.5,
|
||||||
ScriptKillRamCost: 0.5, //Kill and killall
|
ScriptHasRootAccessRamCost: 0.05,
|
||||||
ScriptHasRootAccessRamCost: 0.05,
|
ScriptGetHostnameRamCost: 0.05,
|
||||||
ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp()
|
ScriptGetHackingLevelRamCost: 0.05,
|
||||||
ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel()
|
ScriptGetMultipliersRamCost: 4.0,
|
||||||
ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers()
|
ScriptGetServerRamCost: 0.1,
|
||||||
ScriptGetServerRamCost: 0.1,
|
ScriptFileExistsRamCost: 0.1,
|
||||||
ScriptFileExistsRamCost: 0.1,
|
ScriptIsRunningRamCost: 0.1,
|
||||||
ScriptIsRunningRamCost: 0.1,
|
ScriptHacknetNodesRamCost: 4.0,
|
||||||
ScriptHacknetNodesRamCost: 4.0, //Base cost for accessing Hacknet Node API
|
ScriptHNUpgLevelRamCost: 0.4,
|
||||||
ScriptHNUpgLevelRamCost: 0.4,
|
ScriptHNUpgRamRamCost: 0.6,
|
||||||
ScriptHNUpgRamRamCost: 0.6,
|
ScriptHNUpgCoreRamCost: 0.8,
|
||||||
ScriptHNUpgCoreRamCost: 0.8,
|
ScriptGetStockRamCost: 2.0,
|
||||||
ScriptGetStockRamCost: 2.0,
|
ScriptBuySellStockRamCost: 2.5,
|
||||||
ScriptBuySellStockRamCost: 2.5,
|
|
||||||
ScriptGetPurchaseServerRamCost: 0.25,
|
ScriptGetPurchaseServerRamCost: 0.25,
|
||||||
ScriptPurchaseServerRamCost: 2.25,
|
ScriptPurchaseServerRamCost: 2.25,
|
||||||
ScriptGetPurchasedServerLimit: 0.05,
|
ScriptGetPurchasedServerLimit: 0.05,
|
||||||
ScriptGetPurchasedServerMaxRam: 0.05,
|
ScriptGetPurchasedServerMaxRam: 0.05,
|
||||||
ScriptRoundRamCost: 0.05,
|
ScriptRoundRamCost: 0.05,
|
||||||
ScriptReadWriteRamCost: 1.0,
|
ScriptReadWriteRamCost: 1.0,
|
||||||
ScriptArbScriptRamCost: 1.0, // Functions that apply to all scripts regardless of args
|
ScriptArbScriptRamCost: 1.0,
|
||||||
ScriptGetScriptRamCost: 0.1,
|
ScriptGetScriptRamCost: 0.1,
|
||||||
ScriptGetHackTimeRamCost: 0.05,
|
ScriptGetHackTimeRamCost: 0.05,
|
||||||
ScriptGetFavorToDonate: 0.10,
|
ScriptGetFavorToDonate: 0.10,
|
||||||
ScriptCodingContractBaseRamCost:10,
|
ScriptCodingContractBaseRamCost: 10,
|
||||||
ScriptSleeveBaseRamCost: 4,
|
ScriptSleeveBaseRamCost: 4,
|
||||||
|
|
||||||
ScriptSingularityFn1RamCost: 1,
|
ScriptSingularityFn1RamCost: 1,
|
||||||
ScriptSingularityFn2RamCost: 2,
|
ScriptSingularityFn2RamCost: 2,
|
||||||
ScriptSingularityFn3RamCost: 3,
|
ScriptSingularityFn3RamCost: 3,
|
||||||
|
|
||||||
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
|
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
|
||||||
|
|
||||||
ScriptGangApiBaseRamCost: 4,
|
ScriptGangApiBaseRamCost: 4,
|
||||||
|
|
||||||
ScriptBladeburnerApiBaseRamCost: 4,
|
ScriptBladeburnerApiBaseRamCost: 4,
|
||||||
|
|
||||||
NumNetscriptPorts: 20,
|
NumNetscriptPorts: 20,
|
||||||
|
|
||||||
//Server constants
|
// Server-related constants
|
||||||
HomeComputerMaxRam: 1073741824, // 2 ^ 30
|
HomeComputerMaxRam: 1073741824, // 2 ^ 30
|
||||||
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
|
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
|
||||||
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
|
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
|
||||||
@@ -112,48 +102,50 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
|
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
|
||||||
|
|
||||||
PurchasedServerLimit: 25,
|
PurchasedServerLimit: 25,
|
||||||
PurchasedServerMaxRam: 1048576, //2^20
|
PurchasedServerMaxRam: 1048576, // 2^20
|
||||||
|
|
||||||
//Augmentation Constants
|
// Augmentation Constants
|
||||||
AugmentationCostMultiplier: 5, //Used for balancing costs without having to readjust every Augmentation cost
|
AugmentationCostMultiplier: 5, // Used for balancing costs without having to readjust every Augmentation cost
|
||||||
AugmentationRepMultiplier: 2.5, //Used for balancing rep cost without having to readjust every value
|
AugmentationRepMultiplier: 2.5, // Used for balancing rep cost without having to readjust every value
|
||||||
MultipleAugMultiplier: 1.9,
|
MultipleAugMultiplier: 1.9,
|
||||||
|
|
||||||
//How much a TOR router costs
|
// TOR Router
|
||||||
TorRouterCost: 200000,
|
TorRouterCost: 200e3,
|
||||||
|
|
||||||
//Infiltration constants
|
// Infiltration
|
||||||
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
|
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
|
||||||
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
|
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
|
||||||
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
|
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
|
||||||
|
InfiltrationExpPow: 0.8,
|
||||||
|
|
||||||
//Stock market constants
|
// Stock market
|
||||||
WSEAccountCost: 200e6,
|
WSEAccountCost: 200e6,
|
||||||
TIXAPICost: 5e9,
|
TIXAPICost: 5e9,
|
||||||
MarketData4SCost: 1e9,
|
MarketData4SCost: 1e9,
|
||||||
MarketDataTixApi4SCost: 25e9,
|
MarketDataTixApi4SCost: 25e9,
|
||||||
StockMarketCommission: 100e3,
|
StockMarketCommission: 100e3,
|
||||||
|
|
||||||
//Hospital/Health
|
// Hospital/Health
|
||||||
HospitalCostPerHp: 100e3,
|
HospitalCostPerHp: 100e3,
|
||||||
|
|
||||||
//Intelligence-related constants
|
// Intelligence-related constants
|
||||||
IntelligenceCrimeWeight: 0.05, //Weight for how much int affects crime success rates
|
IntelligenceCrimeWeight: 0.05, // Weight for how much int affects crime success rates
|
||||||
IntelligenceInfiltrationWeight: 0.1, //Weight for how much int affects infiltration success rates
|
IntelligenceInfiltrationWeight: 0.1, // Weight for how much int affects infiltration success rates
|
||||||
IntelligenceCrimeBaseExpGain: 0.001,
|
IntelligenceCrimeBaseExpGain: 0.001,
|
||||||
IntelligenceProgramBaseExpGain: 500, //Program required hack level divided by this to determine int exp gain
|
IntelligenceProgramBaseExpGain: 500, // Program required hack level divided by this to determine int exp gain
|
||||||
IntelligenceTerminalHackBaseExpGain: 200, //Hacking exp divided by this to determine int exp gain
|
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
|
||||||
IntelligenceSingFnBaseExpGain: 0.002,
|
IntelligenceSingFnBaseExpGain: 0.002,
|
||||||
IntelligenceClassBaseExpGain: 0.000001,
|
IntelligenceClassBaseExpGain: 0.000001,
|
||||||
IntelligenceHackingMissionBaseExpGain: 0.03, //Hacking Mission difficulty multiplied by this to get exp gain
|
IntelligenceHackingMissionBaseExpGain: 0.03, // Hacking Mission difficulty multiplied by this to get exp gain
|
||||||
|
|
||||||
//Hacking Missions
|
// Hacking Missions
|
||||||
HackingMissionRepToDiffConversion: 10000, //Faction rep is divided by this to get mission difficulty
|
// TODO Move this into Hacking Mission implementation
|
||||||
HackingMissionRepToRewardConversion: 7, //Faction rep divided byt his to get mission rep reward
|
HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty
|
||||||
HackingMissionSpamTimeIncrease: 25000, //How much time limit increase is gained when conquering a Spam Node (ms)
|
HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward
|
||||||
HackingMissionTransferAttackIncrease: 1.05, //Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
|
HackingMissionSpamTimeIncrease: 25000, // How much time limit increase is gained when conquering a Spam Node (ms)
|
||||||
HackingMissionMiscDefenseIncrease: 1.05, //The amount by which every misc node's defense is multiplied when one is conquered
|
HackingMissionTransferAttackIncrease: 1.05, // Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
|
||||||
HackingMissionDifficultyToHacking: 135, //Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
|
HackingMissionMiscDefenseIncrease: 1.05, // The amount by which every misc node's defense is multiplied when one is conquered
|
||||||
|
HackingMissionDifficultyToHacking: 135, // Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
|
||||||
HackingMissionHowToPlay: "Hacking missions are a minigame that, if won, will reward you with faction reputation.<br><br>" +
|
HackingMissionHowToPlay: "Hacking missions are a minigame that, if won, will reward you with faction reputation.<br><br>" +
|
||||||
"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " +
|
"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " +
|
||||||
"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " +
|
"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " +
|
||||||
@@ -201,7 +193,7 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
|
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
|
||||||
"-Nodes slowly regenerate health over time.",
|
"-Nodes slowly regenerate health over time.",
|
||||||
|
|
||||||
/* Time Constants */
|
// Time-related constants
|
||||||
MillisecondsPer20Hours: 72000000,
|
MillisecondsPer20Hours: 72000000,
|
||||||
GameCyclesPer20Hours: 72000000 / 200,
|
GameCyclesPer20Hours: 72000000 / 200,
|
||||||
|
|
||||||
@@ -229,7 +221,7 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
MillisecondsPerFiveMinutes: 300000,
|
MillisecondsPerFiveMinutes: 300000,
|
||||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||||
|
|
||||||
/* Player Work / Action related Constants */
|
// Player Work & Action
|
||||||
FactionWorkHacking: "Faction Hacking Work",
|
FactionWorkHacking: "Faction Hacking Work",
|
||||||
FactionWorkField: "Faction Field Work",
|
FactionWorkField: "Faction Field Work",
|
||||||
FactionWorkSecurity: "Faction Security Work",
|
FactionWorkSecurity: "Faction Security Work",
|
||||||
@@ -272,47 +264,46 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
CrimeAssassination: "assassinate a high-profile target",
|
CrimeAssassination: "assassinate a high-profile target",
|
||||||
CrimeHeist: "pull off the ultimate heist",
|
CrimeHeist: "pull off the ultimate heist",
|
||||||
|
|
||||||
/* Coding Contract Constants */
|
// Coding Contract
|
||||||
CodingContractBaseFactionRepGain: 2500,
|
// TODO Move this into Coding contract impelmentation?
|
||||||
CodingContractBaseCompanyRepGain: 4000,
|
CodingContractBaseFactionRepGain: 2500,
|
||||||
CodingContractBaseMoneyGain: 75e6,
|
CodingContractBaseCompanyRepGain: 4000,
|
||||||
|
CodingContractBaseMoneyGain: 75e6,
|
||||||
|
|
||||||
// BitNode/Source-File related stuff
|
// BitNode/Source-File related stuff
|
||||||
TotalNumBitNodes: 24,
|
TotalNumBitNodes: 24,
|
||||||
|
|
||||||
LatestUpdate:
|
LatestUpdate:
|
||||||
`
|
`
|
||||||
v0.45.1
|
v0.46.3
|
||||||
* Added two new Corporation Researches
|
* Added a new Augmentation: The Shadow's Simulacrum
|
||||||
* General UI improvements (by hydroflame and koriar)
|
* Improved tab autocompletion feature in Terminal so that it works better with directories
|
||||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
* Bug Fix: Tech vendor location UI now properly refreshed when purchasing a TOR router
|
||||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
* Bug Fix: Fixed UI issue with faction donations
|
||||||
|
* Bug Fix: The money statistics & breakdown should now properly track money earned from Hacknet Server (hashes -> money)
|
||||||
|
* Bug Fix: Fixed issue with changing input in 'Minimum Path Sum in a Triangle' coding contract problem
|
||||||
|
* Fixed several typos in various places
|
||||||
|
|
||||||
v0.45.0
|
v0.46.2
|
||||||
* Corporation changes:
|
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
|
||||||
** Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
** (Karma is a hidden stat and is lowered by committing crimes)
|
||||||
** This means that each Corporation 'state' will now only take 2 seconds, rather than 3
|
* Gang changes:
|
||||||
** Increased initial salaries for newly-hired employees
|
** Bug Fix: Gangs can no longer clash with themselve
|
||||||
** Increased the cost multiplier for upgrading office size (the cost will increase faster)
|
** Bug Fix: Winning against another gang should properly reduce their power
|
||||||
** The stats of your employees now has a slightly larger effect on production & sales
|
|
||||||
** Added several new Research upgrades
|
* Bug Fix: Terminal 'wget' command now works properly
|
||||||
** Market-TA research now allows you to automatically set sale price at optimal values
|
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
|
||||||
** Market-TA research now works for Products (not just Materials)
|
* Bug Fix: Fixed button for creating Corporations
|
||||||
** 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
|
v0.46.1
|
||||||
** It is now slightly easier to increase the Software industry's production multiplier
|
* Added a very rudimentary directory system to the Terminal
|
||||||
** Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
|
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
|
||||||
** You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
|
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
|
||||||
** Significantly changed the effects of the different employee positions. See updated descriptions
|
* 'Generate Coding Contract' hash upgrade is now more expensive
|
||||||
** Reduced the amount of money you gain from private investors
|
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
|
||||||
** Training employees is now 3x more effective
|
* The cost of selling hashes for money no longer increases each time
|
||||||
** Bug Fix: An industry's products are now properly separated between different cities
|
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
|
||||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
|
||||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
|
||||||
* 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
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import { BitNodeMultipliers } from "../BitNode/BitNode
|
|||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
import { Factions } from "../Faction/Factions";
|
import { Factions } from "../Faction/Factions";
|
||||||
import { showLiterature } from "../Literature";
|
import { showLiterature } from "../Literature";
|
||||||
import { Locations } from "../Locations";
|
|
||||||
import { createCityMap } from "../Locations/Cities";
|
import { createCityMap } from "../Locations/Cities";
|
||||||
|
import { CityName } from "../Locations/data/CityNames";
|
||||||
import { Player } from "../Player";
|
import { Player } from "../Player";
|
||||||
|
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
@@ -113,15 +113,15 @@ $(document).mousedown(function(event) {
|
|||||||
var empManualAssignmentModeActive = false;
|
var empManualAssignmentModeActive = false;
|
||||||
function Industry(params={}) {
|
function Industry(params={}) {
|
||||||
this.offices = { //Maps locations to offices. 0 if no office at that location
|
this.offices = { //Maps locations to offices. 0 if no office at that location
|
||||||
[Locations.Aevum]: 0,
|
[CityName.Aevum]: 0,
|
||||||
[Locations.Chongqing]: 0,
|
[CityName.Chongqing]: 0,
|
||||||
[Locations.Sector12]: new OfficeSpace({
|
[CityName.Sector12]: new OfficeSpace({
|
||||||
loc:Locations.Sector12,
|
loc:CityName.Sector12,
|
||||||
size:OfficeInitialSize,
|
size:OfficeInitialSize,
|
||||||
}),
|
}),
|
||||||
[Locations.NewTokyo]: 0,
|
[CityName.NewTokyo]: 0,
|
||||||
[Locations.Ishima]: 0,
|
[CityName.Ishima]: 0,
|
||||||
[Locations.Volhaven]: 0
|
[CityName.Volhaven]: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
this.name = params.name ? params.name : 0;
|
this.name = params.name ? params.name : 0;
|
||||||
@@ -172,17 +172,17 @@ function Industry(params={}) {
|
|||||||
this.newInd = true;
|
this.newInd = true;
|
||||||
|
|
||||||
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
|
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
|
||||||
[Locations.Aevum]: 0,
|
[CityName.Aevum]: 0,
|
||||||
[Locations.Chonqing]: 0,
|
[CityName.Chonqing]: 0,
|
||||||
[Locations.Sector12]: new Warehouse({
|
[CityName.Sector12]: new Warehouse({
|
||||||
corp: params.corp,
|
corp: params.corp,
|
||||||
industry: this,
|
industry: this,
|
||||||
loc: Locations.Sector12,
|
loc: CityName.Sector12,
|
||||||
size: WarehouseInitialSize,
|
size: WarehouseInitialSize,
|
||||||
}),
|
}),
|
||||||
[Locations.NewTokyo]: 0,
|
[CityName.NewTokyo]: 0,
|
||||||
[Locations.Ishima]: 0,
|
[CityName.Ishima]: 0,
|
||||||
[Locations.Volhaven]: 0
|
[CityName.Volhaven]: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
@@ -563,13 +563,15 @@ Industry.prototype.processMaterialMarket = function(marketCycles=1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Process change in demand and competition for this industry's products
|
// Process change in demand and competition for this industry's products
|
||||||
Industry.prototype.processProductMarket = function(marketCycles=1) {
|
Industry.prototype.processProductMarket = function(marketCycles=1) {
|
||||||
//Demand gradually decreases, and competition gradually increases
|
// Demand gradually decreases, and competition gradually increases
|
||||||
for (var name in this.products) {
|
for (const name in this.products) {
|
||||||
if (this.products.hasOwnProperty(name)) {
|
if (this.products.hasOwnProperty(name)) {
|
||||||
var product = this.products[name];
|
const product = this.products[name];
|
||||||
var change = getRandomInt(1, 3) * 0.0004;
|
let change = getRandomInt(0, 3) * 0.0004;
|
||||||
|
if (change === 0) { continue; }
|
||||||
|
|
||||||
if (this.type === Industries.Pharmaceutical || this.type === Industries.Software ||
|
if (this.type === Industries.Pharmaceutical || this.type === Industries.Software ||
|
||||||
this.type === Industries.Robotics) {
|
this.type === Industries.Robotics) {
|
||||||
change *= 3;
|
change *= 3;
|
||||||
@@ -770,7 +772,17 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
|||||||
* advertisingFactor
|
* advertisingFactor
|
||||||
* this.getSalesMultiplier());
|
* this.getSalesMultiplier());
|
||||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||||
const optimalPrice = (numerator / denominator) + mat.bCost;
|
let optimalPrice;
|
||||||
|
if (sqrtDenominator === 0 || denominator === 0) {
|
||||||
|
if (sqrtNumerator === 0) {
|
||||||
|
optimalPrice = 0; // No production
|
||||||
|
} else {
|
||||||
|
optimalPrice = mat.bCost + markupLimit;
|
||||||
|
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
optimalPrice = (numerator / denominator) + mat.bCost;
|
||||||
|
}
|
||||||
|
|
||||||
// We'll store this "Optimal Price" in a property so that we don't have
|
// We'll store this "Optimal Price" in a property so that we don't have
|
||||||
// to re-calculate it for the UI
|
// to re-calculate it for the UI
|
||||||
@@ -1089,7 +1101,12 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
|
|||||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||||
let optimalPrice;
|
let optimalPrice;
|
||||||
if (sqrtDenominator === 0 || denominator === 0) {
|
if (sqrtDenominator === 0 || denominator === 0) {
|
||||||
optimalPrice = 0;
|
if (sqrtNumerator === 0) {
|
||||||
|
optimalPrice = 0; // No production
|
||||||
|
} else {
|
||||||
|
optimalPrice = product.pCost + markupLimit;
|
||||||
|
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
optimalPrice = (numerator / denominator) + product.pCost;
|
optimalPrice = (numerator / denominator) + product.pCost;
|
||||||
}
|
}
|
||||||
@@ -1251,7 +1268,7 @@ Industry.prototype.getAdvertisingFactors = function() {
|
|||||||
|
|
||||||
//Returns a multiplier based on a materials demand and competition that affects sales
|
//Returns a multiplier based on a materials demand and competition that affects sales
|
||||||
Industry.prototype.getMarketFactor = function(mat) {
|
Industry.prototype.getMarketFactor = function(mat) {
|
||||||
return mat.dmd * (100 - mat.cmp)/100;
|
return Math.max(0.1, mat.dmd * (100 - mat.cmp) / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a boolean indicating whether this Industry has the specified Research
|
// Returns a boolean indicating whether this Industry has the specified Research
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class Material {
|
|||||||
this.mku = 6;
|
this.mku = 6;
|
||||||
break;
|
break;
|
||||||
case "Energy":
|
case "Energy":
|
||||||
this.dmd = 90; this.dmdR = [80, 100];
|
this.dmd = 90; this.dmdR = [80, 99];
|
||||||
this.cmp = 80; this.cmpR = [65, 95];
|
this.cmp = 80; this.cmpR = [65, 95];
|
||||||
this.bCost = 2000; this.mv = 0.2;
|
this.bCost = 2000; this.mv = 0.2;
|
||||||
this.mku = 6;
|
this.mku = 6;
|
||||||
@@ -122,26 +122,26 @@ export class Material {
|
|||||||
this.mku = 2;
|
this.mku = 2;
|
||||||
break;
|
break;
|
||||||
case "Real Estate":
|
case "Real Estate":
|
||||||
this.dmd = 50; this.dmdR = [5, 100];
|
this.dmd = 50; this.dmdR = [5, 99];
|
||||||
this.cmp = 50; this.cmpR = [25, 75];
|
this.cmp = 50; this.cmpR = [25, 75];
|
||||||
this.bCost = 80e3; this.mv = 1.5; //Less mv bc its processed twice
|
this.bCost = 80e3; this.mv = 1.5; //Less mv bc its processed twice
|
||||||
this.mku = 1.5;
|
this.mku = 1.5;
|
||||||
break;
|
break;
|
||||||
case "Drugs":
|
case "Drugs":
|
||||||
this.dmd = 60; this.dmdR = [45, 75];
|
this.dmd = 60; this.dmdR = [45, 75];
|
||||||
this.cmp = 70; this.cmpR = [40, 100];
|
this.cmp = 70; this.cmpR = [40, 99];
|
||||||
this.bCost = 40e3; this.mv = 1.6;
|
this.bCost = 40e3; this.mv = 1.6;
|
||||||
this.mku = 1;
|
this.mku = 1;
|
||||||
break;
|
break;
|
||||||
case "Robots":
|
case "Robots":
|
||||||
this.dmd = 90; this.dmdR = [80, 100];
|
this.dmd = 90; this.dmdR = [80, 9];
|
||||||
this.cmp = 90; this.cmpR = [80, 100];
|
this.cmp = 90; this.cmpR = [80, 9];
|
||||||
this.bCost = 75e3; this.mv = 0.5; //Less mv bc its processed twice
|
this.bCost = 75e3; this.mv = 0.5; //Less mv bc its processed twice
|
||||||
this.mku = 1;
|
this.mku = 1;
|
||||||
break;
|
break;
|
||||||
case "AI Cores":
|
case "AI Cores":
|
||||||
this.dmd = 90; this.dmdR = [80, 100];
|
this.dmd = 90; this.dmdR = [80, 99];
|
||||||
this.cmp = 90; this.cmpR = [80, 100];
|
this.cmp = 90; this.cmpR = [80, 9];
|
||||||
this.bCost = 15e3; this.mv = 0.8; //Less mv bc its processed twice
|
this.bCost = 15e3; this.mv = 0.8; //Less mv bc its processed twice
|
||||||
this.mku = 0.5;
|
this.mku = 0.5;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import { MaterialSizes } from "./MaterialSizes";
|
|||||||
import { ProductRatingWeights,
|
import { ProductRatingWeights,
|
||||||
IProductRatingWeight } from "./ProductRatingWeights";
|
IProductRatingWeight } from "./ProductRatingWeights";
|
||||||
|
|
||||||
import { Cities } from "../Locations/Cities";
|
|
||||||
import { createCityMap } from "../Locations/createCityMap";
|
import { createCityMap } from "../Locations/createCityMap";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
|
||||||
import { Generic_fromJSON,
|
import { Generic_fromJSON,
|
||||||
Generic_toJSON,
|
Generic_toJSON,
|
||||||
Reviver } from "../../utils/JSONReviver";
|
Reviver } from "../../utils/JSONReviver";
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { numeralWrapper } from "../../ui/numeralFormat";
|
|||||||
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||||
|
|
||||||
|
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||||
@@ -780,7 +781,12 @@ export class CorporationEventHandler {
|
|||||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||||
|
|
||||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
const ta2OverridesTa1 = createElement("p", {
|
||||||
|
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||||
|
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||||
|
});
|
||||||
|
|
||||||
|
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||||
} else {
|
} else {
|
||||||
// Market-TA.I only
|
// Market-TA.I only
|
||||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||||
@@ -1052,7 +1058,12 @@ export class CorporationEventHandler {
|
|||||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||||
|
|
||||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
const ta2OverridesTa1 = createElement("p", {
|
||||||
|
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||||
|
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||||
|
});
|
||||||
|
|
||||||
|
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||||
} else {
|
} else {
|
||||||
// Market-TA.I only
|
// Market-TA.I only
|
||||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||||
@@ -1406,7 +1417,7 @@ export class CorporationEventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Array of all cities. Used later
|
// Array of all cities. Used later
|
||||||
const cities = Object.values(Cities);
|
const cities = Object.keys(Cities);
|
||||||
|
|
||||||
// Parse quantity
|
// Parse quantity
|
||||||
if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
|
if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
|
||||||
|
|||||||
@@ -300,8 +300,8 @@ export class IndustryOffice extends BaseReactComponent {
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
<p>Avg Employee Morale: {numeralWrapper.format(avgMorale, "0.000")}</p>
|
<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>Avg Employee Energy: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||||
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
||||||
{
|
{
|
||||||
vechain &&
|
vechain &&
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ function MaterialComponent(props) {
|
|||||||
mat.buy === 0 && mat.imp === 0;
|
mat.buy === 0 && mat.imp === 0;
|
||||||
|
|
||||||
// Purchase material button
|
// Purchase material button
|
||||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
|
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
|
||||||
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
||||||
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
||||||
|
|
||||||
@@ -229,9 +229,9 @@ function MaterialComponent(props) {
|
|||||||
let sellButtonText;
|
let sellButtonText;
|
||||||
if (mat.sllman[0]) {
|
if (mat.sllman[0]) {
|
||||||
if (isString(mat.sllman[1])) {
|
if (isString(mat.sllman[1])) {
|
||||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${mat.sllman[1]})`
|
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${mat.sllman[1]})`
|
||||||
} else {
|
} else {
|
||||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
|
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${numeralWrapper.format(mat.sllman[1], nfB)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mat.marketTa2) {
|
if (mat.marketTa2) {
|
||||||
@@ -469,7 +469,7 @@ export class IndustryWarehouse extends BaseReactComponent {
|
|||||||
return (
|
return (
|
||||||
<div className={"cmpy-mgmt-warehouse-panel"}>
|
<div className={"cmpy-mgmt-warehouse-panel"}>
|
||||||
<p className={"tooltip"} style={sizeUsageStyle}>
|
<p className={"tooltip"} style={sizeUsageStyle}>
|
||||||
Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")}
|
Storage: {numeralWrapper.formatBigNumber(warehouse.sizeUsed)} / {numeralWrapper.formatBigNumber(warehouse.size)}
|
||||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
|
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { overviewPage } from "./Routing";
|
|||||||
|
|
||||||
import { OfficeSpace } from "../Corporation";
|
import { OfficeSpace } from "../Corporation";
|
||||||
|
|
||||||
import { Cities } from "../../Locations/Cities";
|
import { CityName } from "../../Locations/data/CityNames";
|
||||||
|
|
||||||
export class MainPanel extends BaseReactComponent {
|
export class MainPanel extends BaseReactComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -19,13 +19,13 @@ export class MainPanel extends BaseReactComponent {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
division: "",
|
division: "",
|
||||||
city: Cities.Sector12,
|
city: CityName.Sector12,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can pass this setter to child components
|
// We can pass this setter to child components
|
||||||
changeCityState(newCity) {
|
changeCityState(newCity) {
|
||||||
if (Object.values(Cities).includes(newCity)) {
|
if (Object.values(CityName).includes(newCity)) {
|
||||||
this.state.city = newCity;
|
this.state.city = newCity;
|
||||||
} else {
|
} else {
|
||||||
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
|
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
|
||||||
@@ -45,7 +45,7 @@ export class MainPanel extends BaseReactComponent {
|
|||||||
const currentDivision = this.routing().current();
|
const currentDivision = this.routing().current();
|
||||||
if (currentDivision !== this.state.division) {
|
if (currentDivision !== this.state.division) {
|
||||||
this.state.division = currentDivision;
|
this.state.division = currentDivision;
|
||||||
this.state.city = Cities.Sector12;
|
this.state.city = CityName.Sector12;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderDivisionPage();
|
return this.renderDivisionPage();
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class Overview extends BaseReactComponent {
|
|||||||
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
||||||
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
||||||
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
|
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
|
||||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br>`;
|
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
|
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
|
||||||
|
|||||||
189
src/DevMenu.jsx
189
src/DevMenu.jsx
@@ -1,38 +1,44 @@
|
|||||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||||
import { CodingContractTypes } from "./CodingContracts";
|
import { CodingContractTypes } from "./CodingContracts";
|
||||||
import { generateContract,
|
import {
|
||||||
generateRandomContract,
|
generateContract,
|
||||||
generateRandomContractOnHome } from "./CodingContractGenerator";
|
generateRandomContract,
|
||||||
import { Companies } from "./Company/Companies";
|
generateRandomContractOnHome
|
||||||
import { Company } from "./Company/Company";
|
} from "./CodingContractGenerator";
|
||||||
import { Programs } from "./Programs/Programs";
|
import { Companies } from "./Company/Companies";
|
||||||
import { Factions } from "./Faction/Factions";
|
import { Company } from "./Company/Company";
|
||||||
import { Player } from "./Player";
|
import { Programs } from "./Programs/Programs";
|
||||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
import { Factions } from "./Faction/Factions";
|
||||||
import { AllServers } from "./Server/AllServers";
|
import { Player } from "./Player";
|
||||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||||
import { hackWorldDaemon } from "./RedPill";
|
import { AllServers } from "./Server/AllServers";
|
||||||
import { StockMarket,
|
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
import { hackWorldDaemon } from "./RedPill";
|
||||||
import { Stock } from "./StockMarket/Stock";
|
import { StockMarket, SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||||
import { Terminal } from "./Terminal";
|
import { Stock } from "./StockMarket/Stock";
|
||||||
|
import { Terminal } from "./Terminal";
|
||||||
|
|
||||||
import { numeralWrapper } from "./ui/numeralFormat";
|
import { numeralWrapper } from "./ui/numeralFormat";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
|
||||||
const Component = React.Component;
|
const Component = React.Component;
|
||||||
|
|
||||||
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12];
|
// Update as additional BitNodes get implemented
|
||||||
|
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
||||||
|
|
||||||
|
// Some dev menu buttons just add a lot of something for convenience
|
||||||
|
const tonsPP = 1e27;
|
||||||
|
const tonsP = 1e12;
|
||||||
|
|
||||||
class ValueAdjusterComponent extends Component {
|
class ValueAdjusterComponent extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -41,7 +47,7 @@ class ValueAdjusterComponent extends Component {
|
|||||||
this.setValue = this.setValue.bind(this);
|
this.setValue = this.setValue.bind(this);
|
||||||
}
|
}
|
||||||
setValue(event) {
|
setValue(event) {
|
||||||
this.setState({ value: event.target.value });
|
this.setState({ value: parseFloat(event.target.value) });
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const { title, add, subtract, reset } = this.props;
|
const { title, add, subtract, reset } = this.props;
|
||||||
@@ -124,7 +130,6 @@ class DevMenuComponent extends Component {
|
|||||||
this.setState({ codingcontract: event.target.value });
|
this.setState({ codingcontract: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addMoney(n) {
|
addMoney(n) {
|
||||||
return function() {
|
return function() {
|
||||||
Player.gainMoney(n);
|
Player.gainMoney(n);
|
||||||
@@ -186,14 +191,20 @@ class DevMenuComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modifyKarma(modifier) {
|
||||||
|
return function(amt) {
|
||||||
|
Player.karma += (amt * modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tonsOfExp() {
|
tonsOfExp() {
|
||||||
Player.gainHackingExp(1e27);
|
Player.gainHackingExp(tonsPP);
|
||||||
Player.gainStrengthExp(1e27);
|
Player.gainStrengthExp(tonsPP);
|
||||||
Player.gainDefenseExp(1e27);
|
Player.gainDefenseExp(tonsPP);
|
||||||
Player.gainDexterityExp(1e27);
|
Player.gainDexterityExp(tonsPP);
|
||||||
Player.gainAgilityExp(1e27);
|
Player.gainAgilityExp(tonsPP);
|
||||||
Player.gainCharismaExp(1e27);
|
Player.gainCharismaExp(tonsPP);
|
||||||
Player.gainIntelligenceExp(1e27);
|
Player.gainIntelligenceExp(tonsPP);
|
||||||
Player.updateSkillLevels();
|
Player.updateSkillLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +248,12 @@ class DevMenuComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetKarma() {
|
||||||
|
return function() {
|
||||||
|
Player.karma = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enableIntelligence() {
|
enableIntelligence() {
|
||||||
if(Player.intelligence === 0) {
|
if(Player.intelligence === 0) {
|
||||||
Player.intelligence = 1;
|
Player.intelligence = 1;
|
||||||
@@ -296,7 +313,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
tonsOfRep() {
|
tonsOfRep() {
|
||||||
for (const i in Factions) {
|
for (const i in Factions) {
|
||||||
Factions[i].playerReputation = 1e27;
|
Factions[i].playerReputation = tonsPP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +325,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
tonsOfFactionFavor() {
|
tonsOfFactionFavor() {
|
||||||
for (const i in Factions) {
|
for (const i in Factions) {
|
||||||
Factions[i].favor = 1e27;
|
Factions[i].favor = tonsPP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +471,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
tonsOfRepCompanies() {
|
tonsOfRepCompanies() {
|
||||||
for (const c in Companies) {
|
for (const c in Companies) {
|
||||||
Companies[c].playerReputation = 1e12;
|
Companies[c].playerReputation = tonsP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +483,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
tonsOfFavorCompanies() {
|
tonsOfFavorCompanies() {
|
||||||
for (const c in Companies) {
|
for (const c in Companies) {
|
||||||
Companies[c].favor = 1e12;
|
Companies[c].favor = tonsP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +508,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
addTonsBladeburnerRank() {
|
addTonsBladeburnerRank() {
|
||||||
if (!!Player.bladeburner) {
|
if (!!Player.bladeburner) {
|
||||||
Player.bladeburner.changeRank(1e12);
|
Player.bladeburner.changeRank(tonsP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,13 +528,13 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
addTonsBladeburnerCycles() {
|
addTonsBladeburnerCycles() {
|
||||||
if (!!Player.bladeburner) {
|
if (!!Player.bladeburner) {
|
||||||
Player.bladeburner.storedCycles += 1e12;
|
Player.bladeburner.storedCycles += tonsP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addTonsGangCycles() {
|
addTonsGangCycles() {
|
||||||
if (!!Player.gang) {
|
if (!!Player.gang) {
|
||||||
Player.gang.storedCycles = 1e12;
|
Player.gang.storedCycles = tonsP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +554,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
addTonsCorporationCycles() {
|
addTonsCorporationCycles() {
|
||||||
if (!!Player.corporation) {
|
if (!!Player.corporation) {
|
||||||
Player.corporation.storedCycles = 1e12;
|
Player.corporation.storedCycles = tonsP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,16 +661,16 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
let sourceFiles = [];
|
let sourceFiles = [];
|
||||||
validSFN.forEach( i => sourceFiles.push(
|
validSFN.forEach( i => sourceFiles.push(
|
||||||
<tr key={'sf-'+i}>
|
<tr key={'sf-'+i}>
|
||||||
<td><span className="text">SF-{i}:</span></td>
|
<td><span className="text">SF-{i}:</span></td>
|
||||||
<td>
|
<td>
|
||||||
<button className="std-button touch-right" onClick={this.setSF(i, 0)}>0</button>
|
<button className="std-button touch-right" onClick={this.setSF(i, 0)}>0</button>
|
||||||
<button className="std-button touch-sides" onClick={this.setSF(i, 1)}>1</button>
|
<button className="std-button touch-sides" onClick={this.setSF(i, 1)}>1</button>
|
||||||
<button className="std-button touch-sides" onClick={this.setSF(i, 2)}>2</button>
|
<button className="std-button touch-sides" onClick={this.setSF(i, 2)}>2</button>
|
||||||
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
|
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -713,11 +730,11 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Hacking:</span>
|
<span className="text text-center">Hacking:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="hacking exp"
|
title="hacking exp"
|
||||||
add={this.modifyExp('hacking', 1)}
|
add={this.modifyExp('hacking', 1)}
|
||||||
subtract={this.modifyExp('hacking', -1)}
|
subtract={this.modifyExp('hacking', -1)}
|
||||||
reset={this.resetExperience('hacking')}
|
reset={this.resetExperience('hacking')}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -726,7 +743,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Strength:</span>
|
<span className="text text-center">Strength:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="strength exp"
|
title="strength exp"
|
||||||
add={this.modifyExp('strength', 1)}
|
add={this.modifyExp('strength', 1)}
|
||||||
subtract={this.modifyExp('strength', -1)}
|
subtract={this.modifyExp('strength', -1)}
|
||||||
@@ -739,7 +756,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Defense:</span>
|
<span className="text text-center">Defense:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="defense exp"
|
title="defense exp"
|
||||||
add={this.modifyExp('defense', 1)}
|
add={this.modifyExp('defense', 1)}
|
||||||
subtract={this.modifyExp('defense', -1)}
|
subtract={this.modifyExp('defense', -1)}
|
||||||
@@ -752,7 +769,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Dexterity:</span>
|
<span className="text text-center">Dexterity:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="dexterity exp"
|
title="dexterity exp"
|
||||||
add={this.modifyExp('dexterity', 1)}
|
add={this.modifyExp('dexterity', 1)}
|
||||||
subtract={this.modifyExp('dexterity', -1)}
|
subtract={this.modifyExp('dexterity', -1)}
|
||||||
@@ -765,7 +782,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Agility:</span>
|
<span className="text text-center">Agility:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="agility exp"
|
title="agility exp"
|
||||||
add={this.modifyExp('agility', 1)}
|
add={this.modifyExp('agility', 1)}
|
||||||
subtract={this.modifyExp('agility', -1)}
|
subtract={this.modifyExp('agility', -1)}
|
||||||
@@ -778,7 +795,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Charisma:</span>
|
<span className="text text-center">Charisma:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="charisma exp"
|
title="charisma exp"
|
||||||
add={this.modifyExp('charisma', 1)}
|
add={this.modifyExp('charisma', 1)}
|
||||||
subtract={this.modifyExp('charisma', -1)}
|
subtract={this.modifyExp('charisma', -1)}
|
||||||
@@ -791,7 +808,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text text-center">Intelligence:</span>
|
<span className="text text-center">Intelligence:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="intelligence exp"
|
title="intelligence exp"
|
||||||
add={this.modifyExp('intelligence', 1)}
|
add={this.modifyExp('intelligence', 1)}
|
||||||
subtract={this.modifyExp('intelligence', -1)}
|
subtract={this.modifyExp('intelligence', -1)}
|
||||||
@@ -805,6 +822,19 @@ class DevMenuComponent extends Component {
|
|||||||
<button className="std-button" onClick={this.disableIntelligence}>Disable</button>
|
<button className="std-button" onClick={this.disableIntelligence}>Disable</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span className="text text-center">Karma:</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ValueAdjusterComponent
|
||||||
|
title="karma"
|
||||||
|
add={this.modifyKarma(1)}
|
||||||
|
subtract={this.modifyKarma(-1)}
|
||||||
|
reset={this.resetKarma()}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -832,7 +862,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text">Reputation:</span>
|
<span className="text">Reputation:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="reputation"
|
title="reputation"
|
||||||
add={this.modifyFactionRep(1)}
|
add={this.modifyFactionRep(1)}
|
||||||
subtract={this.modifyFactionRep(-1)}
|
subtract={this.modifyFactionRep(-1)}
|
||||||
@@ -845,7 +875,7 @@ class DevMenuComponent extends Component {
|
|||||||
<span className="text">Favor:</span>
|
<span className="text">Favor:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="favor"
|
title="favor"
|
||||||
add={this.modifyFactionFavor(1)}
|
add={this.modifyFactionFavor(1)}
|
||||||
subtract={this.modifyFactionFavor(-1)}
|
subtract={this.modifyFactionFavor(-1)}
|
||||||
@@ -979,7 +1009,7 @@ class DevMenuComponent extends Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span className="text">Reputation:</span></td>
|
<td><span className="text">Reputation:</span></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="reputation"
|
title="reputation"
|
||||||
add={this.modifyCompanyRep(1)}
|
add={this.modifyCompanyRep(1)}
|
||||||
subtract={this.modifyCompanyRep(-1)}
|
subtract={this.modifyCompanyRep(-1)}
|
||||||
@@ -990,7 +1020,7 @@ class DevMenuComponent extends Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span className="text">Favor:</span></td>
|
<td><span className="text">Favor:</span></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="favor"
|
title="favor"
|
||||||
add={this.modifyCompanyFavor(1)}
|
add={this.modifyCompanyFavor(1)}
|
||||||
subtract={this.modifyCompanyFavor(-1)}
|
subtract={this.modifyCompanyFavor(-1)}
|
||||||
@@ -1028,7 +1058,7 @@ class DevMenuComponent extends Component {
|
|||||||
<td><span className="text">Rank:</span></td>
|
<td><span className="text">Rank:</span></td>
|
||||||
<td><button className="std-button" onClick={this.addTonsBladeburnerRank}>Tons</button></td>
|
<td><button className="std-button" onClick={this.addTonsBladeburnerRank}>Tons</button></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="rank"
|
title="rank"
|
||||||
add={this.modifyBladeburnerRank(1)}
|
add={this.modifyBladeburnerRank(1)}
|
||||||
subtract={this.modifyBladeburnerRank(-1)}
|
subtract={this.modifyBladeburnerRank(-1)}
|
||||||
@@ -1040,7 +1070,7 @@ class DevMenuComponent extends Component {
|
|||||||
<td><span className="text">Cycles:</span></td>
|
<td><span className="text">Cycles:</span></td>
|
||||||
<td><button className="std-button" onClick={this.addTonsBladeburnerCycles}>Tons</button></td>
|
<td><button className="std-button" onClick={this.addTonsBladeburnerCycles}>Tons</button></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="cycles"
|
title="cycles"
|
||||||
add={this.modifyBladeburnerCycles(1)}
|
add={this.modifyBladeburnerCycles(1)}
|
||||||
subtract={this.modifyBladeburnerCycles(-1)}
|
subtract={this.modifyBladeburnerCycles(-1)}
|
||||||
@@ -1064,7 +1094,7 @@ class DevMenuComponent extends Component {
|
|||||||
<td><span className="text">Cycles:</span></td>
|
<td><span className="text">Cycles:</span></td>
|
||||||
<td><button className="std-button" onClick={this.addTonsGangCycles}>Tons</button></td>
|
<td><button className="std-button" onClick={this.addTonsGangCycles}>Tons</button></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="cycles"
|
title="cycles"
|
||||||
add={this.modifyGangCycles(1)}
|
add={this.modifyGangCycles(1)}
|
||||||
subtract={this.modifyGangCycles(-1)}
|
subtract={this.modifyGangCycles(-1)}
|
||||||
@@ -1088,7 +1118,7 @@ class DevMenuComponent extends Component {
|
|||||||
<td><span className="text">Cycles:</span></td>
|
<td><span className="text">Cycles:</span></td>
|
||||||
<td><button className="std-button" onClick={this.addTonsCorporationCycles}>Tons</button></td>
|
<td><button className="std-button" onClick={this.addTonsCorporationCycles}>Tons</button></td>
|
||||||
<td>
|
<td>
|
||||||
<ValueAdjusterComponent
|
<ValueAdjusterComponent
|
||||||
title="cycles"
|
title="cycles"
|
||||||
add={this.modifyCorporationCycles(1)}
|
add={this.modifyCorporationCycles(1)}
|
||||||
subtract={this.modifyCorporationCycles(-1)}
|
subtract={this.modifyCorporationCycles(-1)}
|
||||||
@@ -1121,7 +1151,7 @@ class DevMenuComponent extends Component {
|
|||||||
{contractTypes}
|
{contractTypes}
|
||||||
</select>
|
</select>
|
||||||
<button className="std-button" onClick={this.specificContract}>Generate Specified Contract Type on Home Comp</button>
|
<button className="std-button" onClick={this.specificContract}>Generate Specified Contract Type on Home Comp</button>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -1185,7 +1215,6 @@ class DevMenuComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const devMenuContainerId = "dev-menu-container";
|
const devMenuContainerId = "dev-menu-container";
|
||||||
|
|
||||||
export function createDevMenu() {
|
export function createDevMenu() {
|
||||||
@@ -1199,11 +1228,11 @@ export function createDevMenu() {
|
|||||||
id: devMenuContainerId,
|
id: devMenuContainerId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const entireGameContainer = document.getElementById("entire-game-container");
|
const entireGameContainer = document.getElementById("entire-game-container");
|
||||||
if (entireGameContainer == null) {
|
if (entireGameContainer == null) {
|
||||||
throw new Error("Could not find entire-game-container DOM element");
|
throw new Error("Could not find entire-game-container DOM element");
|
||||||
}
|
}
|
||||||
entireGameContainer.appendChild(devMenuContainer);
|
entireGameContainer.appendChild(devMenuContainer);
|
||||||
|
|
||||||
ReactDOM.render(<DevMenuComponent />, devMenuContainer);
|
ReactDOM.render(<DevMenuComponent />, devMenuContainer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,16 +97,6 @@ export class Faction {
|
|||||||
return [favorGain, rep];
|
return [favorGain, rep];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Adds all Augmentations to this faction.
|
|
||||||
addAllAugmentations(augs: object): void {
|
|
||||||
this.augmentations.length = 0;
|
|
||||||
for (const name in augs) {
|
|
||||||
if (augs.hasOwnProperty(name)) {
|
|
||||||
this.augmentations.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize the current object to a JSON save state.
|
* Serialize the current object to a JSON save state.
|
||||||
*/
|
*/
|
||||||
|
|||||||
7
src/Faction/FactionHelpers.d.ts
vendored
Normal file
7
src/Faction/FactionHelpers.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Augmentation } from "../Augmentation/Augmentation";
|
||||||
|
import { Faction } from "../Faction/Faction";
|
||||||
|
|
||||||
|
export declare function getNextNeurofluxLevel(): number;
|
||||||
|
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
|
||||||
|
export declare function purchaseAugmentationBoxCreate(aug: Augmentation, fac: Faction): void;
|
||||||
|
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;
|
||||||
@@ -1,695 +0,0 @@
|
|||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
|
||||||
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
|
||||||
import { CONSTANTS } from "../Constants";
|
|
||||||
import { Engine } from "../engine";
|
|
||||||
import { Faction } from "./Faction";
|
|
||||||
import { Factions } from "./Factions";
|
|
||||||
import { FactionInfos } from "./FactionInfo";
|
|
||||||
import { Locations} from "../Location";
|
|
||||||
import { HackingMission, setInMission } from "../Missions";
|
|
||||||
import { Player } from "../Player";
|
|
||||||
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
|
||||||
import { Settings } from "../Settings/Settings";
|
|
||||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
|
||||||
|
|
||||||
import { createPurchaseSleevesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
|
||||||
|
|
||||||
import {Page, routing} from "../ui/navigationTracking";
|
|
||||||
import {numeralWrapper} from "../ui/numeralFormat";
|
|
||||||
import {dialogBoxCreate} from "../../utils/DialogBox";
|
|
||||||
import {factionInvitationBoxCreate} from "../../utils/FactionInvitationBox";
|
|
||||||
import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
|
|
||||||
import {createElement} from "../../utils/uiHelpers/createElement";
|
|
||||||
import {Reviver, Generic_toJSON,
|
|
||||||
Generic_fromJSON} from "../../utils/JSONReviver";
|
|
||||||
import {formatNumber} from "../../utils/StringHelperFunctions";
|
|
||||||
import {yesNoBoxCreate, yesNoBoxGetYesButton,
|
|
||||||
yesNoBoxGetNoButton, yesNoBoxClose} from "../../utils/YesNoBox";
|
|
||||||
|
|
||||||
function inviteToFaction(faction) {
|
|
||||||
if (Settings.SuppressFactionInvites) {
|
|
||||||
faction.alreadyInvited = true;
|
|
||||||
Player.factionInvitations.push(faction.name);
|
|
||||||
if (routing.isOn(Page.Factions)) {
|
|
||||||
Engine.loadFactionsContent();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
factionInvitationBoxCreate(faction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function joinFaction(faction) {
|
|
||||||
faction.isMember = true;
|
|
||||||
Player.factions.push(faction.name);
|
|
||||||
const factionInfo = faction.getInfo();
|
|
||||||
|
|
||||||
//Determine what factions you are banned from now that you have joined this faction
|
|
||||||
for(const i in factionInfo.enemies) {
|
|
||||||
const enemy = factionInfo.enemies[i];
|
|
||||||
if (Factions[enemy] instanceof Faction) {
|
|
||||||
Factions[enemy].isBanned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Displays the HTML content for a specific faction
|
|
||||||
function displayFactionContent(factionName) {
|
|
||||||
var faction = Factions[factionName];
|
|
||||||
if (faction == null) {
|
|
||||||
throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
|
|
||||||
}
|
|
||||||
if (!faction.isMember) {
|
|
||||||
throw new Error("Not a member of this faction, cannot display faction information");
|
|
||||||
}
|
|
||||||
var factionInfo = faction.getInfo();
|
|
||||||
|
|
||||||
removeChildrenFromElement(Engine.Display.factionContent);
|
|
||||||
var elements = [];
|
|
||||||
|
|
||||||
//Header and faction info
|
|
||||||
elements.push(createElement("h1", {
|
|
||||||
innerText:factionName
|
|
||||||
}));
|
|
||||||
elements.push(createElement("pre", {
|
|
||||||
innerHTML:"<i>" + factionInfo.infoText + "</i>"
|
|
||||||
}));
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
innerText:"---------------",
|
|
||||||
}));
|
|
||||||
|
|
||||||
//Faction reputation and favor
|
|
||||||
var favorGain = faction.getFavorGain();
|
|
||||||
if (favorGain.length != 2) {favorGain = 0;}
|
|
||||||
favorGain = favorGain[0];
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
innerText: "Reputation: " + formatNumber(faction.playerReputation, 4),
|
|
||||||
tooltip:"You will earn " + formatNumber(favorGain, 0) +
|
|
||||||
" faction favor upon resetting after installing an Augmentation"
|
|
||||||
}))
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
innerText:"---------------",
|
|
||||||
}));
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
innerText:"Faction Favor: " + formatNumber(faction.favor, 0),
|
|
||||||
tooltip:"Faction favor increases the rate at which " +
|
|
||||||
"you earn reputation for this faction by 1% per favor. Faction favor " +
|
|
||||||
"is gained whenever you reset after installing an Augmentation. The amount of " +
|
|
||||||
"favor you gain depends on how much reputation you have with the faction"
|
|
||||||
}));
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
innerText:"---------------",
|
|
||||||
}));
|
|
||||||
|
|
||||||
//Faction Work Description Text
|
|
||||||
elements.push(createElement("pre", {
|
|
||||||
id:"faction-work-description-text",
|
|
||||||
innerText:"Perform work/carry out assignments for your faction to help further its cause! By doing so " +
|
|
||||||
"you will earn reputation for your faction. You will also gain reputation passively over time, " +
|
|
||||||
"although at a very slow rate. Earning reputation will allow you to purchase Augmentations " +
|
|
||||||
"through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot " +
|
|
||||||
"use your terminal or create scripts when you are performing a task!"
|
|
||||||
}));
|
|
||||||
elements.push(createElement("br"));
|
|
||||||
|
|
||||||
//Hacking Mission Option
|
|
||||||
var hackMissionDiv = createElement("div", { class:"faction-work-div" });
|
|
||||||
var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
hackMissionDiv.appendChild(hackMissionDivWrapper);
|
|
||||||
hackMissionDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"a-link-button", innerText:"Hacking Mission",
|
|
||||||
clickListener:()=>{
|
|
||||||
Engine.loadMissionContent();
|
|
||||||
var mission = new HackingMission(faction.playerReputation, faction);
|
|
||||||
setInMission(true, mission); //Sets inMission flag to true
|
|
||||||
mission.init();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
hackMissionDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText:"Attempt a hacking mission for your faction. " +
|
|
||||||
"A mission is a mini game that, if won, earns you " +
|
|
||||||
"significant reputation with this faction. (Recommended hacking level: 200+)"
|
|
||||||
}));
|
|
||||||
elements.push(hackMissionDiv);
|
|
||||||
|
|
||||||
//Hacking Contracts Option
|
|
||||||
var hackDiv = createElement("div", { class:"faction-work-div", });
|
|
||||||
var hackDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
hackDiv.appendChild(hackDivWrapper);
|
|
||||||
hackDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"std-button", innerText:"Hacking Contracts",
|
|
||||||
clickListener:()=>{
|
|
||||||
Player.startFactionHackWork(faction);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
hackDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText:"Complete hacking contracts for your faction. " +
|
|
||||||
"Your effectiveness, which determines how much " +
|
|
||||||
"reputation you gain for this faction, is based on your hacking skill. " +
|
|
||||||
"You will gain hacking exp."
|
|
||||||
}));
|
|
||||||
elements.push(hackDiv);
|
|
||||||
|
|
||||||
//Field Work Option
|
|
||||||
var fieldWorkDiv = createElement("div", { class:"faction-work-div" });
|
|
||||||
var fieldWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
fieldWorkDiv.appendChild(fieldWorkDivWrapper);
|
|
||||||
fieldWorkDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"std-button", innerText:"Field Work",
|
|
||||||
clickListener:()=>{
|
|
||||||
Player.startFactionFieldWork(faction);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
fieldWorkDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText:"Carry out field missions for your faction. " +
|
|
||||||
"Your effectiveness, which determines how much " +
|
|
||||||
"reputation you gain for this faction, is based on all of your stats. " +
|
|
||||||
"You will gain exp for all stats."
|
|
||||||
}));
|
|
||||||
elements.push(fieldWorkDiv);
|
|
||||||
|
|
||||||
//Security Work Option
|
|
||||||
var securityWorkDiv = createElement("div", { class:"faction-work-div" });
|
|
||||||
var securityWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
securityWorkDiv.appendChild(securityWorkDivWrapper);
|
|
||||||
securityWorkDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"std-button", innerText:"Security Work",
|
|
||||||
clickListener:()=>{
|
|
||||||
Player.startFactionSecurityWork(faction);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
securityWorkDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText:"Serve in a security detail for your faction. " +
|
|
||||||
"Your effectiveness, which determines how much " +
|
|
||||||
"reputation you gain for this faction, is based on your combat stats. " +
|
|
||||||
"You will gain exp for all combat stats."
|
|
||||||
}));
|
|
||||||
elements.push(securityWorkDiv);
|
|
||||||
|
|
||||||
//Donate for reputation
|
|
||||||
var donateDiv = createElement("div", { class:"faction-work-div" });
|
|
||||||
var donateDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
donateDiv.appendChild(donateDivWrapper);
|
|
||||||
var donateRepGain = createElement("p", {
|
|
||||||
innerText:"This donation will result in 0.000 reputation gain"
|
|
||||||
});
|
|
||||||
var donateAmountInput = createElement("input", {
|
|
||||||
class: "text-input", placeholder:"Donation amount",
|
|
||||||
inputListener:()=>{
|
|
||||||
let amt = 0;
|
|
||||||
if(donateAmountInput.value !== "") {
|
|
||||||
amt = parseFloat(donateAmountInput.value);
|
|
||||||
}
|
|
||||||
if (isNaN(amt)) {
|
|
||||||
donateRepGain.innerText = "Invalid donate amount entered!";
|
|
||||||
} else {
|
|
||||||
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
|
|
||||||
donateRepGain.innerText = "This donation will result in " +
|
|
||||||
formatNumber(repGain, 3) + " reputation gain";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
donateDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"std-button", innerText:"Donate Money",
|
|
||||||
clickListener:()=>{
|
|
||||||
var amt = parseFloat(donateAmountInput.value);
|
|
||||||
if (isNaN(amt) || amt < 0) {
|
|
||||||
dialogBoxCreate("Invalid amount entered!");
|
|
||||||
} else if (Player.money.lt(amt)) {
|
|
||||||
dialogBoxCreate("You cannot afford to donate this much money!");
|
|
||||||
} else {
|
|
||||||
Player.loseMoney(amt);
|
|
||||||
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
|
|
||||||
faction.playerReputation += repGain;
|
|
||||||
dialogBoxCreate("You just donated " + numeralWrapper.format(amt, "$0.000a") + " to " +
|
|
||||||
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
|
|
||||||
displayFactionContent(factionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
donateDivWrapper.appendChild(donateAmountInput);
|
|
||||||
donateDivWrapper.appendChild(donateRepGain);
|
|
||||||
elements.push(donateDiv);
|
|
||||||
|
|
||||||
//Purchase Augmentations
|
|
||||||
const purchaseAugmentationsDiv = createElement("div", { class: "faction-work-div", display: "inline" });
|
|
||||||
const purchaseAugmentationsDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
|
|
||||||
purchaseAugmentationsDiv.appendChild(purchaseAugmentationsDivWrapper);
|
|
||||||
purchaseAugmentationsDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"std-button",
|
|
||||||
innerText:"Purchase Augmentations",
|
|
||||||
margin: "5px",
|
|
||||||
clickListener:()=>{
|
|
||||||
Engine.hideAllContent();
|
|
||||||
Engine.Display.factionAugmentationsContent.style.display = "block";
|
|
||||||
|
|
||||||
displayFactionAugmentations(factionName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
purchaseAugmentationsDivWrapper.appendChild(createElement("pre", {
|
|
||||||
innerHTML: "<br>As your reputation with this faction rises, you will " +
|
|
||||||
"unlock Augmentations, which you can purchase to enhance " +
|
|
||||||
"your abilities.<br><br>"
|
|
||||||
}));
|
|
||||||
elements.push(purchaseAugmentationsDiv);
|
|
||||||
|
|
||||||
//Gang (BitNode-2)
|
|
||||||
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
|
|
||||||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
|
|
||||||
factionName == "NiteSec" || factionName == "The Black Hand")) {
|
|
||||||
//Set everything else to invisible
|
|
||||||
hackMissionDiv.style.display = "none";
|
|
||||||
hackDiv.style.display = "none";
|
|
||||||
fieldWorkDiv.style.display = "none";
|
|
||||||
securityWorkDiv.style.display = "none";
|
|
||||||
donateDiv.style.display = "none";
|
|
||||||
|
|
||||||
//Create the 'Manage Gang' button
|
|
||||||
var gangDiv = createElement("div", {
|
|
||||||
id:"faction-gang-div", class:"faction-work-div", display:"inline"
|
|
||||||
});
|
|
||||||
var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
|
||||||
gangDiv.appendChild(gangDivWrapper);
|
|
||||||
gangDivWrapper.appendChild(createElement("a", {
|
|
||||||
class:"a-link-button", innerText:"Manage Gang",
|
|
||||||
clickListener: () => {
|
|
||||||
if (!Player.inGang()) {
|
|
||||||
// Determine whether this is a hacking gang
|
|
||||||
let hacking = false;
|
|
||||||
if (factionName === "NiteSec" || factionName === "The Black Hand") { hacking = true; }
|
|
||||||
|
|
||||||
// Configure Yes/No buttons for the pop-up
|
|
||||||
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
|
|
||||||
yesBtn.innerHTML = "Create Gang";
|
|
||||||
noBtn.innerHTML = "Cancel";
|
|
||||||
yesBtn.addEventListener("click", () => {
|
|
||||||
Player.startGang(factionName, hacking);
|
|
||||||
document.getElementById("world-menu-header").click();
|
|
||||||
document.getElementById("world-menu-header").click();
|
|
||||||
Engine.loadGangContent();
|
|
||||||
yesNoBoxClose();
|
|
||||||
});
|
|
||||||
noBtn.addEventListener("click", () => {
|
|
||||||
yesNoBoxClose();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pop-up text
|
|
||||||
let gangTypeText = "";
|
|
||||||
if (hacking) {
|
|
||||||
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
|
|
||||||
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
|
|
||||||
"is not as important.<br><br>";
|
|
||||||
} else {
|
|
||||||
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
|
|
||||||
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
|
|
||||||
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
|
|
||||||
}
|
|
||||||
yesNoBoxCreate(`Would you like to create a new Gang with ${factionName}?<br><br>` +
|
|
||||||
"Note that this will prevent you from creating a Gang with any other Faction until " +
|
|
||||||
"this BitNode is destroyed.<br><br>" +
|
|
||||||
gangTypeText +
|
|
||||||
"Other than hacking vs combat, there are NO differences between the Factions you can " +
|
|
||||||
"create a Gang with, and each of these Factions have all Augmentations available.");
|
|
||||||
} else {
|
|
||||||
Engine.loadGangContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
gangDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText:"Create and manage a gang for this Faction. " +
|
|
||||||
"Gangs will earn you money and faction reputation."
|
|
||||||
}));
|
|
||||||
//Manage Gang button goes before Faction work stuff
|
|
||||||
elements.splice(7, 1, gangDiv);
|
|
||||||
|
|
||||||
if (Player.inGang() && Player.gang.facName != factionName) {
|
|
||||||
//If the player has a gang but its not for this faction
|
|
||||||
gangDiv.style.display = "none";
|
|
||||||
}
|
|
||||||
//Display all elements
|
|
||||||
for (var i = 0; i < elements.length; ++i) {
|
|
||||||
Engine.Display.factionContent.appendChild(elements[i]);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purchase Sleeves from Covenant
|
|
||||||
if (factionName === "The Covenant" && Player.bitNodeN >= 10 && SourceFileFlags[10]) {
|
|
||||||
const covenantPurchaseSleevesDiv = createElement("div", { class: "faction-work-div", display: "inline" });
|
|
||||||
const covenantPurchaseSleevesDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
|
|
||||||
covenantPurchaseSleevesDiv.appendChild(covenantPurchaseSleevesDivWrapper);
|
|
||||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("a", {
|
|
||||||
class: "std-button",
|
|
||||||
innerText: "Purchase Duplicate Sleeves",
|
|
||||||
clickListener: () => {
|
|
||||||
createPurchaseSleevesFromCovenantPopup(Player);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
|
|
||||||
innerText: "Purchase Duplicate Sleeves. These are permanent! You can purchase up to 5 total.",
|
|
||||||
}));
|
|
||||||
|
|
||||||
elements.push(covenantPurchaseSleevesDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if actions should be possible
|
|
||||||
donateDiv.style.display = faction.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none";
|
|
||||||
|
|
||||||
hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none";
|
|
||||||
hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none";
|
|
||||||
fieldWorkDiv.style.display = factionInfo.offerFieldWork ? "inline" : "none";
|
|
||||||
securityWorkDiv.style.display = factionInfo.offerSecurityWork ? "inline" : "none";
|
|
||||||
|
|
||||||
//Display all elements
|
|
||||||
for (var i = 0; i < elements.length; ++i) {
|
|
||||||
Engine.Display.factionContent.appendChild(elements[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayFactionAugmentations(factionName) {
|
|
||||||
var faction = Factions[factionName];
|
|
||||||
if (faction == null) {
|
|
||||||
throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
|
|
||||||
}
|
|
||||||
|
|
||||||
removeChildrenFromElement(Engine.Display.factionAugmentationsContent);
|
|
||||||
var elements = [];
|
|
||||||
|
|
||||||
//Back button
|
|
||||||
elements.push(createElement("a", {
|
|
||||||
innerText:"Back", class:"a-link-button",
|
|
||||||
clickListener:()=>{
|
|
||||||
Engine.loadFactionContent();
|
|
||||||
displayFactionContent(factionName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
//Header text
|
|
||||||
elements.push(createElement("h1", {innerText:"Faction Augmentations"}));
|
|
||||||
elements.push(createElement("p", {
|
|
||||||
id:"faction-augmentations-page-desc",
|
|
||||||
innerHTML:"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
|
|
||||||
"Augmentations are powerful upgrades that will enhance your abilities."
|
|
||||||
}));
|
|
||||||
|
|
||||||
elements.push(createElement("br"));
|
|
||||||
elements.push(createElement("br"));
|
|
||||||
|
|
||||||
//Augmentations List
|
|
||||||
var augmentationsList = createElement("ul");
|
|
||||||
|
|
||||||
//Sort buttons
|
|
||||||
const sortByCostBtn = createElement("a", {
|
|
||||||
innerText:"Sort by Cost", class:"a-link-button",
|
|
||||||
clickListener:()=>{
|
|
||||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Cost;
|
|
||||||
var augs = faction.augmentations.slice();
|
|
||||||
augs.sort((augName1, augName2)=>{
|
|
||||||
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
|
||||||
if (aug1 == null || aug2 == null) {
|
|
||||||
throw new Error("Invalid Augmentation Names");
|
|
||||||
}
|
|
||||||
return aug1.baseCost - aug2.baseCost;
|
|
||||||
});
|
|
||||||
removeChildrenFromElement(augmentationsList);
|
|
||||||
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const sortByRepBtn = createElement("a", {
|
|
||||||
innerText:"Sort by Reputation", class:"a-link-button",
|
|
||||||
clickListener:()=>{
|
|
||||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Reputation;
|
|
||||||
var augs = faction.augmentations.slice();
|
|
||||||
augs.sort((augName1, augName2)=>{
|
|
||||||
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
|
||||||
if (aug1 == null || aug2 == null) {
|
|
||||||
throw new Error("Invalid Augmentation Names");
|
|
||||||
}
|
|
||||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
|
||||||
});
|
|
||||||
removeChildrenFromElement(augmentationsList);
|
|
||||||
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const defaultSortBtn = createElement("a", {
|
|
||||||
innerText:"Sort by Default Order", class:"a-link-button",
|
|
||||||
clickListener:()=>{
|
|
||||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Default;
|
|
||||||
removeChildrenFromElement(augmentationsList);
|
|
||||||
createFactionAugmentationDisplayElements(augmentationsList, faction.augmentations, faction);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
elements.push(sortByCostBtn);
|
|
||||||
elements.push(sortByRepBtn);
|
|
||||||
elements.push(defaultSortBtn);
|
|
||||||
switch(Settings.PurchaseAugmentationsOrder) {
|
|
||||||
case PurchaseAugmentationsOrderSetting.Cost:
|
|
||||||
sortByCostBtn.click();
|
|
||||||
break;
|
|
||||||
case PurchaseAugmentationsOrderSetting.Reputation:
|
|
||||||
sortByRepBtn.click();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
defaultSortBtn.click();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
elements.push(augmentationsList);
|
|
||||||
|
|
||||||
for (var i = 0; i < elements.length; ++i) {
|
|
||||||
Engine.Display.factionAugmentationsContent.appendChild(elements[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Takes in an array of Augmentation Names, constructs DOM elements
|
|
||||||
//to list them on the faction page, and appends them to the given
|
|
||||||
//DOM element
|
|
||||||
// @augmentationsList DOM List to append Aug DOM elements to
|
|
||||||
// @augs Array of Aug names
|
|
||||||
// @faction Faction for which to display Augmentations
|
|
||||||
function createFactionAugmentationDisplayElements(augmentationsList, augs, faction) {
|
|
||||||
const factionInfo = faction.getInfo();
|
|
||||||
|
|
||||||
for (var i = 0; i < augs.length; ++i) {
|
|
||||||
(function () {
|
|
||||||
var aug = Augmentations[augs[i]];
|
|
||||||
if (aug == null) {
|
|
||||||
throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
|
|
||||||
}
|
|
||||||
var owned = false;
|
|
||||||
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
|
|
||||||
if (Player.queuedAugmentations[j].name == aug.name) {
|
|
||||||
owned = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var j = 0; j < Player.augmentations.length; ++j) {
|
|
||||||
if (Player.augmentations[j].name == aug.name) {
|
|
||||||
owned = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = createElement("li");
|
|
||||||
var span = createElement("span", { display:"inline-block", margin: "4px", padding: "4px" });
|
|
||||||
var aDiv = createElement("div", {tooltip:aug.info});
|
|
||||||
var aElem = createElement("a", {
|
|
||||||
innerText:aug.name, display:"inline",
|
|
||||||
clickListener:()=>{
|
|
||||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
|
||||||
purchaseAugmentationBoxCreate(aug, faction);
|
|
||||||
} else {
|
|
||||||
purchaseAugmentation(aug, faction);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
|
||||||
aElem.innerText += " - Level " + (getNextNeurofluxLevel());
|
|
||||||
}
|
|
||||||
var pElem = createElement("p", {
|
|
||||||
display:"inline",
|
|
||||||
})
|
|
||||||
var req = aug.baseRepRequirement * factionInfo.augmentationRepRequirementMult;
|
|
||||||
var hasPrereqs = hasAugmentationPrereqs(aug);
|
|
||||||
if (!hasPrereqs) {
|
|
||||||
aElem.setAttribute("class", "a-link-button-inactive");
|
|
||||||
pElem.innerHTML = "LOCKED (Requires " + aug.prereqs.join(",") + " as prerequisite(s))";
|
|
||||||
pElem.style.color = "red";
|
|
||||||
} else if (aug.name != AugmentationNames.NeuroFluxGovernor && (aug.owned || owned)) {
|
|
||||||
aElem.setAttribute("class", "a-link-button-inactive");
|
|
||||||
pElem.innerHTML = "ALREADY OWNED";
|
|
||||||
} else if (faction.playerReputation >= req) {
|
|
||||||
aElem.setAttribute("class", "a-link-button");
|
|
||||||
pElem.innerHTML = "UNLOCKED - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
|
|
||||||
} else {
|
|
||||||
aElem.setAttribute("class", "a-link-button-inactive");
|
|
||||||
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
|
|
||||||
pElem.style.color = "red";
|
|
||||||
}
|
|
||||||
aDiv.appendChild(aElem);
|
|
||||||
span.appendChild(aDiv);
|
|
||||||
span.appendChild(pElem);
|
|
||||||
item.appendChild(span);
|
|
||||||
augmentationsList.appendChild(item);
|
|
||||||
}()); //Immediate invocation closure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function purchaseAugmentationBoxCreate(aug, fac) {
|
|
||||||
const factionInfo = fac.getInfo();
|
|
||||||
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
|
|
||||||
yesBtn.innerHTML = "Purchase";
|
|
||||||
noBtn.innerHTML = "Cancel";
|
|
||||||
yesBtn.addEventListener("click", function() {
|
|
||||||
purchaseAugmentation(aug, fac);
|
|
||||||
});
|
|
||||||
noBtn.addEventListener("click", function() {
|
|
||||||
yesNoBoxClose();
|
|
||||||
});
|
|
||||||
|
|
||||||
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
|
|
||||||
aug.info + "<br><br>" +
|
|
||||||
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
|
|
||||||
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns a boolean indicating whether the player has the prerequisites for the
|
|
||||||
//specified Augmentation
|
|
||||||
function hasAugmentationPrereqs(aug) {
|
|
||||||
var hasPrereqs = true;
|
|
||||||
if (aug.prereqs && aug.prereqs.length > 0) {
|
|
||||||
for (var i = 0; i < aug.prereqs.length; ++i) {
|
|
||||||
var prereqAug = Augmentations[aug.prereqs[i]];
|
|
||||||
if (prereqAug == null) {
|
|
||||||
console.log("ERROR: Invalid prereq Augmentation: " + aug.prereqs[i]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prereqAug.owned === false) {
|
|
||||||
hasPrereqs = false;
|
|
||||||
|
|
||||||
//Check if the aug is purchased
|
|
||||||
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
|
|
||||||
if (Player.queuedAugmentations[j].name === prereqAug.name) {
|
|
||||||
hasPrereqs = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasPrereqs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function purchaseAugmentation(aug, fac, sing=false) {
|
|
||||||
const factionInfo = fac.getInfo();
|
|
||||||
var hasPrereqs = hasAugmentationPrereqs(aug);
|
|
||||||
if (!hasPrereqs) {
|
|
||||||
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
|
|
||||||
"purchase this one.";
|
|
||||||
if (sing) {return txt;} else {dialogBoxCreate(txt);}
|
|
||||||
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
|
||||||
let txt = "You don't have enough money to purchase " + aug.name;
|
|
||||||
if (sing) {return txt;}
|
|
||||||
dialogBoxCreate(txt);
|
|
||||||
} else if (fac.playerReputation < aug.baseRepRequirement) {
|
|
||||||
let txt = "You don't have enough faction reputation to purchase " + aug.name;
|
|
||||||
if (sing) {return txt;}
|
|
||||||
dialogBoxCreate(txt);
|
|
||||||
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
|
||||||
if (Player.firstAugPurchased === false) {
|
|
||||||
Player.firstAugPurchased = true;
|
|
||||||
document.getElementById("augmentations-tab").style.display = "list-item";
|
|
||||||
document.getElementById("character-menu-header").click();
|
|
||||||
document.getElementById("character-menu-header").click();
|
|
||||||
}
|
|
||||||
|
|
||||||
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
|
||||||
queuedAugmentation.level = getNextNeurofluxLevel();
|
|
||||||
}
|
|
||||||
Player.queuedAugmentations.push(queuedAugmentation);
|
|
||||||
|
|
||||||
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
|
|
||||||
|
|
||||||
//If you just purchased Neuroflux Governor, recalculate the cost
|
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
|
||||||
var nextLevel = getNextNeurofluxLevel();
|
|
||||||
--nextLevel;
|
|
||||||
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
|
|
||||||
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
|
|
||||||
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
|
||||||
|
|
||||||
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
|
|
||||||
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var name in Augmentations) {
|
|
||||||
if (Augmentations.hasOwnProperty(name)) {
|
|
||||||
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sing) {
|
|
||||||
return "You purchased " + aug.name;
|
|
||||||
} else {
|
|
||||||
if(!Settings.SuppressBuyAugmentationConfirmation){
|
|
||||||
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
|
|
||||||
"effect until they are installed. To install your augmentations, go to the " +
|
|
||||||
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
|
|
||||||
"augmentations will now be more expensive.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
displayFactionAugmentations(fac.name);
|
|
||||||
} else {
|
|
||||||
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
|
|
||||||
"Please report this to the game developer with an explanation of how to " +
|
|
||||||
"reproduce this.");
|
|
||||||
}
|
|
||||||
yesNoBoxClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNextNeurofluxLevel() {
|
|
||||||
// Get current Neuroflux level based on Player's augmentations
|
|
||||||
let currLevel = 0;
|
|
||||||
for (var i = 0; i < Player.augmentations.length; ++i) {
|
|
||||||
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
|
||||||
currLevel = Player.augmentations[i].level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Account for purchased but uninstalled Augmentations
|
|
||||||
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
|
|
||||||
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
|
||||||
++currLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currLevel + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processPassiveFactionRepGain(numCycles) {
|
|
||||||
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
|
|
||||||
for (var name in Factions) {
|
|
||||||
if (Factions.hasOwnProperty(name)) {
|
|
||||||
var faction = Factions[name];
|
|
||||||
|
|
||||||
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
|
|
||||||
//maybe later make this based on
|
|
||||||
//a player's 'status' like how powerful they are and how much money they have
|
|
||||||
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {getNextNeurofluxLevel, inviteToFaction,
|
|
||||||
joinFaction, displayFactionContent, processPassiveFactionRepGain,
|
|
||||||
purchaseAugmentation};
|
|
||||||
240
src/Faction/FactionHelpers.jsx
Normal file
240
src/Faction/FactionHelpers.jsx
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
import { FactionRoot } from "./ui/Root";
|
||||||
|
|
||||||
|
import { Augmentations } from "../Augmentation/Augmentations";
|
||||||
|
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||||
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
|
import { CONSTANTS } from "../Constants";
|
||||||
|
import { Engine } from "../engine";
|
||||||
|
import { Faction } from "./Faction";
|
||||||
|
import { Factions } from "./Factions";
|
||||||
|
import { HackingMission, setInMission } from "../Missions";
|
||||||
|
import { Player } from "../Player";
|
||||||
|
import { Settings } from "../Settings/Settings";
|
||||||
|
|
||||||
|
import { Page, routing } from "../ui/navigationTracking";
|
||||||
|
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||||
|
import { factionInvitationBoxCreate } from "../../utils/FactionInvitationBox";
|
||||||
|
import {
|
||||||
|
Reviver,
|
||||||
|
Generic_toJSON,
|
||||||
|
Generic_fromJSON
|
||||||
|
} from "../../utils/JSONReviver";
|
||||||
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import {
|
||||||
|
yesNoBoxCreate,
|
||||||
|
yesNoBoxGetYesButton,
|
||||||
|
yesNoBoxGetNoButton,
|
||||||
|
yesNoBoxClose
|
||||||
|
} from "../../utils/YesNoBox";
|
||||||
|
|
||||||
|
export function inviteToFaction(faction) {
|
||||||
|
if (Settings.SuppressFactionInvites) {
|
||||||
|
faction.alreadyInvited = true;
|
||||||
|
Player.factionInvitations.push(faction.name);
|
||||||
|
if (routing.isOn(Page.Factions)) {
|
||||||
|
Engine.loadFactionsContent();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
factionInvitationBoxCreate(faction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function joinFaction(faction) {
|
||||||
|
faction.isMember = true;
|
||||||
|
Player.factions.push(faction.name);
|
||||||
|
const factionInfo = faction.getInfo();
|
||||||
|
|
||||||
|
//Determine what factions you are banned from now that you have joined this faction
|
||||||
|
for(const i in factionInfo.enemies) {
|
||||||
|
const enemy = factionInfo.enemies[i];
|
||||||
|
if (Factions[enemy] instanceof Faction) {
|
||||||
|
Factions[enemy].isBanned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startHackingMission(faction) {
|
||||||
|
const mission = new HackingMission(faction.playerReputation, faction);
|
||||||
|
setInMission(true, mission); //Sets inMission flag to true
|
||||||
|
mission.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Displays the HTML content for a specific faction
|
||||||
|
export function displayFactionContent(factionName, initiallyOnAugmentationsPage=false) {
|
||||||
|
const faction = Factions[factionName];
|
||||||
|
if (faction == null) {
|
||||||
|
throw new Error(`Invalid factionName passed into displayFactionContent(): ${factionName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!faction.isMember) {
|
||||||
|
throw new Error(`Not a member of this faction. Cannot display faction information`);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<FactionRoot
|
||||||
|
engine={Engine}
|
||||||
|
initiallyOnAugmentationsPage={initiallyOnAugmentationsPage}
|
||||||
|
faction={faction}
|
||||||
|
p={Player}
|
||||||
|
startHackingMissionFn={startHackingMission}
|
||||||
|
/>,
|
||||||
|
Engine.Display.factionContent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function purchaseAugmentationBoxCreate(aug, fac) {
|
||||||
|
const factionInfo = fac.getInfo();
|
||||||
|
|
||||||
|
const yesBtn = yesNoBoxGetYesButton();
|
||||||
|
yesBtn.innerHTML = "Purchase";
|
||||||
|
yesBtn.addEventListener("click", function() {
|
||||||
|
purchaseAugmentation(aug, fac);
|
||||||
|
});
|
||||||
|
|
||||||
|
const noBtn = yesNoBoxGetNoButton();
|
||||||
|
noBtn.innerHTML = "Cancel";
|
||||||
|
noBtn.addEventListener("click", function() {
|
||||||
|
yesNoBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
|
||||||
|
aug.info + "<br><br>" +
|
||||||
|
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
|
||||||
|
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns a boolean indicating whether the player has the prerequisites for the
|
||||||
|
//specified Augmentation
|
||||||
|
export function hasAugmentationPrereqs(aug) {
|
||||||
|
let hasPrereqs = true;
|
||||||
|
if (aug.prereqs && aug.prereqs.length > 0) {
|
||||||
|
for (let i = 0; i < aug.prereqs.length; ++i) {
|
||||||
|
const prereqAug = Augmentations[aug.prereqs[i]];
|
||||||
|
if (prereqAug == null) {
|
||||||
|
console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (prereqAug.owned === false) {
|
||||||
|
hasPrereqs = false;
|
||||||
|
|
||||||
|
// Check if the aug is purchased
|
||||||
|
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
|
||||||
|
if (Player.queuedAugmentations[j].name === prereqAug.name) {
|
||||||
|
hasPrereqs = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasPrereqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function purchaseAugmentation(aug, fac, sing=false) {
|
||||||
|
const factionInfo = fac.getInfo();
|
||||||
|
var hasPrereqs = hasAugmentationPrereqs(aug);
|
||||||
|
if (!hasPrereqs) {
|
||||||
|
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
|
||||||
|
"purchase this one.";
|
||||||
|
if (sing) {return txt;} else {dialogBoxCreate(txt);}
|
||||||
|
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
||||||
|
let txt = "You don't have enough money to purchase " + aug.name;
|
||||||
|
if (sing) {return txt;}
|
||||||
|
dialogBoxCreate(txt);
|
||||||
|
} else if (fac.playerReputation < aug.baseRepRequirement) {
|
||||||
|
let txt = "You don't have enough faction reputation to purchase " + aug.name;
|
||||||
|
if (sing) {return txt;}
|
||||||
|
dialogBoxCreate(txt);
|
||||||
|
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
||||||
|
if (Player.firstAugPurchased === false) {
|
||||||
|
Player.firstAugPurchased = true;
|
||||||
|
document.getElementById("augmentations-tab").style.display = "list-item";
|
||||||
|
document.getElementById("character-menu-header").click();
|
||||||
|
document.getElementById("character-menu-header").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||||
|
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
queuedAugmentation.level = getNextNeurofluxLevel();
|
||||||
|
}
|
||||||
|
Player.queuedAugmentations.push(queuedAugmentation);
|
||||||
|
|
||||||
|
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
|
||||||
|
|
||||||
|
// If you just purchased Neuroflux Governor, recalculate the cost
|
||||||
|
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
var nextLevel = getNextNeurofluxLevel();
|
||||||
|
--nextLevel;
|
||||||
|
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
|
||||||
|
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||||
|
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||||
|
|
||||||
|
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
|
||||||
|
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var name in Augmentations) {
|
||||||
|
if (Augmentations.hasOwnProperty(name)) {
|
||||||
|
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sing) {
|
||||||
|
return "You purchased " + aug.name;
|
||||||
|
} else {
|
||||||
|
if(!Settings.SuppressBuyAugmentationConfirmation){
|
||||||
|
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
|
||||||
|
"effect until they are installed. To install your augmentations, go to the " +
|
||||||
|
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
|
||||||
|
"augmentations will now be more expensive.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a rerender of the Augmentations page
|
||||||
|
displayFactionContent(fac.name, true);
|
||||||
|
} else {
|
||||||
|
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
|
||||||
|
"Please report this to the game developer with an explanation of how to " +
|
||||||
|
"reproduce this.");
|
||||||
|
}
|
||||||
|
yesNoBoxClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNextNeurofluxLevel() {
|
||||||
|
// Get current Neuroflux level based on Player's augmentations
|
||||||
|
let currLevel = 0;
|
||||||
|
for (var i = 0; i < Player.augmentations.length; ++i) {
|
||||||
|
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
currLevel = Player.augmentations[i].level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for purchased but uninstalled Augmentations
|
||||||
|
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||||
|
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
++currLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currLevel + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processPassiveFactionRepGain(numCycles) {
|
||||||
|
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
|
||||||
|
for (var name in Factions) {
|
||||||
|
if (Factions.hasOwnProperty(name)) {
|
||||||
|
var faction = Factions[name];
|
||||||
|
|
||||||
|
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
|
||||||
|
//maybe later make this based on
|
||||||
|
//a player's 'status' like how powerful they are and how much money they have
|
||||||
|
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/Faction/ui/AugmentationsPage.tsx
Normal file
163
src/Faction/ui/AugmentationsPage.tsx
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Root React Component for displaying a faction's "Purchase Augmentations" page
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
|
||||||
|
|
||||||
|
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||||
|
import { Faction } from "../../Faction/Faction";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
faction: Faction;
|
||||||
|
p: IPlayer;
|
||||||
|
routeToMainPage: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
sortOrder: PurchaseAugmentationsOrderSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
const infoStyleMarkup = {
|
||||||
|
width: "70%",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AugmentationsPage extends React.Component<IProps, IState> {
|
||||||
|
// Flag for whether the player has a gang with this faction
|
||||||
|
isPlayersGang: boolean;
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.isPlayersGang = props.p.inGang() && (props.p.getGangName() === props.faction.name);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
sortOrder: PurchaseAugmentationsOrderSetting.Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rerender = this.rerender.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAugs() {
|
||||||
|
if (this.isPlayersGang) {
|
||||||
|
const augs: string[] = [];
|
||||||
|
for (const augName in Augmentations) {
|
||||||
|
const aug = Augmentations[augName];
|
||||||
|
if (!aug.isSpecial) {
|
||||||
|
augs.push(augName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return augs;
|
||||||
|
} else {
|
||||||
|
return this.props.faction.augmentations.slice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAugsSorted() {
|
||||||
|
switch (Settings.PurchaseAugmentationsOrder) {
|
||||||
|
case PurchaseAugmentationsOrderSetting.Cost: {
|
||||||
|
return this.getAugsSortedByCost();
|
||||||
|
}
|
||||||
|
case PurchaseAugmentationsOrderSetting.Reputation: {
|
||||||
|
return this.getAugsSortedByReputation();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return this.getAugsSortedByDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAugsSortedByCost() {
|
||||||
|
const augs = this.getAugs();
|
||||||
|
augs.sort((augName1, augName2)=>{
|
||||||
|
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
||||||
|
if (aug1 == null || aug2 == null) {
|
||||||
|
throw new Error("Invalid Augmentation Names");
|
||||||
|
}
|
||||||
|
|
||||||
|
return aug1.baseCost - aug2.baseCost;
|
||||||
|
});
|
||||||
|
|
||||||
|
return augs;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAugsSortedByReputation() {
|
||||||
|
const augs = this.getAugs();
|
||||||
|
augs.sort((augName1, augName2)=>{
|
||||||
|
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
||||||
|
if (aug1 == null || aug2 == null) {
|
||||||
|
throw new Error("Invalid Augmentation Names");
|
||||||
|
}
|
||||||
|
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
||||||
|
});
|
||||||
|
|
||||||
|
return augs;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAugsSortedByDefault() {
|
||||||
|
return this.getAugs();
|
||||||
|
}
|
||||||
|
|
||||||
|
switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting) {
|
||||||
|
Settings.PurchaseAugmentationsOrder = newOrder;
|
||||||
|
this.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
rerender() {
|
||||||
|
this.setState((prevState) => {
|
||||||
|
return {
|
||||||
|
rerenderFlag: !prevState.rerenderFlag,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const augs = this.getAugsSorted();
|
||||||
|
const augList = augs.map((aug) => {
|
||||||
|
return (
|
||||||
|
<PurchaseableAugmentation
|
||||||
|
augName={aug}
|
||||||
|
faction={this.props.faction}
|
||||||
|
key={aug}
|
||||||
|
p={this.props.p}
|
||||||
|
rerender={this.rerender}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<StdButton
|
||||||
|
onClick={this.props.routeToMainPage}
|
||||||
|
text={"Back"}
|
||||||
|
/>
|
||||||
|
<h1>Faction Augmentations</h1>
|
||||||
|
<p style={infoStyleMarkup}>
|
||||||
|
These are all of the Augmentations that are available to purchase
|
||||||
|
from {this.props.faction.name}. Augmentations are powerful upgrades
|
||||||
|
that will enhance your abilities.
|
||||||
|
</p>
|
||||||
|
<StdButton
|
||||||
|
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}
|
||||||
|
text={"Sort by Cost"}
|
||||||
|
/>
|
||||||
|
<StdButton
|
||||||
|
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
|
||||||
|
text={"Sort by Reputation"}
|
||||||
|
/>
|
||||||
|
<StdButton
|
||||||
|
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
|
||||||
|
text={"Sort by Default Order"}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
{augList}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/Faction/ui/DonateOption.tsx
Normal file
101
src/Faction/ui/DonateOption.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* React component for a donate option on the Faction UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
import { Faction } from "../../Faction/Faction";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
faction: Faction;
|
||||||
|
p: IPlayer;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
donateAmt: number;
|
||||||
|
statusTxt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputStyleMarkup = {
|
||||||
|
margin: "5px",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DonateOption extends React.Component<IProps, IState> {
|
||||||
|
// Style markup for block elements. Stored as property
|
||||||
|
blockStyle: object = { display: "block" };
|
||||||
|
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
donateAmt: 0,
|
||||||
|
statusTxt: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
this.calculateRepGain = this.calculateRepGain.bind(this);
|
||||||
|
this.donate = this.donate.bind(this);
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns rep gain for a given donation amount
|
||||||
|
calculateRepGain(amt: number): number {
|
||||||
|
return amt / CONSTANTS.DonateMoneyToRepDivisor * this.props.p.faction_rep_mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
donate(): void {
|
||||||
|
const fac = this.props.faction;
|
||||||
|
const amt = this.state.donateAmt;
|
||||||
|
if (isNaN(amt) || amt <= 0) {
|
||||||
|
dialogBoxCreate(`Invalid amount entered!`);
|
||||||
|
} else if (!this.props.p.canAfford(amt)) {
|
||||||
|
dialogBoxCreate(`You cannot afford to donate this much money!`);
|
||||||
|
} else {
|
||||||
|
this.props.p.loseMoney(amt);
|
||||||
|
const repGain = this.calculateRepGain(amt);
|
||||||
|
this.props.faction.playerReputation += repGain;
|
||||||
|
dialogBoxCreate(`You just donated ${numeralWrapper.formatMoney(amt)} to ${fac.name} to gain ` +
|
||||||
|
`${numeralWrapper.format(repGain, "0,0.000")} reputation`);
|
||||||
|
this.props.rerender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
const amt = parseFloat(e.target.value);
|
||||||
|
|
||||||
|
if (isNaN(amt)) {
|
||||||
|
this.setState({
|
||||||
|
donateAmt: 0,
|
||||||
|
statusTxt: "Invalid donate amount entered!",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const repGain = this.calculateRepGain(amt);
|
||||||
|
this.setState({
|
||||||
|
donateAmt: amt,
|
||||||
|
statusTxt: `This donation will result in ${numeralWrapper.format(repGain, "0,0.000")} reputation gain`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={"faction-work-div"}>
|
||||||
|
<div className={"faction-work-div-wrapper"}>
|
||||||
|
<input onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
|
||||||
|
<StdButton
|
||||||
|
onClick={this.donate}
|
||||||
|
text={"Donate Money"}
|
||||||
|
/>
|
||||||
|
<p style={this.blockStyle}>{this.state.statusTxt}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/Faction/ui/Info.tsx
Normal file
74
src/Faction/ui/Info.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* React component for general information about the faction. This includes the
|
||||||
|
* factions "motto", reputation, favor, and gameplay instructions
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Faction } from "../../Faction/Faction";
|
||||||
|
import { FactionInfo } from "../../Faction/FactionInfo";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph";
|
||||||
|
import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
faction: Faction;
|
||||||
|
factionInfo: FactionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IInnerHTMLMarkup = {
|
||||||
|
__html: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockStyleMarkup = {
|
||||||
|
display: "block",
|
||||||
|
}
|
||||||
|
|
||||||
|
const infoStyleMarkup = {
|
||||||
|
display: "block",
|
||||||
|
width: "70%",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Info extends React.Component<IProps, any> {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
|
||||||
|
const favorGain = this.props.faction.getFavorGain()[0];
|
||||||
|
const favorTooltip = "Faction favor increases the rate at which you earn reputation for " +
|
||||||
|
"this faction by 1% per favor. Faction favor is gained whenever you " +
|
||||||
|
"reset after installing an Augmentation. The amount of " +
|
||||||
|
"favor you gain depends on how much reputation you have with the faction"
|
||||||
|
|
||||||
|
const infoText: IInnerHTMLMarkup = {
|
||||||
|
__html: this.props.factionInfo.infoText,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<pre>
|
||||||
|
<i className={"text"} dangerouslySetInnerHTML={infoText}></i>
|
||||||
|
</pre>
|
||||||
|
<p style={blockStyleMarkup}>-------------------------</p>
|
||||||
|
<AutoupdatingParagraph
|
||||||
|
intervalTime={5e3}
|
||||||
|
text={`Reputation: ${formattedRep}`}
|
||||||
|
tooltip={`You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`}
|
||||||
|
/>
|
||||||
|
<p style={blockStyleMarkup}>-------------------------</p>
|
||||||
|
<ParagraphWithTooltip
|
||||||
|
text={`Faction Favor: ${numeralWrapper.format(this.props.faction.favor, "0,0")}`}
|
||||||
|
tooltip={favorTooltip}
|
||||||
|
/>
|
||||||
|
<p style={blockStyleMarkup}>-------------------------</p>
|
||||||
|
<p style={infoStyleMarkup}>
|
||||||
|
Perform work/carry out assignments for your faction to help further its cause!
|
||||||
|
By doing so you will earn reputation for your faction. You will also gain
|
||||||
|
reputation passively over time, although at a very slow rate. Earning
|
||||||
|
reputation will allow you to purchase Augmentations through this faction, which
|
||||||
|
are powerful upgrades that enhance your abilities. Note that you cannot use your
|
||||||
|
terminal or create scripts when you are performing a task!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Faction/ui/Option.tsx
Normal file
30
src/Faction/ui/Option.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* React component for a selectable option on the Faction UI. These
|
||||||
|
* options including working for the faction, hacking missions, purchasing
|
||||||
|
* augmentations, etc.
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
buttonText: string;
|
||||||
|
infoText: string;
|
||||||
|
onClick: (e: React.MouseEvent<HTMLElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Option extends React.Component<IProps, any> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={"faction-work-div"}>
|
||||||
|
<div className={"faction-work-div-wrapper"}>
|
||||||
|
<StdButton
|
||||||
|
onClick={this.props.onClick}
|
||||||
|
text={this.props.buttonText}
|
||||||
|
/>
|
||||||
|
<p>{this.props.infoText}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/Faction/ui/PurchaseableAugmentation.tsx
Normal file
152
src/Faction/ui/PurchaseableAugmentation.tsx
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* React component for displaying a single augmentation for purchase through
|
||||||
|
* the faction UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getNextNeurofluxLevel,
|
||||||
|
hasAugmentationPrereqs,
|
||||||
|
purchaseAugmentation,
|
||||||
|
purchaseAugmentationBoxCreate,
|
||||||
|
} from "../FactionHelpers";
|
||||||
|
|
||||||
|
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||||
|
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||||
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
|
import { Faction } from "../../Faction/Faction";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { IMap } from "../../types";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
augName: string;
|
||||||
|
faction: Faction;
|
||||||
|
p: IPlayer;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const spanStyleMarkup = {
|
||||||
|
margin: "4px",
|
||||||
|
padding: "4px",
|
||||||
|
}
|
||||||
|
|
||||||
|
const inlineStyleMarkup = {
|
||||||
|
display: "inline-block",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PurchaseableAugmentation extends React.Component<IProps, any> {
|
||||||
|
aug: Augmentation | null;
|
||||||
|
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.aug = Augmentations[this.props.augName];
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMoneyCost(): number {
|
||||||
|
return this.aug!.baseCost * this.props.faction.getInfo().augmentationPriceMult;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRepCost(): number {
|
||||||
|
return this.aug!.baseRepRequirement * this.props.faction.getInfo().augmentationRepRequirementMult;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick() {
|
||||||
|
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||||
|
purchaseAugmentationBoxCreate(this.aug!, this.props.faction);
|
||||||
|
} else {
|
||||||
|
purchaseAugmentation(this.aug!, this.props.faction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the player has the prerequisite Augmentations
|
||||||
|
hasPrereqs(): boolean {
|
||||||
|
return hasAugmentationPrereqs(this.aug!);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the player has enough rep for this Augmentation
|
||||||
|
hasReputation(): boolean {
|
||||||
|
return this.props.faction.playerReputation >= this.getRepCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the player has this augmentations (purchased OR installed)
|
||||||
|
owned(): boolean {
|
||||||
|
let owned = false;
|
||||||
|
for (const queuedAug of this.props.p.queuedAugmentations) {
|
||||||
|
if (queuedAug.name === this.props.augName) {
|
||||||
|
owned = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const installedAug of this.props.p.augmentations) {
|
||||||
|
if (installedAug.name === this.props.augName) {
|
||||||
|
owned = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.aug == null) {
|
||||||
|
console.error(`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${this.props.augName}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moneyCost = this.getMoneyCost();
|
||||||
|
const repCost = this.getRepCost();
|
||||||
|
|
||||||
|
// Determine UI properties
|
||||||
|
let disabled: boolean = false;
|
||||||
|
let statusTxt: string = "";
|
||||||
|
let color: string = "";
|
||||||
|
if (!this.hasPrereqs()) {
|
||||||
|
disabled = true;
|
||||||
|
statusTxt = `LOCKED (Requires ${this.aug.prereqs.join(",")} as prerequisite(s))`;
|
||||||
|
color = "red";
|
||||||
|
} else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) {
|
||||||
|
disabled = true;
|
||||||
|
statusTxt = "ALREADY OWNED";
|
||||||
|
} else if (this.hasReputation()) {
|
||||||
|
statusTxt = `UNLOCKED - ${numeralWrapper.formatMoney(moneyCost)}`;
|
||||||
|
} else {
|
||||||
|
disabled = true;
|
||||||
|
statusTxt = `LOCKED (Requires ${numeralWrapper.format(repCost, "0,0.0")} faction reputation - ${numeralWrapper.formatMoney(moneyCost)})`;
|
||||||
|
color = "red";
|
||||||
|
}
|
||||||
|
|
||||||
|
const txtStyle: IMap<string> = {
|
||||||
|
display: "inline-block",
|
||||||
|
}
|
||||||
|
if (color !== "") { txtStyle.color = color; }
|
||||||
|
|
||||||
|
// Determine button txt
|
||||||
|
let btnTxt = this.aug.name;
|
||||||
|
if (this.aug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
btnTxt += ` - Level ${getNextNeurofluxLevel()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<span style={spanStyleMarkup}>
|
||||||
|
<StdButton
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
style={inlineStyleMarkup}
|
||||||
|
text={btnTxt}
|
||||||
|
tooltip={this.aug.info}
|
||||||
|
/>
|
||||||
|
<p style={txtStyle}>{statusTxt}</p>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
302
src/Faction/ui/Root.tsx
Normal file
302
src/Faction/ui/Root.tsx
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
/**
|
||||||
|
* Root React Component for displaying a Faction's UI.
|
||||||
|
* This is the component for displaying a single faction's UI, not the list of all
|
||||||
|
* accessible factions
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { AugmentationsPage } from "./AugmentationsPage";
|
||||||
|
import { DonateOption } from "./DonateOption";
|
||||||
|
import { Info } from "./Info";
|
||||||
|
import { Option } from "./Option";
|
||||||
|
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
import { IEngine } from "../../IEngine";
|
||||||
|
|
||||||
|
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||||
|
import { Faction } from "../../Faction/Faction";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||||
|
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||||
|
|
||||||
|
import {
|
||||||
|
yesNoBoxClose,
|
||||||
|
yesNoBoxCreate,
|
||||||
|
yesNoBoxGetNoButton,
|
||||||
|
yesNoBoxGetYesButton,
|
||||||
|
} from "../../../utils/YesNoBox";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
engine: IEngine;
|
||||||
|
initiallyOnAugmentationsPage?: boolean;
|
||||||
|
faction: Faction;
|
||||||
|
p: IPlayer;
|
||||||
|
startHackingMissionFn: (faction: Faction) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
purchasingAugs: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info text for all options on the UI
|
||||||
|
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " +
|
||||||
|
"faction reputation";
|
||||||
|
const hackingMissionInfo = "Attempt a hacking mission for your faction. " +
|
||||||
|
"A mission is a mini game that, if won, earns you " +
|
||||||
|
"significant reputation with this faction. (Recommended hacking level: 200+)";
|
||||||
|
const hackingContractsInfo = "Complete hacking contracts for your faction. " +
|
||||||
|
"Your effectiveness, which determines how much " +
|
||||||
|
"reputation you gain for this faction, is based on your hacking skill. " +
|
||||||
|
"You will gain hacking exp.";
|
||||||
|
const fieldWorkInfo = "Carry out field missions for your faction. " +
|
||||||
|
"Your effectiveness, which determines how much " +
|
||||||
|
"reputation you gain for this faction, is based on all of your stats. " +
|
||||||
|
"You will gain exp for all stats.";
|
||||||
|
const securityWorkInfo = "Serve in a security detail for your faction. " +
|
||||||
|
"Your effectiveness, which determines how much " +
|
||||||
|
"reputation you gain for this faction, is based on your combat stats. " +
|
||||||
|
"You will gain exp for all combat stats.";
|
||||||
|
const augmentationsInfo = "As your reputation with this faction rises, you will " +
|
||||||
|
"unlock Augmentations, which you can purchase to enhance " +
|
||||||
|
"your abilities.";
|
||||||
|
const sleevePurchasesInfo = "Purchase Duplicate Sleeves and upgrades. These are permanent!";
|
||||||
|
|
||||||
|
const GangNames = [
|
||||||
|
"Slum Snakes",
|
||||||
|
"Tetrads",
|
||||||
|
"The Syndicate",
|
||||||
|
"The Dark Army",
|
||||||
|
"Speakers for the Dead",
|
||||||
|
"NiteSec",
|
||||||
|
"The Black Hand"
|
||||||
|
];
|
||||||
|
|
||||||
|
export class FactionRoot extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
purchasingAugs: props.initiallyOnAugmentationsPage ? props.initiallyOnAugmentationsPage : false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.manageGang = this.manageGang.bind(this);
|
||||||
|
this.rerender = this.rerender.bind(this);
|
||||||
|
this.routeToMain = this.routeToMain.bind(this);
|
||||||
|
this.routeToPurchaseAugs = this.routeToPurchaseAugs.bind(this);
|
||||||
|
this.sleevePurchases = this.sleevePurchases.bind(this);
|
||||||
|
this.startFieldWork = this.startFieldWork.bind(this);
|
||||||
|
this.startHackingContracts = this.startHackingContracts.bind(this);
|
||||||
|
this.startHackingMission = this.startHackingMission.bind(this);
|
||||||
|
this.startSecurityWork = this.startSecurityWork.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
manageGang() {
|
||||||
|
// If player already has a gang, just go to the gang UI
|
||||||
|
if (this.props.p.inGang()) {
|
||||||
|
return this.props.engine.loadGangContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we have to create the gang
|
||||||
|
const facName = this.props.faction.name;
|
||||||
|
let isHacking = false;
|
||||||
|
if (facName === "NiteSec" || facName === "The Black Hand") {
|
||||||
|
isHacking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Yes/No popup box will allow the player to confirm gang creation
|
||||||
|
const yesBtn = yesNoBoxGetYesButton();
|
||||||
|
const noBtn = yesNoBoxGetNoButton();
|
||||||
|
if (yesBtn == null || noBtn == null) { return; }
|
||||||
|
|
||||||
|
yesBtn.innerHTML = "Create Gang";
|
||||||
|
yesBtn.addEventListener("click", () => {
|
||||||
|
this.props.p.startGang(facName, isHacking);
|
||||||
|
const worldMenuHeader = document.getElementById("world-menu-header");
|
||||||
|
if (worldMenuHeader instanceof HTMLElement) {
|
||||||
|
worldMenuHeader.click(); worldMenuHeader.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.engine.loadGangContent();
|
||||||
|
yesNoBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
noBtn.innerHTML = "Cancel";
|
||||||
|
noBtn.addEventListener("click", () => {
|
||||||
|
yesNoBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pop-up text
|
||||||
|
let gangTypeText = "";
|
||||||
|
if (isHacking) {
|
||||||
|
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
|
||||||
|
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
|
||||||
|
"is not as important.<br><br>";
|
||||||
|
} else {
|
||||||
|
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
|
||||||
|
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
|
||||||
|
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
|
||||||
|
}
|
||||||
|
yesNoBoxCreate(`Would you like to create a new Gang with ${facName}?<br><br>` +
|
||||||
|
"Note that this will prevent you from creating a Gang with any other Faction until " +
|
||||||
|
"this BitNode is destroyed. It also resets your reputation with this faction.<br><br>" +
|
||||||
|
gangTypeText +
|
||||||
|
"Other than hacking vs combat, there are NO differences between the Factions you can " +
|
||||||
|
"create a Gang with, and each of these Factions have all Augmentations available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rerender() {
|
||||||
|
this.setState((prevState) => {
|
||||||
|
return {
|
||||||
|
rerenderFlag: !prevState.rerenderFlag,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route to the main faction page
|
||||||
|
routeToMain() {
|
||||||
|
this.setState({ purchasingAugs: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route to the purchase augmentation UI for this faction
|
||||||
|
routeToPurchaseAugs() {
|
||||||
|
this.setState({ purchasingAugs: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
sleevePurchases() {
|
||||||
|
createSleevePurchasesFromCovenantPopup(this.props.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
startFieldWork() {
|
||||||
|
this.props.p.startFactionFieldWork(this.props.faction);
|
||||||
|
}
|
||||||
|
|
||||||
|
startHackingContracts() {
|
||||||
|
this.props.p.startFactionHackWork(this.props.faction);
|
||||||
|
}
|
||||||
|
|
||||||
|
startHackingMission() {
|
||||||
|
const fac = this.props.faction;
|
||||||
|
this.props.engine.loadMissionContent();
|
||||||
|
this.props.startHackingMissionFn(fac);
|
||||||
|
}
|
||||||
|
|
||||||
|
startSecurityWork() {
|
||||||
|
this.props.p.startFactionSecurityWork(this.props.faction);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.state.purchasingAugs ? this.renderAugmentationsPage() : this.renderMainPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMainPage() {
|
||||||
|
const p = this.props.p;
|
||||||
|
const faction = this.props.faction;
|
||||||
|
const factionInfo = faction.getInfo();
|
||||||
|
|
||||||
|
// We have a special flag for whether the player this faction is the player's
|
||||||
|
// gang faction because if the player has a gang, they cannot do any other action
|
||||||
|
const isPlayersGang = p.inGang() && (p.getGangName() === faction.name);
|
||||||
|
|
||||||
|
|
||||||
|
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
|
||||||
|
// should be shown
|
||||||
|
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
|
||||||
|
const canDonate = faction.favor >= favorToDonate;
|
||||||
|
|
||||||
|
const canPurchaseSleeves = (faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10]);
|
||||||
|
|
||||||
|
let canAccessGang = (p.canAccessGang() && GangNames.includes(faction.name));
|
||||||
|
if (p.inGang()) {
|
||||||
|
if (p.getGangName() !== faction.name) {
|
||||||
|
canAccessGang = false;
|
||||||
|
} else if (p.getGangName() === faction.name) {
|
||||||
|
canAccessGang = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{faction.name}</h1>
|
||||||
|
<Info
|
||||||
|
faction={faction}
|
||||||
|
factionInfo={factionInfo}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
canAccessGang &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Manage Gang"}
|
||||||
|
infoText={gangInfo}
|
||||||
|
onClick={this.manageGang}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(!isPlayersGang && factionInfo.offerHackingMission) &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Hacking Mission"}
|
||||||
|
infoText={hackingMissionInfo}
|
||||||
|
onClick={this.startHackingMission}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(!isPlayersGang && factionInfo.offerHackingWork) &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Hacking Contracts"}
|
||||||
|
infoText={hackingContractsInfo}
|
||||||
|
onClick={this.startHackingContracts}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(!isPlayersGang && factionInfo.offerFieldWork) &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Field Work"}
|
||||||
|
infoText={fieldWorkInfo}
|
||||||
|
onClick={this.startFieldWork}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(!isPlayersGang && factionInfo.offerSecurityWork) &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Security Work"}
|
||||||
|
infoText={securityWorkInfo}
|
||||||
|
onClick={this.startSecurityWork}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(!isPlayersGang && canDonate) &&
|
||||||
|
<DonateOption
|
||||||
|
faction={this.props.faction}
|
||||||
|
p={this.props.p}
|
||||||
|
rerender={this.rerender}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<Option
|
||||||
|
buttonText={"Purchase Augmentations"}
|
||||||
|
infoText={augmentationsInfo}
|
||||||
|
onClick={this.routeToPurchaseAugs}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
canPurchaseSleeves &&
|
||||||
|
<Option
|
||||||
|
buttonText={"Purchase Duplicate Sleeves"}
|
||||||
|
infoText={sleevePurchasesInfo}
|
||||||
|
onClick={this.sleevePurchases}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAugmentationsPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AugmentationsPage
|
||||||
|
faction={this.props.faction}
|
||||||
|
p={this.props.p}
|
||||||
|
routeToMainPage={this.routeToMain}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/Gang.js
158
src/Gang.js
@@ -1,31 +1,39 @@
|
|||||||
/*
|
/**
|
||||||
Also add police clashes
|
* TODO
|
||||||
balance point to keep them from running out of control
|
* Add police clashes
|
||||||
|
* balance point to keep them from running out of control
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
|
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
|
||||||
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
|
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
|
||||||
|
|
||||||
|
import { Engine } from "./engine";
|
||||||
|
import { Faction } from "./Faction/Faction";
|
||||||
|
import { Factions } from "./Faction/Factions";
|
||||||
|
import { displayFactionContent } from "./Faction/FactionHelpers";
|
||||||
|
|
||||||
|
import { Page, routing } from "./ui/navigationTracking";
|
||||||
|
import { numeralWrapper } from "./ui/numeralFormat";
|
||||||
|
|
||||||
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
|
import {
|
||||||
|
Reviver,
|
||||||
|
Generic_toJSON,
|
||||||
|
Generic_fromJSON
|
||||||
|
} from "../utils/JSONReviver";
|
||||||
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
|
|
||||||
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
|
import { KEY } from "../utils/helpers/keyCodes";
|
||||||
|
|
||||||
|
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
||||||
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
|
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||||
|
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||||
|
import { removeElement } from "../utils/uiHelpers/removeElement";
|
||||||
|
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||||
|
|
||||||
import { Engine } from "./engine";
|
|
||||||
import { Faction } from "./Faction/Faction";
|
|
||||||
import { Factions } from "./Faction/Factions";
|
|
||||||
import { displayFactionContent } from "./Faction/FactionHelpers";
|
|
||||||
import { numeralWrapper } from "./ui/numeralFormat";
|
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
|
||||||
import { Reviver, Generic_toJSON,
|
|
||||||
Generic_fromJSON } from "../utils/JSONReviver";
|
|
||||||
import { KEY } from "../utils/helpers/keyCodes";
|
|
||||||
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
|
||||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
|
||||||
import { Page,
|
|
||||||
routing } from "./ui/navigationTracking";
|
|
||||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
|
||||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
|
||||||
import { removeElement } from "../utils/uiHelpers/removeElement";
|
|
||||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain
|
const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain
|
||||||
@@ -50,7 +58,7 @@ $(document).keydown(function(event) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Delete upgrade box when clicking outside
|
// Delete upgrade box when clicking outside
|
||||||
$(document).mousedown(function(event) {
|
$(document).mousedown(function(event) {
|
||||||
var boxId = "gang-member-upgrade-popup-box";
|
var boxId = "gang-member-upgrade-popup-box";
|
||||||
var contentId = "gang-member-upgrade-popup-box-content";
|
var contentId = "gang-member-upgrade-popup-box-content";
|
||||||
@@ -66,8 +74,16 @@ $(document).mousedown(function(event) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let GangNames = ["Slum Snakes", "Tetrads", "The Syndicate", "The Dark Army", "Speakers for the Dead",
|
const GangNames = [
|
||||||
"NiteSec", "The Black Hand"];
|
"Slum Snakes",
|
||||||
|
"Tetrads",
|
||||||
|
"The Syndicate",
|
||||||
|
"The Dark Army",
|
||||||
|
"Speakers for the Dead",
|
||||||
|
"NiteSec",
|
||||||
|
"The Black Hand"
|
||||||
|
];
|
||||||
|
|
||||||
export let AllGangs = {
|
export let AllGangs = {
|
||||||
"Slum Snakes" : {
|
"Slum Snakes" : {
|
||||||
power: 1,
|
power: 1,
|
||||||
@@ -137,12 +153,12 @@ export function loadAllGangs(saveString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param facName - Name of corresponding faction
|
* @param facName {string} Name of corresponding faction
|
||||||
* @param hacking - Boolean indicating whether or not its a hacking gang
|
* @param hacking {bollean} Whether or not its a hacking gang
|
||||||
*/
|
*/
|
||||||
export function Gang(facName, hacking=false) {
|
export function Gang(facName, hacking=false) {
|
||||||
this.facName = facName;
|
this.facName = facName;
|
||||||
this.members = []; //Array of GangMembers
|
this.members = [];
|
||||||
this.wanted = 1;
|
this.wanted = 1;
|
||||||
this.respect = 1;
|
this.respect = 1;
|
||||||
|
|
||||||
@@ -197,7 +213,7 @@ Gang.prototype.process = function(numCycles=1, player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Gang.prototype.processGains = function(numCycles=1, player) {
|
Gang.prototype.processGains = function(numCycles=1, player) {
|
||||||
//Get gains per cycle
|
// Get gains per cycle
|
||||||
var moneyGains = 0, respectGains = 0, wantedLevelGains = 0;
|
var moneyGains = 0, respectGains = 0, wantedLevelGains = 0;
|
||||||
for (var i = 0; i < this.members.length; ++i) {
|
for (var i = 0; i < this.members.length; ++i) {
|
||||||
respectGains += (this.members[i].calculateRespectGain(this));
|
respectGains += (this.members[i].calculateRespectGain(this));
|
||||||
@@ -291,9 +307,9 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then process territory
|
// Then process territory
|
||||||
for (var i = 0; i < GangNames.length; ++i) {
|
for (let i = 0; i < GangNames.length; ++i) {
|
||||||
const others = GangNames.filter((e) => {
|
const others = GangNames.filter((e) => {
|
||||||
return e !== i;
|
return e !== GangNames[i];
|
||||||
});
|
});
|
||||||
const other = getRandomInt(0, others.length - 1);
|
const other = getRandomInt(0, others.length - 1);
|
||||||
|
|
||||||
@@ -318,6 +334,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
|||||||
AllGangs[otherGang].territory -= 0.0001;
|
AllGangs[otherGang].territory -= 0.0001;
|
||||||
if (thisGang === gangName) {
|
if (thisGang === gangName) {
|
||||||
this.clash(true); // Player won
|
this.clash(true); // Player won
|
||||||
|
AllGangs[otherGang].power *= (1 / 1.01);
|
||||||
} else if (otherGang === gangName) {
|
} else if (otherGang === gangName) {
|
||||||
this.clash(false); // Player lost
|
this.clash(false); // Player lost
|
||||||
} else {
|
} else {
|
||||||
@@ -333,6 +350,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
|||||||
this.clash(false); // Player lost
|
this.clash(false); // Player lost
|
||||||
} else if (otherGang === gangName) {
|
} else if (otherGang === gangName) {
|
||||||
this.clash(true); // Player won
|
this.clash(true); // Player won
|
||||||
|
AllGangs[thisGang].power *= (1 / 1.01);
|
||||||
} else {
|
} else {
|
||||||
AllGangs[thisGang].power *= (1 / 1.01);
|
AllGangs[thisGang].power *= (1 / 1.01);
|
||||||
}
|
}
|
||||||
@@ -562,10 +580,9 @@ Gang.fromJSON = function(value) {
|
|||||||
|
|
||||||
Reviver.constructors.Gang = Gang;
|
Reviver.constructors.Gang = Gang;
|
||||||
|
|
||||||
/*** Gang Member object ***/
|
|
||||||
function GangMember(name) {
|
function GangMember(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.task = "Unassigned"; //GangMemberTask object
|
this.task = "Unassigned";
|
||||||
|
|
||||||
this.earnedRespect = 0;
|
this.earnedRespect = 0;
|
||||||
|
|
||||||
@@ -597,11 +614,11 @@ function GangMember(name) {
|
|||||||
this.agi_asc_mult = 1;
|
this.agi_asc_mult = 1;
|
||||||
this.cha_asc_mult = 1;
|
this.cha_asc_mult = 1;
|
||||||
|
|
||||||
this.upgrades = []; //Names of upgrades
|
this.upgrades = []; // Names of upgrades
|
||||||
this.augmentations = []; //Names only
|
this.augmentations = []; // Names of augmentations only
|
||||||
}
|
}
|
||||||
|
|
||||||
//Same formula for Player
|
// Same skill calculation formula as Player
|
||||||
GangMember.prototype.calculateSkill = function(exp, mult=1) {
|
GangMember.prototype.calculateSkill = function(exp, mult=1) {
|
||||||
return Math.max(Math.floor(mult * (32 * Math.log(exp + 534.5) - 200)), 1);
|
return Math.max(Math.floor(mult * (32 * Math.log(exp + 534.5) - 200)), 1);
|
||||||
}
|
}
|
||||||
@@ -645,7 +662,7 @@ GangMember.prototype.getTask = function() {
|
|||||||
return GangMemberTasks["Unassigned"];
|
return GangMemberTasks["Unassigned"];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Gains are per cycle
|
// Gains are per cycle
|
||||||
GangMember.prototype.calculateRespectGain = function(gang) {
|
GangMember.prototype.calculateRespectGain = function(gang) {
|
||||||
const task = this.getTask();
|
const task = this.getTask();
|
||||||
if (task == null || !(task instanceof GangMemberTask) || task.baseRespect === 0) {return 0;}
|
if (task == null || !(task instanceof GangMemberTask) || task.baseRespect === 0) {return 0;}
|
||||||
@@ -770,9 +787,11 @@ GangMember.prototype.ascend = function() {
|
|||||||
|
|
||||||
// Returns the multipliers that would be gained from ascension
|
// Returns the multipliers that would be gained from ascension
|
||||||
GangMember.prototype.getAscensionResults = function() {
|
GangMember.prototype.getAscensionResults = function() {
|
||||||
// Calculate ascension bonus to stat multipliers.
|
/**
|
||||||
// This is based on the current number of multipliers from Non-Augmentation upgrades
|
* Calculate ascension bonus to stat multipliers.
|
||||||
// + Ascension Bonus = N% of current bonus from Augmentations
|
* This is based on the current number of multipliers from Non-Augmentation upgrades
|
||||||
|
* + Ascension Bonus = N% of current bonus from Augmentations
|
||||||
|
*/
|
||||||
let hack = 1;
|
let hack = 1;
|
||||||
let str = 1;
|
let str = 1;
|
||||||
let def = 1;
|
let def = 1;
|
||||||
@@ -837,7 +856,7 @@ GangMember.fromJSON = function(value) {
|
|||||||
|
|
||||||
Reviver.constructors.GangMember = GangMember;
|
Reviver.constructors.GangMember = GangMember;
|
||||||
|
|
||||||
//Defines tasks that Gang Members can work on
|
// Defines tasks that Gang Members can work on
|
||||||
function GangMemberTask(name="", desc="", isHacking=false, isCombat=false,
|
function GangMemberTask(name="", desc="", isHacking=false, isCombat=false,
|
||||||
params={baseRespect: 0, baseWanted: 0, baseMoney: 0,
|
params={baseRespect: 0, baseWanted: 0, baseMoney: 0,
|
||||||
hackWeight: 0, strWeight: 0, defWeight: 0,
|
hackWeight: 0, strWeight: 0, defWeight: 0,
|
||||||
@@ -935,7 +954,7 @@ GangMemberUpgrade.prototype.createDescription = function() {
|
|||||||
this.desc = lines.join("<br>");
|
this.desc = lines.join("<br>");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Passes in a GangMember object
|
// Passes in a GangMember object
|
||||||
GangMemberUpgrade.prototype.apply = function(member) {
|
GangMemberUpgrade.prototype.apply = function(member) {
|
||||||
if (this.mults.str != null) { member.str_mult *= this.mults.str; }
|
if (this.mults.str != null) { member.str_mult *= this.mults.str; }
|
||||||
if (this.mults.def != null) { member.def_mult *= this.mults.def; }
|
if (this.mults.def != null) { member.def_mult *= this.mults.def; }
|
||||||
@@ -971,7 +990,7 @@ gangMemberUpgradesMetadata.forEach((e) => {
|
|||||||
Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
||||||
const boxId = "gang-member-upgrade-popup-box";
|
const boxId = "gang-member-upgrade-popup-box";
|
||||||
if (UIElems.gangMemberUpgradeBoxOpened) {
|
if (UIElems.gangMemberUpgradeBoxOpened) {
|
||||||
//Already opened, refreshing
|
// Already opened, refreshing
|
||||||
if (UIElems.gangMemberUpgradeBoxElements == null || UIElems.gangMemberUpgradeBox == null || UIElems.gangMemberUpgradeBoxContent == null) {
|
if (UIElems.gangMemberUpgradeBoxElements == null || UIElems.gangMemberUpgradeBox == null || UIElems.gangMemberUpgradeBoxContent == null) {
|
||||||
console.error("Refreshing Gang member upgrade box throws error because required elements are null");
|
console.error("Refreshing Gang member upgrade box throws error because required elements are null");
|
||||||
return;
|
return;
|
||||||
@@ -991,7 +1010,7 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//New popup
|
// New popup
|
||||||
UIElems.gangMemberUpgradeBoxFilter = createElement("input", {
|
UIElems.gangMemberUpgradeBoxFilter = createElement("input", {
|
||||||
type:"text", placeholder:"Filter gang members",
|
type:"text", placeholder:"Filter gang members",
|
||||||
value:initialFilter,
|
value:initialFilter,
|
||||||
@@ -1023,7 +1042,7 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create upgrade panels for each individual Gang Member
|
// Create upgrade panels for each individual Gang Member
|
||||||
GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
|
GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
|
||||||
var container = createElement("div", {
|
var container = createElement("div", {
|
||||||
border:"1px solid white",
|
border:"1px solid white",
|
||||||
@@ -1045,7 +1064,7 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
|
|||||||
"Cha: " + this.cha + " (x" + formatNumber(this.cha_mult * this.cha_asc_mult, 2) + ")\n",
|
"Cha: " + this.cha + " (x" + formatNumber(this.cha_mult * this.cha_asc_mult, 2) + ")\n",
|
||||||
});
|
});
|
||||||
|
|
||||||
//Already purchased upgrades
|
// Already purchased upgrades
|
||||||
const ownedUpgradesElements = [];
|
const ownedUpgradesElements = [];
|
||||||
function pushOwnedUpgrade(upgName) {
|
function pushOwnedUpgrade(upgName) {
|
||||||
const upg = GangMemberUpgrades[upgName];
|
const upg = GangMemberUpgrades[upgName];
|
||||||
@@ -1071,7 +1090,7 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
|
|||||||
container.appendChild(ownedUpgrades);
|
container.appendChild(ownedUpgrades);
|
||||||
container.appendChild(createElement("br", {}));
|
container.appendChild(createElement("br", {}));
|
||||||
|
|
||||||
//Upgrade buttons. Only show upgrades that can be afforded
|
// Upgrade buttons. Only show upgrades that can be afforded
|
||||||
const weaponUpgrades = [];
|
const weaponUpgrades = [];
|
||||||
const armorUpgrades = [];
|
const armorUpgrades = [];
|
||||||
const vehicleUpgrades = [];
|
const vehicleUpgrades = [];
|
||||||
@@ -1203,18 +1222,18 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
if (!UIElems.gangContentCreated || UIElems.gangContainer == null) {
|
if (!UIElems.gangContentCreated || UIElems.gangContainer == null) {
|
||||||
UIElems.gangContentCreated = true;
|
UIElems.gangContentCreated = true;
|
||||||
|
|
||||||
//Create gang container
|
// Create gang container
|
||||||
UIElems.gangContainer = createElement("div", {
|
UIElems.gangContainer = createElement("div", {
|
||||||
id:"gang-container", class:"generic-menupage-container",
|
id:"gang-container", class:"generic-menupage-container",
|
||||||
});
|
});
|
||||||
|
|
||||||
//Get variables
|
// Get variables
|
||||||
var facName = this.facName,
|
var facName = this.facName,
|
||||||
members = this.members,
|
members = this.members,
|
||||||
wanted = this.wanted,
|
wanted = this.wanted,
|
||||||
respect = this.respect;
|
respect = this.respect;
|
||||||
|
|
||||||
//Back button
|
// Back button
|
||||||
UIElems.gangContainer.appendChild(createElement("a", {
|
UIElems.gangContainer.appendChild(createElement("a", {
|
||||||
class:"a-link-button", display:"inline-block", innerText:"Back",
|
class:"a-link-button", display:"inline-block", innerText:"Back",
|
||||||
clickListener:()=>{
|
clickListener:()=>{
|
||||||
@@ -1224,7 +1243,7 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Buttons to switch between panels
|
// Buttons to switch between panels
|
||||||
UIElems.managementButton = createElement("a", {
|
UIElems.managementButton = createElement("a", {
|
||||||
id:"gang-management-subpage-button", class:"a-link-button-inactive",
|
id:"gang-management-subpage-button", class:"a-link-button-inactive",
|
||||||
display:"inline-block", innerHTML: "Gang Management (Alt+1)",
|
display:"inline-block", innerHTML: "Gang Management (Alt+1)",
|
||||||
@@ -1256,7 +1275,7 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
UIElems.gangContainer.appendChild(UIElems.managementButton);
|
UIElems.gangContainer.appendChild(UIElems.managementButton);
|
||||||
UIElems.gangContainer.appendChild(UIElems.territoryButton);
|
UIElems.gangContainer.appendChild(UIElems.territoryButton);
|
||||||
|
|
||||||
//Subpage for managing gang members
|
// Subpage for managing gang members
|
||||||
UIElems.gangManagementSubpage = createElement("div", {
|
UIElems.gangManagementSubpage = createElement("div", {
|
||||||
display:"block", id:"gang-management-subpage",
|
display:"block", id:"gang-management-subpage",
|
||||||
});
|
});
|
||||||
@@ -1340,6 +1359,7 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
innerText: "Cancel",
|
innerText: "Cancel",
|
||||||
});
|
});
|
||||||
createPopup(popupId, [txt, br, nameInput, yesBtn, noBtn]);
|
createPopup(popupId, [txt, br, nameInput, yesBtn, noBtn]);
|
||||||
|
nameInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitMemberButton);
|
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitMemberButton);
|
||||||
@@ -1352,7 +1372,7 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
});
|
});
|
||||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitRequirementText);
|
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitRequirementText);
|
||||||
|
|
||||||
//Gang Member List management buttons (Expand/Collapse All, select a single member)
|
// Gang Member List management buttons (Expand/Collapse All, select a single member)
|
||||||
UIElems.gangManagementSubpage.appendChild(createElement("br", {}));
|
UIElems.gangManagementSubpage.appendChild(createElement("br", {}));
|
||||||
UIElems.gangExpandAllButton = createElement("a", {
|
UIElems.gangExpandAllButton = createElement("a", {
|
||||||
class:"a-link-button", display:"inline-block",
|
class:"a-link-button", display:"inline-block",
|
||||||
@@ -1400,17 +1420,17 @@ Gang.prototype.displayGangContent = function(player) {
|
|||||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberFilter);
|
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberFilter);
|
||||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangManageEquipmentButton);
|
UIElems.gangManagementSubpage.appendChild(UIElems.gangManageEquipmentButton);
|
||||||
|
|
||||||
//Gang Member list
|
// Gang Member list
|
||||||
UIElems.gangMemberList = createElement("ul", {id:"gang-member-list"});
|
UIElems.gangMemberList = createElement("ul", {id:"gang-member-list"});
|
||||||
this.displayGangMemberList();
|
this.displayGangMemberList();
|
||||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberList);
|
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberList);
|
||||||
|
|
||||||
//Subpage for seeing gang territory information
|
// Subpage for seeing gang territory information
|
||||||
UIElems.gangTerritorySubpage = createElement("div", {
|
UIElems.gangTerritorySubpage = createElement("div", {
|
||||||
id:"gang-territory-subpage", display:"none"
|
id:"gang-territory-subpage", display:"none"
|
||||||
});
|
});
|
||||||
|
|
||||||
//Info text for territory page
|
// Info text for territory page
|
||||||
UIElems.gangTerritoryDescText = createElement("p", {
|
UIElems.gangTerritoryDescText = createElement("p", {
|
||||||
width:"70%",
|
width:"70%",
|
||||||
innerHTML:
|
innerHTML:
|
||||||
@@ -1575,7 +1595,7 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Update information for overall gang
|
// Update information for overall gang
|
||||||
if (UIElems.gangInfo instanceof Element) {
|
if (UIElems.gangInfo instanceof Element) {
|
||||||
var faction = Factions[this.facName];
|
var faction = Factions[this.facName];
|
||||||
var rep;
|
var rep;
|
||||||
@@ -1585,7 +1605,7 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
rep = faction.playerReputation;
|
rep = faction.playerReputation;
|
||||||
}
|
}
|
||||||
removeChildrenFromElement(UIElems.gangInfo);
|
removeChildrenFromElement(UIElems.gangInfo);
|
||||||
UIElems.gangInfo.appendChild(createElement("p", { // Respect
|
UIElems.gangInfo.appendChild(createElement("p", { // Respect
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
innerText: "Respect: " + formatNumber(this.respect, 6) +
|
innerText: "Respect: " + formatNumber(this.respect, 6) +
|
||||||
" (" + formatNumber(5*this.respectGainRate, 6) + " / sec)",
|
" (" + formatNumber(5*this.respectGainRate, 6) + " / sec)",
|
||||||
@@ -1596,7 +1616,7 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
}));
|
}));
|
||||||
UIElems.gangInfo.appendChild(createElement("br"));
|
UIElems.gangInfo.appendChild(createElement("br"));
|
||||||
|
|
||||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
|
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
innerText: "Wanted Level: " + formatNumber(this.wanted, 6) +
|
innerText: "Wanted Level: " + formatNumber(this.wanted, 6) +
|
||||||
" (" + formatNumber(5*this.wantedGainRate, 6) + " / sec)",
|
" (" + formatNumber(5*this.wantedGainRate, 6) + " / sec)",
|
||||||
@@ -1608,20 +1628,20 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
|
|
||||||
var wantedPenalty = this.getWantedPenalty();
|
var wantedPenalty = this.getWantedPenalty();
|
||||||
wantedPenalty = (1 - wantedPenalty) * 100;
|
wantedPenalty = (1 - wantedPenalty) * 100;
|
||||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
|
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`,
|
innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`,
|
||||||
tooltip: "Penalty for respect and money gain rates due to Wanted Level"
|
tooltip: "Penalty for respect and money gain rates due to Wanted Level"
|
||||||
}));
|
}));
|
||||||
UIElems.gangInfo.appendChild(createElement("br"));
|
UIElems.gangInfo.appendChild(createElement("br"));
|
||||||
|
|
||||||
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
|
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`,
|
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`,
|
||||||
}));
|
}));
|
||||||
UIElems.gangInfo.appendChild(createElement("br"));
|
UIElems.gangInfo.appendChild(createElement("br"));
|
||||||
|
|
||||||
//Fix some rounding issues graphically
|
// Fix some rounding issues graphically
|
||||||
var territoryMult = AllGangs[this.facName].territory * 100;
|
var territoryMult = AllGangs[this.facName].territory * 100;
|
||||||
let displayNumber;
|
let displayNumber;
|
||||||
if (territoryMult <= 0) {
|
if (territoryMult <= 0) {
|
||||||
@@ -1656,7 +1676,7 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
console.error("gang-info DOM element DNE");
|
console.error("gang-info DOM element DNE");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Toggle the 'Recruit member button' if valid
|
// Toggle the 'Recruit member button' if valid
|
||||||
const numMembers = this.members.length;
|
const numMembers = this.members.length;
|
||||||
const respectCost = this.getRespectNeededToRecruitMember();
|
const respectCost = this.getRespectNeededToRecruitMember();
|
||||||
|
|
||||||
@@ -1674,14 +1694,14 @@ Gang.prototype.updateGangContent = function() {
|
|||||||
UIElems.gangRecruitRequirementText.innerHTML = `${formatNumber(respectCost, 2)} respect needed to recruit next member`;
|
UIElems.gangRecruitRequirementText.innerHTML = `${formatNumber(respectCost, 2)} respect needed to recruit next member`;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update information for each gang member
|
// Update information for each gang member
|
||||||
for (let i = 0; i < this.members.length; ++i) {
|
for (let i = 0; i < this.members.length; ++i) {
|
||||||
this.updateGangMemberDisplayElement(this.members[i]);
|
this.updateGangMemberDisplayElement(this.members[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Takes in a GangMember object
|
// Takes in a GangMember object
|
||||||
Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
||||||
if (!UIElems.gangContentCreated) { return; }
|
if (!UIElems.gangContentCreated) { return; }
|
||||||
const name = memberObj.name;
|
const name = memberObj.name;
|
||||||
@@ -1822,7 +1842,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
|||||||
taskDiv.appendChild(taskSelector);
|
taskDiv.appendChild(taskSelector);
|
||||||
taskDiv.appendChild(gainInfo);
|
taskDiv.appendChild(gainInfo);
|
||||||
|
|
||||||
//Panel for Description of task
|
// Panel for Description of task
|
||||||
var taskDescDiv = createElement("div", {
|
var taskDescDiv = createElement("div", {
|
||||||
class:"gang-member-info-div",
|
class:"gang-member-info-div",
|
||||||
id: name + "gang-member-task-desc",
|
id: name + "gang-member-task-desc",
|
||||||
|
|||||||
1
src/Hacking/README.md
Normal file
1
src/Hacking/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Implementation of underlying Hacking mechanics
|
||||||
53
src/Hacking/netscriptCanHack.ts
Normal file
53
src/Hacking/netscriptCanHack.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Functions used to determine whether the target can be hacked (or grown/weakened).
|
||||||
|
* Meant to be used for Netscript implementation
|
||||||
|
*
|
||||||
|
* The returned status object's message should be used for logging in Netscript
|
||||||
|
*/
|
||||||
|
import { IReturnStatus } from "../types";
|
||||||
|
|
||||||
|
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||||
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
|
import { Server } from "../Server/Server";
|
||||||
|
|
||||||
|
function baseCheck(server: Server | HacknetServer, fnName: string): IReturnStatus {
|
||||||
|
if (server instanceof HacknetServer) {
|
||||||
|
return {
|
||||||
|
res: false,
|
||||||
|
msg: `Cannot ${fnName} ${server.hostname} server because it is a Hacknet Node`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.hasAdminRights === false) {
|
||||||
|
return {
|
||||||
|
res: false,
|
||||||
|
msg: `Cannot ${fnName} ${server.hostname} server because you do not have root access`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { res: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IReturnStatus {
|
||||||
|
const initialCheck = baseCheck(server, "hack");
|
||||||
|
if (!initialCheck.res) { return initialCheck; }
|
||||||
|
|
||||||
|
let s = <Server>server;
|
||||||
|
|
||||||
|
if (s.requiredHackingSkill > p.hacking_skill) {
|
||||||
|
return {
|
||||||
|
res: false,
|
||||||
|
msg: `Cannot hack ${server.hostname} server because your hacking skill is not high enough`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { res: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function netscriptCanGrow(server: Server | HacknetServer): IReturnStatus {
|
||||||
|
return baseCheck(server, "grow");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function netscriptCanWeaken(server: Server | HacknetServer): IReturnStatus {
|
||||||
|
return baseCheck(server, "weaken");
|
||||||
|
}
|
||||||
419
src/Hacknet/HacknetHelpers.jsx
Normal file
419
src/Hacknet/HacknetHelpers.jsx
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
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 { generateRandomContract } from "../CodingContractGenerator";
|
||||||
|
import { iTutorialSteps, iTutorialNextStep,
|
||||||
|
ITutorial} from "../InteractiveTutorial";
|
||||||
|
import { Player } from "../Player";
|
||||||
|
import { AddToAllServers,
|
||||||
|
AllServers } 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.removeEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||||
|
|
||||||
|
// 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 */
|
||||||
|
|
||||||
|
const numOwned = Player.hacknetNodes.length;
|
||||||
|
if (hasHacknetServers()) {
|
||||||
|
const cost = getCostOfNextHacknetServer();
|
||||||
|
if (isNaN(cost)) {
|
||||||
|
throw new Error(`Calculated cost of purchasing HacknetServer is NaN`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Player.canAfford(cost)) { return -1; }
|
||||||
|
Player.loseMoney(cost);
|
||||||
|
const server = Player.createHacknetServer();
|
||||||
|
Player.hashManager.updateCapacity(Player);
|
||||||
|
|
||||||
|
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 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(numCycles);
|
||||||
|
} else if (Player.hacknetNodes[0] instanceof HacknetNode) {
|
||||||
|
return processAllHacknetNodeEarnings(numCycles);
|
||||||
|
} 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) {
|
||||||
|
const hserver = AllServers[Player.hacknetNodes[i]]; // hacknetNodes array only contains the IP addresses
|
||||||
|
hashes += hserver.process(numCycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.hashManager.storeHashes(hashes);
|
||||||
|
|
||||||
|
return hashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
Player.recordMoneySource(upg.value, "hacknetnode");
|
||||||
|
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 isnt in Bladeburner
|
||||||
|
try {
|
||||||
|
Player.bladeburner.changeRank(upg.value);
|
||||||
|
} catch(e) {
|
||||||
|
Player.hashManager.refundUpgrade(upgName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Exchange for Bladeburner SP": {
|
||||||
|
// This will throw if player isn't in Bladeburner
|
||||||
|
try {
|
||||||
|
// As long as we don't change `Bladeburner.totalSkillPoints`, this
|
||||||
|
// shouldn't affect anything else
|
||||||
|
Player.bladeburner.skillPoints += upg.value;
|
||||||
|
} catch(e) {
|
||||||
|
Player.hashManager.refundUpgrade(upgName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Generate Coding Contract": {
|
||||||
|
generateRandomContract();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`)
|
||||||
|
Player.hashManager.refundUpgrade(upgName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
350
src/Hacknet/HacknetServer.ts
Normal file
350
src/Hacknet/HacknetServer.ts
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
/**
|
||||||
|
* Hacknet Servers - Reworked Hacknet Node mechanic for BitNode-9
|
||||||
|
*/
|
||||||
|
import { CONSTANTS } from "../Constants";
|
||||||
|
|
||||||
|
import { IHacknetNode } from "./IHacknetNode";
|
||||||
|
|
||||||
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
|
import { BaseServer } from "../Server/BaseServer";
|
||||||
|
import { RunningScript } from "../Script/RunningScript";
|
||||||
|
|
||||||
|
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 = 50e3;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
this.updateHashRate(p);
|
||||||
|
|
||||||
|
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 = 32 * Math.pow(2, this.cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHashRate(p: IPlayer): void {
|
||||||
|
const baseGain = HacknetServerHashesPerLevel * this.level;
|
||||||
|
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
|
||||||
|
const coreMultiplier = 1 + (this.cores - 1) / 5;
|
||||||
|
const ramRatio = (1 - this.ramUsed / this.maxRam);
|
||||||
|
|
||||||
|
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
|
||||||
|
|
||||||
|
this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney;
|
||||||
|
|
||||||
|
if (isNaN(this.hashRate)) {
|
||||||
|
this.hashRate = 0;
|
||||||
|
console.error(`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;
|
||||||
177
src/Hacknet/HashManager.ts
Normal file
177
src/Hacknet/HashManager.ts
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* 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 { AllServers } from "../Server/AllServers";
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
prestige(p: IPlayer): void {
|
||||||
|
for (const name in HashUpgrades) {
|
||||||
|
this.upgrades[name] = 0;
|
||||||
|
}
|
||||||
|
this.hashes = 0;
|
||||||
|
if (p != null) {
|
||||||
|
this.updateCapacity(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeHashes(numHashes: number): void {
|
||||||
|
this.hashes += numHashes;
|
||||||
|
this.hashes = Math.min(this.hashes, this.capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCapacity(p: IPlayer): void {
|
||||||
|
if (p.hacknetNodes.length <= 0) {
|
||||||
|
this.capacity = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the Player's `hacknetNodes` property actually holds Hacknet Servers
|
||||||
|
const ip: string = <string>p.hacknetNodes[0];
|
||||||
|
if (typeof ip !== "string") {
|
||||||
|
this.capacity = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hserver = <HacknetServer>AllServers[ip];
|
||||||
|
if (!(hserver instanceof HacknetServer)) {
|
||||||
|
this.capacity = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let total: number = 0;
|
||||||
|
for (let i = 0; i < p.hacknetNodes.length; ++i) {
|
||||||
|
const h = <HacknetServer>AllServers[<string>p.hacknetNodes[i]];
|
||||||
|
total += h.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];
|
||||||
|
if (upg == null) {
|
||||||
|
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cost = this.getUpgradeCost(upgName);
|
||||||
|
|
||||||
|
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;
|
||||||
61
src/Hacknet/HashUpgrade.ts
Normal file
61
src/Hacknet/HashUpgrade.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Object representing an upgrade that can be purchased with hashes
|
||||||
|
*/
|
||||||
|
export interface IConstructorParams {
|
||||||
|
cost?: number;
|
||||||
|
costPerLevel: number;
|
||||||
|
desc: string;
|
||||||
|
hasTargetServer?: boolean;
|
||||||
|
name: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HashUpgrade {
|
||||||
|
/**
|
||||||
|
* If the upgrade has a flat cost (never increases), it goes here
|
||||||
|
* Otherwise, this property should be undefined
|
||||||
|
*
|
||||||
|
* This property overrides the 'costPerLevel' property
|
||||||
|
*/
|
||||||
|
cost?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
if (p.cost != null) { this.cost = p.cost; }
|
||||||
|
|
||||||
|
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 {
|
||||||
|
if (typeof this.cost === "number") { return this.cost; }
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
71
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
71
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Metadata used to construct all Hash Upgrades
|
||||||
|
import { IConstructorParams } from "../HashUpgrade";
|
||||||
|
|
||||||
|
export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||||
|
{
|
||||||
|
cost: 4,
|
||||||
|
costPerLevel: 4,
|
||||||
|
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: 50,
|
||||||
|
desc: "Use hashes to decrease the minimum security of a single server by 2%. " +
|
||||||
|
"Note that a server's minimum security cannot go below 1.",
|
||||||
|
hasTargetServer: true,
|
||||||
|
name: "Reduce Minimum Security",
|
||||||
|
value: 0.98,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
costPerLevel: 50,
|
||||||
|
desc: "Use hashes to increase the maximum amount of money on a single server by 2%",
|
||||||
|
hasTargetServer: true,
|
||||||
|
name: "Increase Maximum Money",
|
||||||
|
value: 1.02,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
costPerLevel: 50,
|
||||||
|
desc: "Use hashes to improve the experience earned when studying at a university by 20%. " +
|
||||||
|
"This effect persists until you install Augmentations",
|
||||||
|
name: "Improve Studying",
|
||||||
|
value: 20, // Improves studying by value%
|
||||||
|
},
|
||||||
|
{
|
||||||
|
costPerLevel: 50,
|
||||||
|
desc: "Use hashes to improve the experience earned when training at the gym by 20%. This effect " +
|
||||||
|
"persists until you install Augmentations",
|
||||||
|
name: "Improve Gym Training",
|
||||||
|
value: 20, // Improves training by value%
|
||||||
|
},
|
||||||
|
{
|
||||||
|
costPerLevel: 200,
|
||||||
|
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: 250,
|
||||||
|
desc: "Exchanges hashes for 10 Bladeburner Skill Points",
|
||||||
|
name: "Exchange for Bladeburner SP",
|
||||||
|
value: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
costPerLevel: 200,
|
||||||
|
desc: "Generate a random Coding Contract somewhere on the network",
|
||||||
|
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 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.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
130
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
130
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* 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 { PopupCloseButton } from "../../ui/React/PopupCloseButton";
|
||||||
|
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.state = {
|
||||||
|
totalHashes: Player.hashManager.hashes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.interval = setInterval(() => this.tick(), 1e3);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
<PopupCloseButton popup={this.props.popupId} text={"Close"} />
|
||||||
|
<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>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
154
src/Hacknet/ui/Root.jsx
Normal file
154
src/Hacknet/ui/Root.jsx
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* 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 { AllServers } from "../../Server/AllServers";
|
||||||
|
|
||||||
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
|
import { PopupCloseButton } from "../../ui/React/PopupCloseButton";
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
const hserver = AllServers[Player.hacknetNodes[i]];
|
||||||
|
if (hserver) {
|
||||||
|
total += hserver.hashRate;
|
||||||
|
} else {
|
||||||
|
console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`)
|
||||||
|
}
|
||||||
|
} 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
const hserver = AllServers[node];
|
||||||
|
if (hserver == null) {
|
||||||
|
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<HacknetServer
|
||||||
|
key={hserver.hostname}
|
||||||
|
node={hserver}
|
||||||
|
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
|
|
||||||
};
|
|
||||||
15
src/IEngine.ts
Normal file
15
src/IEngine.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* TypeScript interface for the game engine (engine.js), which can't be converted
|
||||||
|
* to TypeScript at the moment
|
||||||
|
*/
|
||||||
|
export interface IEngine {
|
||||||
|
hideAllContent: () => void;
|
||||||
|
loadBladeburnerContent: () => void;
|
||||||
|
loadFactionContent: () => void;
|
||||||
|
loadFactionsContent: () => void;
|
||||||
|
loadGangContent: () => void;
|
||||||
|
loadInfiltrationContent: () => void;
|
||||||
|
loadMissionContent: () => void;
|
||||||
|
loadResleevingContent: () => void;
|
||||||
|
loadStockMarketContent: () => void;
|
||||||
|
}
|
||||||
7
src/Infiltration.d.ts
vendored
Normal file
7
src/Infiltration.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { LocationName } from "./Locations/data/LocationNames";
|
||||||
|
|
||||||
|
export declare function beginInfiltration(companyName: LocationName,
|
||||||
|
startLevel: number,
|
||||||
|
rewardVal: number,
|
||||||
|
maxClearance: number,
|
||||||
|
diff: number): void;
|
||||||
@@ -1,30 +1,12 @@
|
|||||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||||
import { CONSTANTS } from "./Constants";
|
import { CONSTANTS } from "./Constants";
|
||||||
import { Engine } from "./engine";
|
import { Engine } from "./engine";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { infiltrationBoxCreate } from "../utils/InfiltrationBox";
|
import { infiltrationBoxCreate } from "../utils/InfiltrationBox";
|
||||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
|
|
||||||
/* Infiltration.js
|
|
||||||
*
|
|
||||||
* Kill
|
|
||||||
* Knockout (nonlethal)
|
|
||||||
* Stealth Knockout (nonlethal)
|
|
||||||
* Assassinate
|
|
||||||
*
|
|
||||||
* Hack Security
|
|
||||||
* Destroy Security
|
|
||||||
* Sneak past Security
|
|
||||||
*
|
|
||||||
* Pick the locked door
|
|
||||||
*
|
|
||||||
* Bribe security
|
|
||||||
*
|
|
||||||
* Escape
|
|
||||||
*/
|
|
||||||
|
|
||||||
let InfiltrationScenarios = {
|
let InfiltrationScenarios = {
|
||||||
Guards: "You see an armed security guard patrolling the area.",
|
Guards: "You see an armed security guard patrolling the area.",
|
||||||
@@ -35,21 +17,26 @@ let InfiltrationScenarios = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff) {
|
function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff) {
|
||||||
this.companyName = companyName;
|
this.companyName = companyName;
|
||||||
this.clearanceLevel = 0;
|
this.clearanceLevel = 0;
|
||||||
this.maxClearanceLevel = maxClearance;
|
this.maxClearanceLevel = maxClearance;
|
||||||
this.securityLevel = startLevel;
|
this.securityLevel = startLevel;
|
||||||
this.difficulty = diff; //Affects how much security level increases. Represents a percentage
|
this.difficulty = diff; // Affects how much security level increases. Represents a percentage
|
||||||
this.baseValue = val; //Base value of company secrets
|
this.baseValue = val; // Base value of company secrets
|
||||||
this.secretsStolen = []; //Numbers representing value of stolen secrets
|
this.secretsStolen = []; // Numbers representing value of stolen secrets
|
||||||
|
|
||||||
this.hackingExpGained = 0;
|
this.hackingExpGained = 0;
|
||||||
this.strExpGained = 0;
|
this.strExpGained = 0;
|
||||||
this.defExpGained = 0;
|
this.defExpGained = 0;
|
||||||
this.dexExpGained = 0;
|
this.dexExpGained = 0;
|
||||||
this.agiExpGained = 0;
|
this.agiExpGained = 0;
|
||||||
this.chaExpGained = 0;
|
this.chaExpGained = 0;
|
||||||
this.intExpGained = 0;
|
this.intExpGained = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||||
|
if (!this.clearanceLevel || isNaN(this.clearanceLevel) || !this.maxClearanceLevel ||isNaN(this.maxClearanceLevel)) return 1;
|
||||||
|
return 2.5 * this.clearanceLevel / this.maxClearanceLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||||
@@ -57,36 +44,71 @@ InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
|||||||
this.hackingExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.strExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.defExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.dexExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.agiExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.chaExpGained += amt;
|
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) {
|
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
||||||
if (isNaN(amt)) {return;}
|
if (isNaN(amt)) {return;}
|
||||||
this.intExpGained += amt;
|
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) {
|
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
||||||
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
|
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
|
||||||
clearInfiltrationStatusText();
|
clearInfiltrationStatusText();
|
||||||
@@ -137,7 +159,7 @@ function nextInfiltrationLevel(inst) {
|
|||||||
bribeButton.style.display = "none";
|
bribeButton.style.display = "none";
|
||||||
escapeButton.style.display = "none";
|
escapeButton.style.display = "none";
|
||||||
|
|
||||||
var rand = getRandomInt(0, 5); //This needs to change if more scenarios are added
|
var rand = getRandomInt(0, 5); // This needs to change if more scenarios are added
|
||||||
var scenario = null;
|
var scenario = null;
|
||||||
switch (rand) {
|
switch (rand) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -420,7 +442,7 @@ function nextInfiltrationLevel(inst) {
|
|||||||
|
|
||||||
|
|
||||||
function endInfiltrationLevel(inst) {
|
function endInfiltrationLevel(inst) {
|
||||||
//Check if you gained any secrets
|
// Check if you gained any secrets
|
||||||
if (inst.clearanceLevel % 5 == 0) {
|
if (inst.clearanceLevel % 5 == 0) {
|
||||||
var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2;
|
var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2;
|
||||||
var secretValue = baseSecretValue * Player.faction_rep_mult *
|
var secretValue = baseSecretValue * Player.faction_rep_mult *
|
||||||
@@ -434,12 +456,12 @@ function endInfiltrationLevel(inst) {
|
|||||||
"could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
|
"could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Increase security level based on difficulty
|
// Increase security level based on difficulty
|
||||||
inst.securityLevel *= (1 + (inst.difficulty / 100));
|
inst.securityLevel *= (1 + (inst.difficulty / 100));
|
||||||
writeInfiltrationStatusText("You move on to the facility's next clearance level. This " +
|
writeInfiltrationStatusText("You move on to the facility's next clearance level. This " +
|
||||||
"clearance level has " + inst.difficulty + "% higher security");
|
"clearance level has " + inst.difficulty + "% higher security");
|
||||||
|
|
||||||
//If this is max level, force endInfiltration
|
// If this is max level, force endInfiltration
|
||||||
if (inst.clearanceLevel >= inst.maxClearanceLevel) {
|
if (inst.clearanceLevel >= inst.maxClearanceLevel) {
|
||||||
endInfiltration(inst, true);
|
endInfiltration(inst, true);
|
||||||
} else {
|
} else {
|
||||||
@@ -477,12 +499,12 @@ function updateInfiltrationLevelText(inst) {
|
|||||||
"Total value of stolen secrets<br>" +
|
"Total value of stolen secrets<br>" +
|
||||||
"Reputation: <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
|
"Reputation: <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
|
||||||
"Money: <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
|
"Money: <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
|
||||||
"Hack exp gained: " + formatNumber(inst.hackingExpGained * expMultiplier, 3) + "<br>" +
|
"Hack exp gained: " + formatNumber(inst.calcGainedHackingExp(), 3) + "<br>" +
|
||||||
"Str exp gained: " + formatNumber(inst.strExpGained * expMultiplier, 3) + "<br>" +
|
"Str exp gained: " + formatNumber(inst.calcGainedStrengthExp(), 3) + "<br>" +
|
||||||
"Def exp gained: " + formatNumber(inst.defExpGained * expMultiplier, 3) + "<br>" +
|
"Def exp gained: " + formatNumber(inst.calcGainedDefenseExp(), 3) + "<br>" +
|
||||||
"Dex exp gained: " + formatNumber(inst.dexExpGained * expMultiplier, 3) + "<br>" +
|
"Dex exp gained: " + formatNumber(inst.calcGainedDexterityExp(), 3) + "<br>" +
|
||||||
"Agi exp gained: " + formatNumber(inst.agiExpGained * expMultiplier, 3) + "<br>" +
|
"Agi exp gained: " + formatNumber(inst.calcGainedAgilityExp(), 3) + "<br>" +
|
||||||
"Cha exp gained: " + formatNumber(inst.chaExpGained * expMultiplier, 3);
|
"Cha exp gained: " + formatNumber(inst.calcGainedCharismaExp(), 3);
|
||||||
/* eslint-enable no-irregular-whitespace */
|
/* eslint-enable no-irregular-whitespace */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,8 +629,8 @@ function updateInfiltrationButtons(inst, scenario) {
|
|||||||
|
|
||||||
let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
|
let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
|
||||||
|
|
||||||
//Kill
|
// Kill
|
||||||
//Success: 5%, Failure 10%, -Karma
|
// Success: 5%, Failure 10%, -Karma
|
||||||
function attemptInfiltrationKill(inst) {
|
function attemptInfiltrationKill(inst) {
|
||||||
var chance = getInfiltrationKillChance(inst);
|
var chance = getInfiltrationKillChance(inst);
|
||||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||||
@@ -633,8 +655,8 @@ function getInfiltrationKillChance(inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Knockout
|
// Knockout
|
||||||
//Success: 3%, Failure: 10%
|
// Success: 3%, Failure: 10%
|
||||||
function attemptInfiltrationKnockout(inst) {
|
function attemptInfiltrationKnockout(inst) {
|
||||||
var chance = getInfiltrationKnockoutChance(inst);
|
var chance = getInfiltrationKnockoutChance(inst);
|
||||||
inst.gainStrengthExp(inst.securityLevel / 70) * Player.strength_exp_mult;
|
inst.gainStrengthExp(inst.securityLevel / 70) * Player.strength_exp_mult;
|
||||||
@@ -658,8 +680,8 @@ function getInfiltrationKnockoutChance(inst) {
|
|||||||
Player.agility) / (1.7 * lvl));
|
Player.agility) / (1.7 * lvl));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Stealth knockout
|
// Stealth knockout
|
||||||
//Success: 0%, Failure: 10%
|
// Success: 0%, Failure: 10%
|
||||||
function attemptInfiltrationStealthKnockout(inst) {
|
function attemptInfiltrationStealthKnockout(inst) {
|
||||||
var chance = getInfiltrationStealthKnockoutChance(inst);
|
var chance = getInfiltrationStealthKnockoutChance(inst);
|
||||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||||
@@ -682,8 +704,8 @@ function getInfiltrationStealthKnockoutChance(inst) {
|
|||||||
intWgt * Player.intelligence) / (3 * lvl));
|
intWgt * Player.intelligence) / (3 * lvl));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Assassination
|
// Assassination
|
||||||
//Success: 0%, Failure: 5%, -Karma
|
// Success: 0%, Failure: 5%, -Karma
|
||||||
function attemptInfiltrationAssassinate(inst) {
|
function attemptInfiltrationAssassinate(inst) {
|
||||||
var chance = getInfiltrationAssassinateChance(inst);
|
var chance = getInfiltrationAssassinateChance(inst);
|
||||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||||
@@ -706,8 +728,8 @@ function getInfiltrationAssassinateChance(inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Destroy security
|
// Destroy security
|
||||||
//Success: 5%, Failure: 10%
|
// Success: 5%, Failure: 10%
|
||||||
function attemptInfiltrationDestroySecurity(inst) {
|
function attemptInfiltrationDestroySecurity(inst) {
|
||||||
var chance = getInfiltrationDestroySecurityChance(inst);
|
var chance = getInfiltrationDestroySecurityChance(inst);
|
||||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||||
@@ -733,8 +755,8 @@ function getInfiltrationDestroySecurityChance(inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Hack security
|
// Hack security
|
||||||
//Success: 3%, Failure: 5%
|
// Success: 3%, Failure: 5%
|
||||||
function attemptInfiltrationHack(inst) {
|
function attemptInfiltrationHack(inst) {
|
||||||
var chance = getInfiltrationHackChance(inst);
|
var chance = getInfiltrationHackChance(inst);
|
||||||
inst.gainHackingExp(inst.securityLevel / 30) * Player.hacking_exp_mult;
|
inst.gainHackingExp(inst.securityLevel / 30) * Player.hacking_exp_mult;
|
||||||
@@ -756,8 +778,8 @@ function getInfiltrationHackChance(inst) {
|
|||||||
(intWgt * Player.intelligence)) / lvl);
|
(intWgt * Player.intelligence)) / lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sneak past security
|
// Sneak past security
|
||||||
//Success: 0%, Failure: 8%
|
// Success: 0%, Failure: 8%
|
||||||
function attemptInfiltrationSneak(inst) {
|
function attemptInfiltrationSneak(inst) {
|
||||||
var chance = getInfiltrationSneakChance(inst);
|
var chance = getInfiltrationSneakChance(inst);
|
||||||
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
||||||
@@ -777,8 +799,8 @@ function getInfiltrationSneakChance(inst) {
|
|||||||
intWgt * Player.intelligence) / (2 * lvl));
|
intWgt * Player.intelligence) / (2 * lvl));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Pick locked door
|
// Pick locked door
|
||||||
//Success: 1%, Failure: 3%
|
// Success: 1%, Failure: 3%
|
||||||
function attemptInfiltrationPickLockedDoor(inst) {
|
function attemptInfiltrationPickLockedDoor(inst) {
|
||||||
var chance = getInfiltrationPickLockedDoorChance(inst);
|
var chance = getInfiltrationPickLockedDoorChance(inst);
|
||||||
inst.gainDexterityExp(inst.securityLevel / 25) * Player.dexterity_exp_mult;
|
inst.gainDexterityExp(inst.securityLevel / 25) * Player.dexterity_exp_mult;
|
||||||
@@ -798,8 +820,8 @@ function getInfiltrationPickLockedDoorChance(inst) {
|
|||||||
intWgt * Player.intelligence) / lvl);
|
intWgt * Player.intelligence) / lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Bribe
|
// Bribe
|
||||||
//Success: 0%, Failure: 15%,
|
// Success: 0%, Failure: 15%,
|
||||||
function attemptInfiltrationBribe(inst) {
|
function attemptInfiltrationBribe(inst) {
|
||||||
var chance = getInfiltrationBribeChance(inst);
|
var chance = getInfiltrationBribeChance(inst);
|
||||||
inst.gainCharismaExp(inst.securityLevel / 8) * Player.charisma_exp_mult;
|
inst.gainCharismaExp(inst.securityLevel / 8) * Player.charisma_exp_mult;
|
||||||
@@ -817,8 +839,8 @@ function getInfiltrationBribeChance(inst) {
|
|||||||
(Player.charisma) / lvl);
|
(Player.charisma) / lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Escape
|
// Escape
|
||||||
//Failure: 5%
|
// Failure: 5%
|
||||||
function attemptInfiltrationEscape(inst) {
|
function attemptInfiltrationEscape(inst) {
|
||||||
var chance = getInfiltrationEscapeChance(inst);
|
var chance = getInfiltrationEscapeChance(inst);
|
||||||
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
||||||
|
|||||||
@@ -1,34 +1,37 @@
|
|||||||
import { Engine } from "./engine";
|
import { Engine } from "./engine";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { Settings } from "./Settings/Settings";
|
import { Settings } from "./Settings/Settings";
|
||||||
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
|
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
|
||||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
|
||||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
|
||||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
|
||||||
|
|
||||||
//Ordered array of keys to Interactive Tutorial Steps
|
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
|
||||||
|
|
||||||
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
|
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||||
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
|
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||||
|
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||||
|
|
||||||
|
|
||||||
|
// Ordered array of keys to Interactive Tutorial Steps
|
||||||
const orderedITutorialSteps = [
|
const orderedITutorialSteps = [
|
||||||
"Start",
|
"Start",
|
||||||
"GoToCharacterPage", //Click on 'Stats' page
|
"GoToCharacterPage", // Click on 'Stats' page
|
||||||
"CharacterPage", //Introduction to 'Stats' page
|
"CharacterPage", // Introduction to 'Stats' page
|
||||||
"CharacterGoToTerminalPage", //Go back to Terminal
|
"CharacterGoToTerminalPage", // Go back to Terminal
|
||||||
"TerminalIntro", //Introduction to Terminal
|
"TerminalIntro", // Introduction to Terminal
|
||||||
"TerminalHelp", //Using 'help' Terminal command
|
"TerminalHelp", // Using 'help' Terminal command
|
||||||
"TerminalLs", //Using 'ls' Terminal command
|
"TerminalLs", // Using 'ls' Terminal command
|
||||||
"TerminalScan", //Using 'scan' Terminal command
|
"TerminalScan", // Using 'scan' Terminal command
|
||||||
"TerminalScanAnalyze1", //Using 'scan-analyze' Terminal command
|
"TerminalScanAnalyze1", // Using 'scan-analyze' Terminal command
|
||||||
"TerminalScanAnalyze2", //Using 'scan-analyze 3' Terminal command
|
"TerminalScanAnalyze2", // Using 'scan-analyze 3' Terminal command
|
||||||
"TerminalConnect", //Connecting to foodnstuff
|
"TerminalConnect", // Connecting to foodnstuff
|
||||||
"TerminalAnalyze", //Analyzing foodnstuff
|
"TerminalAnalyze", // Analyzing foodnstuff
|
||||||
"TerminalNuke", //NUKE foodnstuff
|
"TerminalNuke", // NUKE foodnstuff
|
||||||
"TerminalManualHack", //Hack foodnstuff
|
"TerminalManualHack", // Hack foodnstuff
|
||||||
"TerminalHackingMechanics", //Explanation of hacking mechanics
|
"TerminalHackingMechanics", // Explanation of hacking mechanics
|
||||||
"TerminalCreateScript", //Create a script using 'nano'
|
"TerminalCreateScript", // Create a script using 'nano'
|
||||||
"TerminalTypeScript", //Script Editor page - Type script and then save & close
|
"TerminalTypeScript", // Script Editor page - Type script and then save & close
|
||||||
"TerminalFree", //Using 'Free' Terminal command
|
"TerminalFree", // Using 'Free' Terminal command
|
||||||
"TerminalRunScript", //Running script using 'run' Terminal command
|
"TerminalRunScript", // Running script using 'run' Terminal command
|
||||||
"TerminalGoToActiveScriptsPage",
|
"TerminalGoToActiveScriptsPage",
|
||||||
"ActiveScriptsPage",
|
"ActiveScriptsPage",
|
||||||
"ActiveScriptsToTerminal",
|
"ActiveScriptsToTerminal",
|
||||||
@@ -41,22 +44,22 @@ const orderedITutorialSteps = [
|
|||||||
"End"
|
"End"
|
||||||
]
|
]
|
||||||
|
|
||||||
//Create an 'enum' for the Steps
|
// Create an 'enum' for the Steps
|
||||||
const iTutorialSteps = {};
|
const iTutorialSteps = {};
|
||||||
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
|
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
|
||||||
iTutorialSteps[orderedITutorialSteps[i]] = i;
|
iTutorialSteps[orderedITutorialSteps[i]] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ITutorial = {
|
var ITutorial = {
|
||||||
currStep: 0, //iTutorialSteps.Start
|
currStep: 0, // iTutorialSteps.Start
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
|
|
||||||
//Keeps track of whether each step has been done
|
// Keeps track of whether each step has been done
|
||||||
stepIsDone: {},
|
stepIsDone: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
function iTutorialStart() {
|
function iTutorialStart() {
|
||||||
//Initialize Interactive Tutorial state by settings 'done' for each state to false
|
// Initialize Interactive Tutorial state by settings 'done' for each state to false
|
||||||
ITutorial.stepIsDone = {};
|
ITutorial.stepIsDone = {};
|
||||||
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
|
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
|
||||||
ITutorial.stepIsDone[i] = false;
|
ITutorial.stepIsDone[i] = false;
|
||||||
@@ -64,7 +67,7 @@ function iTutorialStart() {
|
|||||||
|
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
|
|
||||||
//Don't autosave during this interactive tutorial
|
// Don't autosave during this interactive tutorial
|
||||||
Engine.Counters.autoSaveCounter = Infinity;
|
Engine.Counters.autoSaveCounter = Infinity;
|
||||||
console.log("Interactive Tutorial started");
|
console.log("Interactive Tutorial started");
|
||||||
ITutorial.currStep = 0;
|
ITutorial.currStep = 0;
|
||||||
@@ -72,21 +75,21 @@ function iTutorialStart() {
|
|||||||
|
|
||||||
document.getElementById("interactive-tutorial-container").style.display = "block";
|
document.getElementById("interactive-tutorial-container").style.display = "block";
|
||||||
|
|
||||||
//Exit tutorial button
|
// Exit tutorial button
|
||||||
var exitButton = clearEventListeners("interactive-tutorial-exit");
|
var exitButton = clearEventListeners("interactive-tutorial-exit");
|
||||||
exitButton.addEventListener("click", function() {
|
exitButton.addEventListener("click", function() {
|
||||||
iTutorialEnd();
|
iTutorialEnd();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
//Back button
|
// Back button
|
||||||
var backButton = clearEventListeners("interactive-tutorial-back");
|
var backButton = clearEventListeners("interactive-tutorial-back");
|
||||||
backButton.addEventListener("click", function() {
|
backButton.addEventListener("click", function() {
|
||||||
iTutorialPrevStep();
|
iTutorialPrevStep();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
//Next button
|
// Next button
|
||||||
var nextButton = clearEventListeners("interactive-tutorial-next");
|
var nextButton = clearEventListeners("interactive-tutorial-next");
|
||||||
nextButton.addEventListener("click", function() {
|
nextButton.addEventListener("click", function() {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
@@ -99,7 +102,7 @@ function iTutorialStart() {
|
|||||||
function iTutorialEvaluateStep() {
|
function iTutorialEvaluateStep() {
|
||||||
if (!ITutorial.isRunning) {console.log("Interactive Tutorial not running"); return;}
|
if (!ITutorial.isRunning) {console.log("Interactive Tutorial not running"); return;}
|
||||||
|
|
||||||
//Disable and clear main menu
|
// Disable and clear main menu
|
||||||
var terminalMainMenu = clearEventListeners("terminal-menu-link");
|
var terminalMainMenu = clearEventListeners("terminal-menu-link");
|
||||||
var statsMainMenu = clearEventListeners("stats-menu-link");
|
var statsMainMenu = clearEventListeners("stats-menu-link");
|
||||||
var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
|
var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
|
||||||
@@ -113,7 +116,7 @@ function iTutorialEvaluateStep() {
|
|||||||
cityMainMenu.removeAttribute("class");
|
cityMainMenu.removeAttribute("class");
|
||||||
tutorialMainMenu.removeAttribute("class");
|
tutorialMainMenu.removeAttribute("class");
|
||||||
|
|
||||||
//Interactive Tutorial Next button
|
// Interactive Tutorial Next button
|
||||||
var nextBtn = document.getElementById("interactive-tutorial-next");
|
var nextBtn = document.getElementById("interactive-tutorial-next");
|
||||||
|
|
||||||
switch(ITutorial.currStep) {
|
switch(ITutorial.currStep) {
|
||||||
@@ -131,7 +134,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"the main navigation menu (left-hand side of the screen)");
|
"the main navigation menu (left-hand side of the screen)");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Stats' menu and set its tutorial click handler
|
// Flash 'Stats' menu and set its tutorial click handler
|
||||||
statsMainMenu.setAttribute("class", "flashing-button");
|
statsMainMenu.setAttribute("class", "flashing-button");
|
||||||
statsMainMenu.addEventListener("click", function() {
|
statsMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadCharacterContent();
|
Engine.loadCharacterContent();
|
||||||
@@ -151,7 +154,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"main navigation menu.");
|
"main navigation menu.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Terminal' menu and set its tutorial click handler
|
// Flash 'Terminal' menu and set its tutorial click handler
|
||||||
terminalMainMenu.setAttribute("class", "flashing-button");
|
terminalMainMenu.setAttribute("class", "flashing-button");
|
||||||
terminalMainMenu.addEventListener("click", function() {
|
terminalMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -169,13 +172,13 @@ function iTutorialEvaluateStep() {
|
|||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
iTutorialSetText("Let's try it out. Start by entering the 'help' command into the Terminal " +
|
iTutorialSetText("Let's try it out. Start by entering the 'help' command into the Terminal " +
|
||||||
"(Don't forget to press Enter after typing the command)");
|
"(Don't forget to press Enter after typing the command)");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalLs:
|
case iTutorialSteps.TerminalLs:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
iTutorialSetText("The 'help' command displays a list of all available Terminal commands, how to use them, " +
|
iTutorialSetText("The 'help' command displays a list of all available Terminal commands, how to use them, " +
|
||||||
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
|
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalScan:
|
case iTutorialSteps.TerminalScan:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -184,7 +187,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
|
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
|
||||||
"to other machines throughout the world. Let's do that now by first entering " +
|
"to other machines throughout the world. Let's do that now by first entering " +
|
||||||
"the 'scan' command.");
|
"the 'scan' command.");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalScanAnalyze1:
|
case iTutorialSteps.TerminalScanAnalyze1:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -194,7 +197,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"That's great and all, but there's so many servers. Which one should you go to? " +
|
"That's great and all, but there's so many servers. Which one should you go to? " +
|
||||||
"The 'scan-analyze' command gives some more detailed information about servers on the " +
|
"The 'scan-analyze' command gives some more detailed information about servers on the " +
|
||||||
"network. Try it now");
|
"network. Try it now");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalScanAnalyze2:
|
case iTutorialSteps.TerminalScanAnalyze2:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -202,7 +205,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"information about each server that you can connect to (servers that are a distance of " +
|
"information about each server that you can connect to (servers that are a distance of " +
|
||||||
"one node away). <br><br> It is also possible to run 'scan-analyze' with " +
|
"one node away). <br><br> It is also possible to run 'scan-analyze' with " +
|
||||||
"a higher depth. Let's try a depth of two with the following command: 'scan-analyze 2'.")
|
"a higher depth. Let's try a depth of two with the following command: 'scan-analyze 2'.")
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalConnect:
|
case iTutorialSteps.TerminalConnect:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -212,7 +215,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"the ip or the hostname, but dont use both.<br><br>" +
|
"the ip or the hostname, but dont use both.<br><br>" +
|
||||||
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
|
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
|
||||||
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
|
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalAnalyze:
|
case iTutorialSteps.TerminalAnalyze:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -221,7 +224,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"on servers and computers. Using your hacking abilities, you can hack servers " +
|
"on servers and computers. Using your hacking abilities, you can hack servers " +
|
||||||
"to steal money and gain experience. <br><br> " +
|
"to steal money and gain experience. <br><br> " +
|
||||||
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
|
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalNuke:
|
case iTutorialSteps.TerminalNuke:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -233,13 +236,13 @@ function iTutorialEvaluateStep() {
|
|||||||
"open ports.<br><br> The 'analyze' results shows that there do not need to be any open ports " +
|
"open ports.<br><br> The 'analyze' results shows that there do not need to be any open ports " +
|
||||||
"on this machine for the NUKE virus to work, so go ahead and run the virus using the " +
|
"on this machine for the NUKE virus to work, so go ahead and run the virus using the " +
|
||||||
"'run NUKE.exe' command.");
|
"'run NUKE.exe' command.");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalManualHack:
|
case iTutorialSteps.TerminalManualHack:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
iTutorialSetText("You now have root access! You can hack the server using the 'hack' command. " +
|
iTutorialSetText("You now have root access! You can hack the server using the 'hack' command. " +
|
||||||
"Try doing that now.");
|
"Try doing that now.");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalHackingMechanics:
|
case iTutorialSteps.TerminalHackingMechanics:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -262,7 +265,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"command. Scripts must end with the '.script' extension. Let's make a script now by " +
|
"command. Scripts must end with the '.script' extension. Let's make a script now by " +
|
||||||
"entering 'nano foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
|
"entering 'nano foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
|
||||||
" will end a command like hack early)");
|
" will end a command like hack early)");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalTypeScript:
|
case iTutorialSteps.TerminalTypeScript:
|
||||||
Engine.loadScriptEditorContent("foodnstuff.script", "");
|
Engine.loadScriptEditorContent("foodnstuff.script", "");
|
||||||
@@ -278,7 +281,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"For anyone with basic programming experience, this code should be straightforward. " +
|
"For anyone with basic programming experience, this code should be straightforward. " +
|
||||||
"This script will continuously hack the 'foodnstuff' server.<br><br>" +
|
"This script will continuously hack the 'foodnstuff' server.<br><br>" +
|
||||||
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
|
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
|
||||||
nextBtn.style.display = "none"; //next step triggered in saveAndCloseScriptEditor() (Script.js)
|
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalFree:
|
case iTutorialSteps.TerminalFree:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -286,13 +289,13 @@ function iTutorialEvaluateStep() {
|
|||||||
"run on any machine which you have root access to. Different servers have different " +
|
"run on any machine which you have root access to. Different servers have different " +
|
||||||
"amounts of RAM. You can also purchase more RAM for your home server.<br><br>To check how much " +
|
"amounts of RAM. You can also purchase more RAM for your home server.<br><br>To check how much " +
|
||||||
"RAM is available on this machine, enter the 'free' command.");
|
"RAM is available on this machine, enter the 'free' command.");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal commmand
|
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalRunScript:
|
case iTutorialSteps.TerminalRunScript:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
|
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
|
||||||
"script. Let's run our script using 'run foodnstuff.script'.");
|
"script. Let's run our script using 'run foodnstuff.script'.");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal commmand
|
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalGoToActiveScriptsPage:
|
case iTutorialSteps.TerminalGoToActiveScriptsPage:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -306,7 +309,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"'Active Scripts' link in the main navigation menu.");
|
"'Active Scripts' link in the main navigation menu.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Active Scripts' menu and set its tutorial click handler
|
// Flash 'Active Scripts' menu and set its tutorial click handler
|
||||||
activeScriptsMainMenu.setAttribute("class", "flashing-button");
|
activeScriptsMainMenu.setAttribute("class", "flashing-button");
|
||||||
activeScriptsMainMenu.addEventListener("click", function() {
|
activeScriptsMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadActiveScriptsContent();
|
Engine.loadActiveScriptsContent();
|
||||||
@@ -322,7 +325,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"link.");
|
"link.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Terminal' button and set its tutorial click handler
|
// Flash 'Terminal' button and set its tutorial click handler
|
||||||
terminalMainMenu.setAttribute("class", "flashing-button");
|
terminalMainMenu.setAttribute("class", "flashing-button");
|
||||||
terminalMainMenu.addEventListener("click", function() {
|
terminalMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -335,7 +338,7 @@ function iTutorialEvaluateStep() {
|
|||||||
iTutorialSetText("One last thing about scripts, each active script contains logs that detail " +
|
iTutorialSetText("One last thing about scripts, each active script contains logs that detail " +
|
||||||
"what it's doing. We can check these logs using the 'tail' command. Do that " +
|
"what it's doing. We can check these logs using the 'tail' command. Do that " +
|
||||||
"now for the script we just ran by typing 'tail foodnstuff.script'");
|
"now for the script we just ran by typing 'tail foodnstuff.script'");
|
||||||
nextBtn.style.display = "none"; //next step triggered by terminal command
|
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalTailScript:
|
case iTutorialSteps.TerminalTailScript:
|
||||||
Engine.loadTerminalContent();
|
Engine.loadTerminalContent();
|
||||||
@@ -356,7 +359,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"the 'Hacknet Nodes' page through the main navigation menu now.");
|
"the 'Hacknet Nodes' page through the main navigation menu now.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Hacknet' menu and set its tutorial click handler
|
// Flash 'Hacknet' menu and set its tutorial click handler
|
||||||
hacknetMainMenu.setAttribute("class", "flashing-button");
|
hacknetMainMenu.setAttribute("class", "flashing-button");
|
||||||
hacknetMainMenu.addEventListener("click", function() {
|
hacknetMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadHacknetNodesContent();
|
Engine.loadHacknetNodesContent();
|
||||||
@@ -368,7 +371,7 @@ function iTutorialEvaluateStep() {
|
|||||||
Engine.loadHacknetNodesContent();
|
Engine.loadHacknetNodesContent();
|
||||||
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
|
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
|
||||||
"existing ones. Let's purchase a new one now.");
|
"existing ones. Let's purchase a new one now.");
|
||||||
nextBtn.style.display = "none"; //Next step triggered by purchaseHacknet() (HacknetNode.js)
|
nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js)
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.HacknetNodesGoToWorldPage:
|
case iTutorialSteps.HacknetNodesGoToWorldPage:
|
||||||
Engine.loadHacknetNodesContent();
|
Engine.loadHacknetNodesContent();
|
||||||
@@ -379,16 +382,16 @@ function iTutorialEvaluateStep() {
|
|||||||
"Let's go to the 'City' page through the main navigation menu.");
|
"Let's go to the 'City' page through the main navigation menu.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'City' menu and set its tutorial click handler
|
// Flash 'City' menu and set its tutorial click handler
|
||||||
cityMainMenu.setAttribute("class", "flashing-button");
|
cityMainMenu.setAttribute("class", "flashing-button");
|
||||||
cityMainMenu.addEventListener("click", function() {
|
cityMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadWorldContent();
|
Engine.loadLocationContent();
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.WorldDescription:
|
case iTutorialSteps.WorldDescription:
|
||||||
Engine.loadWorldContent();
|
Engine.loadLocationContent();
|
||||||
iTutorialSetText("This page lists all of the different locations you can currently " +
|
iTutorialSetText("This page lists all of the different locations you can currently " +
|
||||||
"travel to. Each location has something that you can do. " +
|
"travel to. Each location has something that you can do. " +
|
||||||
"There's a lot of content out in the world, make sure " +
|
"There's a lot of content out in the world, make sure " +
|
||||||
@@ -396,7 +399,7 @@ function iTutorialEvaluateStep() {
|
|||||||
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
|
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
|
||||||
nextBtn.style.display = "none";
|
nextBtn.style.display = "none";
|
||||||
|
|
||||||
//Flash 'Tutorial' menu and set its tutorial click handler
|
// Flash 'Tutorial' menu and set its tutorial click handler
|
||||||
tutorialMainMenu.setAttribute("class", "flashing-button");
|
tutorialMainMenu.setAttribute("class", "flashing-button");
|
||||||
tutorialMainMenu.addEventListener("click", function() {
|
tutorialMainMenu.addEventListener("click", function() {
|
||||||
Engine.loadTutorialContent();
|
Engine.loadTutorialContent();
|
||||||
@@ -425,9 +428,9 @@ function iTutorialEvaluateStep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Go to the next step and evaluate it
|
// Go to the next step and evaluate it
|
||||||
function iTutorialNextStep() {
|
function iTutorialNextStep() {
|
||||||
//Special behavior for certain steps
|
// Special behavior for certain steps
|
||||||
if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
|
if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
|
||||||
document.getElementById("stats-menu-link").removeAttribute("class");
|
document.getElementById("stats-menu-link").removeAttribute("class");
|
||||||
}
|
}
|
||||||
@@ -457,7 +460,7 @@ function iTutorialNextStep() {
|
|||||||
iTutorialEvaluateStep();
|
iTutorialEvaluateStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Go to previous step and evaluate
|
// Go to previous step and evaluate
|
||||||
function iTutorialPrevStep() {
|
function iTutorialPrevStep() {
|
||||||
if (ITutorial.currStep > iTutorialSteps.Start) {
|
if (ITutorial.currStep > iTutorialSteps.Start) {
|
||||||
ITutorial.currStep -= 1;
|
ITutorial.currStep -= 1;
|
||||||
@@ -466,7 +469,7 @@ function iTutorialPrevStep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function iTutorialEnd() {
|
function iTutorialEnd() {
|
||||||
//Re-enable auto save
|
// Re-enable auto save
|
||||||
if (Settings.AutosaveInterval === 0) {
|
if (Settings.AutosaveInterval === 0) {
|
||||||
Engine.Counters.autoSaveCounter = Infinity;
|
Engine.Counters.autoSaveCounter = Infinity;
|
||||||
} else {
|
} else {
|
||||||
@@ -491,7 +494,7 @@ function iTutorialEnd() {
|
|||||||
ITutorial.isRunning = false;
|
ITutorial.isRunning = false;
|
||||||
document.getElementById("interactive-tutorial-container").style.display = "none";
|
document.getElementById("interactive-tutorial-container").style.display = "none";
|
||||||
|
|
||||||
//Create a popup with final introductory stuff
|
// Create a popup with final introductory stuff
|
||||||
var popupId = "interactive-tutorial-ending-popup";
|
var popupId = "interactive-tutorial-ending-popup";
|
||||||
var txt = createElement("p", {
|
var txt = createElement("p", {
|
||||||
innerHTML:
|
innerHTML:
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
/**
|
||||||
|
* Lore / world building literature files that can be found on servers.
|
||||||
/* Literature.js
|
* These files can be read by the player
|
||||||
* Lore / world building literature that can be found on servers
|
|
||||||
*/
|
*/
|
||||||
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
|
|
||||||
function Literature(title, filename, txt) {
|
function Literature(title, filename, txt) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.fn = filename;
|
this.fn = filename;
|
||||||
@@ -10,10 +11,9 @@ function Literature(title, filename, txt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showLiterature(fn) {
|
function showLiterature(fn) {
|
||||||
var litObj = Literatures[fn];
|
const litObj = Literatures[fn];
|
||||||
if (litObj == null) {return;}
|
if (litObj == null) { return; }
|
||||||
var txt = "<i>" + litObj.title + "</i><br><br>" +
|
const txt = `<i>${litObj.title}</i><br><br>${litObj.txt}`;
|
||||||
litObj.txt;
|
|
||||||
dialogBoxCreate(txt);
|
dialogBoxCreate(txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +430,10 @@ function initLiterature() {
|
|||||||
fn = "the-secret-war.lit";
|
fn = "the-secret-war.lit";
|
||||||
txt = ""
|
txt = ""
|
||||||
Literatures[fn] = new Literature(title, fn, txt);
|
Literatures[fn] = new Literature(title, fn, txt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Literatures, initLiterature, showLiterature};
|
export {
|
||||||
|
Literatures,
|
||||||
|
initLiterature,
|
||||||
|
showLiterature
|
||||||
|
};
|
||||||
|
|||||||
2295
src/Location.js
2295
src/Location.js
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
|||||||
import { IMap } from "./types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display Location Content when visiting somewhere in the World
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line:variable-name
|
|
||||||
export const Locations: IMap<string> = {
|
|
||||||
// Cities
|
|
||||||
Aevum: "Aevum",
|
|
||||||
Chongqing: "Chongqing",
|
|
||||||
Ishima: "Ishima",
|
|
||||||
NewTokyo: "New Tokyo",
|
|
||||||
Sector12: "Sector-12",
|
|
||||||
Volhaven: "Volhaven",
|
|
||||||
|
|
||||||
// Aevum Locations
|
|
||||||
AevumAeroCorp: "AeroCorp",
|
|
||||||
AevumBachmanAndAssociates: "Bachman & Associates",
|
|
||||||
AevumClarkeIncorporated: "Clarke Incorporated",
|
|
||||||
AevumCrushFitnessGym: "Crush Fitness Gym",
|
|
||||||
AevumECorp: "ECorp",
|
|
||||||
AevumFulcrumTechnologies: "Fulcrum Technologies",
|
|
||||||
AevumGalacticCybersystems: "Galactic Cybersystems",
|
|
||||||
AevumNetLinkTechnologies: "NetLink Technologies",
|
|
||||||
AevumPolice: "Aevum Police Headquarters",
|
|
||||||
AevumRhoConstruction: "Rho Construction",
|
|
||||||
AevumSlums: "Aevum Slums",
|
|
||||||
AevumSnapFitnessGym: "Snap Fitness Gym",
|
|
||||||
AevumSummitUniversity: "Summit University",
|
|
||||||
AevumTravelAgency: "Aevum Travel Agency",
|
|
||||||
AevumWatchdogSecurity: "Watchdog Security",
|
|
||||||
|
|
||||||
// Chongqing locations
|
|
||||||
ChongqingKuaiGongInternational: "KuaiGong International",
|
|
||||||
ChongqingSlums: "Chongqing Slums",
|
|
||||||
ChongqingSolarisSpaceSystems: "Solaris Space Systems",
|
|
||||||
ChongqingTravelAgency: "Chongqing Travel Agency",
|
|
||||||
|
|
||||||
// Sector 12
|
|
||||||
Sector12AlphaEnterprises: "Alpha Enterprises",
|
|
||||||
Sector12BladeIndustries: "Blade Industries",
|
|
||||||
Sector12CIA: "Central Intelligence Agency",
|
|
||||||
Sector12CarmichaelSecurity: "Carmichael Security",
|
|
||||||
Sector12CityHall: "Sector-12 City Hall",
|
|
||||||
Sector12DeltaOne: "DeltaOne",
|
|
||||||
Sector12FoodNStuff: "FoodNStuff",
|
|
||||||
Sector12FourSigma: "Four Sigma",
|
|
||||||
Sector12IcarusMicrosystems: "Icarus Microsystems",
|
|
||||||
Sector12IronGym: "Iron Gym",
|
|
||||||
Sector12JoesGuns: "Joe's Guns",
|
|
||||||
Sector12MegaCorp: "MegaCorp",
|
|
||||||
Sector12NSA: "National Security Agency",
|
|
||||||
Sector12PowerhouseGym: "Powerhouse Gym",
|
|
||||||
Sector12RothmanUniversity: "Rothman University",
|
|
||||||
Sector12Slums: "Sector-12 Slums",
|
|
||||||
Sector12TravelAgency: "Sector-12 Travel Agency",
|
|
||||||
Sector12UniversalEnergy: "Universal Energy",
|
|
||||||
|
|
||||||
// New Tokyo
|
|
||||||
NewTokyoDefComm: "DefComm",
|
|
||||||
NewTokyoGlobalPharmaceuticals: "Global Pharmaceuticals",
|
|
||||||
NewTokyoNoodleBar: "Noodle Bar",
|
|
||||||
NewTokyoSlums: "New Tokyo Slums",
|
|
||||||
NewTokyoTravelAgency: "New Tokyo Travel Agency",
|
|
||||||
NewTokyoVitaLife: "VitaLife",
|
|
||||||
|
|
||||||
// Ishima
|
|
||||||
IshimaNovaMedical: "Nova Medical",
|
|
||||||
IshimaOmegaSoftware: "Omega Software",
|
|
||||||
IshimaSlums: "Ishima Slums",
|
|
||||||
IshimaStormTechnologies: "Storm Technologies",
|
|
||||||
IshimaTravelAgency: "Ishima Travel Agency",
|
|
||||||
|
|
||||||
// Volhaven
|
|
||||||
VolhavenCompuTek: "CompuTek",
|
|
||||||
VolhavenHeliosLabs: "Helios Labs",
|
|
||||||
VolhavenLexoCorp: "LexoCorp",
|
|
||||||
VolhavenMilleniumFitnessGym: "Millenium Fitness Gym",
|
|
||||||
VolhavenNWO: "NWO",
|
|
||||||
VolhavenOmniTekIncorporated: "OmniTek Incorporated",
|
|
||||||
VolhavenOmniaCybersystems: "Omnia Cybersystems",
|
|
||||||
VolhavenSlums: "Volhaven Slums",
|
|
||||||
VolhavenSysCoreSecurities: "SysCore Securities",
|
|
||||||
VolhavenTravelAgency: "Volhaven Travel Agency",
|
|
||||||
VolhavenZBInstituteOfTechnology: "ZB Institute of Technology",
|
|
||||||
|
|
||||||
// Generic locations
|
|
||||||
Hospital: "Hospital",
|
|
||||||
WorldStockExchange: "World Stock Exchange",
|
|
||||||
};
|
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Map of all Cities in the game
|
||||||
|
* Key = City Name, Value = City object
|
||||||
|
*/
|
||||||
|
import { City } from "./City";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
/**
|
export const Cities: IMap<City> = {};
|
||||||
* Display Location Content when visiting somewhere in the World
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line:variable-name
|
|
||||||
export const Cities: IMap<string> = {
|
|
||||||
Aevum: "Aevum",
|
|
||||||
Chongqing: "Chongqing",
|
|
||||||
Ishima: "Ishima",
|
|
||||||
NewTokyo: "New Tokyo",
|
|
||||||
Sector12: "Sector-12",
|
|
||||||
Volhaven: "Volhaven",
|
|
||||||
};
|
|
||||||
|
|||||||
26
src/Locations/City.ts
Normal file
26
src/Locations/City.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Class representing a City in the game
|
||||||
|
*/
|
||||||
|
import { CityName } from "./data/CityNames";
|
||||||
|
import { LocationName } from "./data/LocationNames";
|
||||||
|
|
||||||
|
export class City {
|
||||||
|
/**
|
||||||
|
* List of all locations in this city
|
||||||
|
*/
|
||||||
|
locations: LocationName[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this city
|
||||||
|
*/
|
||||||
|
name: CityName;
|
||||||
|
|
||||||
|
constructor(name: CityName, locations: LocationName[]=[]) {
|
||||||
|
this.name = name;
|
||||||
|
this.locations = locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLocation(loc: LocationName): void {
|
||||||
|
this.locations.push(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user