mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-17 14:59:16 +02:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
433b399de9 | ||
|
|
58d04c0cbb | ||
|
|
e3a74f23a1 | ||
|
|
3a374de210 | ||
|
|
4cc6437408 | ||
|
|
821725cf4d | ||
|
|
931de230ae | ||
|
|
7301946236 | ||
|
|
a15041da75 | ||
|
|
00f8c0a51f | ||
|
|
63483837bc | ||
|
|
dc5f4e6694 | ||
|
|
35f8a5115a | ||
|
|
8398fd47f0 | ||
|
|
9d7c869c0a | ||
|
|
74587f269e | ||
|
|
6a3ffff3ad | ||
|
|
2f8eac07ee | ||
|
|
3eaefa01f9 | ||
|
|
b250af913d | ||
|
|
0b4968d148 | ||
|
|
8817d179c6 | ||
|
|
e5e3fec1a9 | ||
|
|
eecb0c0f01 | ||
|
|
d45689c7df | ||
|
|
2201dfc371 | ||
|
|
3ef9042051 | ||
|
|
1236ad252b | ||
|
|
65331ab22e | ||
|
|
c485fdfa87 | ||
|
|
6effda29a9 | ||
|
|
7035154454 | ||
|
|
d7f3ab9177 | ||
|
|
3660dde75f | ||
|
|
99688b78c7 | ||
|
|
6841f24932 | ||
|
|
9f94d0838a | ||
|
|
086fc67ecc | ||
|
|
a2551f98c2 | ||
|
|
95c928afc9 | ||
|
|
8a00e6e532 | ||
|
|
287a97aea6 | ||
|
|
664267bff0 | ||
|
|
2597b33f81 | ||
|
|
9442b348e6 | ||
|
|
3b7f9c9fb0 | ||
|
|
20ca7533b0 | ||
|
|
15a324a946 | ||
|
|
94175877d7 | ||
|
|
c1ec3c5eba | ||
|
|
42804b0cd3 | ||
|
|
b1248521f3 | ||
|
|
b744997c72 | ||
|
|
2d37409392 | ||
|
|
bd02e724e5 | ||
|
|
fef7aaba8f | ||
|
|
1775ea86ff | ||
|
|
b0918d7bd3 | ||
|
|
29e0ce5f96 | ||
|
|
44c26165f4 | ||
|
|
9dd68947f1 | ||
|
|
db5fdb1fcb | ||
|
|
74e72854d8 | ||
|
|
ece246b391 | ||
|
|
cdb5dfec62 | ||
|
|
8a5b6f6cbc | ||
|
|
585e1ac7aa | ||
|
|
8726946d4a | ||
|
|
064008d200 | ||
|
|
d955280f90 | ||
|
|
580a7fac24 | ||
|
|
9df054dd0c | ||
|
|
8fa7b112e1 | ||
|
|
dd9df0a18c | ||
|
|
3a601a015d | ||
|
|
87b4698d5b | ||
|
|
67632ced09 | ||
|
|
d7fb335815 | ||
|
|
4809a21e38 | ||
|
|
6b3646e981 | ||
|
|
0c64bf470a | ||
|
|
99e034921e | ||
|
|
7a3a3de7d1 | ||
|
|
f91c5bd7b9 | ||
|
|
bcb198220d | ||
|
|
bf1af6a68c | ||
|
|
3dd2975c61 | ||
|
|
33f1e0cb3c | ||
|
|
7514f63dcd | ||
|
|
b6ff73391d | ||
|
|
369ea8d381 | ||
|
|
a7296c512c | ||
|
|
8f70817c10 | ||
|
|
7417fb6ef8 | ||
|
|
d044739f1c | ||
|
|
3d1684f825 | ||
|
|
f6af9e94ab | ||
|
|
215cf59e0b | ||
|
|
0d14cd6e7e | ||
|
|
98a04e4932 | ||
|
|
8d33c5b571 | ||
|
|
221b81d802 | ||
|
|
df89cc5002 | ||
|
|
3b6b37f8a6 | ||
|
|
cf2acb8844 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,6 +3,6 @@ Netburner.txt
|
|||||||
/doc/build
|
/doc/build
|
||||||
/node_modules
|
/node_modules
|
||||||
/dist/*.map
|
/dist/*.map
|
||||||
/tests/*.map
|
/test/*.map
|
||||||
/tests/*.bundle.*
|
/test/*.bundle.*
|
||||||
/tests/*.css
|
/test/*.css
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
126
css/activescripts.scss
Normal file
126
css/activescripts.scss
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
@import "theme";
|
||||||
|
|
||||||
|
.active-scripts-list {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#active-scripts-container {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
width: 70%;
|
||||||
|
margin: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header {
|
||||||
|
> pre {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-server-header {
|
||||||
|
background-color: #444;
|
||||||
|
font-size: $defaultFontSize * 1.25;
|
||||||
|
color: #fff;
|
||||||
|
margin: 6px 6px 0 6px;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 60%;
|
||||||
|
text-align: left;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '\02795'; /* "plus" sign (+) */
|
||||||
|
font-size: $defaultFontSize * 0.8125;
|
||||||
|
color: #fff;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active, &:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-server-header.active {
|
||||||
|
&:after {
|
||||||
|
content: "\2796"; /* "minus" sign (-) */
|
||||||
|
font-size: $defaultFontSize * 0.8125;
|
||||||
|
color: #fff;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-server-panel {
|
||||||
|
margin: 0 6px 6px 6px;
|
||||||
|
padding: 0 6px 6px 6px;
|
||||||
|
width: 55%;
|
||||||
|
margin-left: 5%;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
div, ul, ul > li {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-script-header {
|
||||||
|
background-color: #555;
|
||||||
|
border: none;
|
||||||
|
color: var(--my-font-color);
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
padding: 4px 25px 4px 10px;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '\02795'; /* "plus" sign (+) */
|
||||||
|
font-size: $defaultFontSize * 0.8125;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: 0 0 0 var(--my-font-color);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active:after {
|
||||||
|
content: "\2796"; /* "minus" sign (-) */
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active:hover {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-script-panel {
|
||||||
|
background-color: #555;
|
||||||
|
display: none;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding: 0 18px;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
pre, h2, ul, li {
|
||||||
|
background-color: #555;
|
||||||
|
width: auto;
|
||||||
|
color: #fff;
|
||||||
|
margin-left: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
css/augmentations.scss
Normal file
31
css/augmentations.scss
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Styling for the Augmentations UI. This is the page that displays all of the
|
||||||
|
* player's owned and purchased Augmentations and Source-Files. It also allows
|
||||||
|
* the player to install Augmentations
|
||||||
|
*/
|
||||||
|
@import "theme";
|
||||||
|
|
||||||
|
#augmentations-container {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#augmentations-content {
|
||||||
|
> p {
|
||||||
|
font-size: $defaultFontSize * 0.875;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.augmentations-list {
|
||||||
|
button,
|
||||||
|
div {
|
||||||
|
color: var(--my-font-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,126 +18,6 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Active scripts */
|
|
||||||
.active-scripts-list {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#active-scripts-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#active-scripts-text,
|
|
||||||
#active-scripts-total-prod {
|
|
||||||
width: 70%;
|
|
||||||
margin: 6px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header {
|
|
||||||
background-color: #444;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
color: #fff;
|
|
||||||
margin: 6px 6px 0 6px;
|
|
||||||
padding: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 60%;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header.active,
|
|
||||||
.active-scripts-server-header:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header.active:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header:after {
|
|
||||||
content: '\02795'; /* "plus" sign (+) */
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
color: #fff;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header.active:after {
|
|
||||||
content: "\2796"; /* "minus" sign (-) */
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
color: #fff;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-panel {
|
|
||||||
margin: 0 6px 6px 6px;
|
|
||||||
padding: 0 6px 6px 6px;
|
|
||||||
width: 55%;
|
|
||||||
margin-left: 5%;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-panel div,
|
|
||||||
.active-scripts-server-panel ul,
|
|
||||||
.active-scripts-server-panel ul > li {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-script-header {
|
|
||||||
background-color: #555;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
padding: 4px 25px 4px 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: auto;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '\02795'; /* "plus" sign (+) */
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 0 var(--my-font-color);
|
|
||||||
position: absolute;
|
|
||||||
bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active:after {
|
|
||||||
content: "\2796"; /* "minus" sign (-) */
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&.active:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-script-panel {
|
|
||||||
padding: 0 18px;
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
display: none;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
|
|
||||||
p, h2, ul, li {
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container {
|
#world-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -185,19 +65,6 @@
|
|||||||
width: 70%;
|
width: 70%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#faction-donate-amount-txt,
|
|
||||||
#faction-donate-input {
|
|
||||||
padding: 6px;
|
|
||||||
margin: 6px;
|
|
||||||
display: inline-block;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#faction-donate-amount-txt {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#faction-container p,
|
#faction-container p,
|
||||||
#faction-container pre {
|
#faction-container pre {
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
@@ -213,45 +80,12 @@
|
|||||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Faction Augmentations */
|
|
||||||
#faction-augmentations-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
p, a, ul, h1 {
|
|
||||||
margin: 8px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container li {
|
#world-container li {
|
||||||
margin: 0 0 15px 0;
|
margin: 0 0 15px 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Augmentations */
|
|
||||||
#augmentations-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.augmentations-list {
|
|
||||||
button,
|
|
||||||
div {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 2px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tutorial */
|
/* Tutorial */
|
||||||
#tutorial-container {
|
#tutorial-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
resize: none;
|
resize: none;
|
||||||
width: 50%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#script-editor-status {
|
#script-editor-status {
|
||||||
|
|||||||
@@ -7,35 +7,45 @@
|
|||||||
p {
|
p {
|
||||||
font-size: $defaultFontSize * 0.8125;
|
font-size: $defaultFontSize * 0.8125;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-size: $defaultFontSize * 0.875;
|
font-size: $defaultFontSize * 0.875;
|
||||||
}
|
}
|
||||||
h2 {
|
}
|
||||||
|
|
||||||
|
.stock-market-info-and-purchases {
|
||||||
|
> h2 {
|
||||||
|
display: block;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
display: block;
|
display: block;
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> a, > button {
|
||||||
|
margin: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#stock-market-list li {
|
#stock-market-list {
|
||||||
button {
|
list-style: none;
|
||||||
font-size: $defaultFontSize;
|
|
||||||
|
li {
|
||||||
|
button {
|
||||||
|
font-size: $defaultFontSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#stock-market-container p {
|
|
||||||
padding: 6px;
|
|
||||||
margin: 6px;
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#stock-market-container a {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#stock-market-watchlist-filter {
|
#stock-market-watchlist-filter {
|
||||||
|
display: block;
|
||||||
|
margin: 5px 5px 5px 10px;
|
||||||
|
padding: 4px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-input {
|
.stock-market-input {
|
||||||
@@ -47,14 +57,36 @@
|
|||||||
color: var(--my-font-color);
|
color: var(--my-font-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock-market-price-movement-warning {
|
||||||
|
border: 1px solid white;
|
||||||
|
color: red;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.stock-market-position-text {
|
.stock-market-position-text {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: inline-block;
|
display: block;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-order-list {
|
.stock-market-order-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-order-cancel-btn {
|
.stock-market-order-cancel-btn {
|
||||||
|
|||||||
@@ -243,8 +243,8 @@ a:visited {
|
|||||||
/* Accordion menus (Header with collapsible panel) */
|
/* Accordion menus (Header with collapsible panel) */
|
||||||
.accordion-header {
|
.accordion-header {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-size: $defaultFontSize * 1.25;
|
||||||
margin: 6px 6px 0 6px;
|
margin: 6px 6px 0 6px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/engineStyle.bundle.js
vendored
Normal file
2
dist/engineStyle.bundle.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([361,0]),o()}({304:function(n,t,o){},306:function(n,t,o){},308:function(n,t,o){},310:function(n,t,o){},312:function(n,t,o){},314:function(n,t,o){},316:function(n,t,o){},318:function(n,t,o){},320:function(n,t,o){},322:function(n,t,o){},324:function(n,t,o){},326:function(n,t,o){},328:function(n,t,o){},330:function(n,t,o){},332:function(n,t,o){},334:function(n,t,o){},336:function(n,t,o){},338:function(n,t,o){},340:function(n,t,o){},342:function(n,t,o){},344:function(n,t,o){},346:function(n,t,o){},348:function(n,t,o){},350:function(n,t,o){},352:function(n,t,o){},354:function(n,t,o){},356:function(n,t,o){},358:function(n,t,o){},361:function(n,t,o){"use strict";o.r(t);o(360),o(358),o(356),o(354),o(352),o(350),o(348),o(346),o(344),o(342),o(340),o(338),o(336),o(334),o(332),o(330),o(328),o(326),o(324),o(322),o(320),o(318),o(316),o(314),o(312),o(310),o(308),o(306),o(304)}});
|
||||||
|
//# sourceMappingURL=engineStyle.bundle.js.map
|
||||||
395
dist/engine.css → dist/engineStyle.css
vendored
395
dist/engine.css → dist/engineStyle.css
vendored
@@ -1,46 +1,3 @@
|
|||||||
/* COLORS */
|
|
||||||
/* Attributes */
|
|
||||||
/**
|
|
||||||
* Customized styling for the Code Mirror editor
|
|
||||||
*/
|
|
||||||
#codemirror-form-wrapper {
|
|
||||||
height: 80%;
|
|
||||||
margin: 10px 0px 0px 6px; }
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
z-index: 1;
|
|
||||||
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
|
|
||||||
font-size: 16px; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlight matches
|
|
||||||
*/
|
|
||||||
.cm-matchhighlight {
|
|
||||||
background-color: #8F908A; }
|
|
||||||
|
|
||||||
.CodeMirror-selection-highlight-scrollbar {
|
|
||||||
background-color: #8F908A; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show Invisibles
|
|
||||||
*/
|
|
||||||
.cm-whitespace::before {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
color: #404F7D; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vim command display
|
|
||||||
*/
|
|
||||||
#codemirror-vim-command-display-wrapper {
|
|
||||||
background-color: white;
|
|
||||||
font-size: 13px;
|
|
||||||
height: 30px;
|
|
||||||
margin-left: 6px; }
|
|
||||||
|
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
@@ -304,8 +261,8 @@ a:visited {
|
|||||||
/* Accordion menus (Header with collapsible panel) */
|
/* Accordion menus (Header with collapsible panel) */
|
||||||
.accordion-header {
|
.accordion-header {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
font-size: 20px;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
margin: 6px 6px 0 6px;
|
margin: 6px 6px 0 6px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -893,7 +850,7 @@ button {
|
|||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
resize: none;
|
resize: none;
|
||||||
width: 50%; }
|
width: 60%; }
|
||||||
|
|
||||||
#script-editor-status {
|
#script-editor-status {
|
||||||
float: left;
|
float: left;
|
||||||
@@ -933,6 +890,147 @@ 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 */
|
||||||
|
/**
|
||||||
|
* Customized styling for the Code Mirror editor
|
||||||
|
*/
|
||||||
|
#codemirror-form-wrapper {
|
||||||
|
height: 80%;
|
||||||
|
margin: 10px 0px 0px 6px; }
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid var(--my-highlight-color);
|
||||||
|
z-index: 1;
|
||||||
|
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
|
||||||
|
font-size: 16px; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight matches
|
||||||
|
*/
|
||||||
|
.cm-matchhighlight {
|
||||||
|
background-color: #8F908A; }
|
||||||
|
|
||||||
|
.CodeMirror-selection-highlight-scrollbar {
|
||||||
|
background-color: #8F908A; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Invisibles
|
||||||
|
*/
|
||||||
|
.cm-whitespace::before {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #404F7D; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vim command display
|
||||||
|
*/
|
||||||
|
#codemirror-vim-command-display-wrapper {
|
||||||
|
background-color: white;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 30px;
|
||||||
|
margin-left: 6px; }
|
||||||
|
|
||||||
|
/* COLORS */
|
||||||
|
/* Attributes */
|
||||||
|
.active-scripts-list {
|
||||||
|
list-style-type: none; }
|
||||||
|
|
||||||
|
#active-scripts-container {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px; }
|
||||||
|
#active-scripts-container > p {
|
||||||
|
width: 70%;
|
||||||
|
margin: 6px;
|
||||||
|
padding: 4px; }
|
||||||
|
#active-scripts-container .accordion-header > pre {
|
||||||
|
color: white; }
|
||||||
|
|
||||||
|
.active-scripts-server-header {
|
||||||
|
background-color: #444;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 6px 6px 0 6px;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 60%;
|
||||||
|
text-align: left;
|
||||||
|
border: none;
|
||||||
|
outline: none; }
|
||||||
|
.active-scripts-server-header:after {
|
||||||
|
content: '\2795';
|
||||||
|
/* "plus" sign (+) */
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fff;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px; }
|
||||||
|
.active-scripts-server-header.active, .active-scripts-server-header:hover {
|
||||||
|
background-color: #555; }
|
||||||
|
|
||||||
|
.active-scripts-server-header.active:after {
|
||||||
|
content: "\2796";
|
||||||
|
/* "minus" sign (-) */
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fff;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px; }
|
||||||
|
|
||||||
|
.active-scripts-server-header.active:hover {
|
||||||
|
background-color: #666; }
|
||||||
|
|
||||||
|
.active-scripts-server-panel {
|
||||||
|
margin: 0 6px 6px 6px;
|
||||||
|
padding: 0 6px 6px 6px;
|
||||||
|
width: 55%;
|
||||||
|
margin-left: 5%;
|
||||||
|
display: none; }
|
||||||
|
.active-scripts-server-panel div, .active-scripts-server-panel ul, .active-scripts-server-panel ul > li {
|
||||||
|
background-color: #555; }
|
||||||
|
|
||||||
|
.active-scripts-script-header {
|
||||||
|
background-color: #555;
|
||||||
|
border: none;
|
||||||
|
color: var(--my-font-color);
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
padding: 4px 25px 4px 10px;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
width: auto; }
|
||||||
|
.active-scripts-script-header:after {
|
||||||
|
content: '\2795';
|
||||||
|
/* "plus" sign (+) */
|
||||||
|
font-size: 13px;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: 0 0 0 var(--my-font-color);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px; }
|
||||||
|
.active-scripts-script-header.active:after {
|
||||||
|
content: "\2796";
|
||||||
|
/* "minus" sign (-) */ }
|
||||||
|
.active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
|
||||||
|
background-color: #666; }
|
||||||
|
.active-scripts-script-header.active {
|
||||||
|
background-color: #555; }
|
||||||
|
|
||||||
|
.active-scripts-script-panel {
|
||||||
|
background-color: #555;
|
||||||
|
display: none;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding: 0 18px;
|
||||||
|
width: auto; }
|
||||||
|
.active-scripts-script-panel pre, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
|
||||||
|
background-color: #555;
|
||||||
|
width: auto;
|
||||||
|
color: #fff;
|
||||||
|
margin-left: 5%; }
|
||||||
|
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
/**
|
/**
|
||||||
@@ -1007,107 +1105,6 @@ button {
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
position: fixed; }
|
position: fixed; }
|
||||||
|
|
||||||
/* Active scripts */
|
|
||||||
.active-scripts-list {
|
|
||||||
list-style-type: none; }
|
|
||||||
|
|
||||||
#active-scripts-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px; }
|
|
||||||
|
|
||||||
#active-scripts-text,
|
|
||||||
#active-scripts-total-prod {
|
|
||||||
width: 70%;
|
|
||||||
margin: 6px;
|
|
||||||
padding: 4px; }
|
|
||||||
|
|
||||||
.active-scripts-server-header {
|
|
||||||
background-color: #444;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #fff;
|
|
||||||
margin: 6px 6px 0 6px;
|
|
||||||
padding: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 60%;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none; }
|
|
||||||
|
|
||||||
.active-scripts-server-header.active,
|
|
||||||
.active-scripts-server-header:hover {
|
|
||||||
background-color: #555; }
|
|
||||||
|
|
||||||
.active-scripts-server-header.active:hover {
|
|
||||||
background-color: #666; }
|
|
||||||
|
|
||||||
.active-scripts-server-header:after {
|
|
||||||
content: '\2795';
|
|
||||||
/* "plus" sign (+) */
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px; }
|
|
||||||
|
|
||||||
.active-scripts-server-header.active:after {
|
|
||||||
content: "\2796";
|
|
||||||
/* "minus" sign (-) */
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px; }
|
|
||||||
|
|
||||||
.active-scripts-server-panel {
|
|
||||||
margin: 0 6px 6px 6px;
|
|
||||||
padding: 0 6px 6px 6px;
|
|
||||||
width: 55%;
|
|
||||||
margin-left: 5%;
|
|
||||||
display: none; }
|
|
||||||
|
|
||||||
.active-scripts-server-panel div,
|
|
||||||
.active-scripts-server-panel ul,
|
|
||||||
.active-scripts-server-panel ul > li {
|
|
||||||
background-color: #555; }
|
|
||||||
|
|
||||||
.active-scripts-script-header {
|
|
||||||
background-color: #555;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
padding: 4px 25px 4px 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: auto;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
position: relative; }
|
|
||||||
.active-scripts-script-header:after {
|
|
||||||
content: '\2795';
|
|
||||||
/* "plus" sign (+) */
|
|
||||||
font-size: 13px;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 0 var(--my-font-color);
|
|
||||||
position: absolute;
|
|
||||||
bottom: 4px; }
|
|
||||||
.active-scripts-script-header.active:after {
|
|
||||||
content: "\2796";
|
|
||||||
/* "minus" sign (-) */ }
|
|
||||||
.active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
|
|
||||||
background-color: #666; }
|
|
||||||
.active-scripts-script-header.active {
|
|
||||||
background-color: #555; }
|
|
||||||
|
|
||||||
.active-scripts-script-panel {
|
|
||||||
padding: 0 18px;
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
display: none;
|
|
||||||
margin-bottom: 6px; }
|
|
||||||
.active-scripts-script-panel p, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5%; }
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container {
|
#world-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1147,17 +1144,6 @@ button {
|
|||||||
margin: 6px;
|
margin: 6px;
|
||||||
width: 70%; }
|
width: 70%; }
|
||||||
|
|
||||||
#faction-donate-amount-txt,
|
|
||||||
#faction-donate-input {
|
|
||||||
padding: 6px;
|
|
||||||
margin: 6px;
|
|
||||||
display: inline-block;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
background-color: #000; }
|
|
||||||
|
|
||||||
#faction-donate-amount-txt {
|
|
||||||
width: 50%; }
|
|
||||||
|
|
||||||
#faction-container p,
|
#faction-container p,
|
||||||
#faction-container pre {
|
#faction-container pre {
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
@@ -1176,35 +1162,11 @@ button {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
/* Internet Explorer 5.5+ */ }
|
/* Internet Explorer 5.5+ */ }
|
||||||
|
|
||||||
/* Faction Augmentations */
|
|
||||||
#faction-augmentations-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px; }
|
|
||||||
#faction-augmentations-container p, #faction-augmentations-container a, #faction-augmentations-container ul, #faction-augmentations-container h1 {
|
|
||||||
margin: 8px;
|
|
||||||
padding: 4px; }
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container li {
|
#world-container li {
|
||||||
margin: 0 0 15px 0;
|
margin: 0 0 15px 0;
|
||||||
list-style-type: none; }
|
list-style-type: none; }
|
||||||
|
|
||||||
/* Augmentations */
|
|
||||||
#augmentations-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px; }
|
|
||||||
|
|
||||||
.augmentations-list button,
|
|
||||||
.augmentations-list div {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
text-decoration: none; }
|
|
||||||
|
|
||||||
.augmentations-list button {
|
|
||||||
padding: 2px 5px; }
|
|
||||||
|
|
||||||
.augmentations-list div {
|
|
||||||
padding: 6px; }
|
|
||||||
|
|
||||||
/* Tutorial */
|
/* Tutorial */
|
||||||
#tutorial-container {
|
#tutorial-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1286,6 +1248,29 @@ button {
|
|||||||
display: inline;
|
display: inline;
|
||||||
width: 25%; }
|
width: 25%; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styling for the Augmentations UI. This is the page that displays all of the
|
||||||
|
* player's owned and purchased Augmentations and Source-Files. It also allows
|
||||||
|
* the player to install Augmentations
|
||||||
|
*/
|
||||||
|
/* COLORS */
|
||||||
|
/* Attributes */
|
||||||
|
#augmentations-container {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px; }
|
||||||
|
|
||||||
|
#augmentations-content > p {
|
||||||
|
font-size: 14px;
|
||||||
|
width: 70%; }
|
||||||
|
|
||||||
|
.augmentations-list button,
|
||||||
|
.augmentations-list div {
|
||||||
|
color: var(--my-font-color);
|
||||||
|
text-decoration: none; }
|
||||||
|
|
||||||
|
.augmentations-list button {
|
||||||
|
padding: 4px; }
|
||||||
|
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
/**
|
/**
|
||||||
@@ -1313,25 +1298,30 @@ button {
|
|||||||
font-size: 13px; }
|
font-size: 13px; }
|
||||||
#stock-market-container a {
|
#stock-market-container a {
|
||||||
font-size: 14px; }
|
font-size: 14px; }
|
||||||
#stock-market-container h2 {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: 10px;
|
|
||||||
display: block; }
|
|
||||||
|
|
||||||
#stock-market-list li button {
|
.stock-market-info-and-purchases > h2 {
|
||||||
font-size: 16px; }
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 10px; }
|
||||||
|
|
||||||
#stock-market-container p {
|
.stock-market-info-and-purchases > p {
|
||||||
padding: 6px;
|
display: block;
|
||||||
margin: 6px;
|
margin-left: 10px;
|
||||||
width: 70%; }
|
width: 70%; }
|
||||||
|
|
||||||
#stock-market-container a {
|
.stock-market-info-and-purchases > a, .stock-market-info-and-purchases > button {
|
||||||
margin: 10px; }
|
margin: 10px; }
|
||||||
|
|
||||||
|
#stock-market-list {
|
||||||
|
list-style: none; }
|
||||||
|
#stock-market-list li button {
|
||||||
|
font-size: 16px; }
|
||||||
|
|
||||||
#stock-market-watchlist-filter {
|
#stock-market-watchlist-filter {
|
||||||
width: 50%;
|
display: block;
|
||||||
margin-left: 10px; }
|
margin: 5px 5px 5px 10px;
|
||||||
|
padding: 4px;
|
||||||
|
width: 50%; }
|
||||||
|
|
||||||
.stock-market-input {
|
.stock-market-input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -1341,13 +1331,28 @@ button {
|
|||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
color: var(--my-font-color); }
|
color: var(--my-font-color); }
|
||||||
|
|
||||||
|
.stock-market-price-movement-warning {
|
||||||
|
border: 1px solid white;
|
||||||
|
color: red;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 2px; }
|
||||||
|
|
||||||
.stock-market-position-text {
|
.stock-market-position-text {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: inline-block; }
|
display: block; }
|
||||||
|
.stock-market-position-text p {
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px; }
|
||||||
|
.stock-market-position-text h3 {
|
||||||
|
margin: 4px; }
|
||||||
|
|
||||||
.stock-market-order-list {
|
.stock-market-order-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 100px; }
|
max-height: 100px; }
|
||||||
|
.stock-market-order-list li {
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px; }
|
||||||
|
|
||||||
.stock-market-order-cancel-btn {
|
.stock-market-order-cancel-btn {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
@@ -4866,4 +4871,4 @@ html {
|
|||||||
margin-right: 0px; }
|
margin-right: 0px; }
|
||||||
|
|
||||||
|
|
||||||
/*# sourceMappingURL=engine.css.map*/
|
/*# sourceMappingURL=engineStyle.css.map*/
|
||||||
623
dist/vendor.bundle.js
vendored
623
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@@ -86,8 +86,11 @@ 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
|
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.
|
switch BitNodes its synchronization will initially be set to 10, rather than 1.
|
||||||
|
|
||||||
Memory can only be increased by purchasing upgrades from The Covenant.
|
Memory can only be increased by purchasing upgrades from The Covenant. Just like
|
||||||
It is a persistent stat, meaning it never gets reset back to 1.
|
the ability to purchase additional sleeves, this is only available in BitNodes-10
|
||||||
|
and above, and is only available after defeating BitNode-10 at least once.
|
||||||
|
|
||||||
|
Memory is a persistent stat, meaning it never gets reset back to 1.
|
||||||
The maximum possible value for a sleeve's memory is 100.
|
The maximum possible value for a sleeve's memory is 100.
|
||||||
|
|
||||||
Re-sleeving
|
Re-sleeving
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ buy and sell stocks in order to make money.
|
|||||||
|
|
||||||
The WSE can be found in the 'City' tab, and is accessible in every city.
|
The WSE can be found in the 'City' tab, and is accessible in every city.
|
||||||
|
|
||||||
Automating the Stock Market
|
Fundamentals
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
------------
|
||||||
You can write scripts to perform automatic and algorithmic trading on the Stock Market.
|
The Stock Market is not as simple as "buy at price X and sell at price Y". The following
|
||||||
See :ref:`netscript_tixapi` for more details.
|
are several fundamental concepts you need to understand about the stock market.
|
||||||
|
|
||||||
|
.. note:: For those that have experience with finance/trading/investing, please be aware
|
||||||
|
that the game's stock market does not function exactly like it does in the real
|
||||||
|
world. So these concepts below should seem similar, but won't be exactly the same.
|
||||||
|
|
||||||
Positions: Long vs Short
|
Positions: Long vs Short
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@@ -21,16 +25,73 @@ is the exact opposite. In a Short position you purchase shares of a stock and
|
|||||||
earn a profit if the price of that stock decreases. This is also called 'shorting'
|
earn a profit if the price of that stock decreases. This is also called 'shorting'
|
||||||
a stock.
|
a stock.
|
||||||
|
|
||||||
NOTE: Shorting stocks is not available immediately, and must be unlocked later in the
|
.. note:: Shorting stocks is not available immediately, and must be unlocked later in the
|
||||||
game.
|
game.
|
||||||
|
|
||||||
|
Forecast & Second-Order Forecast
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
A stock's forecast is its likelihood of increasing or decreasing in value. The
|
||||||
|
forecast is typically represented by its probability of increasing in either
|
||||||
|
a decimal or percentage form. For example, a forecast of 70% means the stock
|
||||||
|
has a 70% chance of increasing and a 30% chance of decreasing.
|
||||||
|
|
||||||
|
A stock's second-order forecast is the target value that its forecast trends towards.
|
||||||
|
For example, if a stock has a forecast of 60% and a second-order forecast of 70%,
|
||||||
|
then the stock's forecast should slowly trend towards 70% over time. However, this is
|
||||||
|
determined by RNG so there is a chance that it may never reach 70%.
|
||||||
|
|
||||||
|
Both the forecast and the second-order forecast change over time.
|
||||||
|
|
||||||
|
A stock's forecast can be viewed after purchasing Four Sigma (4S) Market Data
|
||||||
|
access. This lets you see the forecast info on the Stock Market UI. If you also
|
||||||
|
purchase access to the 4S Market Data TIX API, then you can view a stock's forecast
|
||||||
|
using the :js:func:`getStockForecast` function.
|
||||||
|
|
||||||
|
A stock's second-order forecast is always hidden.
|
||||||
|
|
||||||
|
.. _gameplay_stock_market_spread:
|
||||||
|
|
||||||
|
Spread (Bid Price & Ask Price)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
The **bid price** is the maximum price at which someone will buy a stock on the
|
||||||
|
stock market.
|
||||||
|
|
||||||
|
The **ask price** is the minimum price that a seller is willing to receive for a stock
|
||||||
|
on the stock market
|
||||||
|
|
||||||
|
The ask price will always be higher than the bid price (This is because if a seller
|
||||||
|
is willing to receive less than the bid price, that transaction is guaranteed to
|
||||||
|
happen). The difference between the bid and ask price is known as the **spread**.
|
||||||
|
A stock's "price" will be the average of the bid and ask price.
|
||||||
|
|
||||||
|
The bid and ask price are important because these are the prices at which a
|
||||||
|
transaction actually occurs. If you purchase a stock in the long position, the cost
|
||||||
|
of your purchase depends on that stock's ask price. If you then try to sell that
|
||||||
|
stock (still in the long position), the price at which you sell is the stock's
|
||||||
|
bid price. Note that this is reversed for a short position. Purchasing a stock
|
||||||
|
in the short position will occur at the stock's bid price, and selling a stock
|
||||||
|
in the short position will occur at the stock's ask price.
|
||||||
|
|
||||||
|
Transactions Influencing Stock Forecast
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Buying or selling a large number of shares
|
||||||
|
of a stock will influence that stock's forecast & second-order forecast.
|
||||||
|
The forecast is the likelihood that the stock will increase or decrease in price.
|
||||||
|
The magnitude of this effect depends on the number of shares being transacted.
|
||||||
|
More shares will have a bigger effect.
|
||||||
|
|
||||||
|
The effect that transactions have on a stock's second-order forecast is
|
||||||
|
significantly smaller than the effect on its forecast.
|
||||||
|
|
||||||
|
.. _gameplay_stock_market_order_types:
|
||||||
|
|
||||||
Order Types
|
Order Types
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
There are three different types of orders you can make to buy or sell stocks on the exchange:
|
There are three different types of orders you can make to buy or sell stocks on the exchange:
|
||||||
Market Order, Limit Order, and Stop Order.
|
Market Order, Limit Order, and Stop Order.
|
||||||
|
|
||||||
Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked
|
.. note:: Limit Orders and Stop Orders are not available immediately, and must be unlocked
|
||||||
later in the game.
|
later in the game.
|
||||||
|
|
||||||
When you place a Market Order to buy or sell a stock, the order executes immediately at
|
When you place a Market Order to buy or sell a stock, the order executes immediately at
|
||||||
whatever the current price of the stock is. For example if you choose to short a stock
|
whatever the current price of the stock is. For example if you choose to short a stock
|
||||||
@@ -71,3 +132,77 @@ A Limit Order to sell will execute if the stock's price <= order's price
|
|||||||
A Stop Order to buy will execute if the stock's price <= order's price
|
A Stop Order to buy will execute if the stock's price <= order's price
|
||||||
|
|
||||||
A Stop Order to sell will execute if the stock's price >= order's price.
|
A Stop Order to sell will execute if the stock's price >= order's price.
|
||||||
|
|
||||||
|
.. _gameplay_stock_market_player_actions_influencing_stock:
|
||||||
|
|
||||||
|
Player Actions Influencing Stocks
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
It is possible for your actions elsewhere in the game to influence the stock market.
|
||||||
|
|
||||||
|
Hacking
|
||||||
|
If a server has a corresponding stock (e.g. *foodnstuff* server -> FoodNStuff
|
||||||
|
stock), then hacking that server can decrease the stock's second-order
|
||||||
|
forecast. This causes the corresponding stock's forecast to trend downwards in value
|
||||||
|
over time.
|
||||||
|
|
||||||
|
This effect only occurs if you set the *stock* option to
|
||||||
|
true when calling the :js:func:`hack` function. The chance that hacking a
|
||||||
|
server will cause this effect is based on what percentage of the
|
||||||
|
server's total money you steal.
|
||||||
|
|
||||||
|
A single hack will have a minor
|
||||||
|
effect, but continuously hacking a server for lots of money over time
|
||||||
|
will have a noticeable effect in making the stock's forecast trend downwards.
|
||||||
|
|
||||||
|
Growing
|
||||||
|
If a server has a corresponding stock (e.g. *foodnstuff* server -> FoodNStuff
|
||||||
|
stock), then growing that server's money can increase the stock's
|
||||||
|
second-order forecast. This causes the corresponding stock's
|
||||||
|
forecast to trend upwards in value over time.
|
||||||
|
|
||||||
|
This effect only occurs if you set the *stock* option to true when calling the
|
||||||
|
:js:func:`grow` function. The chance that growing a server will cause this
|
||||||
|
effect is based on what percentage of the server's total money to add to it.
|
||||||
|
|
||||||
|
A single grow operation will have a minor effect, but continuously growing
|
||||||
|
a server for lots of money over time will have a noticeable effect in making
|
||||||
|
the stock's forecast trend upwards.
|
||||||
|
|
||||||
|
Working for a Company
|
||||||
|
If a company has a corresponding stock, then working for that company will
|
||||||
|
increase the corresponding stock's second-order forecast. This will
|
||||||
|
cause the stock's forecast to (slowly) trend upwards in value
|
||||||
|
over time.
|
||||||
|
|
||||||
|
The potency of this effect is based on how "effective" you are when you work
|
||||||
|
(i.e. its based on your stats and multipliers).
|
||||||
|
|
||||||
|
Automating the Stock Market
|
||||||
|
---------------------------
|
||||||
|
You can write scripts to perform automatic and algorithmic trading on the Stock Market.
|
||||||
|
See :ref:`netscript_tixapi` for more details.
|
||||||
|
|
||||||
|
Under the Hood
|
||||||
|
--------------
|
||||||
|
Stock prices are updated very ~6 seconds.
|
||||||
|
|
||||||
|
Whether a stock's price moves up or down is determined by RNG. However,
|
||||||
|
stocks have properties that can influence the way their price moves. These properties
|
||||||
|
are hidden, although some of them can be made visible by purchasing the
|
||||||
|
Four Sigma (4S) Market Data upgrade. Some examples of these properties are:
|
||||||
|
|
||||||
|
* Volatility
|
||||||
|
* Likelihood of increasing or decreasing (i.e. the stock's forecast)
|
||||||
|
* Likelihood of forecast increasing or decreasing (i.e. the stock's second-order forecast)
|
||||||
|
* How easily a stock's price/forecast is influenced by transactions
|
||||||
|
* Spread percentage
|
||||||
|
* Maximum price (not a real maximum, more of a "soft cap")
|
||||||
|
|
||||||
|
Each stock has its own unique values for these properties.
|
||||||
|
|
||||||
|
Offline Progression
|
||||||
|
-------------------
|
||||||
|
The Stock Market does not change or process anything while the game has closed.
|
||||||
|
However, it does accumulate time when offline. This accumulated time allows
|
||||||
|
the stock market to run 50% faster when the game is opened again. This means
|
||||||
|
that stock prices will update every ~4 seconds instead of 6.
|
||||||
|
|||||||
@@ -3,6 +3,101 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
v0.47.1 - 6/27/2019
|
||||||
|
-------------------
|
||||||
|
* Stock Market changes:
|
||||||
|
* Transactions no longer influence stock prices (but they still influence forecast)
|
||||||
|
* Changed the way stocks behave, particularly with regard to how the stock forecast occasionally "flips"
|
||||||
|
* Hacking & growing a server can potentially affect the way the corresponding stock's forecast changes
|
||||||
|
* Working for a company positively affects the way the corresponding stock's forecast changes
|
||||||
|
|
||||||
|
* Scripts now start/stop instantly
|
||||||
|
* Improved performance when starting up many copies of a new NetscriptJS script (by Ornedan)
|
||||||
|
* Improved performance when killing scripts
|
||||||
|
* Dialog boxes can now be closed with the ESC key (by jaguilar)
|
||||||
|
* NetscriptJS scripts should now be "re-compiled" if their dependencies change (by jaguilar)
|
||||||
|
* write() function should now properly cause NetscriptJS scripts to "re-compile" (by jaguilar)
|
||||||
|
|
||||||
|
v0.47.0 - 5/17/2019
|
||||||
|
-------------------
|
||||||
|
* Stock Market changes:
|
||||||
|
* Implemented spread. Stock's now have bid and ask prices at which transactions occur
|
||||||
|
* Large transactions will now influence a stock's price and forecast
|
||||||
|
* This "influencing" can take effect in the middle of a transaction
|
||||||
|
* See documentation for more details on these changes
|
||||||
|
* Added getStockAskPrice(), getStockBidPrice() Netscript functions to the TIX API
|
||||||
|
* Added getStockPurchaseCost(), getStockSaleGain() Netscript functions to the TIX API
|
||||||
|
|
||||||
|
* Re-sleeves can no longer have the NeuroFlux Governor augmentation
|
||||||
|
* This is just a temporary patch until the mechanic gets re-worked
|
||||||
|
|
||||||
|
* hack(), grow(), and weaken() functions now take optional arguments for number of threads to use (by MasonD)
|
||||||
|
* codingcontract.attempt() now takes an optional argument that allows you to configure the function to return a contract's reward
|
||||||
|
* Adjusted RAM costs of Netscript Singularity functions (mostly increased)
|
||||||
|
* Adjusted RAM cost of codingcontract.getNumTriesRemaining() Netscript function
|
||||||
|
* Netscript Singularity functions no longer cost extra RAM outside of BitNode-4
|
||||||
|
* Corporation employees no longer have an "age" stat
|
||||||
|
* Gang Wanted level gain rate capped at 100 (per employee)
|
||||||
|
* Script startup/kill is now processed every 3 seconds, instead of 6 seconds
|
||||||
|
* getHackTime(), getGrowTime(), and getWeakenTime() now return Infinity if called on a Hacknet Server
|
||||||
|
* Money/Income tracker now displays money lost from hospitalizations
|
||||||
|
* Exported saves now have a unique filename based on current BitNode and timestamp
|
||||||
|
* Maximum number of Hacknet Servers decreased from 25 to 20
|
||||||
|
* Bug Fix: Corporation employees stats should no longer become negative
|
||||||
|
* Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios
|
||||||
|
* Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server
|
||||||
|
* Bug Fix: Duplicate Sleeves now properly have access to all Augmentations if you have a gang
|
||||||
|
* Bug Fix: getAugmentationsFromFaction() & purchaseAugmentation() functions should now work properly if you have a gang
|
||||||
|
* Bug Fix: Fixed issue that caused messages (.msg) to be sent when refreshing/reloading the game
|
||||||
|
* Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes
|
||||||
|
* Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads
|
||||||
|
* Bug Fix: Faction UI should now automatically update reputation
|
||||||
|
* Bug Fix: Fixed purchase4SMarketData()
|
||||||
|
* Bug Fix: Netscript1.0 now works properly for multiple 'namespace' imports (import * as namespace from "script")
|
||||||
|
* Bug Fix: Terminal 'wget' command now correctly evaluates directory paths
|
||||||
|
* Bug Fix: wget(), write(), and scp() Netscript functions now fail if an invalid filepath is passed in
|
||||||
|
* Bug Fix: Having Corporation warehouses at full capacity should no longer freeze game in certain conditions
|
||||||
|
* Bug Fix: Prevented an exploit that allows you to buy multiple copies of an Augmentation by holding the 'Enter' button
|
||||||
|
* Bug Fix: gang.getOtherGangInformation() now properly returns a deep copy
|
||||||
|
* Bug Fix: Fixed getScriptIncome() returning an undefined value
|
||||||
|
* Bug Fix: Fixed an issue with Hacknet Server hash rate not always updating
|
||||||
|
|
||||||
|
v0.46.3 - 4/20/2019
|
||||||
|
-------------------
|
||||||
|
* Added a new Augmentation: The Shadow's Simulacrum
|
||||||
|
* Improved tab autocompletion feature in Terminal so that it works better with directories
|
||||||
|
* Bug Fix: Tech vendor location UI now properly refreshed when purchasing a TOR router
|
||||||
|
* 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.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
|
v0.46.0 - 4/3/2019
|
||||||
------------------
|
------------------
|
||||||
* Added BitNode-9: Hacktocracy
|
* Added BitNode-9: Hacktocracy
|
||||||
|
|||||||
@@ -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.46'
|
version = '0.47'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.46.0'
|
release = '0.47.0'
|
||||||
|
|
||||||
# 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?
|
||||||
------------------
|
------------------
|
||||||
@@ -26,6 +27,7 @@ secrets that you've been searching for.
|
|||||||
Script Editors <scripteditors>
|
Script Editors <scripteditors>
|
||||||
Game Frozen or Stuck? <gamefrozen>
|
Game Frozen or Stuck? <gamefrozen>
|
||||||
Guides & Tips <guidesandtips>
|
Guides & Tips <guidesandtips>
|
||||||
|
Tools & Resources <toolsandresources>
|
||||||
Changelog <changelog>
|
Changelog <changelog>
|
||||||
Donate <https://paypal.me/danielyxie>
|
Donate <https://paypal.me/danielyxie>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
grow() Netscript Function
|
grow() Netscript Function
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
.. js:function:: grow(hostname/ip)
|
.. js:function:: grow(hostname/ip[, opts={}])
|
||||||
|
|
||||||
:param string hostname/ip: IP or hostname of the target server to grow
|
:param string hostname/ip: IP or hostname of the target server to grow
|
||||||
|
:param object opts: Optional parameters for configuring function behavior. Properties:
|
||||||
|
|
||||||
|
* threads (*number*) - Number of threads to use for this function.
|
||||||
|
Must be less than or equal to the number of threads the script is running with.
|
||||||
|
* stock (*boolean*) - If true, the function can affect the stock market. See
|
||||||
|
:ref:`gameplay_stock_market_player_actions_influencing_stock`
|
||||||
|
|
||||||
:returns: The number by which the money on the server was multiplied for the growth
|
:returns: The number by which the money on the server was multiplied for the growth
|
||||||
:RAM cost: 0.15 GB
|
:RAM cost: 0.15 GB
|
||||||
|
|
||||||
@@ -19,3 +26,4 @@ grow() Netscript Function
|
|||||||
Example::
|
Example::
|
||||||
|
|
||||||
grow("foodnstuff");
|
grow("foodnstuff");
|
||||||
|
grow("foodnstuff", { threads: 5 }); // Only use 5 threads to grow
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
hack() Netscript Function
|
hack() Netscript Function
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
.. js:function:: hack(hostname/ip)
|
.. js:function:: hack(hostname/ip[, opts={}])
|
||||||
|
|
||||||
:param string hostname/ip: IP or hostname of the target server to hack
|
:param string hostname/ip: IP or hostname of the target server to hack
|
||||||
|
:param object opts: Optional parameters for configuring function behavior. Properties:
|
||||||
|
|
||||||
|
* threads (*number*) - Number of threads to use for this function.
|
||||||
|
Must be less than or equal to the number of threads the script is running with.
|
||||||
|
* stock (*boolean*) - If true, the function can affect the stock market. See
|
||||||
|
:ref:`gameplay_stock_market_player_actions_influencing_stock`
|
||||||
|
|
||||||
:returns: The amount of money stolen if the hack is successful, and zero otherwise
|
:returns: The amount of money stolen if the hack is successful, and zero otherwise
|
||||||
:RAM cost: 0.1 GB
|
:RAM cost: 0.1 GB
|
||||||
|
|
||||||
@@ -20,3 +27,4 @@ hack() Netscript Function
|
|||||||
|
|
||||||
hack("foodnstuff");
|
hack("foodnstuff");
|
||||||
hack("10.1.2.3");
|
hack("10.1.2.3");
|
||||||
|
hack("foodnstuff", { threads: 5 }); // Only use 5 threads to hack
|
||||||
|
|||||||
@@ -21,4 +21,13 @@ hackAnalyzeThreads() Netscript Function
|
|||||||
If this function returns 50, this means that if your next `hack()` call
|
If this function returns 50, this means that if your next `hack()` call
|
||||||
is run on a script with 50 threads, it will steal $1m from the `foodnstuff` server.
|
is run on a script with 50 threads, it will steal $1m from the `foodnstuff` server.
|
||||||
|
|
||||||
**Warning**: The value returned by this function isn't necessarily a whole number.
|
.. warning:: The value returned by this function isn't necessarily a whole number.
|
||||||
|
.. warning:: It is possible for this function to return :code:`Infinity` or :code:`NaN` in
|
||||||
|
certain uncommon scenarios. This is because in JavaScript:
|
||||||
|
|
||||||
|
* :code:`0 / 0 = NaN`
|
||||||
|
* :code:`N / 0 = Infinity` for 0 < N < Infinity.
|
||||||
|
|
||||||
|
For example, if a server has no money available and you want to hack some positive
|
||||||
|
amount from it, then the function would return :code:`Infinity` because
|
||||||
|
this would be impossible.
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
weaken() Netscript Function
|
weaken() Netscript Function
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
.. js:function:: weaken(hostname/ip)
|
.. js:function:: weaken(hostname/ip[, opts={}])
|
||||||
|
|
||||||
:param string hostname/ip: IP or hostname of the target server to weaken
|
:param string hostname/ip: IP or hostname of the target server to weaken
|
||||||
|
:param object opts: Optional parameters for configuring function behavior. Properties:
|
||||||
|
|
||||||
|
* threads (*number*) - Number of threads to use for this function.
|
||||||
|
Must be less than or equal to the number of threads the script is running with.
|
||||||
|
|
||||||
:returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied
|
:returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied
|
||||||
by the number of script threads
|
by the number of script threads
|
||||||
:RAM cost: 0.15 GB
|
:RAM cost: 0.15 GB
|
||||||
@@ -18,3 +23,4 @@ weaken() Netscript Function
|
|||||||
Example::
|
Example::
|
||||||
|
|
||||||
weaken("foodnstuff");
|
weaken("foodnstuff");
|
||||||
|
weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
attempt() Netscript Function
|
attempt() Netscript Function
|
||||||
============================
|
============================
|
||||||
|
|
||||||
.. js:function:: attempt(answer, fn[, hostname/ip=current ip])
|
.. js:function:: attempt(answer, fn[, hostname/ip=current ip, opts={}])
|
||||||
|
|
||||||
:param answer: Solution for the contract
|
:param answer: Solution for the contract
|
||||||
:param string fn: Filename of the contract
|
:param string fn: Filename of the contract
|
||||||
:param string hostname/ip: Hostname or IP of the server containing the contract.
|
:param string hostname/ip: Hostname or IP of the server containing the contract.
|
||||||
Optional. Defaults to current server if not provided
|
Optional. Defaults to current server if not provided
|
||||||
|
:param object opts: Optional parameters for configuring function behavior. Properties:
|
||||||
|
|
||||||
|
* returnReward (*boolean*) If truthy, then the function will return a string
|
||||||
|
that states the contract's reward when it is successfully solved.
|
||||||
|
|
||||||
Attempts to solve the Coding Contract with the provided solution.
|
Attempts to solve the Coding Contract with the provided solution.
|
||||||
|
|
||||||
:returns: Boolean indicating whether the solution was correct
|
:returns: Boolean indicating whether the solution was correct. If the :code:`returnReward`
|
||||||
|
option is configured, then the function will instead return a string. If the
|
||||||
|
contract is successfully solved, the string will contain a description of the
|
||||||
|
contract's reward. Otherwise, it will be an empty string.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ hashCost() Netscript Function
|
|||||||
|
|
||||||
.. warning:: This page contains spoilers for the game
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
.. js:function:: hashCost(upgName, upgTarget)
|
.. js:function:: hashCost(upgName)
|
||||||
|
|
||||||
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ later in the game
|
|||||||
|
|
||||||
.. warning:: This page contains spoilers for the game
|
.. warning:: This page contains spoilers for the game
|
||||||
|
|
||||||
The Gang API is unlocked in BitNode-2. Currently, BitNode-2 is the only location
|
The Gang mechanic and the Gang API are unlocked in BitNode-2.
|
||||||
where the Gang mechanic is accessible. This may change in the future
|
|
||||||
|
|
||||||
**Gang API functions must be accessed through the 'gang' namespace**
|
**Gang API functions must be accessed through the 'gang' namespace**
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,15 @@ access even after you 'reset' by installing Augmentations
|
|||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: API Functions:
|
:caption: API Functions:
|
||||||
|
|
||||||
getStockSymbols() <tixapi/getStockSymbols>
|
getStockSymbols() <tixapi/getStockSymbols>
|
||||||
getStockPrice() <tixapi/getStockPrice>
|
getStockPrice() <tixapi/getStockPrice>
|
||||||
|
getStockAskPrice() <tixapi/getStockAskPrice>
|
||||||
|
getStockBidPrice() <tixapi/getStockBidPrice>
|
||||||
getStockPosition() <tixapi/getStockPosition>
|
getStockPosition() <tixapi/getStockPosition>
|
||||||
getStockMaxShares() <tixapi/getStockMaxShares>
|
getStockMaxShares() <tixapi/getStockMaxShares>
|
||||||
|
getStockPurchaseCost() <tixapi/getStockPurchaseCost>
|
||||||
|
getStockSaleGain() <tixapi/getStockSaleGain>
|
||||||
buyStock() <tixapi/buyStock>
|
buyStock() <tixapi/buyStock>
|
||||||
sellStock() <tixapi/sellStock>
|
sellStock() <tixapi/sellStock>
|
||||||
shortStock() <tixapi/shortStock>
|
shortStock() <tixapi/shortStock>
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ You can use the Singularity Functions in other BitNodes if and only if you have
|
|||||||
Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to
|
Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to
|
||||||
level 3, then you will be able to access all of the Singularity Functions.
|
level 3, then you will be able to access all of the Singularity Functions.
|
||||||
|
|
||||||
Note that Singularity Functions require twice as much RAM outside of BitNode-4
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: Functions:
|
:caption: Functions:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ workForFaction() Netscript Function
|
|||||||
|
|
||||||
:param string factionName: Name of faction to work for. CASE-SENSITIVE
|
:param string factionName: Name of faction to work for. CASE-SENSITIVE
|
||||||
:param string workType:
|
:param string workType:
|
||||||
Type of work to perform for the faction
|
Type of work to perform for the faction:
|
||||||
|
|
||||||
* hacking/hacking contracts/hackingcontracts
|
* hacking/hacking contracts/hackingcontracts
|
||||||
* field/fieldwork/field work
|
* field/fieldwork/field work
|
||||||
|
|||||||
@@ -61,5 +61,5 @@ getInformation() Netscript Function
|
|||||||
workChaExpGain: charisma exp gained from work,
|
workChaExpGain: charisma exp gained from work,
|
||||||
workMoneyGain: money gained from work,
|
workMoneyGain: money gained from work,
|
||||||
},
|
},
|
||||||
workRepGain: sl.getRepGain(),
|
workRepGain: Reputation gain rate when working for factions or companies
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
getSleeveAugmentations() Netscript Function
|
getSleeveAugmentations() Netscript Function
|
||||||
=======================================
|
===========================================
|
||||||
|
|
||||||
.. js:function:: getSleeveAugmentations(sleeveNumber)
|
.. js:function:: getSleeveAugmentations(sleeveNumber)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ getOrders() Netscript Function
|
|||||||
:RAM cost: 2.5 GB
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Returns your order book for the stock market. This is an object containing information
|
Returns your order book for the stock market. This is an object containing information
|
||||||
for all the Limit and Stop Orders you have in the stock market.
|
for all the :ref:`Limit and Stop Orders <gameplay_stock_market_order_types>`
|
||||||
|
you have in the stock market.
|
||||||
|
|
||||||
|
.. note:: This function isn't accessible until you have unlocked the ability to use
|
||||||
|
Limit and Stop Orders.
|
||||||
|
|
||||||
The object has the following structure::
|
The object has the following structure::
|
||||||
|
|
||||||
|
|||||||
12
doc/source/netscript/tixapi/getStockAskPrice.rst
Normal file
12
doc/source/netscript/tixapi/getStockAskPrice.rst
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
getStockAskPrice() Netscript Function
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. js:function:: getStockAskPrice(sym)
|
||||||
|
|
||||||
|
:param string sym: Stock symbol
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
|
Given a stock's symbol, returns the ask price of that stock (the symbol is a sequence
|
||||||
|
of two to four capital letters, **not** the name of the company to which that stock belongs).
|
||||||
|
|
||||||
|
See :ref:`gameplay_stock_market_spread` for details on what the ask price is.
|
||||||
12
doc/source/netscript/tixapi/getStockBidPrice.rst
Normal file
12
doc/source/netscript/tixapi/getStockBidPrice.rst
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
getStockBidPrice() Netscript Function
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. js:function:: getStockBidPrice(sym)
|
||||||
|
|
||||||
|
:param string sym: Stock symbol
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
|
Given a stock's symbol, returns the bid price of that stock (the symbol is a sequence
|
||||||
|
of two to four capital letters, **not** the name of the company to which that stock belongs).
|
||||||
|
|
||||||
|
See :ref:`gameplay_stock_market_spread` for details on what the bid price is.
|
||||||
@@ -6,9 +6,12 @@ getStockPrice() Netscript Function
|
|||||||
:param string sym: Stock symbol
|
:param string sym: Stock symbol
|
||||||
:RAM cost: 2 GB
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
Returns the price of a stock, given its symbol (NOT the company name). The symbol is a sequence
|
Given a stock's symbol, returns the price of that stock (the symbol is a sequence
|
||||||
of two to four capital letters.
|
of two to four capital letters, **not** the name of the company to which that stock belongs).
|
||||||
|
|
||||||
|
.. note:: The stock's price is the average of its bid and ask price.
|
||||||
|
See :ref:`gameplay_stock_market_spread` for details on what this means.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
getStockPrice("FISG");
|
getStockPrice("FSIG");
|
||||||
|
|||||||
14
doc/source/netscript/tixapi/getStockPurchaseCost.rst
Normal file
14
doc/source/netscript/tixapi/getStockPurchaseCost.rst
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
getStockPurchaseCost() Netscript Function
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
.. js:function:: getStockPurchaseCost(sym, shares, posType)
|
||||||
|
|
||||||
|
:param string sym: Stock symbol
|
||||||
|
:param number shares: Number of shares to purchase
|
||||||
|
:param string posType: Specifies whether the order is a "Long" or "Short" position.
|
||||||
|
The values "L" or "S" can also be used.
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
|
Calculates and returns how much it would cost to buy a given number of
|
||||||
|
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`
|
||||||
|
and commission fees.
|
||||||
14
doc/source/netscript/tixapi/getStockSaleGain.rst
Normal file
14
doc/source/netscript/tixapi/getStockSaleGain.rst
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
getStockSaleGain() Netscript Function
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. js:function:: getStockSaleGain(sym, shares, posType)
|
||||||
|
|
||||||
|
:param string sym: Stock symbol
|
||||||
|
:param number shares: Number of shares to sell
|
||||||
|
:param string posType: Specifies whether the order is a "Long" or "Short" position.
|
||||||
|
The values "L" or "S" can also be used.
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
|
Calculates and returns how much you would gain from selling a given number of
|
||||||
|
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`
|
||||||
|
and commission fees.
|
||||||
@@ -19,8 +19,10 @@ placeOrder() Netscript Function
|
|||||||
NOT case-sensitive.
|
NOT case-sensitive.
|
||||||
:RAM cost: 2.5 GB
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Places an order on the stock market. This function only works for `Limit and Stop Orders <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
Places an order on the stock market. This function only works
|
||||||
|
for :ref:`Limit and Stop Orders <gameplay_stock_market_order_types>`.
|
||||||
The ability to place limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
|
|
||||||
|
|
||||||
Returns true if the order is successfully placed, and false otherwise.
|
Returns true if the order is successfully placed, and false otherwise.
|
||||||
|
|
||||||
|
.. note:: The ability to place limit and stop orders is **not** immediately available to
|
||||||
|
the player and must be unlocked later on in the game.
|
||||||
|
|||||||
23
doc/source/toolsandresources.rst
Normal file
23
doc/source/toolsandresources.rst
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Tools & Resources
|
||||||
|
=================
|
||||||
|
|
||||||
|
Official Script Repository
|
||||||
|
--------------------------
|
||||||
|
There are plans to create an official repository of Bitburner scripts. As of right now,
|
||||||
|
this is not a priority and has not been started. However, if you'd like
|
||||||
|
to contribute scripts now, you can find the repository
|
||||||
|
`here <https://github.com/bitburner-official/bitburner-scripts>`_ and submit pull requests.
|
||||||
|
|
||||||
|
Visual Studio Code Extension
|
||||||
|
----------------------------
|
||||||
|
One user created a Bitburner extension for the Visual Studio Code (VSCode) editor.
|
||||||
|
|
||||||
|
This extension includes several features such as:
|
||||||
|
|
||||||
|
* Dynamic RAM calculation
|
||||||
|
* RAM Usage breakdown
|
||||||
|
* Typescript definition files with jsdoc comments
|
||||||
|
* Netscript syntax highlighting
|
||||||
|
|
||||||
|
You can find more information and download links
|
||||||
|
`on this reddit post <https://www.reddit.com/r/Bitburner/comments/bh48y2/visual_studio_code_ram_calculator_extra/>`_.
|
||||||
56
index.html
56
index.html
@@ -25,7 +25,7 @@
|
|||||||
ga('create', 'UA-100157497-1', 'auto');
|
ga('create', 'UA-100157497-1', 'auto');
|
||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
</script>
|
</script>
|
||||||
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="dist/engine.css" rel="stylesheet"></head>
|
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="dist/engineStyle.css" rel="stylesheet"></head>
|
||||||
<body>
|
<body>
|
||||||
<div id="entire-game-container" style="visibility:hidden;">
|
<div id="entire-game-container" style="visibility:hidden;">
|
||||||
<div id="mainmenu-container">
|
<div id="mainmenu-container">
|
||||||
@@ -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>
|
||||||
@@ -221,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>
|
||||||
|
|
||||||
@@ -279,53 +277,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="stock-market-container" class="generic-menupage-container">
|
<div id="stock-market-container" class="generic-menupage-container">
|
||||||
<p>
|
<!-- React Component -->
|
||||||
Welcome to the World Stock Exchange (WSE)! <br/><br/>
|
|
||||||
|
|
||||||
To begin trading, you must first purchase an account. WSE accounts will persist
|
|
||||||
after you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
<a id="stock-market-buy-account" class="a-link-button-inactive"> Buy WSE Account </a>
|
|
||||||
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
|
|
||||||
|
|
||||||
<h2> Trade Information eXchange (TIX) API </h2>
|
|
||||||
<p>
|
|
||||||
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
|
|
||||||
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
|
|
||||||
trading strategies.
|
|
||||||
<br/><br/>
|
|
||||||
If you purchase access to the TIX API, you will retain that access even after
|
|
||||||
you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
|
|
||||||
|
|
||||||
<h2> Four Sigma (4S) Market Data Feed </h2>
|
|
||||||
<p>
|
|
||||||
Four Sigma's (4S) Market Data Feed provides information about stocks
|
|
||||||
that will help your trading strategies.
|
|
||||||
<br/><br/>
|
|
||||||
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
|
|
||||||
retain that access even after you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<a id="stock-market-buy-4s-data" class="a-link-button-inactive tooltip">
|
|
||||||
Buy 4S Market Data Feed
|
|
||||||
</a>
|
|
||||||
<div class="help-tip-big" id="stock-market-4s-data-help-tip">?</div>
|
|
||||||
|
|
||||||
<a id="stock-market-buy-4s-tix-api" class="a-link-button-inactive tooltip">
|
|
||||||
Buy 4S Market Data TIX API Access
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<p id="stock-market-commission"> </p>
|
|
||||||
<a id="stock-market-mode" class="a-link-button tooltip"></a>
|
|
||||||
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
|
|
||||||
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
|
|
||||||
<br/><br/>
|
|
||||||
<input id="stock-market-watchlist-filter" class="text-input" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
|
||||||
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
|
|
||||||
<ul id="stock-market-list" style="list-style:none;">
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Log Box -->
|
<!-- Log Box -->
|
||||||
@@ -636,7 +588,7 @@
|
|||||||
<p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p>
|
<p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script></body>
|
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script><script type="text/javascript" src="dist/engineStyle.bundle.js"></script></body>
|
||||||
|
|
||||||
<!-- Misc Scripts -->
|
<!-- Misc Scripts -->
|
||||||
<script src="src/ThirdParty/raphael.min.js"></script>
|
<script src="src/ThirdParty/raphael.min.js"></script>
|
||||||
|
|||||||
1252
package-lock.json
generated
1252
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -9,8 +9,9 @@
|
|||||||
"@types/numeral": "0.0.25",
|
"@types/numeral": "0.0.25",
|
||||||
"@types/react": "^16.8.6",
|
"@types/react": "^16.8.6",
|
||||||
"@types/react-dom": "^16.8.2",
|
"@types/react-dom": "^16.8.2",
|
||||||
"acorn": "^5.0.0",
|
"acorn": "^5.7.3",
|
||||||
"acorn-dynamic-import": "^2.0.0",
|
"acorn-dynamic-import": "^2.0.0",
|
||||||
|
"acorn-walk": "^6.1.1",
|
||||||
"ajv": "^5.1.5",
|
"ajv": "^5.1.5",
|
||||||
"ajv-keywords": "^2.0.0",
|
"ajv-keywords": "^2.0.0",
|
||||||
"async": "^2.6.1",
|
"async": "^2.6.1",
|
||||||
@@ -44,12 +45,13 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.3.4",
|
"@babel/core": "^7.3.4",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"@types/chai": "^4.1.7",
|
||||||
|
"@types/mocha": "^5.2.7",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"beautify-lint": "^1.0.3",
|
"beautify-lint": "^1.0.3",
|
||||||
"benchmark": "^2.1.1",
|
"benchmark": "^2.1.1",
|
||||||
"bundle-loader": "~0.5.0",
|
"bundle-loader": "~0.5.0",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.2.0",
|
||||||
"chai-as-promised": "^7.1.1",
|
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"es6-promise-polyfill": "^1.1.1",
|
"es6-promise-polyfill": "^1.1.1",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^4.19.1",
|
||||||
@@ -59,15 +61,18 @@
|
|||||||
"i18n-webpack-plugin": "^1.0.0",
|
"i18n-webpack-plugin": "^1.0.0",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"js-beautify": "^1.5.10",
|
"js-beautify": "^1.5.10",
|
||||||
|
"jsdom": "^15.0.0",
|
||||||
|
"jsdom-global": "^3.0.2",
|
||||||
"json5": "^1.0.1",
|
"json5": "^1.0.1",
|
||||||
"less": "^3.9.0",
|
"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",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^6.1.4",
|
||||||
"mocha-lcov-reporter": "^1.0.0",
|
"mochapack": "^1.1.1",
|
||||||
"node-sass": "^4.10.0",
|
"node-sass": "^4.10.0",
|
||||||
|
"null-loader": "^1.0.0",
|
||||||
"raw-loader": "~0.5.0",
|
"raw-loader": "~0.5.0",
|
||||||
"sass-loader": "^7.0.3",
|
"sass-loader": "^7.0.3",
|
||||||
"script-loader": "~0.7.0",
|
"script-loader": "~0.7.0",
|
||||||
@@ -106,13 +111,15 @@
|
|||||||
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"build:dev": "webpack --mode development",
|
"build:dev": "webpack --mode development",
|
||||||
|
"build:test": "webpack --config webpack.config-test.js",
|
||||||
"lint": "npm run lint:typescript & npm run lint:javascript & npm run lint:style",
|
"lint": "npm run lint:typescript & npm run lint:javascript & npm run lint:style",
|
||||||
"lint:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js",
|
"lint:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js",
|
||||||
"lint:style": "stylelint ./css/*",
|
"lint:style": "stylelint ./css/*",
|
||||||
"lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts",
|
"lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts",
|
||||||
"preinstall": "node ./scripts/engines-check.js",
|
"preinstall": "node ./scripts/engines-check.js",
|
||||||
|
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
|
||||||
"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.47.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,328 +0,0 @@
|
|||||||
import {workerScripts,
|
|
||||||
killWorkerScript} from "./NetscriptWorker";
|
|
||||||
import { Player } from "./Player";
|
|
||||||
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";
|
|
||||||
|
|
||||||
/* {
|
|
||||||
* serverName: {
|
|
||||||
* header: Server Header Element
|
|
||||||
* panel: Server Panel List (ul) element
|
|
||||||
* scripts: {
|
|
||||||
* script id: Ref to Script information
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
*/
|
|
||||||
let ActiveScriptsUI = {};
|
|
||||||
let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements
|
|
||||||
|
|
||||||
const getHeaderHtml = (server) => {
|
|
||||||
// TODO: calculate the longest hostname length rather than hard coding it
|
|
||||||
const longestHostnameLength = 18;
|
|
||||||
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
|
|
||||||
const barOptions = {
|
|
||||||
progress: server.ramUsed / server.maxRam,
|
|
||||||
totalTicks: 30
|
|
||||||
};
|
|
||||||
return `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateHeaderHtml = (server) => {
|
|
||||||
const accordion = ActiveScriptsUI[server.hostname];
|
|
||||||
if (accordion === null || accordion === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
|
||||||
accordion.header.dataset.ramPercentage = ramPercentage;
|
|
||||||
accordion.header.innerHTML = getHeaderHtml(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createActiveScriptsServerPanel(server) {
|
|
||||||
let hostname = server.hostname;
|
|
||||||
|
|
||||||
var activeScriptsList = document.getElementById("active-scripts-list");
|
|
||||||
|
|
||||||
let res = createAccordionElement({
|
|
||||||
hdrText: getHeaderHtml(server)
|
|
||||||
});
|
|
||||||
let li = res[0];
|
|
||||||
var hdr = res[1];
|
|
||||||
let panel = res[2];
|
|
||||||
|
|
||||||
if (ActiveScriptsUI[hostname] != null) {
|
|
||||||
console.log("WARNING: Tried to create already-existing Active Scripts Server panel. This is most likely fine. It probably means many scripts just got started up on a new server. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var panelScriptList = createElement("ul");
|
|
||||||
panel.appendChild(panelScriptList);
|
|
||||||
activeScriptsList.appendChild(li);
|
|
||||||
|
|
||||||
ActiveScriptsUI[hostname] = {
|
|
||||||
header: hdr,
|
|
||||||
panel: panel,
|
|
||||||
panelList: panelScriptList,
|
|
||||||
scripts: {}, //Holds references to li 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
|
|
||||||
};
|
|
||||||
|
|
||||||
return li;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Deletes the info for a particular server (Dropdown header + Panel with all info)
|
|
||||||
//in the Active Scripts page if it exists
|
|
||||||
function deleteActiveScriptsServerPanel(server) {
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
console.log("WARNING: Tried to delete non-existent Active Scripts Server panel. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Make sure it's empty
|
|
||||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
|
|
||||||
console.log("WARNING: Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeElement(ActiveScriptsUI[hostname].panel);
|
|
||||||
removeElement(ActiveScriptsUI[hostname].header);
|
|
||||||
delete ActiveScriptsUI[hostname];
|
|
||||||
}
|
|
||||||
|
|
||||||
function addActiveScriptsItem(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.log("ERROR: Invalid server IP for workerscript in addActiveScriptsItem()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
|
|
||||||
ActiveScriptsTasks.push(function(workerscript, hostname) {
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
createActiveScriptsServerPanel(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create the unique identifier (key) for this script
|
|
||||||
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
let res = createAccordionElement({hdrText:workerscript.name});
|
|
||||||
let li = res[0];
|
|
||||||
let hdr = res[1];
|
|
||||||
let panel = res[2];
|
|
||||||
|
|
||||||
hdr.classList.remove("accordion-header");
|
|
||||||
hdr.classList.add("active-scripts-script-header");
|
|
||||||
panel.classList.remove("accordion-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
|
|
||||||
panel.appendChild(createElement("p", {
|
|
||||||
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
|
|
||||||
"Args: " + arrayToString(workerscript.args)
|
|
||||||
}));
|
|
||||||
var panelText = createElement("p", {
|
|
||||||
innerText: "Loading...",
|
|
||||||
fontSize: "14px",
|
|
||||||
});
|
|
||||||
panel.appendChild(panelText);
|
|
||||||
panel.appendChild(createElement("br"));
|
|
||||||
panel.appendChild(createElement("span", {
|
|
||||||
innerText: "Log",
|
|
||||||
class: "accordion-button",
|
|
||||||
margin: "4px",
|
|
||||||
padding: "4px",
|
|
||||||
clickListener: () => {
|
|
||||||
logBoxCreate(workerscript.scriptRef);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
panel.appendChild(createElement("span", {
|
|
||||||
innerText: "Kill Script",
|
|
||||||
class: "accordion-button",
|
|
||||||
margin: "4px",
|
|
||||||
padding: "4px",
|
|
||||||
clickListener: () => {
|
|
||||||
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.server);
|
|
||||||
dialogBoxCreate("Killing script, may take a few minutes to complete...");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
//Append element to list
|
|
||||||
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
|
||||||
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
|
||||||
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
|
||||||
ActiveScriptsUI[hostname].scriptStats[itemName] = panelText;
|
|
||||||
}.bind(null, workerscript, hostname));
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteActiveScriptsItem(workerscript) {
|
|
||||||
ActiveScriptsTasks.push(function(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
throw new Error("ERROR: Invalid server IP for workerscript. This most likely occurred because " +
|
|
||||||
"you tried to delete a large number of scripts and also deleted servers at the " +
|
|
||||||
"same time. It's not a big deal, just save and refresh the game.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
console.log("ERROR: Trying to delete Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
let li = ActiveScriptsUI[hostname].scripts[itemName];
|
|
||||||
if (li == null) {
|
|
||||||
console.log("ERROR: Cannot find Active Script UI element for workerscript: ");
|
|
||||||
console.log(workerscript);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
removeElement(li);
|
|
||||||
delete ActiveScriptsUI[hostname].scripts[itemName];
|
|
||||||
delete ActiveScriptsUI[hostname].scriptHdrs[itemName];
|
|
||||||
delete ActiveScriptsUI[hostname].scriptStats[itemName];
|
|
||||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length === 0) {
|
|
||||||
deleteActiveScriptsServerPanel(server);
|
|
||||||
}
|
|
||||||
}.bind(null, workerscript));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the ActiveScriptsItems array
|
|
||||||
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...
|
|
||||||
let numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
|
||||||
for (let i = 0; i < numTasks; ++i) {
|
|
||||||
let task = ActiveScriptsTasks.shift();
|
|
||||||
try {
|
|
||||||
task();
|
|
||||||
} catch(e) {
|
|
||||||
exceptionAlert(e);
|
|
||||||
console.log(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!routing.isOn(Page.ActiveScripts)) {return;}
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < workerScripts.length; ++i) {
|
|
||||||
try {
|
|
||||||
total += updateActiveScriptsItemContent(workerScripts[i]);
|
|
||||||
} catch(e) {
|
|
||||||
exceptionAlert(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Updates the content of the given item in the Active Scripts list
|
|
||||||
function updateActiveScriptsItemContent(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.log("ERROR: Invalid server IP for workerscript in updateActiveScriptsItemContent().");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
return; //Hasn't been created yet. We'll skip it
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHeaderHtml(server);
|
|
||||||
|
|
||||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
|
||||||
return; //Hasn't been fully added yet. We'll skip it
|
|
||||||
}
|
|
||||||
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
|
||||||
|
|
||||||
//Update the text if necessary. This fn returns the online $/s production
|
|
||||||
return updateActiveScriptsText(workerscript, item, itemName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateActiveScriptsText(workerscript, item, itemName) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.log("ERROR: Invalid server IP for workerscript for updateActiveScriptsText()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null || ActiveScriptsUI[hostname].scriptHdrs[itemName] == null) {
|
|
||||||
console.log("ERROR: Trying to update Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHeaderHtml(server);
|
|
||||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
|
||||||
|
|
||||||
//Only update if the item is visible
|
|
||||||
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
|
||||||
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
|
||||||
|
|
||||||
removeChildrenFromElement(item);
|
|
||||||
|
|
||||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
|
||||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
|
||||||
|
|
||||||
//Online
|
|
||||||
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 onlineMpsText = "Online production rate: " + numeralWrapper.formatMoney(onlineMps) + " / second";
|
|
||||||
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
|
||||||
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
|
||||||
|
|
||||||
//Offline
|
|
||||||
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 offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
|
|
||||||
var offlineMpsText = "Offline production rate: " + numeralWrapper.formatMoney(offlineMps) + " / second";
|
|
||||||
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
|
|
||||||
var offlineEpsText = (Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, " ");
|
|
||||||
|
|
||||||
item.innerHTML = onlineTime + "<br>" + offlineTime + "<br>" + onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
|
|
||||||
onlineMpsText + "<br>" + onlineEpsText + "<br>" + offlineTotalMoneyMade + "<br>" + offlineTotalExpEarned + "<br>" +
|
|
||||||
offlineMpsText + "<br>" + offlineEpsText + "<br>";
|
|
||||||
return onlineMps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {addActiveScriptsItem, deleteActiveScriptsItem, updateActiveScriptsItems};
|
|
||||||
14
src/Alias.ts
14
src/Alias.ts
@@ -1,5 +1,5 @@
|
|||||||
import { IMap } from "./types";
|
import { IMap } from "./types";
|
||||||
import { post } from "./ui/postToTerminal";
|
import { post } from "./ui/postToTerminal";
|
||||||
|
|
||||||
export let Aliases: IMap<string> = {};
|
export let Aliases: IMap<string> = {};
|
||||||
export let GlobalAliases: IMap<string> = {};
|
export let GlobalAliases: IMap<string> = {};
|
||||||
@@ -20,7 +20,7 @@ export function loadGlobalAliases(saveString: string): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Print all aliases to terminal
|
// Prints all aliases to terminal
|
||||||
export function printAliases(): void {
|
export function printAliases(): void {
|
||||||
for (var name in Aliases) {
|
for (var name in Aliases) {
|
||||||
if (Aliases.hasOwnProperty(name)) {
|
if (Aliases.hasOwnProperty(name)) {
|
||||||
@@ -34,7 +34,7 @@ export function printAliases(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//True if successful, false otherwise
|
// Returns true if successful, false otherwise
|
||||||
export function parseAliasDeclaration(dec: string, global: boolean=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);
|
||||||
@@ -90,8 +90,10 @@ export function removeAlias(name: string): boolean {
|
|||||||
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.
|
||||||
|
* Aliases are only applied to "whole words", one level deep
|
||||||
|
*/
|
||||||
export function substituteAliases(origCommand: string): string {
|
export function substituteAliases(origCommand: string): string {
|
||||||
const commandArray = origCommand.split(" ");
|
const commandArray = origCommand.split(" ");
|
||||||
if (commandArray.length > 0){
|
if (commandArray.length > 0){
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
42
src/Augmentation/ui/InstalledAugmentations.tsx
Normal file
42
src/Augmentation/ui/InstalledAugmentations.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* React Component for displaying a list of the player's installed Augmentations
|
||||||
|
* on the Augmentations UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||||
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
|
|
||||||
|
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||||
|
|
||||||
|
export function InstalledAugmentations(): React.ReactElement {
|
||||||
|
const sourceAugs = Player.augmentations.slice();
|
||||||
|
|
||||||
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
|
sourceAugs.sort((aug1, aug2) => {
|
||||||
|
return aug1.name <= aug2.name ? -1 : 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const augs = sourceAugs.map((e) => {
|
||||||
|
const aug = Augmentations[e.name];
|
||||||
|
|
||||||
|
let level = null;
|
||||||
|
if (e.name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
level = e.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={e.name}>
|
||||||
|
<AugmentationAccordion aug={aug} level={level} />
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>{augs}</>
|
||||||
|
)
|
||||||
|
}
|
||||||
107
src/Augmentation/ui/InstalledAugmentationsAndSourceFiles.tsx
Normal file
107
src/Augmentation/ui/InstalledAugmentationsAndSourceFiles.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* React Component for displaying all of the player's installed Augmentations and
|
||||||
|
* Source-Files.
|
||||||
|
*
|
||||||
|
* It also contains 'configuration' buttons that allow you to change how the
|
||||||
|
* Augs/SF's are displayed
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||||
|
import { ListConfiguration } from "./ListConfiguration";
|
||||||
|
import { OwnedSourceFiles } from "./OwnedSourceFiles";
|
||||||
|
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
|
|
||||||
|
type IProps = {}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
|
||||||
|
listRef: React.RefObject<HTMLUListElement>;
|
||||||
|
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
|
||||||
|
this.expandAllHeaders = this.expandAllHeaders.bind(this);
|
||||||
|
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
|
||||||
|
this.sortInOrder = this.sortInOrder.bind(this);
|
||||||
|
|
||||||
|
this.listRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
collapseAllHeaders() {
|
||||||
|
const ul = this.listRef.current;
|
||||||
|
if (ul == null) { return; }
|
||||||
|
const tickers = ul.getElementsByClassName("accordion-header");
|
||||||
|
for (let i = 0; i < tickers.length; ++i) {
|
||||||
|
const ticker = tickers[i];
|
||||||
|
if (!(ticker instanceof HTMLButtonElement)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticker.classList.contains("active")) {
|
||||||
|
ticker.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expandAllHeaders() {
|
||||||
|
const ul = this.listRef.current;
|
||||||
|
if (ul == null) { return; }
|
||||||
|
const tickers = ul.getElementsByClassName("accordion-header");
|
||||||
|
for (let i = 0; i < tickers.length; ++i) {
|
||||||
|
const ticker = tickers[i];
|
||||||
|
if (!(ticker instanceof HTMLButtonElement)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ticker.classList.contains("active")) {
|
||||||
|
ticker.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rerender() {
|
||||||
|
this.setState((prevState) => {
|
||||||
|
return {
|
||||||
|
rerenderFlag: !prevState.rerenderFlag,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByAcquirementTime() {
|
||||||
|
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
|
||||||
|
this.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
sortInOrder() {
|
||||||
|
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically
|
||||||
|
this.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ListConfiguration
|
||||||
|
collapseAllButtonsFn={this.collapseAllHeaders}
|
||||||
|
expandAllButtonsFn={this.expandAllHeaders}
|
||||||
|
sortByAcquirementTimeFn={this.sortByAcquirementTime}
|
||||||
|
sortInOrderFn={this.sortInOrder}
|
||||||
|
/>
|
||||||
|
<ul className="augmentations-list" ref={this.listRef}>
|
||||||
|
<OwnedSourceFiles />
|
||||||
|
<InstalledAugmentations />
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Augmentation/ui/ListConfiguration.tsx
Normal file
39
src/Augmentation/ui/ListConfiguration.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* React Component for configuring the way installed augmentations and
|
||||||
|
* Source-Files are displayed in the Augmentations UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
collapseAllButtonsFn: () => void;
|
||||||
|
expandAllButtonsFn: () => void;
|
||||||
|
sortByAcquirementTimeFn: () => void;
|
||||||
|
sortInOrderFn: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListConfiguration(props: IProps): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StdButton
|
||||||
|
onClick={props.expandAllButtonsFn}
|
||||||
|
text="Expand All"
|
||||||
|
/>
|
||||||
|
<StdButton
|
||||||
|
onClick={props.collapseAllButtonsFn}
|
||||||
|
text="Collapse All"
|
||||||
|
/>
|
||||||
|
<StdButton
|
||||||
|
onClick={props.sortInOrderFn}
|
||||||
|
text="Sort in Order"
|
||||||
|
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
|
||||||
|
/>
|
||||||
|
<StdButton
|
||||||
|
onClick={props.sortByAcquirementTimeFn}
|
||||||
|
text="Sort by Acquirement Time"
|
||||||
|
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
41
src/Augmentation/ui/OwnedSourceFiles.tsx
Normal file
41
src/Augmentation/ui/OwnedSourceFiles.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* React Component for displaying a list of the player's Source-Files
|
||||||
|
* on the Augmentations UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
|
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||||
|
|
||||||
|
import { SourceFileAccordion } from "../../ui/React/SourceFileAccordion";
|
||||||
|
|
||||||
|
export function OwnedSourceFiles(): React.ReactElement {
|
||||||
|
const sourceSfs = Player.sourceFiles.slice();
|
||||||
|
|
||||||
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
|
sourceSfs.sort((sf1, sf2) => {
|
||||||
|
return sf1.n - sf2.n;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sfs = sourceSfs.map((e) => {
|
||||||
|
const srcFileKey = "SourceFile" + e.n;
|
||||||
|
const sfObj = SourceFiles[srcFileKey];
|
||||||
|
if (sfObj == null) {
|
||||||
|
console.error(`Invalid source file number: ${e.n}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={e.n}>
|
||||||
|
<SourceFileAccordion level={e.lvl} sf={sfObj} />
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>{sfs}</>
|
||||||
|
);
|
||||||
|
}
|
||||||
96
src/Augmentation/ui/PlayerMultipliers.tsx
Normal file
96
src/Augmentation/ui/PlayerMultipliers.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* React component for displaying the player's multipliers on the Augmentation UI page
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
export function PlayerMultipliers(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p><strong><u>Total Multipliers:</u></strong></p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{'Hacking Chance multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_chance_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacking Speed multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_speed_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacking Money multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_money_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacking Growth multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_grow_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Hacking Level multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacking Experience multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_exp_mult)}
|
||||||
|
</pre>
|
||||||
|
<br />
|
||||||
|
<pre>
|
||||||
|
{'Strength Level multiplier: ' + numeralWrapper.formatPercentage(Player.strength_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Strength Experience multiplier: ' + numeralWrapper.formatPercentage(Player.strength_exp_mult)}
|
||||||
|
</pre>
|
||||||
|
<br />
|
||||||
|
<pre>
|
||||||
|
{'Defense Level multiplier: ' + numeralWrapper.formatPercentage(Player.defense_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Defense Experience multiplier: ' + numeralWrapper.formatPercentage(Player.defense_exp_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Dexterity Level multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Dexterity Experience multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_exp_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Agility Level multiplier: ' + numeralWrapper.formatPercentage(Player.agility_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Agility Experience multiplier: ' + numeralWrapper.formatPercentage(Player.agility_exp_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Charisma Level multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Charisma Experience multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_exp_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Hacknet Node production multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_money_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacknet Node purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_purchase_cost_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacknet Node RAM upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_ram_cost_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacknet Node Core purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_core_cost_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Hacknet Node level upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_level_cost_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Company reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.company_rep_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Faction reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.faction_rep_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Salary multiplier: ' + numeralWrapper.formatPercentage(Player.work_money_mult)}
|
||||||
|
</pre><br />
|
||||||
|
<pre>
|
||||||
|
{'Crime success multiplier: ' + numeralWrapper.formatPercentage(Player.crime_success_mult)}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{'Crime money multiplier: ' + numeralWrapper.formatPercentage(Player.crime_money_mult)}
|
||||||
|
</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
src/Augmentation/ui/PurchasedAugmentations.tsx
Normal file
32
src/Augmentation/ui/PurchasedAugmentations.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* React component for displaying all of the player's purchased (but not installed)
|
||||||
|
* Augmentations on the Augmentations UI.
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||||
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
|
||||||
|
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||||
|
|
||||||
|
export function PurchasedAugmentations(): React.ReactElement {
|
||||||
|
const augs: React.ReactElement[] = [];
|
||||||
|
for (const ownedAug of Player.queuedAugmentations) {
|
||||||
|
const aug = Augmentations[ownedAug.name];
|
||||||
|
let level = null;
|
||||||
|
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
level = ownedAug.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
augs.push(
|
||||||
|
<li key={`${ownedAug.name}${ownedAug.level}`}>
|
||||||
|
<AugmentationAccordion aug={aug} level={level} />
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="augmentations-list">{augs}</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
83
src/Augmentation/ui/Root.tsx
Normal file
83
src/Augmentation/ui/Root.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Root React component for the Augmentations UI page that display all of your
|
||||||
|
* owned and purchased Augmentations and Source-Files.
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
|
||||||
|
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||||
|
import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
||||||
|
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
exportGameFn: () => void;
|
||||||
|
installAugmentationsFn: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AugmentationsRoot extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div id="augmentations-content">
|
||||||
|
<h1>Purchased Augmentations</h1>
|
||||||
|
<p>
|
||||||
|
Below is a list of all Augmentations you have purchased but not
|
||||||
|
yet installed. Click the button below to install them.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
WARNING: Installing your Augmentations resets most of your progress,
|
||||||
|
including:
|
||||||
|
</p><br />
|
||||||
|
<p>- Stats/Skill levels and Experience</p>
|
||||||
|
<p>- Money</p>
|
||||||
|
<p>- Scripts on every computer but your home computer</p>
|
||||||
|
<p>- Purchased servers</p>
|
||||||
|
<p>- Hacknet Nodes</p>
|
||||||
|
<p>- Faction/Company reputation</p>
|
||||||
|
<p>- Stocks</p><br />
|
||||||
|
<p>
|
||||||
|
Installing Augmentations lets you start over with the perks and
|
||||||
|
benefits granted by all of the Augmentations you have ever
|
||||||
|
installed. Also, you will keep any scripts and RAM/Core upgrades
|
||||||
|
on your home computer (but you will lose all programs besides
|
||||||
|
NUKE.exe)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<StdButton
|
||||||
|
onClick={this.props.installAugmentationsFn}
|
||||||
|
text="Install Augmentations"
|
||||||
|
tooltip="'I never asked for this'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<StdButton
|
||||||
|
addClasses="flashing-button"
|
||||||
|
onClick={this.props.exportGameFn}
|
||||||
|
text="Backup Save (Export)"
|
||||||
|
tooltip="It's always a good idea to backup/export your save!"
|
||||||
|
/>
|
||||||
|
<PurchasedAugmentations />
|
||||||
|
|
||||||
|
<h1>Installed Augmentations</h1>
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
|
||||||
|
`that have been installed. You have gained the effects of these.`
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<InstalledAugmentationsAndSourceFiles />
|
||||||
|
|
||||||
|
<br /> <br />
|
||||||
|
<PlayerMultipliers />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,232 +25,232 @@ class BitNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export let BitNodes: IMap<BitNode> = {};
|
export const BitNodes: IMap<BitNode> = {};
|
||||||
|
|
||||||
export function initBitNodes() {
|
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
|
||||||
BitNodes = {};
|
"The first BitNode created by the Enders to imprison the minds of humans. It became " +
|
||||||
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
|
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
|
||||||
"The first BitNode created by the Enders to imprison the minds of humans. It became " +
|
"This is the first BitNode that you play through. It has no special " +
|
||||||
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
|
"modifications or mechanics.<br><br>" +
|
||||||
"This is the first BitNode that you play through. It has no special " +
|
"Destroying this BitNode will give you Source-File 1, or if you already have " +
|
||||||
"modifications or mechanics.<br><br>" +
|
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
|
||||||
"Destroying this BitNode will give you Source-File 1, or if you already have " +
|
"lets the player start with 32GB of RAM on his/her home computer when entering a " +
|
||||||
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
|
"new BitNode, and also increases all of the player's multipliers by:<br><br>" +
|
||||||
"lets the player start with 32GB of RAM on his/her home computer when entering a " +
|
"Level 1: 16%<br>" +
|
||||||
"new BitNode, and also increases all of the player's multipliers by:<br><br>" +
|
"Level 2: 24%<br>" +
|
||||||
"Level 1: 16%<br>" +
|
"Level 3: 28%");
|
||||||
"Level 2: 24%<br>" +
|
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
|
||||||
"Level 3: 28%");
|
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
|
||||||
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
|
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
|
||||||
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
|
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
|
||||||
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
|
"factions quickly rose to the top of the modern world.<br><br>" +
|
||||||
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
|
"In this BitNode:<br><br>" +
|
||||||
"factions quickly rose to the top of the modern world.<br><br>" +
|
"Your hacking level is reduced by 20%<br>" +
|
||||||
"In this BitNode:<br><br>" +
|
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
|
||||||
"Your hacking level is reduced by 20%<br>" +
|
"The amount of money gained from crimes and Infiltration is tripled<br>" +
|
||||||
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
|
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
|
||||||
"The amount of money gained from crimes and Infiltration is tripled<br>" +
|
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
|
||||||
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
|
"will earn the player money and reputation with the corresponding Faction<br>" +
|
||||||
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
|
"Every Augmentation in the game will be available through the Factions listed above<br>" +
|
||||||
"will earn the player money and reputation with the corresponding Faction<br>" +
|
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
||||||
"Every Augmentation in the game will be available through the Factions listed above<br>" +
|
"You will no longer gain passive reputation with Factions<br><br>" +
|
||||||
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
||||||
"You will no longer gain passive reputation with Factions<br><br>" +
|
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
|
||||||
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
"once your karma decreases to a certain value. " +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File increases the player's crime success rate, " +
|
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||||
"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%");
|
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
|
||||||
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
|
"Our greatest illusion is that a healthy society can revolve around a " +
|
||||||
"Our greatest illusion is that a healthy society can revolve around a " +
|
"single-minded pursuit of wealth.<br><br>" +
|
||||||
"single-minded pursuit of wealth.<br><br>" +
|
"Sometime in the early 21st century economic and political globalization turned " +
|
||||||
"Sometime in the early 21st century economic and political globalization turned " +
|
"the world into a corporatocracy, and it never looked back. Now, the privileged " +
|
||||||
"the world into a corporatocracy, and it never looked back. Now, the privileged " +
|
"elite will happily bankrupt their own countrymen, decimate their own community, " +
|
||||||
"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.<br><br>" +
|
||||||
"and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" +
|
"In this BitNode you can create and manage your own corporation. Running a successful corporation " +
|
||||||
"In this BitNode you can create and manage your own corporation. Running a successful corporation " +
|
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
|
||||||
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
|
"The price and reputation cost of all Augmentations is tripled<br>" +
|
||||||
"The price and reputation cost of all Augmentations is tripled<br>" +
|
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
|
||||||
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
|
"Server growth rate is reduced by 80%<br>" +
|
||||||
"Server growth rate is reduced by 80%<br>" +
|
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
|
||||||
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
|
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
|
||||||
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
|
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
|
||||||
"upgrade its level up to a maximum of 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:<br>" +
|
||||||
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
|
"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",
|
||||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine", "The Singularity has arrived. The human race is gone, replaced " +
|
"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>" +
|
||||||
"In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " +
|
"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, " +
|
"These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " +
|
||||||
"purchasing/installing Augmentations, and creating programs.<br><br>" +
|
"purchasing/installing Augmentations, and creating programs.<br><br>" +
|
||||||
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
|
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
|
||||||
"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",
|
||||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
"They said it couldn't be done. They said the human brain, " +
|
||||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||||
"by 1's and 0's. They were wrong.<br><br>" +
|
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||||
"In this BitNode:<br><br>" +
|
"by 1's and 0's. They were wrong.<br><br>" +
|
||||||
"The base security level of servers is doubled<br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"The starting money on servers is halved, but the maximum money remains the same<br>" +
|
"The base security level of servers is doubled<br>" +
|
||||||
"Most methods of earning money now give significantly less<br>" +
|
"The starting money on servers is halved, but the maximum money remains the same<br>" +
|
||||||
"Infiltration gives 50% more reputation and money<br>" +
|
"Most methods of earning money now give significantly less<br>" +
|
||||||
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
|
"Infiltration gives 50% more reputation and money<br>" +
|
||||||
"Augmentations are more expensive<br>" +
|
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
|
||||||
"Hacking experience gain rates are reduced<br><br>" +
|
"Augmentations are more expensive<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
|
"Hacking experience gain rates are reduced<br><br>" +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
|
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
|
||||||
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
|
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
|
||||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
|
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
|
||||||
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
|
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
|
||||||
"in the game. <br><br>" +
|
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
|
||||||
"In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " +
|
"in the game. <br><br>" +
|
||||||
"and will also raise all of your hacking-related multipliers by:<br><br>" +
|
"In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " +
|
||||||
"Level 1: 8%<br>" +
|
"and will also raise all of your hacking-related multipliers by:<br><br>" +
|
||||||
"Level 2: 12%<br>" +
|
"Level 1: 8%<br>" +
|
||||||
"Level 3: 14%");
|
"Level 2: 12%<br>" +
|
||||||
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
|
"Level 3: 14%");
|
||||||
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
|
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
|
||||||
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
|
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
|
||||||
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
|
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
|
||||||
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
|
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
|
||||||
"than the humans that had created them.<br><br>" +
|
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
|
||||||
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
|
"than the humans that had created them.<br><br>" +
|
||||||
"for progression. Furthermore:<br><br>" +
|
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
|
||||||
"Hacking and Hacknet Nodes will be less profitable<br>" +
|
"for progression. Furthermore:<br><br>" +
|
||||||
"Your hacking level is reduced by 65%<br>" +
|
"Hacking and Hacknet Nodes will be less profitable<br>" +
|
||||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
"Your hacking level is reduced by 65%<br>" +
|
||||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||||
"Working for companies is 50% less profitable<br>" +
|
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
"Working for companies is 50% less profitable<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
|
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||||
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
|
||||||
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
||||||
"Level 1: 8%<br>" +
|
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
||||||
"Level 2: 12%<br>" +
|
"Level 1: 8%<br>" +
|
||||||
"Level 3: 14%");
|
"Level 2: 12%<br>" +
|
||||||
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
|
"Level 3: 14%");
|
||||||
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
|
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
|
||||||
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
|
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
|
||||||
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
|
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
|
||||||
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
|
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
|
||||||
"and more intelligent than the humans that had created them.<br><br>" +
|
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
|
||||||
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
|
"and more intelligent than the humans that had created them.<br><br>" +
|
||||||
"functionality through Netscript. Furthermore: <br><br>" +
|
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
|
||||||
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
|
"functionality through Netscript. Furthermore: <br><br>" +
|
||||||
"Bladeburner skills cost twice as many skill points<br>" +
|
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
|
||||||
"Augmentations are 3x more expensive<br>" +
|
"Bladeburner skills cost twice as many skill points<br>" +
|
||||||
"Hacking and Hacknet Nodes will be significantly less profitable<br>" +
|
"Augmentations are 3x more expensive<br>" +
|
||||||
"Your hacking level is reduced by 65%<br>" +
|
"Hacking and Hacknet Nodes will be significantly less profitable<br>" +
|
||||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
"Your hacking level is reduced by 65%<br>" +
|
||||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||||
"Working for companies is 50% less profitable<br>" +
|
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
"Working for companies is 50% less profitable<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
|
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||||
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
|
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
|
||||||
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
|
||||||
"Level 1: 8%<br>" +
|
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
||||||
"Level 2: 12%<br>" +
|
"Level 1: 8%<br>" +
|
||||||
"Level 3: 14%");
|
"Level 2: 12%<br>" +
|
||||||
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
|
"Level 3: 14%");
|
||||||
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
|
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
|
||||||
"In this BitNode:<br><br>" +
|
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
|
||||||
"You start with $250 million<br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"The only way to earn money is by trading on the stock market<br>" +
|
"You start with $250 million<br>" +
|
||||||
"You start with a WSE membership and access to the TIX API<br>" +
|
"The only way to earn money is by trading on the stock market<br>" +
|
||||||
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
|
"You start with a WSE membership and access to the TIX API<br>" +
|
||||||
"You can immediately donate to factions to gain reputation<br><br>" +
|
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
|
"You can immediately donate to factions to gain reputation<br><br>" +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
|
||||||
"Level 1: Permanent access to WSE and TIX API<br>" +
|
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||||
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
"Level 1: Permanent access to WSE and TIX API<br>" +
|
||||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
||||||
"This Source-File also increases your hacking growth multipliers by: " +
|
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
"This Source-File also increases your hacking growth multipliers by: " +
|
||||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||||
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||||
"abandoned the project and dissociated themselves from it.<br><br>" +
|
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||||
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||||
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||||
"In this BitNode:<br><br>" +
|
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||||
"Your stats are significantly decreased<br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"You cannnot purchase additional servers<br>" +
|
"Your stats are significantly decreased<br>" +
|
||||||
"Hacking is significantly less profitable<br><br>" +
|
"You cannnot purchase additional servers<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
"Hacking is significantly less profitable<br><br>" +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||||
"when installing Augmentations)");
|
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
"when installing Augmentations)");
|
||||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||||
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
|
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||||
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
|
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
|
||||||
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
|
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
|
||||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
|
||||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||||
"In this BitNode:<br><br>" +
|
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||||
"Your stats are significantly decreased<br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
"Your stats are significantly decreased<br>" +
|
||||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
|
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
|
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
|
||||||
"Each level of this Source-File also grants you a Duplicate Sleeve");
|
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
|
||||||
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
|
"Each level of this Source-File also grants you a Duplicate Sleeve");
|
||||||
"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 " +
|
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
|
||||||
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
|
"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 " +
|
||||||
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
|
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
|
||||||
"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 " +
|
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
|
||||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
"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 " +
|
||||||
"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>" +
|
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||||
"In this BitNode:<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>" +
|
||||||
"Your hacking stat and experience gain are halved<br>" +
|
"In this BitNode:<br><br>" +
|
||||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
"Your hacking stat and experience gain are halved<br>" +
|
||||||
"The growth rate of servers is significantly reduced<br>" +
|
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||||
"Weakening a server is twice as effective<br>" +
|
"The growth rate of servers is significantly reduced<br>" +
|
||||||
"Company wages are decreased by 50%<br>" +
|
"Weakening a server is twice as effective<br>" +
|
||||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
"Company wages are decreased by 50%<br>" +
|
||||||
"Hacknet Node production is significantly decreased<br>" +
|
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||||
"Crime and Infiltration are more lucrative<br>" +
|
"Hacknet Node production is significantly decreased<br>" +
|
||||||
"Augmentations are twice as expensive<br><br>" +
|
"Crime and Infiltration are more lucrative<br>" +
|
||||||
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
|
"Augmentations are twice as expensive<br><br>" +
|
||||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
|
||||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||||
"Level 1: 32%<br>" +
|
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||||
"Level 2: 48%<br>" +
|
"Level 1: 32%<br>" +
|
||||||
"Level 3: 56%");
|
"Level 2: 48%<br>" +
|
||||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
"Level 3: 56%");
|
||||||
"To iterate is human, to recurse divine.<br><br>" +
|
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
"To iterate is human, to recurse divine.<br><br>" +
|
||||||
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
|
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||||
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
|
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
|
||||||
"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)");
|
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
|
||||||
//Books: Frontera, Shiner
|
"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)");
|
||||||
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
// Books: Frontera, Shiner
|
||||||
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
|
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||||
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
|
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
|
||||||
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
|
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
|
||||||
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
|
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
|
||||||
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
|
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
|
||||||
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
|
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
|
||||||
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
|
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
|
||||||
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
|
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
|
||||||
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
|
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
|
||||||
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
|
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
|
||||||
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
|
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
|
||||||
}
|
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
|
||||||
|
|
||||||
export function initBitNodeMultipliers(p: IPlayer) {
|
export function initBitNodeMultipliers(p: IPlayer) {
|
||||||
if (p.bitNodeN == null) {
|
if (p.bitNodeN == null) {
|
||||||
|
|||||||
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,16 @@
|
|||||||
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 { SpecialServerNames } from "./Server/SpecialServerIps";
|
||||||
|
|
||||||
|
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 +131,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 +147,7 @@ function getRandomReward() {
|
|||||||
reward.type = CodingContractRewardType.Money;
|
reward.type = CodingContractRewardType.Money;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -157,7 +163,9 @@ function getRandomServer() {
|
|||||||
// An infinite loop shouldn't ever happen, but to be safe we'll use
|
// An infinite loop shouldn't ever happen, but to be safe we'll use
|
||||||
// a for loop with a limited number of tries
|
// a for loop with a limited number of tries
|
||||||
for (let i = 0; i < 200; ++i) {
|
for (let i = 0; i < 200; ++i) {
|
||||||
if (randServer.purchasedByPlayer === false) { break; }
|
if (!randServer.purchasedByPlayer && randServer.hostname !== SpecialServerNames.WorldDaemon) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
randIndex = getRandomInt(0, servers.length - 1);
|
randIndex = getRandomInt(0, servers.length - 1);
|
||||||
randServer = AllServers[servers[randIndex]];
|
randServer = AllServers[servers[randIndex]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
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";
|
||||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
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 */
|
||||||
@@ -198,6 +202,7 @@ export class CodingContract {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
placeholder: "Enter Solution here",
|
placeholder: "Enter Solution here",
|
||||||
|
width: "50%",
|
||||||
}) as HTMLInputElement;
|
}) as HTMLInputElement;
|
||||||
solveBtn = createElement("a", {
|
solveBtn = createElement("a", {
|
||||||
class: "a-link-button",
|
class: "a-link-button",
|
||||||
|
|||||||
178
src/Constants.ts
178
src/Constants.ts
@@ -3,98 +3,44 @@
|
|||||||
*
|
*
|
||||||
* Constants for specific mechanics or features will NOT be here.
|
* Constants for specific mechanics or features will NOT be here.
|
||||||
*/
|
*/
|
||||||
import {IMap} from "./types";
|
import { IMap } from "./types";
|
||||||
|
|
||||||
export let CONSTANTS: IMap<any> = {
|
export let CONSTANTS: IMap<any> = {
|
||||||
Version: "0.46.1",
|
Version: "0.47.1",
|
||||||
|
|
||||||
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||||
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||||
//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
|
||||||
|
|
||||||
|
// Cost to travel to another city
|
||||||
TravelCost: 200e3,
|
TravelCost: 200e3,
|
||||||
|
|
||||||
/* Faction and Company favor */
|
// Faction and Company favor-related things
|
||||||
BaseFavorToDonate: 150,
|
BaseFavorToDonate: 150,
|
||||||
DonateMoneyToRepDivisor: 1e6,
|
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 */
|
NumNetscriptPorts: 20,
|
||||||
//RAM Costs for different commands
|
|
||||||
ScriptBaseRamCost: 1.6,
|
|
||||||
ScriptDomRamCost: 25,
|
|
||||||
ScriptWhileRamCost: 0,
|
|
||||||
ScriptForRamCost: 0,
|
|
||||||
ScriptIfRamCost: 0,
|
|
||||||
ScriptHackRamCost: 0.1,
|
|
||||||
ScriptHackAnalyzeRamCost: 1,
|
|
||||||
ScriptGrowRamCost: 0.15,
|
|
||||||
ScriptGrowthAnalyzeRamCost: 1,
|
|
||||||
ScriptWeakenRamCost: 0.15,
|
|
||||||
ScriptScanRamCost: 0.2,
|
|
||||||
ScriptPortProgramRamCost: 0.05,
|
|
||||||
ScriptRunRamCost: 1.0,
|
|
||||||
ScriptExecRamCost: 1.3,
|
|
||||||
ScriptSpawnRamCost: 2.0,
|
|
||||||
ScriptScpRamCost: 0.6,
|
|
||||||
ScriptKillRamCost: 0.5, //Kill and killall
|
|
||||||
ScriptHasRootAccessRamCost: 0.05,
|
|
||||||
ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp()
|
|
||||||
ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel()
|
|
||||||
ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers()
|
|
||||||
ScriptGetServerRamCost: 0.1,
|
|
||||||
ScriptFileExistsRamCost: 0.1,
|
|
||||||
ScriptIsRunningRamCost: 0.1,
|
|
||||||
ScriptHacknetNodesRamCost: 4.0, //Base cost for accessing Hacknet Node API
|
|
||||||
ScriptHNUpgLevelRamCost: 0.4,
|
|
||||||
ScriptHNUpgRamRamCost: 0.6,
|
|
||||||
ScriptHNUpgCoreRamCost: 0.8,
|
|
||||||
ScriptGetStockRamCost: 2.0,
|
|
||||||
ScriptBuySellStockRamCost: 2.5,
|
|
||||||
ScriptGetPurchaseServerRamCost: 0.25,
|
|
||||||
ScriptPurchaseServerRamCost: 2.25,
|
|
||||||
ScriptGetPurchasedServerLimit: 0.05,
|
|
||||||
ScriptGetPurchasedServerMaxRam: 0.05,
|
|
||||||
ScriptRoundRamCost: 0.05,
|
|
||||||
ScriptReadWriteRamCost: 1.0,
|
|
||||||
ScriptArbScriptRamCost: 1.0, // Functions that apply to all scripts regardless of args
|
|
||||||
ScriptGetScriptRamCost: 0.1,
|
|
||||||
ScriptGetHackTimeRamCost: 0.05,
|
|
||||||
ScriptGetFavorToDonate: 0.10,
|
|
||||||
ScriptCodingContractBaseRamCost:10,
|
|
||||||
ScriptSleeveBaseRamCost: 4,
|
|
||||||
|
|
||||||
ScriptSingularityFn1RamCost: 1,
|
// Server-related constants
|
||||||
ScriptSingularityFn2RamCost: 2,
|
|
||||||
ScriptSingularityFn3RamCost: 3,
|
|
||||||
|
|
||||||
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
|
|
||||||
|
|
||||||
ScriptGangApiBaseRamCost: 4,
|
|
||||||
|
|
||||||
ScriptBladeburnerApiBaseRamCost: 4,
|
|
||||||
|
|
||||||
NumNetscriptPorts: 20,
|
|
||||||
|
|
||||||
//Server 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)
|
||||||
@@ -102,49 +48,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,
|
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 " +
|
||||||
@@ -192,7 +139,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,
|
||||||
|
|
||||||
@@ -220,7 +167,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",
|
||||||
@@ -263,26 +210,29 @@ 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.46.1
|
v0.47.1
|
||||||
* Added a very rudimentary directory system to the Terminal
|
* Stock Market changes:
|
||||||
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
|
** Transactions no longer influence stock prices (but they still influence forecast)
|
||||||
|
** Changed the way stocks behave, particularly with regard to how the stock forecast occasionally "flips"
|
||||||
|
** Hacking & growing a server can potentially affect the way the corresponding stock's forecast changes
|
||||||
|
** Working for a company positively affects the way the corresponding stock's forecast changes
|
||||||
|
|
||||||
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
|
* Scripts now start/stop instantly
|
||||||
* 'Generate Coding Contract' hash upgrade is now more expensive
|
* Improved performance when starting up many copies of a new NetscriptJS script (by Ornedan)
|
||||||
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
|
* Improved performance when killing scripts
|
||||||
* The cost of selling hashes for money no longer increases each time
|
* Dialog boxes can now be closed with the ESC key (by jaguilar)
|
||||||
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
|
* NetscriptJS scripts should now be "re-compiled" if their dependencies change (by jaguilar)
|
||||||
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
|
* write() function should now properly cause NetscriptJS scripts to "re-compile" (by jaguilar)
|
||||||
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -521,15 +521,18 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process production, purchase, and import/export of materials
|
// Process production, purchase, and import/export of materials
|
||||||
var res = this.processMaterials(marketCycles, company);
|
let res = this.processMaterials(marketCycles, company);
|
||||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
if (Array.isArray(res)) {
|
||||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||||
|
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||||
|
}
|
||||||
|
|
||||||
// Process creation, production & sale of products
|
// Process creation, production & sale of products
|
||||||
res = this.processProducts(marketCycles, company);
|
res = this.processProducts(marketCycles, company);
|
||||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
if (Array.isArray(res)) {
|
||||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||||
|
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process change in demand and competition for this industry's materials
|
// Process change in demand and competition for this industry's materials
|
||||||
@@ -846,7 +849,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
|||||||
sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles);
|
sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles);
|
||||||
sellAmt = Math.min(mat.qty, sellAmt);
|
sellAmt = Math.min(mat.qty, sellAmt);
|
||||||
if (sellAmt < 0) {
|
if (sellAmt < 0) {
|
||||||
console.log("sellAmt calculated to be negative");
|
console.warn(`sellAmt calculated to be negative for ${matName} in ${city}`);
|
||||||
mat.sll = 0;
|
mat.sll = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -899,9 +902,11 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make sure theres enough space in warehouse
|
// Make sure theres enough space in warehouse
|
||||||
if (expWarehouse.sizeUsed >= expWarehouse.size) {
|
if (expWarehouse.sizeUsed >= expWarehouse.size) {
|
||||||
return; //Warehouse at capacity
|
// Warehouse at capacity. Exporting doesnt
|
||||||
|
// affect revenue so just return 0's
|
||||||
|
return [0, 0];
|
||||||
} else {
|
} else {
|
||||||
var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]);
|
var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]);
|
||||||
amt = Math.min(maxAmt, amt);
|
amt = Math.min(maxAmt, amt);
|
||||||
@@ -1463,7 +1468,6 @@ function Employee(params={}) {
|
|||||||
this.hap = params.happiness ? params.happiness : getRandomInt(50, 100);
|
this.hap = params.happiness ? params.happiness : getRandomInt(50, 100);
|
||||||
this.ene = params.energy ? params.energy : getRandomInt(50, 100);
|
this.ene = params.energy ? params.energy : getRandomInt(50, 100);
|
||||||
|
|
||||||
this.age = params.age ? params.age : getRandomInt(20, 50);
|
|
||||||
this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50);
|
this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50);
|
||||||
this.cha = params.charisma ? params.charisma : getRandomInt(10, 50);
|
this.cha = params.charisma ? params.charisma : getRandomInt(10, 50);
|
||||||
this.exp = params.experience ? params.experience : getRandomInt(10, 50);
|
this.exp = params.experience ? params.experience : getRandomInt(10, 50);
|
||||||
@@ -1482,13 +1486,7 @@ function Employee(params={}) {
|
|||||||
Employee.prototype.process = function(marketCycles=1, office) {
|
Employee.prototype.process = function(marketCycles=1, office) {
|
||||||
var gain = 0.003 * marketCycles,
|
var gain = 0.003 * marketCycles,
|
||||||
det = gain * Math.random();
|
det = gain * Math.random();
|
||||||
this.age += gain;
|
|
||||||
this.exp += gain;
|
this.exp += gain;
|
||||||
if (this.age > 150) {
|
|
||||||
this.int -= det;
|
|
||||||
this.eff -= det;
|
|
||||||
this.cha -= det;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Employee salaries slowly go up over time
|
// Employee salaries slowly go up over time
|
||||||
this.cyclesUntilRaise -= marketCycles;
|
this.cyclesUntilRaise -= marketCycles;
|
||||||
@@ -1577,7 +1575,6 @@ Employee.prototype.createUI = function(panel, corporation, industry) {
|
|||||||
innerHTML:"Morale: " + formatNumber(this.mor, 3) + "<br>" +
|
innerHTML:"Morale: " + formatNumber(this.mor, 3) + "<br>" +
|
||||||
"Happiness: " + formatNumber(this.hap, 3) + "<br>" +
|
"Happiness: " + formatNumber(this.hap, 3) + "<br>" +
|
||||||
"Energy: " + formatNumber(this.ene, 3) + "<br>" +
|
"Energy: " + formatNumber(this.ene, 3) + "<br>" +
|
||||||
"Age: " + formatNumber(this.age, 3) + "<br>" +
|
|
||||||
"Intelligence: " + formatNumber(effInt, 3) + "<br>" +
|
"Intelligence: " + formatNumber(effInt, 3) + "<br>" +
|
||||||
"Charisma: " + formatNumber(effCha, 3) + "<br>" +
|
"Charisma: " + formatNumber(effCha, 3) + "<br>" +
|
||||||
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
|
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
|
||||||
|
|||||||
@@ -495,8 +495,6 @@ export class IndustryOffice extends BaseReactComponent {
|
|||||||
<br />
|
<br />
|
||||||
Energy: {numeralWrapper.format(this.state.employee.ene, nf)}
|
Energy: {numeralWrapper.format(this.state.employee.ene, nf)}
|
||||||
<br />
|
<br />
|
||||||
Age: {numeralWrapper.format(this.state.employee.age, nf)}
|
|
||||||
<br />
|
|
||||||
Intelligence: {numeralWrapper.format(effInt, nf)}
|
Intelligence: {numeralWrapper.format(effInt, nf)}
|
||||||
<br />
|
<br />
|
||||||
Charisma: {numeralWrapper.format(effCha, nf)}
|
Charisma: {numeralWrapper.format(effCha, nf)}
|
||||||
|
|||||||
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,694 +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 { HackingMission, setInMission } from "../Missions";
|
|
||||||
import { Player } from "../Player";
|
|
||||||
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
|
||||||
import { Settings } from "../Settings/Settings";
|
|
||||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
|
||||||
|
|
||||||
import { createSleevePurchasesFromCovenantPopup } 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: () => {
|
|
||||||
createSleevePurchasesFromCovenantPopup(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};
|
|
||||||
245
src/Faction/FactionHelpers.jsx
Normal file
245
src/Faction/FactionHelpers.jsx
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
import { FactionRoot } from "./ui/Root";
|
||||||
|
|
||||||
|
import { Augmentations } from "../Augmentation/Augmentations";
|
||||||
|
import { isRepeatableAug } from "../Augmentation/AugmentationHelpers";
|
||||||
|
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() {
|
||||||
|
if (!isRepeatableAug(aug) && Player.hasAugmentation(aug)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
purchaseAugmentation(aug, fac);
|
||||||
|
yesNoBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/Faction/ui/Info.tsx
Normal file
88
src/Faction/ui/Info.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* 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> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.getFavorGainText = this.getFavorGainText.bind(this);
|
||||||
|
this.getReputationText = this.getReputationText.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFavorGainText(): string {
|
||||||
|
const favorGain = this.props.faction.getFavorGain()[0];
|
||||||
|
return `You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`
|
||||||
|
}
|
||||||
|
|
||||||
|
getReputationText(): string {
|
||||||
|
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
|
||||||
|
return `Reputation: ${formattedRep}`
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
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}
|
||||||
|
getText={this.getReputationText}
|
||||||
|
getTooltip={this.getFavorGainText}
|
||||||
|
/>
|
||||||
|
<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 & Upgrade Duplicate Sleeves"}
|
||||||
|
infoText={sleevePurchasesInfo}
|
||||||
|
onClick={this.sleevePurchases}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAugmentationsPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AugmentationsPage
|
||||||
|
faction={this.props.faction}
|
||||||
|
p={this.props.p}
|
||||||
|
routeToMainPage={this.routeToMain}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/Gang.js
180
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;}
|
||||||
@@ -665,21 +682,25 @@ GangMember.prototype.calculateRespectGain = function(gang) {
|
|||||||
|
|
||||||
GangMember.prototype.calculateWantedLevelGain = function(gang) {
|
GangMember.prototype.calculateWantedLevelGain = function(gang) {
|
||||||
const task = this.getTask();
|
const task = this.getTask();
|
||||||
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) {return 0;}
|
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) { return 0; }
|
||||||
var statWeight = (task.hackWeight/100) * this.hack +
|
let statWeight = (task.hackWeight / 100) * this.hack +
|
||||||
(task.strWeight/100) * this.str +
|
(task.strWeight / 100) * this.str +
|
||||||
(task.defWeight/100) * this.def +
|
(task.defWeight / 100) * this.def +
|
||||||
(task.dexWeight/100) * this.dex +
|
(task.dexWeight / 100) * this.dex +
|
||||||
(task.agiWeight/100) * this.agi +
|
(task.agiWeight / 100) * this.agi +
|
||||||
(task.chaWeight/100) * this.cha;
|
(task.chaWeight / 100) * this.cha;
|
||||||
statWeight -= (3.5 * task.difficulty);
|
statWeight -= (3.5 * task.difficulty);
|
||||||
if (statWeight <= 0) { return 0; }
|
if (statWeight <= 0) { return 0; }
|
||||||
const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100;
|
const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100;
|
||||||
if (isNaN(territoryMult) || territoryMult <= 0) { return 0; }
|
if (isNaN(territoryMult) || territoryMult <= 0) { return 0; }
|
||||||
if (task.baseWanted < 0) {
|
if (task.baseWanted < 0) {
|
||||||
return 0.5 * task.baseWanted * statWeight * territoryMult;
|
return 0.4 * task.baseWanted * statWeight * territoryMult;
|
||||||
} else {
|
} else {
|
||||||
return 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8));
|
const calc = 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8));
|
||||||
|
|
||||||
|
// Put an arbitrary cap on this to prevent wanted level from rising too fast if the
|
||||||
|
// denominator is very small. Might want to rethink formula later
|
||||||
|
return Math.min(100, calc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -770,9 +791,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 +860,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 +958,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 +994,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 +1014,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 +1046,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 +1068,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 +1094,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 +1226,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 +1247,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 +1279,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 +1363,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 +1376,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 +1424,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 +1599,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 +1609,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 +1620,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 +1632,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 +1680,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 +1698,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 +1846,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",
|
||||||
|
|||||||
@@ -6,34 +6,34 @@
|
|||||||
*/
|
*/
|
||||||
import { IReturnStatus } from "../types";
|
import { IReturnStatus } from "../types";
|
||||||
|
|
||||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
|
|
||||||
function baseCheck(server: Server | HacknetServer, fnName: string): IReturnStatus {
|
function baseCheck(server: Server, fnName: string): IReturnStatus {
|
||||||
if (server instanceof HacknetServer) {
|
const hostname = server.hostname;
|
||||||
|
|
||||||
|
if (!("requiredHackingSkill" in server)) {
|
||||||
return {
|
return {
|
||||||
res: false,
|
res: false,
|
||||||
msg: `Cannot ${fnName} ${server.hostname} server because it is a Hacknet Node`
|
msg: `Cannot ${fnName} ${hostname} server because it is a Hacknet Node`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.hasAdminRights === false) {
|
if (server.hasAdminRights === false) {
|
||||||
return {
|
return {
|
||||||
res: false,
|
res: false,
|
||||||
msg: `Cannot ${fnName} ${server.hostname} server because you do not have root access`,
|
msg: `Cannot ${fnName} ${hostname} server because you do not have root access`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { res: true }
|
return { res: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IReturnStatus {
|
export function netscriptCanHack(server: Server, p: IPlayer): IReturnStatus {
|
||||||
const initialCheck = baseCheck(server, "hack");
|
const initialCheck = baseCheck(server, "hack");
|
||||||
if (!initialCheck.res) { return initialCheck; }
|
if (!initialCheck.res) { return initialCheck; }
|
||||||
|
|
||||||
let s = <Server>server;
|
let s = server;
|
||||||
|
|
||||||
if (s.requiredHackingSkill > p.hacking_skill) {
|
if (s.requiredHackingSkill > p.hacking_skill) {
|
||||||
return {
|
return {
|
||||||
res: false,
|
res: false,
|
||||||
@@ -44,10 +44,10 @@ export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IR
|
|||||||
return { res: true }
|
return { res: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function netscriptCanGrow(server: Server | HacknetServer): IReturnStatus {
|
export function netscriptCanGrow(server: Server): IReturnStatus {
|
||||||
return baseCheck(server, "grow");
|
return baseCheck(server, "grow");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function netscriptCanWeaken(server: Server | HacknetServer): IReturnStatus {
|
export function netscriptCanWeaken(server: Server): IReturnStatus {
|
||||||
return baseCheck(server, "weaken");
|
return baseCheck(server, "weaken");
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/Hacknet/GetMaxNumberUpgradeFns.js
Normal file
4
src/Hacknet/GetMaxNumberUpgradeFns.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Utility functions for calculating the maximum number of Hacknet upgrades the player
|
||||||
|
* can purchase for a Node with his/her current money
|
||||||
|
*/
|
||||||
@@ -1,35 +1,51 @@
|
|||||||
import { HacknetNode,
|
/**
|
||||||
BaseCostForHacknetNode,
|
* Generic helper/utility functions for the Hacknet mechanic:
|
||||||
HacknetNodePurchaseNextMult,
|
* - Purchase nodes/upgrades
|
||||||
HacknetNodeMaxLevel,
|
* - Calculating maximum number of upgrades
|
||||||
HacknetNodeMaxRam,
|
* - Processing Hacknet earnings
|
||||||
HacknetNodeMaxCores } from "./HacknetNode";
|
* - Updating Hash Manager capacity
|
||||||
import { HacknetServer,
|
* - Purchasing hash upgrades
|
||||||
BaseCostForHacknetServer,
|
*
|
||||||
HacknetServerPurchaseMult,
|
* TODO Should probably split the different types of functions into their own modules
|
||||||
HacknetServerMaxLevel,
|
*/
|
||||||
HacknetServerMaxRam,
|
import {
|
||||||
HacknetServerMaxCores,
|
HacknetNode,
|
||||||
HacknetServerMaxCache,
|
BaseCostForHacknetNode,
|
||||||
MaxNumberHacknetServers } from "./HacknetServer";
|
HacknetNodePurchaseNextMult,
|
||||||
import { HashManager } from "./HashManager";
|
HacknetNodeMaxLevel,
|
||||||
import { HashUpgrades } from "./HashUpgrades";
|
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 { generateRandomContract } from "../CodingContractGenerator";
|
||||||
import { iTutorialSteps, iTutorialNextStep,
|
import {
|
||||||
ITutorial} from "../InteractiveTutorial";
|
iTutorialSteps,
|
||||||
import { Player } from "../Player";
|
iTutorialNextStep,
|
||||||
import { AddToAllServers,
|
ITutorial
|
||||||
AllServers } from "../Server/AllServers";
|
} from "../InteractiveTutorial";
|
||||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
import { Player } from "../Player";
|
||||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
import { AllServers } from "../Server/AllServers";
|
||||||
import { Page, routing } from "../ui/navigationTracking";
|
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||||
|
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||||
|
import { Page, routing } from "../ui/navigationTracking";
|
||||||
|
|
||||||
import {getElementById} from "../../utils/uiHelpers/getElementById";
|
import { getElementById } from "../../utils/uiHelpers/getElementById";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { HacknetRoot } from "./ui/Root";
|
import { HacknetRoot } from "./ui/Root";
|
||||||
|
|
||||||
let hacknetNodesDiv;
|
let hacknetNodesDiv;
|
||||||
function hacknetNodesInit() {
|
function hacknetNodesInit() {
|
||||||
@@ -66,7 +82,7 @@ export function purchaseHacknet() {
|
|||||||
if (!Player.canAfford(cost)) { return -1; }
|
if (!Player.canAfford(cost)) { return -1; }
|
||||||
Player.loseMoney(cost);
|
Player.loseMoney(cost);
|
||||||
const server = Player.createHacknetServer();
|
const server = Player.createHacknetServer();
|
||||||
Player.hashManager.updateCapacity(Player);
|
updateHashManagerCapacity();
|
||||||
|
|
||||||
return numOwned;
|
return numOwned;
|
||||||
} else {
|
} else {
|
||||||
@@ -79,8 +95,7 @@ export function purchaseHacknet() {
|
|||||||
|
|
||||||
// Auto generate a name for the Node
|
// Auto generate a name for the Node
|
||||||
const name = "hacknet-node-" + numOwned;
|
const name = "hacknet-node-" + numOwned;
|
||||||
const node = new HacknetNode(name);
|
const node = new HacknetNode(name, Player.hacknet_node_money_mult);
|
||||||
node.updateMoneyGainRate(Player);
|
|
||||||
|
|
||||||
Player.loseMoney(cost);
|
Player.loseMoney(cost);
|
||||||
Player.hacknetNodes.push(node);
|
Player.hacknetNodes.push(node);
|
||||||
@@ -110,32 +125,32 @@ export function getCostOfNextHacknetServer() {
|
|||||||
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
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
|
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
|
||||||
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
||||||
if (maxLevel == null) {
|
if (maxLevel == null) {
|
||||||
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
|
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player))) {
|
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player.hacknet_node_level_cost_mult))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let min = 1;
|
let min = 1;
|
||||||
let max = maxLevel - 1;
|
let max = maxLevel - 1;
|
||||||
let levelsToMax = maxLevel - nodeObj.level;
|
let levelsToMax = maxLevel - nodeObj.level;
|
||||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player))) {
|
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.hacknet_node_level_cost_mult))) {
|
||||||
return levelsToMax;
|
return levelsToMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (min <= max) {
|
while (min <= max) {
|
||||||
var curr = (min + max) / 2 | 0;
|
var curr = (min + max) / 2 | 0;
|
||||||
if (curr !== maxLevel &&
|
if (curr !== maxLevel &&
|
||||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player)) &&
|
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult)) &&
|
||||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player))) {
|
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player.hacknet_node_level_cost_mult))) {
|
||||||
return Math.min(levelsToMax, curr);
|
return Math.min(levelsToMax, curr);
|
||||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
|
||||||
max = curr - 1;
|
max = curr - 1;
|
||||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
|
||||||
min = curr + 1;
|
min = curr + 1;
|
||||||
} else {
|
} else {
|
||||||
return Math.min(levelsToMax, curr);
|
return Math.min(levelsToMax, curr);
|
||||||
@@ -144,12 +159,13 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM
|
||||||
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
||||||
if (maxLevel == null) {
|
if (maxLevel == null) {
|
||||||
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
|
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player))) {
|
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player.hacknet_node_ram_cost_mult))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,45 +175,46 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
|||||||
} else {
|
} else {
|
||||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
|
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
|
||||||
}
|
}
|
||||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player))) {
|
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player.hacknet_node_ram_cost_mult))) {
|
||||||
return levelsToMax;
|
return levelsToMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
//We'll just loop until we find the max
|
//We'll just loop until we find the max
|
||||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player))) {
|
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player.hacknet_node_ram_cost_mult))) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores
|
||||||
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
||||||
if (maxLevel == null) {
|
if (maxLevel == null) {
|
||||||
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
|
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player))) {
|
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player.hacknet_node_core_cost_mult))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let min = 1;
|
let min = 1;
|
||||||
let max = maxLevel - 1;
|
let max = maxLevel - 1;
|
||||||
const levelsToMax = maxLevel - nodeObj.cores;
|
const levelsToMax = maxLevel - nodeObj.cores;
|
||||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player))) {
|
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.hacknet_node_core_cost_mult))) {
|
||||||
return levelsToMax;
|
return levelsToMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Use a binary search to find the max possible number of upgrades
|
// Use a binary search to find the max possible number of upgrades
|
||||||
while (min <= max) {
|
while (min <= max) {
|
||||||
let curr = (min + max) / 2 | 0;
|
let curr = (min + max) / 2 | 0;
|
||||||
if (curr != maxLevel &&
|
if (curr != maxLevel &&
|
||||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player)) &&
|
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult)) &&
|
||||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player))) {
|
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player.hacknet_node_core_cost_mult))) {
|
||||||
return Math.min(levelsToMax, curr);
|
return Math.min(levelsToMax, curr);
|
||||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
|
||||||
max = curr - 1;
|
max = curr - 1;
|
||||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
|
||||||
min = curr + 1;
|
min = curr + 1;
|
||||||
} else {
|
} else {
|
||||||
return Math.min(levelsToMax, curr);
|
return Math.min(levelsToMax, curr);
|
||||||
@@ -207,6 +224,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache
|
||||||
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
||||||
if (maxLevel == null) {
|
if (maxLevel == null) {
|
||||||
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
|
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
|
||||||
@@ -242,7 +260,136 @@ export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial construction of Hacknet Nodes UI
|
export function purchaseLevelUpgrade(node, levels=1) {
|
||||||
|
const sanitizedLevels = Math.round(levels);
|
||||||
|
const cost = node.calculateLevelUpgradeCost(sanitizedLevels, Player.hacknet_node_level_cost_mult);
|
||||||
|
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isServer = (node instanceof HacknetServer);
|
||||||
|
|
||||||
|
// If we're at max level, return false
|
||||||
|
if (node.level >= (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the number of specified upgrades would exceed the max level, calculate
|
||||||
|
// the maximum number of upgrades and use that
|
||||||
|
if (node.level + sanitizedLevels > (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
|
||||||
|
const diff = Math.max(0, (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel) - node.level);
|
||||||
|
return purchaseLevelUpgrade(node, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Player.canAfford(cost)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.loseMoney(cost);
|
||||||
|
node.upgradeLevel(sanitizedLevels, Player.hacknet_node_money_mult);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function purchaseRamUpgrade(node, levels=1) {
|
||||||
|
const sanitizedLevels = Math.round(levels);
|
||||||
|
const cost = node.calculateRamUpgradeCost(sanitizedLevels, Player.hacknet_node_ram_cost_mult);
|
||||||
|
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isServer = (node instanceof HacknetServer);
|
||||||
|
|
||||||
|
// Fail if we're already at max
|
||||||
|
if (node.ram >= (isServer ? HacknetServerMaxRam : 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 (isServer) {
|
||||||
|
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
|
||||||
|
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / node.maxRam)));
|
||||||
|
return purchaseRamUpgrade(node, diff);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
|
||||||
|
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / node.ram)));
|
||||||
|
return purchaseRamUpgrade(node, diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!Player.canAfford(cost)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.loseMoney(cost);
|
||||||
|
node.upgradeRam(sanitizedLevels, Player.hacknet_node_money_mult);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function purchaseCoreUpgrade(node, levels=1) {
|
||||||
|
const sanitizedLevels = Math.round(levels);
|
||||||
|
const cost = node.calculateCoreUpgradeCost(sanitizedLevels, Player.hacknet_node_core_cost_mult);
|
||||||
|
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isServer = (node instanceof HacknetServer);
|
||||||
|
|
||||||
|
// Fail if we're already at max
|
||||||
|
if (node.cores >= (isServer ? HacknetServerMaxCores : 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 (node.cores + sanitizedLevels > (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) {
|
||||||
|
const diff = Math.max(0, (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores) - node.cores);
|
||||||
|
return purchaseCoreUpgrade(node, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Player.canAfford(cost)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.loseMoney(cost);
|
||||||
|
node.upgradeCore(sanitizedLevels, Player.hacknet_node_money_mult);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function purchaseCacheUpgrade(node, levels=1) {
|
||||||
|
const sanitizedLevels = Math.round(levels);
|
||||||
|
const cost = node.calculateCacheUpgradeCost(sanitizedLevels);
|
||||||
|
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(node instanceof HacknetServer)) {
|
||||||
|
console.warn(`purchaseCacheUpgrade() called for a non-HacknetNode`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail if we're already at max
|
||||||
|
if (node.cache + sanitizedLevels > HacknetServerMaxCache) {
|
||||||
|
const diff = Math.max(0, HacknetServerMaxCache - node.cache);
|
||||||
|
return purchaseCacheUpgrade(node, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Player.canAfford(cost)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.loseMoney(cost);
|
||||||
|
node.upgradeCache(sanitizedLevels);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create/Refresh Hacknet Nodes UI
|
||||||
export function renderHacknetNodesUI() {
|
export function renderHacknetNodesUI() {
|
||||||
if (!routing.isOn(Page.HacknetNodes)) { return; }
|
if (!routing.isOn(Page.HacknetNodes)) { return; }
|
||||||
|
|
||||||
@@ -294,7 +441,10 @@ function processAllHacknetServerEarnings(numCycles) {
|
|||||||
|
|
||||||
let hashes = 0;
|
let hashes = 0;
|
||||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||||
const hserver = AllServers[Player.hacknetNodes[i]]; // hacknetNodes array only contains the IP addresses
|
// hacknetNodes array only contains the IP addresses of the servers.
|
||||||
|
// Also, update the hash rate before processing
|
||||||
|
const hserver = AllServers[Player.hacknetNodes[i]];
|
||||||
|
hserver.updateHashRate(Player.hacknet_node_money_mult);
|
||||||
hashes += hserver.process(numCycles);
|
hashes += hserver.process(numCycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,6 +453,37 @@ function processAllHacknetServerEarnings(numCycles) {
|
|||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateHashManagerCapacity() {
|
||||||
|
if (!(Player.hashManager instanceof HashManager)) {
|
||||||
|
console.error(`Player does not have a HashManager`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodes = Player.hacknetNodes;
|
||||||
|
if (nodes.length === 0) {
|
||||||
|
Player.hashManager.updateCapacity(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
for (let i = 0; i < nodes.length; ++i) {
|
||||||
|
if (typeof nodes[i] !== "string") {
|
||||||
|
Player.hashManager.updateCapacity(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const h = AllServers[nodes[i]];
|
||||||
|
if (!(h instanceof HacknetServer)) {
|
||||||
|
Player.hashManager.updateCapacity(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
total += h.hashCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.hashManager.updateCapacity(total);
|
||||||
|
}
|
||||||
|
|
||||||
export function purchaseHashUpgrade(upgName, upgTarget) {
|
export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||||
if (!(Player.hashManager instanceof HashManager)) {
|
if (!(Player.hashManager instanceof HashManager)) {
|
||||||
console.error(`Player does not have a HashManager`);
|
console.error(`Player does not have a HashManager`);
|
||||||
@@ -317,6 +498,7 @@ export function purchaseHashUpgrade(upgName, upgTarget) {
|
|||||||
switch (upgName) {
|
switch (upgName) {
|
||||||
case "Sell for Money": {
|
case "Sell for Money": {
|
||||||
Player.gainMoney(upg.value);
|
Player.gainMoney(upg.value);
|
||||||
|
Player.recordMoneySource(upg.value, "hacknetnode");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Sell for Corporation Funds": {
|
case "Sell for Corporation Funds": {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { IHacknetNode } from "./IHacknetNode";
|
|||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
|
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||||
import { Generic_fromJSON,
|
import { Generic_fromJSON,
|
||||||
@@ -62,12 +61,14 @@ export class HacknetNode implements IHacknetNode {
|
|||||||
// Total money earned by this Node
|
// Total money earned by this Node
|
||||||
totalMoneyGenerated: number = 0;
|
totalMoneyGenerated: number = 0;
|
||||||
|
|
||||||
constructor(name: string="") {
|
constructor(name: string="", prodMult: number=1) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
|
this.updateMoneyGainRate(prodMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the cost to upgrade this Node's number of cores
|
// Get the cost to upgrade this Node's number of cores
|
||||||
calculateCoreUpgradeCost(levels: number=1, p: IPlayer): number {
|
calculateCoreUpgradeCost(levels: number=1, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -86,13 +87,13 @@ export class HacknetNode implements IHacknetNode {
|
|||||||
++currentCores;
|
++currentCores;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCost *= p.hacknet_node_core_cost_mult;
|
totalCost *= costMult;
|
||||||
|
|
||||||
return totalCost;
|
return totalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the cost to upgrade this Node's level
|
// Get the cost to upgrade this Node's level
|
||||||
calculateLevelUpgradeCost(levels: number=1, p: IPlayer): number {
|
calculateLevelUpgradeCost(levels: number=1, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -110,11 +111,11 @@ export class HacknetNode implements IHacknetNode {
|
|||||||
++currLevel;
|
++currLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BaseCostForHacknetNode / 2 * totalMultiplier * p.hacknet_node_level_cost_mult;
|
return BaseCostForHacknetNode / 2 * totalMultiplier * costMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the cost to upgrade this Node's RAM
|
// Get the cost to upgrade this Node's RAM
|
||||||
calculateRamUpgradeCost(levels: number=1, p: IPlayer): number {
|
calculateRamUpgradeCost(levels: number=1, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -138,7 +139,7 @@ export class HacknetNode implements IHacknetNode {
|
|||||||
++numUpgrades;
|
++numUpgrades;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
totalCost *= costMult;
|
||||||
|
|
||||||
return totalCost;
|
return totalCost;
|
||||||
}
|
}
|
||||||
@@ -161,112 +162,37 @@ export class HacknetNode implements IHacknetNode {
|
|||||||
|
|
||||||
// Upgrade this Node's number of cores, if possible
|
// Upgrade this Node's number of cores, if possible
|
||||||
// Returns a boolean indicating whether new cores were successfully bought
|
// Returns a boolean indicating whether new cores were successfully bought
|
||||||
purchaseCoreUpgrade(levels: number=1, p: IPlayer): boolean {
|
upgradeCore(levels: number=1, prodMult: number): void {
|
||||||
const sanitizedLevels = Math.round(levels);
|
this.cores = Math.min(HacknetNodeMaxCores, Math.round(this.cores + levels));
|
||||||
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
|
this.updateMoneyGainRate(prodMult);
|
||||||
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
|
// Upgrade this Node's level, if possible
|
||||||
// Returns a boolean indicating whether the level was successfully updated
|
// Returns a boolean indicating whether the level was successfully updated
|
||||||
purchaseLevelUpgrade(levels: number=1, p: IPlayer): boolean {
|
upgradeLevel(levels: number=1, prodMult: number): void {
|
||||||
const sanitizedLevels = Math.round(levels);
|
this.level = Math.min(HacknetNodeMaxLevel, Math.round(this.level + levels));
|
||||||
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
|
this.updateMoneyGainRate(prodMult);
|
||||||
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
|
// Upgrade this Node's RAM, if possible
|
||||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||||
purchaseRamUpgrade(levels: number=1, p: IPlayer): boolean {
|
upgradeRam(levels: number=1, prodMult: number): void {
|
||||||
const sanitizedLevels = Math.round(levels);
|
for (let i = 0; i < levels; ++i) {
|
||||||
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 *= 2; // Ram is always doubled
|
||||||
}
|
}
|
||||||
this.ram = Math.round(this.ram); // Handle any floating point precision issues
|
this.ram = Math.round(this.ram); // Handle any floating point precision issues
|
||||||
this.updateMoneyGainRate(p);
|
this.updateMoneyGainRate(prodMult);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
|
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
|
||||||
updateMoneyGainRate(p: IPlayer): void {
|
updateMoneyGainRate(prodMult: number): void {
|
||||||
//How much extra $/s is gained per level
|
//How much extra $/s is gained per level
|
||||||
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
|
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
|
||||||
|
|
||||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||||
Math.pow(1.035, this.ram - 1) *
|
Math.pow(1.035, this.ram - 1) *
|
||||||
((this.cores + 5) / 6) *
|
((this.cores + 5) / 6) *
|
||||||
p.hacknet_node_money_mult *
|
prodMult *
|
||||||
BitNodeMultipliers.HacknetNodeMoney;
|
BitNodeMultipliers.HacknetNodeMoney;
|
||||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||||
this.moneyGainRatePerSecond = 0;
|
this.moneyGainRatePerSecond = 0;
|
||||||
|
|||||||
@@ -6,15 +6,16 @@ import { CONSTANTS } from "../Constants";
|
|||||||
import { IHacknetNode } from "./IHacknetNode";
|
import { IHacknetNode } from "./IHacknetNode";
|
||||||
|
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
|
||||||
import { BaseServer } from "../Server/BaseServer";
|
import { BaseServer } from "../Server/BaseServer";
|
||||||
import { RunningScript } from "../Script/RunningScript";
|
import { RunningScript } from "../Script/RunningScript";
|
||||||
|
|
||||||
import { createRandomIp } from "../../utils/IPAddress";
|
import { createRandomIp } from "../../utils/IPAddress";
|
||||||
|
|
||||||
import { Generic_fromJSON,
|
import {
|
||||||
Generic_toJSON,
|
Generic_fromJSON,
|
||||||
Reviver } from "../../utils/JSONReviver";
|
Generic_toJSON,
|
||||||
|
Reviver
|
||||||
|
} from "../../utils/JSONReviver";
|
||||||
|
|
||||||
// Constants for Hacknet Server stats/production
|
// Constants for Hacknet Server stats/production
|
||||||
export const HacknetServerHashesPerLevel: number = 0.001;
|
export const HacknetServerHashesPerLevel: number = 0.001;
|
||||||
@@ -31,7 +32,7 @@ export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for co
|
|||||||
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
|
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 HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
|
||||||
|
|
||||||
export const MaxNumberHacknetServers: number = 25; // Max number of Hacknet Servers you can own
|
export const MaxNumberHacknetServers: number = 20; // Max number of Hacknet Servers you can own
|
||||||
|
|
||||||
// Constants for max upgrade levels for Hacknet Server
|
// Constants for max upgrade levels for Hacknet Server
|
||||||
export const HacknetServerMaxLevel: number = 300;
|
export const HacknetServerMaxLevel: number = 300;
|
||||||
@@ -46,7 +47,6 @@ interface IConstructorParams {
|
|||||||
isConnectedTo?: boolean;
|
isConnectedTo?: boolean;
|
||||||
maxRam?: number;
|
maxRam?: number;
|
||||||
organizationName?: string;
|
organizationName?: string;
|
||||||
player?: IPlayer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HacknetServer extends BaseServer implements IHacknetNode {
|
export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||||
@@ -81,10 +81,6 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
|
|
||||||
this.maxRam = 1;
|
this.maxRam = 1;
|
||||||
this.updateHashCapacity();
|
this.updateHashCapacity();
|
||||||
|
|
||||||
if (params.player) {
|
|
||||||
this.updateHashRate(params.player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateCacheUpgradeCost(levels: number): number {
|
calculateCacheUpgradeCost(levels: number): number {
|
||||||
@@ -109,7 +105,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
return totalCost;
|
return totalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateCoreUpgradeCost(levels: number, p: IPlayer): number {
|
calculateCoreUpgradeCost(levels: number, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -127,12 +123,12 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
++currentCores;
|
++currentCores;
|
||||||
}
|
}
|
||||||
totalCost *= BaseCostForHacknetServerCore;
|
totalCost *= BaseCostForHacknetServerCore;
|
||||||
totalCost *= p.hacknet_node_core_cost_mult;
|
totalCost *= costMult;
|
||||||
|
|
||||||
return totalCost;
|
return totalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateLevelUpgradeCost(levels: number, p: IPlayer): number {
|
calculateLevelUpgradeCost(levels: number, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -150,10 +146,10 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
++currLevel;
|
++currLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 10 * BaseCostForHacknetServer * totalMultiplier * p.hacknet_node_level_cost_mult;
|
return 10 * BaseCostForHacknetServer * totalMultiplier * costMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateRamUpgradeCost(levels: number, p: IPlayer): number {
|
calculateRamUpgradeCost(levels: number, costMult: number): number {
|
||||||
const sanitizedLevels = Math.round(levels);
|
const sanitizedLevels = Math.round(levels);
|
||||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -175,149 +171,48 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
currentRam *= 2;
|
currentRam *= 2;
|
||||||
++numUpgrades;
|
++numUpgrades;
|
||||||
}
|
}
|
||||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
totalCost *= costMult;
|
||||||
|
|
||||||
return totalCost;
|
return totalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process this Hacknet Server in the game loop.
|
// Process this Hacknet Server in the game loop. Returns the number of hashes generated
|
||||||
// Returns the number of hashes generated
|
|
||||||
process(numCycles: number=1): number {
|
process(numCycles: number=1): number {
|
||||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||||
|
|
||||||
return this.hashRate * seconds;
|
return this.hashRate * seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a boolean indicating whether the cache was successfully upgraded
|
upgradeCache(levels: number): void {
|
||||||
purchaseCacheUpgrade(levels: number, p: IPlayer): boolean {
|
this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels));
|
||||||
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();
|
this.updateHashCapacity();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a boolean indicating whether the number of cores was successfully upgraded
|
upgradeCore(levels: number, prodMult: number): void {
|
||||||
purchaseCoreUpgrade(levels: number, p: IPlayer): boolean {
|
this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels));
|
||||||
const sanitizedLevels = Math.round(levels);
|
this.updateHashRate(prodMult);
|
||||||
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
|
upgradeLevel(levels: number, prodMult: number): void {
|
||||||
purchaseLevelUpgrade(levels: number, p: IPlayer): boolean {
|
this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels));
|
||||||
const sanitizedLevels = Math.round(levels);
|
this.updateHashRate(prodMult);
|
||||||
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
|
upgradeRam(levels: number, prodMult: number): boolean {
|
||||||
purchaseRamUpgrade(levels: number, p: IPlayer): boolean {
|
for (let i = 0; i < levels; ++i) {
|
||||||
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 *= 2;
|
||||||
}
|
}
|
||||||
this.maxRam = Math.round(this.maxRam);
|
this.maxRam = Math.min(HacknetServerMaxRam, Math.round(this.maxRam));
|
||||||
this.updateHashRate(p);
|
this.updateHashRate(prodMult);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Whenever a script is run, we must update this server's hash rate
|
||||||
* Whenever a script is run, we must update this server's hash rate
|
runScript(script: RunningScript, prodMult?: number): void {
|
||||||
*/
|
|
||||||
runScript(script: RunningScript, p?: IPlayer): void {
|
|
||||||
super.runScript(script);
|
super.runScript(script);
|
||||||
if (p) {
|
if (prodMult != null && typeof prodMult === "number") {
|
||||||
this.updateHashRate(p);
|
this.updateHashRate(prodMult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +220,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
this.hashCapacity = 32 * Math.pow(2, this.cache);
|
this.hashCapacity = 32 * Math.pow(2, this.cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHashRate(p: IPlayer): void {
|
updateHashRate(prodMult: number): void {
|
||||||
const baseGain = HacknetServerHashesPerLevel * this.level;
|
const baseGain = HacknetServerHashesPerLevel * this.level;
|
||||||
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
|
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
|
||||||
const coreMultiplier = 1 + (this.cores - 1) / 5;
|
const coreMultiplier = 1 + (this.cores - 1) / 5;
|
||||||
@@ -333,7 +228,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
|||||||
|
|
||||||
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
|
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
|
||||||
|
|
||||||
this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney;
|
this.hashRate = hashRate * prodMult * BitNodeMultipliers.HacknetNodeMoney;
|
||||||
|
|
||||||
if (isNaN(this.hashRate)) {
|
if (isNaN(this.hashRate)) {
|
||||||
this.hashRate = 0;
|
this.hashRate = 0;
|
||||||
|
|||||||
@@ -6,12 +6,9 @@
|
|||||||
* his hashes, and contains method for grabbing the data/multipliers from
|
* his hashes, and contains method for grabbing the data/multipliers from
|
||||||
* those upgrades
|
* those upgrades
|
||||||
*/
|
*/
|
||||||
import { HacknetServer } from "./HacknetServer";
|
|
||||||
import { HashUpgrades } from "./HashUpgrades";
|
import { HashUpgrades } from "./HashUpgrades";
|
||||||
|
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
|
||||||
import { AllServers } from "../Server/AllServers";
|
|
||||||
import { Generic_fromJSON,
|
import { Generic_fromJSON,
|
||||||
Generic_toJSON,
|
Generic_toJSON,
|
||||||
Reviver } from "../../utils/JSONReviver";
|
Reviver } from "../../utils/JSONReviver";
|
||||||
@@ -84,14 +81,14 @@ export class HashManager {
|
|||||||
return upg.getCost(currLevel);
|
return upg.getCost(currLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
prestige(p: IPlayer): void {
|
prestige(): void {
|
||||||
for (const name in HashUpgrades) {
|
for (const name in HashUpgrades) {
|
||||||
this.upgrades[name] = 0;
|
this.upgrades[name] = 0;
|
||||||
}
|
}
|
||||||
this.hashes = 0;
|
this.hashes = 0;
|
||||||
if (p != null) {
|
|
||||||
this.updateCapacity(p);
|
// When prestiging, player's hacknet nodes are always reset. So capacity = 0
|
||||||
}
|
this.updateCapacity(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,14 +96,16 @@ export class HashManager {
|
|||||||
*/
|
*/
|
||||||
refundUpgrade(upgName: string): void {
|
refundUpgrade(upgName: string): void {
|
||||||
const upg = HashUpgrades[upgName];
|
const upg = HashUpgrades[upgName];
|
||||||
|
|
||||||
|
// Reduce the level first, so we get the right cost
|
||||||
|
--this.upgrades[upgName];
|
||||||
|
|
||||||
const currLevel = this.upgrades[upgName];
|
const currLevel = this.upgrades[upgName];
|
||||||
if (upg == null || currLevel == null || currLevel === 0) {
|
if (upg == null || currLevel == null || currLevel < 0) {
|
||||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce the level first, so we get the right cost
|
|
||||||
--this.upgrades[upgName];
|
|
||||||
const cost = upg.getCost(currLevel);
|
const cost = upg.getCost(currLevel);
|
||||||
this.hashes += cost;
|
this.hashes += cost;
|
||||||
}
|
}
|
||||||
@@ -116,33 +115,11 @@ export class HashManager {
|
|||||||
this.hashes = Math.min(this.hashes, this.capacity);
|
this.hashes = Math.min(this.hashes, this.capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCapacity(p: IPlayer): void {
|
updateCapacity(newCap: number): void {
|
||||||
if (p.hacknetNodes.length <= 0) {
|
if (newCap < 0) {
|
||||||
this.capacity = 0;
|
this.capacity = 0;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
this.capacity = Math.max(newCap, 0);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
// Interface for a Hacknet Node. Implemented by both a basic Hacknet Node,
|
// Interface for a Hacknet Node. Implemented by both a basic Hacknet Node,
|
||||||
// and the upgraded Hacknet Server in BitNode-9
|
// and the upgraded Hacknet Server in BitNode-9
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
|
||||||
|
|
||||||
export interface IHacknetNode {
|
export interface IHacknetNode {
|
||||||
cores: number;
|
cores: number;
|
||||||
level: number;
|
level: number;
|
||||||
onlineTimeSeconds: number;
|
onlineTimeSeconds: number;
|
||||||
|
|
||||||
calculateCoreUpgradeCost: (levels: number, p: IPlayer) => number;
|
calculateCoreUpgradeCost: (levels: number, costMult: number) => number;
|
||||||
calculateLevelUpgradeCost: (levels: number, p: IPlayer) => number;
|
calculateLevelUpgradeCost: (levels: number, costMult: number) => number;
|
||||||
calculateRamUpgradeCost: (levels: number, p: IPlayer) => number;
|
calculateRamUpgradeCost: (levels: number, costMult: number) => number;
|
||||||
purchaseCoreUpgrade: (levels: number, p: IPlayer) => boolean;
|
upgradeCore: (levels: number, prodMult: number) => void;
|
||||||
purchaseLevelUpgrade: (levels: number, p: IPlayer) => boolean;
|
upgradeLevel: (levels: number, prodMult: number) => void;
|
||||||
purchaseRamUpgrade: (levels: number, p: IPlayer) => boolean;
|
upgradeRam: (levels: number, prodMult: number) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class GeneralInfo extends React.Component {
|
|||||||
`you hashes. Hashes can be spent on a variety of different upgrades.`;
|
`you hashes. Hashes can be spent on a variety of different upgrades.`;
|
||||||
} else {
|
} else {
|
||||||
return `Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
|
return `Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
|
||||||
`and contribute its resources to the Hacknet networ. This allows you to take ` +
|
`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, ` +
|
`a small percentage of profits from hacks performed on the network. Essentially, ` +
|
||||||
`you are renting out your Node's computing power.`;
|
`you are renting out your Node's computing power.`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,19 @@
|
|||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { HacknetNodeMaxLevel,
|
import {
|
||||||
HacknetNodeMaxRam,
|
HacknetNodeMaxLevel,
|
||||||
HacknetNodeMaxCores } from "../HacknetNode";
|
HacknetNodeMaxRam,
|
||||||
import { getMaxNumberLevelUpgrades,
|
HacknetNodeMaxCores
|
||||||
getMaxNumberRamUpgrades,
|
} from "../HacknetNode";
|
||||||
getMaxNumberCoreUpgrades } from "../HacknetHelpers";
|
import {
|
||||||
|
getMaxNumberLevelUpgrades,
|
||||||
|
getMaxNumberRamUpgrades,
|
||||||
|
getMaxNumberCoreUpgrades,
|
||||||
|
purchaseLevelUpgrade,
|
||||||
|
purchaseRamUpgrade,
|
||||||
|
purchaseCoreUpgrade,
|
||||||
|
} from "../HacknetHelpers";
|
||||||
|
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
|
|
||||||
@@ -35,7 +42,7 @@ export class HacknetNode extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
|
||||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||||
if (Player.money.lt(upgradeLevelCost)) {
|
if (Player.money.lt(upgradeLevelCost)) {
|
||||||
upgradeLevelClass = "std-button-disabled";
|
upgradeLevelClass = "std-button-disabled";
|
||||||
@@ -48,7 +55,7 @@ export class HacknetNode extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
||||||
}
|
}
|
||||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
purchaseLevelUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -66,7 +73,7 @@ export class HacknetNode extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
|
||||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||||
if (Player.money.lt(upgradeRamCost)) {
|
if (Player.money.lt(upgradeRamCost)) {
|
||||||
upgradeRamClass = "std-button-disabled";
|
upgradeRamClass = "std-button-disabled";
|
||||||
@@ -79,7 +86,7 @@ export class HacknetNode extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
||||||
}
|
}
|
||||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
purchaseRamUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -97,7 +104,7 @@ export class HacknetNode extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
|
||||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||||
if (Player.money.lt(upgradeCoreCost)) {
|
if (Player.money.lt(upgradeCoreCost)) {
|
||||||
upgradeCoresClass = "std-button-disabled";
|
upgradeCoresClass = "std-button-disabled";
|
||||||
@@ -110,7 +117,7 @@ export class HacknetNode extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
||||||
}
|
}
|
||||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
purchaseCoreUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,23 @@
|
|||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { HacknetServerMaxLevel,
|
import {
|
||||||
HacknetServerMaxRam,
|
HacknetServerMaxLevel,
|
||||||
HacknetServerMaxCores,
|
HacknetServerMaxRam,
|
||||||
HacknetServerMaxCache } from "../HacknetServer";
|
HacknetServerMaxCores,
|
||||||
import { getMaxNumberLevelUpgrades,
|
HacknetServerMaxCache
|
||||||
getMaxNumberRamUpgrades,
|
} from "../HacknetServer";
|
||||||
getMaxNumberCoreUpgrades,
|
import {
|
||||||
getMaxNumberCacheUpgrades } from "../HacknetHelpers";
|
getMaxNumberLevelUpgrades,
|
||||||
|
getMaxNumberRamUpgrades,
|
||||||
|
getMaxNumberCoreUpgrades,
|
||||||
|
getMaxNumberCacheUpgrades,
|
||||||
|
purchaseLevelUpgrade,
|
||||||
|
purchaseRamUpgrade,
|
||||||
|
purchaseCoreUpgrade,
|
||||||
|
purchaseCacheUpgrade,
|
||||||
|
updateHashManagerCapacity,
|
||||||
|
} from "../HacknetHelpers";
|
||||||
|
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
|
|
||||||
@@ -37,7 +46,7 @@ export class HacknetServer extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
|
||||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||||
if (Player.money.lt(upgradeLevelCost)) {
|
if (Player.money.lt(upgradeLevelCost)) {
|
||||||
upgradeLevelClass = "std-button-disabled";
|
upgradeLevelClass = "std-button-disabled";
|
||||||
@@ -50,7 +59,7 @@ export class HacknetServer extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
||||||
}
|
}
|
||||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
purchaseLevelUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -69,7 +78,7 @@ export class HacknetServer extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
|
||||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||||
if (Player.money.lt(upgradeRamCost)) {
|
if (Player.money.lt(upgradeRamCost)) {
|
||||||
upgradeRamClass = "std-button-disabled";
|
upgradeRamClass = "std-button-disabled";
|
||||||
@@ -82,7 +91,7 @@ export class HacknetServer extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
||||||
}
|
}
|
||||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
purchaseRamUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -101,7 +110,7 @@ export class HacknetServer extends React.Component {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
|
||||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||||
if (Player.money.lt(upgradeCoreCost)) {
|
if (Player.money.lt(upgradeCoreCost)) {
|
||||||
upgradeCoresClass = "std-button-disabled";
|
upgradeCoresClass = "std-button-disabled";
|
||||||
@@ -114,7 +123,7 @@ export class HacknetServer extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
||||||
}
|
}
|
||||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
purchaseCoreUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -146,9 +155,9 @@ export class HacknetServer extends React.Component {
|
|||||||
if (purchaseMult === "MAX") {
|
if (purchaseMult === "MAX") {
|
||||||
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
||||||
}
|
}
|
||||||
node.purchaseCacheUpgrade(numUpgrades, Player);
|
purchaseCacheUpgrade(node, numUpgrades);
|
||||||
recalculate();
|
recalculate();
|
||||||
Player.hashManager.updateCapacity(Player);
|
updateHashManagerCapacity();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export class HacknetRoot extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
|
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
|
||||||
|
this.handlePurchaseButtonClick = this.handlePurchaseButtonClick.bind(this);
|
||||||
|
this.recalculateTotalProduction = this.recalculateTotalProduction.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -50,6 +52,12 @@ export class HacknetRoot extends React.Component {
|
|||||||
createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup });
|
createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePurchaseButtonClick() {
|
||||||
|
if (purchaseHacknet() >= 0) {
|
||||||
|
this.recalculateTotalProduction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
recalculateTotalProduction() {
|
recalculateTotalProduction() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||||
@@ -85,13 +93,6 @@ export class HacknetRoot extends React.Component {
|
|||||||
purchaseCost = getCostOfNextHacknetNode();
|
purchaseCost = getCostOfNextHacknetNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// onClick event handler for purchase button
|
|
||||||
const purchaseOnClick = () => {
|
|
||||||
if (purchaseHacknet() >= 0) {
|
|
||||||
this.recalculateTotalProduction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// onClick event handlers for purchase multiplier buttons
|
// onClick event handlers for purchase multiplier buttons
|
||||||
const purchaseMultiplierOnClicks = [
|
const purchaseMultiplierOnClicks = [
|
||||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
|
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
|
||||||
@@ -112,7 +113,7 @@ export class HacknetRoot extends React.Component {
|
|||||||
key={hserver.hostname}
|
key={hserver.hostname}
|
||||||
node={hserver}
|
node={hserver}
|
||||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
recalculate={this.recalculateTotalProduction}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -121,7 +122,7 @@ export class HacknetRoot extends React.Component {
|
|||||||
key={node.name}
|
key={node.name}
|
||||||
node={node}
|
node={node}
|
||||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
recalculate={this.recalculateTotalProduction}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -132,7 +133,7 @@ export class HacknetRoot extends React.Component {
|
|||||||
<h1>Hacknet Nodes</h1>
|
<h1>Hacknet Nodes</h1>
|
||||||
<GeneralInfo />
|
<GeneralInfo />
|
||||||
|
|
||||||
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={purchaseOnClick} />
|
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={this.handlePurchaseButtonClick} />
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<div id={"hacknet-nodes-money-multipliers-div"}>
|
<div id={"hacknet-nodes-money-multipliers-div"}>
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
* to TypeScript at the moment
|
* to TypeScript at the moment
|
||||||
*/
|
*/
|
||||||
export interface IEngine {
|
export interface IEngine {
|
||||||
|
hideAllContent: () => void;
|
||||||
loadBladeburnerContent: () => void;
|
loadBladeburnerContent: () => void;
|
||||||
|
loadFactionContent: () => void;
|
||||||
|
loadFactionsContent: () => void;
|
||||||
|
loadGangContent: () => void;
|
||||||
loadInfiltrationContent: () => void;
|
loadInfiltrationContent: () => void;
|
||||||
|
loadMissionContent: () => void;
|
||||||
loadResleevingContent: () => void;
|
loadResleevingContent: () => void;
|
||||||
loadStockMarketContent: () => void;
|
loadStockMarketContent: () => 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,21 @@ 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() {
|
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||||
@@ -124,7 +106,7 @@ InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
|||||||
|
|
||||||
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
|
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
|
||||||
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
|
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
|
||||||
return Math.pow(this.intExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
return Math.pow(this.intExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
||||||
@@ -177,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:
|
||||||
@@ -460,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 *
|
||||||
@@ -474,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 {
|
||||||
@@ -647,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;
|
||||||
@@ -673,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;
|
||||||
@@ -698,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;
|
||||||
@@ -722,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;
|
||||||
@@ -746,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;
|
||||||
@@ -773,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;
|
||||||
@@ -796,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;
|
||||||
@@ -817,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;
|
||||||
@@ -838,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;
|
||||||
@@ -857,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();
|
||||||
@@ -318,11 +321,11 @@ function iTutorialEvaluateStep() {
|
|||||||
Engine.loadActiveScriptsContent();
|
Engine.loadActiveScriptsContent();
|
||||||
iTutorialSetText("This page displays stats/information about all of your scripts that are " +
|
iTutorialSetText("This page displays stats/information about all of your scripts that are " +
|
||||||
"running across every existing server. You can use this to gauge how well " +
|
"running across every existing server. You can use this to gauge how well " +
|
||||||
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal'" +
|
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal' " +
|
||||||
"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,7 +382,7 @@ 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.loadLocationContent();
|
Engine.loadLocationContent();
|
||||||
@@ -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
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,46 +2,53 @@
|
|||||||
* Location and traveling-related helper functions.
|
* Location and traveling-related helper functions.
|
||||||
* Mostly used for UI
|
* Mostly used for UI
|
||||||
*/
|
*/
|
||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
|
|
||||||
import { CityName } from "./data/CityNames";
|
import { CityName } from "./data/CityNames";
|
||||||
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { AllServers,
|
import {
|
||||||
AddToAllServers } from "../Server/AllServers";
|
AddToAllServers,
|
||||||
import { Server } from "../Server/Server";
|
createUniqueRandomIp,
|
||||||
import { getPurchaseServerCost,
|
} from "../Server/AllServers";
|
||||||
purchaseRamForHomeComputer,
|
import { safetlyCreateUniqueServer } from "../Server/ServerHelpers";
|
||||||
purchaseServer } from "../Server/ServerPurchases";
|
import {
|
||||||
import { SpecialServerIps } from "../Server/SpecialServerIps";
|
getPurchaseServerCost,
|
||||||
import { Settings } from "../Settings/Settings";
|
purchaseRamForHomeComputer,
|
||||||
|
purchaseServer
|
||||||
|
} from "../Server/ServerPurchases";
|
||||||
|
import { SpecialServerIps } from "../Server/SpecialServerIps";
|
||||||
|
import { Settings } from "../Settings/Settings";
|
||||||
|
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||||
import { createRandomIp } from "../../utils/IPAddress";
|
import {
|
||||||
import { yesNoBoxGetYesButton,
|
yesNoBoxGetYesButton,
|
||||||
yesNoBoxGetNoButton,
|
yesNoBoxGetNoButton,
|
||||||
yesNoBoxClose,
|
yesNoBoxClose,
|
||||||
yesNoBoxCreate,
|
yesNoBoxCreate,
|
||||||
yesNoTxtInpBoxGetYesButton,
|
yesNoTxtInpBoxGetYesButton,
|
||||||
yesNoTxtInpBoxGetNoButton,
|
yesNoTxtInpBoxGetNoButton,
|
||||||
yesNoTxtInpBoxClose,
|
yesNoTxtInpBoxClose,
|
||||||
yesNoTxtInpBoxCreate } from "../../utils/YesNoBox";
|
yesNoTxtInpBoxCreate
|
||||||
|
} from "../../utils/YesNoBox";
|
||||||
|
|
||||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||||
import { createPopup } from "../../utils/uiHelpers/createPopup";
|
import { createPopup } from "../../utils/uiHelpers/createPopup";
|
||||||
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
|
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
|
||||||
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
|
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pop-up box that lets the player confirm traveling to a different city
|
* Create a pop-up box that lets the player confirm traveling to a different city.
|
||||||
* If settings are configured to suppress this popup, just instantly travel
|
* If settings are configured to suppress this popup, just instantly travel.
|
||||||
* The actual "Travel" implementation is implemented in the UI, and is passed in
|
* The actual "Travel" implementation is implemented in the UI, and is passed in
|
||||||
* as an argument
|
* as an argument.
|
||||||
|
* @param {CityName} destination - City that the player is traveling to
|
||||||
|
* @param {Function} travelFn - Function that changes the player's state for traveling
|
||||||
*/
|
*/
|
||||||
type TravelFunction = (to: CityName) => void;
|
type TravelFunction = (to: CityName) => void;
|
||||||
export function createTravelPopup(destination: CityName, travelFn: TravelFunction) {
|
export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
|
||||||
const cost = CONSTANTS.TravelCost;
|
const cost = CONSTANTS.TravelCost;
|
||||||
|
|
||||||
if (Settings.SuppressTravelConfirmation) {
|
if (Settings.SuppressTravelConfirmation) {
|
||||||
@@ -75,10 +82,10 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pop-up box that lets the player purchase a server.
|
* Create a pop-up box that lets the player purchase a server.
|
||||||
* @param ram - Amount of RAM (GB) on server
|
* @param {number} ram - Amount of RAM (GB) on server
|
||||||
* @param p - Player object
|
* @param {IPlayer} p - Player object
|
||||||
*/
|
*/
|
||||||
export function createPurchaseServerPopup(ram: number, p: IPlayer) {
|
export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
|
||||||
const cost = getPurchaseServerCost(ram);
|
const cost = getPurchaseServerCost(ram);
|
||||||
if (cost === Infinity) {
|
if (cost === Infinity) {
|
||||||
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
|
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
|
||||||
@@ -106,9 +113,10 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a popup that lets the player start a Corporation
|
* Create a popup that lets the player start a Corporation
|
||||||
|
* @param {IPlayer} p - Player object
|
||||||
*/
|
*/
|
||||||
export function createStartCorporationPopup(p: IPlayer) {
|
export function createStartCorporationPopup(p: IPlayer) {
|
||||||
if (!p.canAccessCorporation() || p.hasCorporation) { return; }
|
if (!p.canAccessCorporation() || p.hasCorporation()) { return; }
|
||||||
|
|
||||||
const popupId = "create-corporation-popup";
|
const popupId = "create-corporation-popup";
|
||||||
const txt = createElement("p", {
|
const txt = createElement("p", {
|
||||||
@@ -167,8 +175,10 @@ export function createStartCorporationPopup(p: IPlayer) {
|
|||||||
if (worldHeader instanceof HTMLElement) {
|
if (worldHeader instanceof HTMLElement) {
|
||||||
worldHeader.click(); worldHeader.click();
|
worldHeader.click(); worldHeader.click();
|
||||||
}
|
}
|
||||||
dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " +
|
dialogBoxCreate(
|
||||||
"You can visit and manage your company in the City");
|
"Congratulations! You just started your own corporation with government seed money. " +
|
||||||
|
"You can visit and manage your company in the City"
|
||||||
|
);
|
||||||
removeElementById(popupId);
|
removeElementById(popupId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -182,21 +192,23 @@ export function createStartCorporationPopup(p: IPlayer) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a popup that lets the player upgrade the cores on his/her home computer
|
* Create a popup that lets the player upgrade the cores on his/her home computer
|
||||||
* @param p - Player object
|
* @param {IPlayer} p - Player object
|
||||||
*/
|
*/
|
||||||
export function createUpgradeHomeCoresPopup(p: IPlayer) {
|
export function createUpgradeHomeCoresPopup(p: IPlayer) {
|
||||||
const currentCores = p.getHomeComputer().cpuCores;
|
const currentCores = p.getHomeComputer().cpuCores;
|
||||||
if (currentCores >= 8) { return; } // Max of 8 cores
|
if (currentCores >= 8) { return; } // Max of 8 cores
|
||||||
|
|
||||||
//Cost of purchasing another cost is found by indexing this array with number of current cores
|
// Cost of purchasing another cost is found by indexing this array with number of current cores
|
||||||
const allCosts = [0,
|
const allCosts = [
|
||||||
10e9, // 1->2 Cores - 10 bn
|
0,
|
||||||
250e9, // 2->3 Cores - 250 bn
|
10e9,
|
||||||
5e12, // 3->4 Cores - 5 trillion
|
250e9,
|
||||||
100e12, // 4->5 Cores - 100 trillion
|
5e12,
|
||||||
1e15, // 5->6 Cores - 1 quadrillion
|
100e12,
|
||||||
20e15, // 6->7 Cores - 20 quadrillion
|
1e15,
|
||||||
200e15]; // 7->8 Cores - 200 quadrillion
|
20e15,
|
||||||
|
200e15
|
||||||
|
];
|
||||||
const cost: number = allCosts[currentCores];
|
const cost: number = allCosts[currentCores];
|
||||||
|
|
||||||
const yesBtn = yesNoBoxGetYesButton();
|
const yesBtn = yesNoBoxGetYesButton();
|
||||||
@@ -210,8 +222,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
|
|||||||
} else {
|
} else {
|
||||||
p.loseMoney(cost);
|
p.loseMoney(cost);
|
||||||
p.getHomeComputer().cpuCores++;
|
p.getHomeComputer().cpuCores++;
|
||||||
dialogBoxCreate("You purchased an additional CPU Core for your home computer! It now has " +
|
dialogBoxCreate(
|
||||||
p.getHomeComputer().cpuCores + " cores.");
|
"You purchased an additional CPU Core for your home computer! It now has " +
|
||||||
|
p.getHomeComputer().cpuCores + " cores."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
yesNoBoxClose();
|
yesNoBoxClose();
|
||||||
});
|
});
|
||||||
@@ -221,15 +235,17 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
|
|||||||
yesNoBoxClose();
|
yesNoBoxClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
yesNoBoxCreate("Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " +
|
yesNoBoxCreate(
|
||||||
"lets you start with an additional Core Node in Hacking Missions.<br><br>" +
|
"Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " +
|
||||||
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " +
|
"lets you start with an additional Core Node in Hacking Missions.<br><br>" +
|
||||||
"cost " + numeralWrapper.formatMoney(cost));
|
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " +
|
||||||
|
"cost " + numeralWrapper.formatMoney(cost)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a popup that lets the player upgrade the RAM on his/her home computer
|
* Create a popup that lets the player upgrade the RAM on his/her home computer
|
||||||
* @param p - Player object
|
* @param {IPlayer} p - Player object
|
||||||
*/
|
*/
|
||||||
export function createUpgradeHomeRamPopup(p: IPlayer) {
|
export function createUpgradeHomeRamPopup(p: IPlayer) {
|
||||||
const cost: number = p.getUpgradeHomeRamCost();
|
const cost: number = p.getUpgradeHomeRamCost();
|
||||||
@@ -250,25 +266,31 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
|
|||||||
yesNoBoxClose();
|
yesNoBoxClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
yesNoBoxCreate("Would you like to purchase additional RAM for your home computer? <br><br>" +
|
yesNoBoxCreate(
|
||||||
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" +
|
"Would you like to purchase additional RAM for your home computer? <br><br>" +
|
||||||
"This will cost " + numeralWrapper.format(cost, '$0.000a'));
|
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" +
|
||||||
|
"This will cost " + numeralWrapper.format(cost, '$0.000a')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to purchase a TOR router
|
* Attempt to purchase a TOR router
|
||||||
* @param p - Player object
|
* @param {IPlayer} p - Player object
|
||||||
*/
|
*/
|
||||||
export function purchaseTorRouter(p: IPlayer) {
|
export function purchaseTorRouter(p: IPlayer) {
|
||||||
|
if (p.hasTorRouter()) {
|
||||||
|
dialogBoxCreate(`You already have a TOR Router`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!p.canAfford(CONSTANTS.TorRouterCost)) {
|
if (!p.canAfford(CONSTANTS.TorRouterCost)) {
|
||||||
dialogBoxCreate("You cannot afford to purchase the Tor router");
|
dialogBoxCreate("You cannot afford to purchase the Tor router");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p.loseMoney(CONSTANTS.TorRouterCost);
|
p.loseMoney(CONSTANTS.TorRouterCost);
|
||||||
|
|
||||||
const darkweb = new Server({
|
const darkweb = safetlyCreateUniqueServer({
|
||||||
ip: createRandomIp(), hostname:"darkweb", organizationName:"",
|
ip: createUniqueRandomIp(), hostname:"darkweb", organizationName:"",
|
||||||
isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1
|
isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1
|
||||||
});
|
});
|
||||||
AddToAllServers(darkweb);
|
AddToAllServers(darkweb);
|
||||||
@@ -276,7 +298,9 @@ export function purchaseTorRouter(p: IPlayer) {
|
|||||||
|
|
||||||
p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
|
p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
|
||||||
darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
|
darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
|
||||||
dialogBoxCreate("You have purchased a Tor router!<br>" +
|
dialogBoxCreate(
|
||||||
"You now have access to the dark web from your home computer<br>" +
|
"You have purchased a Tor router!<br>" +
|
||||||
"Use the scan/scan-analyze commands to search for the dark web connection.");
|
"You now have access to the dark web from your home computer<br>" +
|
||||||
|
"Use the scan/scan-analyze commands to search for the dark web connection."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
|||||||
<StdButton
|
<StdButton
|
||||||
onClick={this.startInfiltration}
|
onClick={this.startInfiltration}
|
||||||
style={this.btnStyle}
|
style={this.btnStyle}
|
||||||
text={"Infiltration Company"}
|
text={"Infiltrate Company"}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ export class TechVendorLocation extends React.Component<IProps, any> {
|
|||||||
|
|
||||||
purchaseTorRouter() {
|
purchaseTorRouter() {
|
||||||
purchaseTorRouter(this.props.p);
|
purchaseTorRouter(this.props.p);
|
||||||
|
this.setState({
|
||||||
|
torPurchased: this.props.p.hasTorRouter(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Message } from "./Message";
|
import { Message } from "./Message";
|
||||||
import { Augmentatation } from "../Augmentation/Augmentation";
|
import { Augmentatation } from "../Augmentation/Augmentation";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { Augmentations } from "../Augmentation/Augmentations";
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
import { Programs } from "../Programs/Programs";
|
import { Programs } from "../Programs/Programs";
|
||||||
import { inMission } from "../Missions";
|
import { inMission } from "../Missions";
|
||||||
import { Player } from "../Player";
|
import { Player } from "../Player";
|
||||||
import { redPillFlag } from "../RedPill";
|
import { redPillFlag } from "../RedPill";
|
||||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
import { dialogBoxCreate,
|
import { dialogBoxCreate, dialogBoxOpened} from "../../utils/DialogBox";
|
||||||
dialogBoxOpened} from "../../utils/DialogBox";
|
import { Reviver } from "../../utils/JSONReviver";
|
||||||
|
|
||||||
//Sends message to player, including a pop up
|
//Sends message to player, including a pop up
|
||||||
function sendMessage(msg, forced=false) {
|
function sendMessage(msg, forced=false) {
|
||||||
@@ -31,7 +31,7 @@ function showMessage(msg) {
|
|||||||
function addMessageToServer(msg, serverHostname) {
|
function addMessageToServer(msg, serverHostname) {
|
||||||
var server = GetServerByHostname(serverHostname);
|
var server = GetServerByHostname(serverHostname);
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
console.log("WARNING: Did not locate " + serverHostname);
|
console.warn(`Could not find server ${serverHostname}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < server.messages.length; ++i) {
|
for (var i = 0; i < server.messages.length; ++i) {
|
||||||
|
|||||||
324
src/Missions.js
324
src/Missions.js
@@ -1,16 +1,21 @@
|
|||||||
import { CONSTANTS } from "./Constants";
|
import { CONSTANTS } from "./Constants";
|
||||||
import { Engine } from "./engine";
|
import { Engine } from "./engine";
|
||||||
import { displayFactionContent } from "./Faction/FactionHelpers";
|
import { displayFactionContent } from "./Faction/FactionHelpers";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
|
||||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
|
||||||
import { addOffset } from "../utils/helpers/addOffset";
|
|
||||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
|
||||||
import { isString } from "../utils/helpers/isString";
|
|
||||||
import jsplumb from 'jsplumb'
|
|
||||||
|
|
||||||
let inMission = false; //Flag to denote whether a mission is running
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
|
|
||||||
|
import { addOffset } from "../utils/helpers/addOffset";
|
||||||
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
|
import { isString } from "../utils/helpers/isString";
|
||||||
|
|
||||||
|
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||||
|
|
||||||
|
import jsplumb from "jsplumb";
|
||||||
|
|
||||||
|
|
||||||
|
let inMission = false; // Flag to denote whether a mission is running
|
||||||
let currMission = null;
|
let currMission = null;
|
||||||
function setInMission(bool, mission) {
|
function setInMission(bool, mission) {
|
||||||
inMission = bool;
|
inMission = bool;
|
||||||
@@ -21,26 +26,26 @@ function setInMission(bool, mission) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
$(document).keydown(function(e) {
|
$(document).keydown(function(e) {
|
||||||
if (inMission && currMission && currMission.selectedNode.length != 0) {
|
if (inMission && currMission && currMission.selectedNode.length != 0) {
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case 65: //a for Attack
|
case 65: // a for Attack
|
||||||
currMission.actionButtons[0].click();
|
currMission.actionButtons[0].click();
|
||||||
break;
|
break;
|
||||||
case 83: //s for Scan
|
case 83: // s for Scan
|
||||||
currMission.actionButtons[1].click();
|
currMission.actionButtons[1].click();
|
||||||
break;
|
break;
|
||||||
case 87: //w for Weaken
|
case 87: // w for Weaken
|
||||||
currMission.actionButtons[2].click();
|
currMission.actionButtons[2].click();
|
||||||
break;
|
break;
|
||||||
case 70: //f for Fortify
|
case 70: // f for Fortify
|
||||||
currMission.actionButtons[3].click();
|
currMission.actionButtons[3].click();
|
||||||
break;
|
break;
|
||||||
case 82: //r for Overflow
|
case 82: // r for Overflow
|
||||||
currMission.actionButtons[4].click();
|
currMission.actionButtons[4].click();
|
||||||
break;
|
break;
|
||||||
case 68: //d for Detach connection
|
case 68: // d for Detach connection
|
||||||
currMission.actionButtons[5].click();
|
currMission.actionButtons[5].click();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -50,20 +55,20 @@ $(document).keydown(function(e) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let NodeTypes = {
|
let NodeTypes = {
|
||||||
Core: "CPU Core Node", //All actions available
|
Core: "CPU Core Node", // All actions available
|
||||||
Firewall: "Firewall Node", //No actions available
|
Firewall: "Firewall Node", // No actions available
|
||||||
Database: "Database Node", //No actions available
|
Database: "Database Node", // No actions available
|
||||||
Spam: "Spam Node", //No actions Available
|
Spam: "Spam Node", // No actions Available
|
||||||
Transfer: "Transfer Node", //Can Weaken, Scan, Fortify and Overflow
|
Transfer: "Transfer Node", // Can Weaken, Scan, Fortify and Overflow
|
||||||
Shield: "Shield Node" //Can Fortify
|
Shield: "Shield Node" // Can Fortify
|
||||||
}
|
}
|
||||||
|
|
||||||
let NodeActions = {
|
let NodeActions = {
|
||||||
Attack: "Attacking", //Damaged based on attack stat + hacking level + opp def
|
Attack: "Attacking", // Damaged based on attack stat + hacking level + opp def
|
||||||
Scan: "Scanning", //-Def for target, affected by attack and hacking level
|
Scan: "Scanning", // -Def for target, affected by attack and hacking level
|
||||||
Weaken: "Weakening", //-Attack for target, affected by attack and hacking level
|
Weaken: "Weakening", // -Attack for target, affected by attack and hacking level
|
||||||
Fortify: "Fortifying", //+Defense for Node, affected by hacking level
|
Fortify: "Fortifying", // +Defense for Node, affected by hacking level
|
||||||
Overflow: "Overflowing", //+Attack but -Defense for Node, affected by hacking level
|
Overflow: "Overflowing", // +Attack but -Defense for Node, affected by hacking level
|
||||||
}
|
}
|
||||||
|
|
||||||
function Node(type, stats) {
|
function Node(type, stats) {
|
||||||
@@ -74,14 +79,15 @@ function Node(type, stats) {
|
|||||||
this.maxhp = this.hp;
|
this.maxhp = this.hp;
|
||||||
this.plyrCtrl = false;
|
this.plyrCtrl = false;
|
||||||
this.enmyCtrl = false;
|
this.enmyCtrl = false;
|
||||||
this.pos = [0, 0]; //x, y
|
this.pos = [0, 0]; // x, y
|
||||||
this.el = null; //Holds the Node's DOM element
|
this.el = null; // Holds the Node's DOM element
|
||||||
this.action = null;
|
this.action = null;
|
||||||
this.targetedCount = 0; //Count of how many connections this node is the target of
|
this.targetedCount = 0; // Count of how many connections this node is the target of
|
||||||
|
|
||||||
//Holds the JsPlumb Connection object for this Node,
|
/**
|
||||||
//where this Node is the Source (since each Node
|
* Holds the JsPlumb Connection object for this Node, where this Node is the Source (since each Node
|
||||||
//can only have 1 outgoing Connection)
|
* can only have 1 outgoing Connection)
|
||||||
|
*/
|
||||||
this.conn = null;
|
this.conn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,12 +113,12 @@ Node.prototype.setControlledByEnemy = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sets this node to be the active node
|
// Sets this node to be the active node
|
||||||
Node.prototype.select = function(actionButtons) {
|
Node.prototype.select = function(actionButtons) {
|
||||||
if (this.enmyCtrl) {return;}
|
if (this.enmyCtrl) {return;}
|
||||||
this.el.classList.add("hack-mission-player-node-active");
|
this.el.classList.add("hack-mission-player-node-active");
|
||||||
|
|
||||||
//Make all buttons inactive
|
// Make all buttons inactive
|
||||||
for (var i = 0; i < actionButtons.length; ++i) {
|
for (var i = 0; i < actionButtons.length; ++i) {
|
||||||
actionButtons[i].classList.remove("a-link-button");
|
actionButtons[i].classList.remove("a-link-button");
|
||||||
actionButtons[i].classList.add("a-link-button-inactive");
|
actionButtons[i].classList.add("a-link-button-inactive");
|
||||||
@@ -120,7 +126,7 @@ Node.prototype.select = function(actionButtons) {
|
|||||||
|
|
||||||
switch(this.type) {
|
switch(this.type) {
|
||||||
case NodeTypes.Core:
|
case NodeTypes.Core:
|
||||||
//All buttons active
|
// All buttons active
|
||||||
for (var i = 0; i < actionButtons.length; ++i) {
|
for (var i = 0; i < actionButtons.length; ++i) {
|
||||||
actionButtons[i].classList.remove("a-link-button-inactive");
|
actionButtons[i].classList.remove("a-link-button-inactive");
|
||||||
actionButtons[i].classList.add("a-link-button");
|
actionButtons[i].classList.add("a-link-button");
|
||||||
@@ -165,31 +171,33 @@ Node.prototype.untarget = function() {
|
|||||||
--this.targetedCount;
|
--this.targetedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Hacking mission instance
|
/**
|
||||||
//Takes in the reputation of the Faction for which the mission is
|
* Hacking mission instance
|
||||||
//being conducted
|
* @param rep {number} How much reputation the player has for the faction
|
||||||
|
* @param fac {Faction} Faction for which this mission is being conducted
|
||||||
|
*/
|
||||||
function HackingMission(rep, fac) {
|
function HackingMission(rep, fac) {
|
||||||
this.faction = fac;
|
this.faction = fac;
|
||||||
|
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.time = 180000; //5 minutes to start, milliseconds
|
this.time = 180000; // 5 minutes to start, milliseconds
|
||||||
|
|
||||||
this.playerCores = [];
|
this.playerCores = [];
|
||||||
this.playerNodes = []; //Non-core nodes
|
this.playerNodes = []; // Non-core nodes
|
||||||
this.playerAtk = 0;
|
this.playerAtk = 0;
|
||||||
this.playerDef = 0;
|
this.playerDef = 0;
|
||||||
|
|
||||||
this.enemyCores = [];
|
this.enemyCores = [];
|
||||||
this.enemyDatabases = [];
|
this.enemyDatabases = [];
|
||||||
this.enemyNodes = []; //Non-core nodes
|
this.enemyNodes = []; // Non-core nodes
|
||||||
this.enemyAtk = 0;
|
this.enemyAtk = 0;
|
||||||
this.enemyDef = 0;
|
this.enemyDef = 0;
|
||||||
|
|
||||||
this.miscNodes = [];
|
this.miscNodes = [];
|
||||||
|
|
||||||
this.selectedNode = []; //Which of the player's nodes are currently selected
|
this.selectedNode = []; // Which of the player's nodes are currently selected
|
||||||
|
|
||||||
this.actionButtons = []; //DOM buttons for actions
|
this.actionButtons = []; // DOM buttons for actions
|
||||||
|
|
||||||
this.availablePositions = [];
|
this.availablePositions = [];
|
||||||
for (var r = 0; r < 8; ++r) {
|
for (var r = 0; r < 8; ++r) {
|
||||||
@@ -211,10 +219,10 @@ function HackingMission(rep, fac) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HackingMission.prototype.init = function() {
|
HackingMission.prototype.init = function() {
|
||||||
//Create Header DOM
|
// Create Header DOM
|
||||||
this.createPageDom();
|
this.createPageDom();
|
||||||
|
|
||||||
//Create player starting nodes
|
// Create player starting nodes
|
||||||
var home = Player.getHomeComputer()
|
var home = Player.getHomeComputer()
|
||||||
for (var i = 0; i < home.cpuCores; ++i) {
|
for (var i = 0; i < home.cpuCores; ++i) {
|
||||||
var stats = {
|
var stats = {
|
||||||
@@ -228,7 +236,7 @@ HackingMission.prototype.init = function() {
|
|||||||
this.removeAvailablePosition(i, 0);
|
this.removeAvailablePosition(i, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Randomly generate enemy nodes (CPU and Firewall) based on difficulty
|
// Randomly generate enemy nodes (CPU and Firewall) based on difficulty
|
||||||
var numNodes = Math.min(8, Math.max(1, Math.round(this.difficulty / 4)));
|
var numNodes = Math.min(8, Math.max(1, Math.round(this.difficulty / 4)));
|
||||||
var numFirewalls = Math.min(20,
|
var numFirewalls = Math.min(20,
|
||||||
getRandomInt(Math.round(this.difficulty/3), Math.round(this.difficulty/3) + 1));
|
getRandomInt(Math.round(this.difficulty/3), Math.round(this.difficulty/3) + 1));
|
||||||
@@ -304,10 +312,10 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
wikiGuideBtn.style.display = "inline-block";
|
wikiGuideBtn.style.display = "inline-block";
|
||||||
wikiGuideBtn.classList.add("hack-mission-header-element");
|
wikiGuideBtn.classList.add("hack-mission-header-element");
|
||||||
wikiGuideBtn.target = "_blank";
|
wikiGuideBtn.target = "_blank";
|
||||||
//TODO Add link to wiki page wikiGuideBtn.href =
|
// TODO Add link to wiki page wikiGuideBtn.href =
|
||||||
|
|
||||||
|
|
||||||
//Start button will get replaced with forfeit when game is started
|
// Start button will get replaced with forfeit when game is started
|
||||||
var startBtn = document.createElement("a");
|
var startBtn = document.createElement("a");
|
||||||
startBtn.innerHTML = "Start";
|
startBtn.innerHTML = "Start";
|
||||||
startBtn.setAttribute("id", "hack-mission-start-btn");
|
startBtn.setAttribute("id", "hack-mission-start-btn");
|
||||||
@@ -334,15 +342,15 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
timer.style.display = "inline-block";
|
timer.style.display = "inline-block";
|
||||||
timer.style.margin = "6px";
|
timer.style.margin = "6px";
|
||||||
|
|
||||||
//Create Action Buttons (Attack/Scan/Weaken/ etc...)
|
// Create Action Buttons (Attack/Scan/Weaken/ etc...)
|
||||||
var actionsContainer = document.createElement("span");
|
var actionsContainer = document.createElement("span");
|
||||||
actionsContainer.style.display = "block";
|
actionsContainer.style.display = "block";
|
||||||
actionsContainer.classList.add("hack-mission-action-buttons-container");
|
actionsContainer.classList.add("hack-mission-action-buttons-container");
|
||||||
for (var i = 0; i < 6; ++i) {
|
for (var i = 0; i < 6; ++i) {
|
||||||
this.actionButtons.push(document.createElement("a"));
|
this.actionButtons.push(document.createElement("a"));
|
||||||
this.actionButtons[i].style.display = "inline-block";
|
this.actionButtons[i].style.display = "inline-block";
|
||||||
this.actionButtons[i].classList.add("a-link-button-inactive"); //Disabled at start
|
this.actionButtons[i].classList.add("a-link-button-inactive"); // Disabled at start
|
||||||
this.actionButtons[i].classList.add("tooltip"); //Disabled at start
|
this.actionButtons[i].classList.add("tooltip"); // Disabled at start
|
||||||
this.actionButtons[i].classList.add("hack-mission-header-element");
|
this.actionButtons[i].classList.add("hack-mission-header-element");
|
||||||
actionsContainer.appendChild(this.actionButtons[i]);
|
actionsContainer.appendChild(this.actionButtons[i]);
|
||||||
}
|
}
|
||||||
@@ -383,7 +391,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
"also be done by simply clicking the white connection line.";
|
"also be done by simply clicking the white connection line.";
|
||||||
this.actionButtons[5].appendChild(dropconnTooltip);
|
this.actionButtons[5].appendChild(dropconnTooltip);
|
||||||
|
|
||||||
//Player/enemy defense displays will be in action container
|
// Player/enemy defense displays will be in action container
|
||||||
var playerStats = document.createElement("p");
|
var playerStats = document.createElement("p");
|
||||||
var enemyStats = document.createElement("p");
|
var enemyStats = document.createElement("p");
|
||||||
playerStats.style.display = "inline-block";
|
playerStats.style.display = "inline-block";
|
||||||
@@ -397,7 +405,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
actionsContainer.appendChild(playerStats);
|
actionsContainer.appendChild(playerStats);
|
||||||
actionsContainer.appendChild(enemyStats);
|
actionsContainer.appendChild(enemyStats);
|
||||||
|
|
||||||
//Set Action Button event listeners
|
// Set Action Button event listeners
|
||||||
this.actionButtons[0].addEventListener("click", ()=>{
|
this.actionButtons[0].addEventListener("click", ()=>{
|
||||||
if (!(this.selectedNode.length > 0)) {
|
if (!(this.selectedNode.length > 0)) {
|
||||||
console.log("ERR: Pressing Action button without selected node");
|
console.log("ERR: Pressing Action button without selected node");
|
||||||
@@ -405,7 +413,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
}
|
}
|
||||||
if (this.selectedNode[0].type !== NodeTypes.Core) {return;}
|
if (this.selectedNode[0].type !== NodeTypes.Core) {return;}
|
||||||
this.setActionButtonsActive(this.selectedNode[0].type);
|
this.setActionButtonsActive(this.selectedNode[0].type);
|
||||||
this.setActionButton(NodeActions.Attack, false); //Set attack button inactive
|
this.setActionButton(NodeActions.Attack, false); // Set attack button inactive
|
||||||
this.selectedNode.forEach(function(node){
|
this.selectedNode.forEach(function(node){
|
||||||
node.action = NodeActions.Attack;
|
node.action = NodeActions.Attack;
|
||||||
});
|
});
|
||||||
@@ -416,10 +424,10 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
console.log("ERR: Pressing Action button without selected node");
|
console.log("ERR: Pressing Action button without selected node");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var nodeType = this.selectedNode[0].type; //In a multiselect, every Node will have the same type
|
var nodeType = this.selectedNode[0].type; // In a multiselect, every Node will have the same type
|
||||||
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
||||||
this.setActionButtonsActive(nodeType);
|
this.setActionButtonsActive(nodeType);
|
||||||
this.setActionButton(NodeActions.Scan, false); //Set scan button inactive
|
this.setActionButton(NodeActions.Scan, false); // Set scan button inactive
|
||||||
this.selectedNode.forEach(function(node){
|
this.selectedNode.forEach(function(node){
|
||||||
node.action = NodeActions.Scan;
|
node.action = NodeActions.Scan;
|
||||||
});
|
});
|
||||||
@@ -430,10 +438,10 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
console.log("ERR: Pressing Action button without selected node");
|
console.log("ERR: Pressing Action button without selected node");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var nodeType = this.selectedNode[0].type; //In a multiselect, every Node will have the same type
|
var nodeType = this.selectedNode[0].type; // In a multiselect, every Node will have the same type
|
||||||
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
||||||
this.setActionButtonsActive(nodeType);
|
this.setActionButtonsActive(nodeType);
|
||||||
this.setActionButton(NodeActions.Weaken, false); //Set Weaken button inactive
|
this.setActionButton(NodeActions.Weaken, false); // Set Weaken button inactive
|
||||||
this.selectedNode.forEach(function(node){
|
this.selectedNode.forEach(function(node){
|
||||||
node.action = NodeActions.Weaken;
|
node.action = NodeActions.Weaken;
|
||||||
});
|
});
|
||||||
@@ -445,7 +453,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setActionButtonsActive(this.selectedNode[0].type);
|
this.setActionButtonsActive(this.selectedNode[0].type);
|
||||||
this.setActionButton(NodeActions.Fortify, false); //Set Fortify button inactive
|
this.setActionButton(NodeActions.Fortify, false); // Set Fortify button inactive
|
||||||
this.selectedNode.forEach(function(node){
|
this.selectedNode.forEach(function(node){
|
||||||
node.action = NodeActions.Fortify;
|
node.action = NodeActions.Fortify;
|
||||||
});
|
});
|
||||||
@@ -459,7 +467,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
var nodeType = this.selectedNode[0].type;
|
var nodeType = this.selectedNode[0].type;
|
||||||
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
|
||||||
this.setActionButtonsActive(nodeType);
|
this.setActionButtonsActive(nodeType);
|
||||||
this.setActionButton(NodeActions.Overflow, false); //Set Overflow button inactive
|
this.setActionButton(NodeActions.Overflow, false); // Set Overflow button inactive
|
||||||
this.selectedNode.forEach(function(node){
|
this.selectedNode.forEach(function(node){
|
||||||
node.action = NodeActions.Overflow;
|
node.action = NodeActions.Overflow;
|
||||||
});
|
});
|
||||||
@@ -477,11 +485,7 @@ HackingMission.prototype.createPageDom = function() {
|
|||||||
}
|
}
|
||||||
node.action = NodeActions.Fortify;
|
node.action = NodeActions.Fortify;
|
||||||
});
|
});
|
||||||
// if (this.selectedNode.conn) {
|
});
|
||||||
// var endpoints = this.selectedNode.conn.endpoints;
|
|
||||||
// endpoints[0].detachFrom(endpoints[1]);
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
|
|
||||||
var timeDisplay = document.createElement("p");
|
var timeDisplay = document.createElement("p");
|
||||||
|
|
||||||
@@ -508,8 +512,10 @@ HackingMission.prototype.setActionButtonsActive = function(nodeType=null) {
|
|||||||
this.actionButtons[i].classList.remove("a-link-button-inactive");
|
this.actionButtons[i].classList.remove("a-link-button-inactive");
|
||||||
}
|
}
|
||||||
|
|
||||||
//For Transfer, FireWall and Shield Nodes, certain buttons should always be disabled
|
/**
|
||||||
//0 = Attack, 1 = Scan, 2 = Weaken, 3 = Fortify, 4 = overflow, 5 = Drop conn
|
* For Transfer, FireWall and Shield Nodes, certain buttons should always be disabled
|
||||||
|
* 0 = Attack, 1 = Scan, 2 = Weaken, 3 = Fortify, 4 = overflow, 5 = Drop conn
|
||||||
|
*/
|
||||||
if (nodeType) {
|
if (nodeType) {
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case NodeTypes.Firewall:
|
case NodeTypes.Firewall:
|
||||||
@@ -535,7 +541,7 @@ HackingMission.prototype.setActionButtonsActive = function(nodeType=null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//True for active, false for inactive
|
// True for active, false for inactive
|
||||||
HackingMission.prototype.setActionButton = function(i, active=true) {
|
HackingMission.prototype.setActionButton = function(i, active=true) {
|
||||||
if (isString(i)) {
|
if (isString(i)) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
@@ -652,7 +658,7 @@ HackingMission.prototype.setNodePosition = function(nodeObj, x, y) {
|
|||||||
HackingMission.prototype.setNodeRandomPosition = function(nodeObj, xlimit=0) {
|
HackingMission.prototype.setNodeRandomPosition = function(nodeObj, xlimit=0) {
|
||||||
var i = getRandomInt(0, this.availablePositions.length - 1);
|
var i = getRandomInt(0, this.availablePositions.length - 1);
|
||||||
if (this.availablePositions[i][1] < xlimit) {
|
if (this.availablePositions[i][1] < xlimit) {
|
||||||
//Recurse if not within limit
|
// Recurse if not within limit
|
||||||
return this.setNodeRandomPosition(nodeObj, xlimit);
|
return this.setNodeRandomPosition(nodeObj, xlimit);
|
||||||
}
|
}
|
||||||
var pos = this.availablePositions.splice(i, 1);
|
var pos = this.availablePositions.splice(i, 1);
|
||||||
@@ -661,15 +667,15 @@ HackingMission.prototype.setNodeRandomPosition = function(nodeObj, xlimit=0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HackingMission.prototype.createMap = function() {
|
HackingMission.prototype.createMap = function() {
|
||||||
//Use a grid
|
// Use a grid
|
||||||
var map = document.createElement("div");
|
var map = document.createElement("div");
|
||||||
map.classList.add("hack-mission-grid");
|
map.classList.add("hack-mission-grid");
|
||||||
map.setAttribute("id", "hacking-mission-map");
|
map.setAttribute("id", "hacking-mission-map");
|
||||||
document.getElementById("mission-container").appendChild(map);
|
document.getElementById("mission-container").appendChild(map);
|
||||||
|
|
||||||
//Create random Nodes for every space in the map that
|
// Create random Nodes for every space in the map that
|
||||||
//hasn't been filled yet. The stats of each Node will be based on
|
// hasn't been filled yet. The stats of each Node will be based on
|
||||||
//the player/enemy attack
|
// the player/enemy attack
|
||||||
var averageAttack = (this.playerAtk + this.enemyAtk) / 2;
|
var averageAttack = (this.playerAtk + this.enemyAtk) / 2;
|
||||||
for (var x = 0; x < 8; ++x) {
|
for (var x = 0; x < 8; ++x) {
|
||||||
for (var y = 0; y < 8; ++y) {
|
for (var y = 0; y < 8; ++y) {
|
||||||
@@ -677,7 +683,7 @@ HackingMission.prototype.createMap = function() {
|
|||||||
var node, type = getRandomInt(0, 2);
|
var node, type = getRandomInt(0, 2);
|
||||||
var randMult = addOffset(0.85 + (this.difficulty / 2), 15);
|
var randMult = addOffset(0.85 + (this.difficulty / 2), 15);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0: //Spam
|
case 0: // Spam
|
||||||
var stats = {
|
var stats = {
|
||||||
atk: 0,
|
atk: 0,
|
||||||
def: averageAttack * 1.1 + getRandomInt(15, 45),
|
def: averageAttack * 1.1 + getRandomInt(15, 45),
|
||||||
@@ -685,7 +691,7 @@ HackingMission.prototype.createMap = function() {
|
|||||||
}
|
}
|
||||||
node = new Node(NodeTypes.Spam, stats);
|
node = new Node(NodeTypes.Spam, stats);
|
||||||
break;
|
break;
|
||||||
case 1: //Transfer
|
case 1: // Transfer
|
||||||
var stats = {
|
var stats = {
|
||||||
atk: 0,
|
atk: 0,
|
||||||
def: averageAttack * 1.1 + getRandomInt(15, 45),
|
def: averageAttack * 1.1 + getRandomInt(15, 45),
|
||||||
@@ -693,7 +699,7 @@ HackingMission.prototype.createMap = function() {
|
|||||||
}
|
}
|
||||||
node = new Node(NodeTypes.Transfer, stats);
|
node = new Node(NodeTypes.Transfer, stats);
|
||||||
break;
|
break;
|
||||||
case 2: //Shield
|
case 2: // Shield
|
||||||
default:
|
default:
|
||||||
var stats = {
|
var stats = {
|
||||||
atk: 0,
|
atk: 0,
|
||||||
@@ -710,14 +716,14 @@ HackingMission.prototype.createMap = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create DOM elements in order
|
// Create DOM elements in order
|
||||||
for (var r = 0; r < 8; ++r) {
|
for (var r = 0; r < 8; ++r) {
|
||||||
for (var c = 0; c < 8; ++c) {
|
for (var c = 0; c < 8; ++c) {
|
||||||
this.createNodeDomElement(this.map[r][c]);
|
this.createNodeDomElement(this.map[r][c]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configure all Player CPUS
|
// Configure all Player CPUS
|
||||||
for (var i = 0; i < this.playerCores.length; ++i) {
|
for (var i = 0; i < this.playerCores.length; ++i) {
|
||||||
console.log("Configuring Player Node: " + this.playerCores[i].el.id);
|
console.log("Configuring Player Node: " + this.playerCores[i].el.id);
|
||||||
this.configurePlayerNodeElement(this.playerCores[i].el);
|
this.configurePlayerNodeElement(this.playerCores[i].el);
|
||||||
@@ -728,12 +734,12 @@ HackingMission.prototype.createNodeDomElement = function(nodeObj) {
|
|||||||
var nodeDiv = document.createElement("a"), txtEl = document.createElement('p');
|
var nodeDiv = document.createElement("a"), txtEl = document.createElement('p');
|
||||||
nodeObj.el = nodeDiv;
|
nodeObj.el = nodeDiv;
|
||||||
|
|
||||||
//Set the node element's id based on its coordinates
|
// Set the node element's id based on its coordinates
|
||||||
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
|
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
|
||||||
nodeDiv.setAttribute("id", id);
|
nodeDiv.setAttribute("id", id);
|
||||||
txtEl.setAttribute("id", id + "-txt");
|
txtEl.setAttribute("id", id + "-txt");
|
||||||
|
|
||||||
//Set node classes for owner
|
// Set node classes for owner
|
||||||
nodeDiv.classList.add("hack-mission-node");
|
nodeDiv.classList.add("hack-mission-node");
|
||||||
if (nodeObj.plyrCtrl) {
|
if (nodeObj.plyrCtrl) {
|
||||||
nodeDiv.classList.add("hack-mission-player-node");
|
nodeDiv.classList.add("hack-mission-player-node");
|
||||||
@@ -741,7 +747,7 @@ HackingMission.prototype.createNodeDomElement = function(nodeObj) {
|
|||||||
nodeDiv.classList.add("hack-mission-enemy-node");
|
nodeDiv.classList.add("hack-mission-enemy-node");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set node classes based on type
|
// Set node classes based on type
|
||||||
var txt;
|
var txt;
|
||||||
switch (nodeObj.type) {
|
switch (nodeObj.type) {
|
||||||
case NodeTypes.Core:
|
case NodeTypes.Core:
|
||||||
@@ -794,7 +800,7 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
|
|||||||
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
|
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
|
||||||
var nodeDiv = document.getElementById(id), txtEl = document.getElementById(id + "-txt");
|
var nodeDiv = document.getElementById(id), txtEl = document.getElementById(id + "-txt");
|
||||||
|
|
||||||
//Set node classes based on type
|
// Set node classes based on type
|
||||||
var txt;
|
var txt;
|
||||||
switch (nodeObj.type) {
|
switch (nodeObj.type) {
|
||||||
case NodeTypes.Core:
|
case NodeTypes.Core:
|
||||||
@@ -832,9 +838,11 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
|
|||||||
txtEl.innerHTML = txt;
|
txtEl.innerHTML = txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Gets a Node DOM element's corresponding Node object using its
|
/**
|
||||||
//element id. Function accepts either the DOM element object or the ID as
|
* Gets a Node DOM element's corresponding Node object using its
|
||||||
//an argument
|
* element id. Function accepts either the DOM element object or the ID as
|
||||||
|
* an argument
|
||||||
|
*/
|
||||||
HackingMission.prototype.getNodeFromElement = function(el) {
|
HackingMission.prototype.getNodeFromElement = function(el) {
|
||||||
var id;
|
var id;
|
||||||
if (isString(el)) {
|
if (isString(el)) {
|
||||||
@@ -897,14 +905,16 @@ function clearAllSelectedNodes(hackMissionInst) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configures a DOM element representing a player-owned node to
|
/**
|
||||||
//be selectable and actionable
|
* Configures a DOM element representing a player-owned node to
|
||||||
//Note: Does NOT change its css class. This is handled by Node.setControlledBy...
|
* be selectable and actionable.
|
||||||
|
* Note: Does NOT change its css class. This is handled by Node.setControlledBy...
|
||||||
|
*/
|
||||||
HackingMission.prototype.configurePlayerNodeElement = function(el) {
|
HackingMission.prototype.configurePlayerNodeElement = function(el) {
|
||||||
var nodeObj = this.getNodeFromElement(el);
|
var nodeObj = this.getNodeFromElement(el);
|
||||||
if (nodeObj == null) {console.log("Error getting Node object");}
|
if (nodeObj == null) {console.log("Error getting Node object");}
|
||||||
|
|
||||||
//Add event listener
|
// Add event listener
|
||||||
var self = this;
|
var self = this;
|
||||||
function selectNodeWrapper() {
|
function selectNodeWrapper() {
|
||||||
selectNode(self, el);
|
selectNode(self, el);
|
||||||
@@ -922,10 +932,12 @@ HackingMission.prototype.configurePlayerNodeElement = function(el) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configures a DOM element representing an enemy-node by removing
|
/**
|
||||||
//any event listeners
|
* Configures a DOM element representing an enemy-node by removing
|
||||||
|
* any event listeners
|
||||||
|
*/
|
||||||
HackingMission.prototype.configureEnemyNodeElement = function(el) {
|
HackingMission.prototype.configureEnemyNodeElement = function(el) {
|
||||||
//Deselect node if it was the selected node
|
// Deselect node if it was the selected node
|
||||||
var nodeObj = this.getNodeFromElement(el);
|
var nodeObj = this.getNodeFromElement(el);
|
||||||
for (var i = 0; i < this.selectedNode.length; ++i) {
|
for (var i = 0; i < this.selectedNode.length; ++i) {
|
||||||
if (this.selectedNode[i] == nodeObj) {
|
if (this.selectedNode[i] == nodeObj) {
|
||||||
@@ -936,8 +948,10 @@ HackingMission.prototype.configureEnemyNodeElement = function(el) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns bool indicating whether a node is reachable by player
|
/**
|
||||||
//by checking if any of the adjacent nodes are owned by the player
|
* Returns bool indicating whether a node is reachable by player
|
||||||
|
* by checking if any of the adjacent nodes are owned by the player
|
||||||
|
*/
|
||||||
HackingMission.prototype.nodeReachable = function(node) {
|
HackingMission.prototype.nodeReachable = function(node) {
|
||||||
var x = node.pos[0], y = node.pos[1];
|
var x = node.pos[0], y = node.pos[1];
|
||||||
if (x > 0 && this.map[x-1][y].plyrCtrl) {return true;}
|
if (x > 0 && this.map[x-1][y].plyrCtrl) {return true;}
|
||||||
@@ -980,7 +994,7 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
|
|
||||||
this.jsplumbinstance = instance;
|
this.jsplumbinstance = instance;
|
||||||
|
|
||||||
//All player cores are sources
|
// All player cores are sources
|
||||||
for (var i = 0; i < this.playerCores.length; ++i) {
|
for (var i = 0; i < this.playerCores.length; ++i) {
|
||||||
instance.makeSource(this.playerCores[i].el, {
|
instance.makeSource(this.playerCores[i].el, {
|
||||||
deleteEndpointsOnEmpty:true,
|
deleteEndpointsOnEmpty:true,
|
||||||
@@ -990,7 +1004,7 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Everything else is a target
|
// Everything else is a target
|
||||||
for (var i = 0; i < this.enemyCores.length; ++i) {
|
for (var i = 0; i < this.enemyCores.length; ++i) {
|
||||||
instance.makeTarget(this.enemyCores[i].el, {
|
instance.makeTarget(this.enemyCores[i].el, {
|
||||||
maxConnections:-1,
|
maxConnections:-1,
|
||||||
@@ -1020,7 +1034,7 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Clicking a connection drops it
|
// Clicking a connection drops it
|
||||||
instance.bind("click", (conn, originalEvent) => {
|
instance.bind("click", (conn, originalEvent) => {
|
||||||
// Cannot drop enemy's connections
|
// Cannot drop enemy's connections
|
||||||
const sourceNode = this.getNodeFromElement(conn.source);
|
const sourceNode = this.getNodeFromElement(conn.source);
|
||||||
@@ -1030,15 +1044,15 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
endpoints[0].detachFrom(endpoints[1]);
|
endpoints[0].detachFrom(endpoints[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Connection events
|
// Connection events
|
||||||
instance.bind("connection", (info) => {
|
instance.bind("connection", (info) => {
|
||||||
var targetNode = this.getNodeFromElement(info.target);
|
var targetNode = this.getNodeFromElement(info.target);
|
||||||
|
|
||||||
//Do not detach for enemy nodes
|
// Do not detach for enemy nodes
|
||||||
var thisNode = this.getNodeFromElement(info.source);
|
var thisNode = this.getNodeFromElement(info.source);
|
||||||
if (thisNode.enmyCtrl) {return;}
|
if (thisNode.enmyCtrl) {return;}
|
||||||
|
|
||||||
//If the node is not reachable, drop the connection
|
// If the node is not reachable, drop the connection
|
||||||
if (!this.nodeReachable(targetNode)) {
|
if (!this.nodeReachable(targetNode)) {
|
||||||
info.sourceEndpoint.detachFrom(info.targetEndpoint);
|
info.sourceEndpoint.detachFrom(info.targetEndpoint);
|
||||||
return;
|
return;
|
||||||
@@ -1050,7 +1064,7 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
++targetNode.targetedCount;
|
++targetNode.targetedCount;
|
||||||
});
|
});
|
||||||
|
|
||||||
//Detach Connection events
|
// Detach Connection events
|
||||||
instance.bind("connectionDetached", (info, originalEvent)=>{
|
instance.bind("connectionDetached", (info, originalEvent)=>{
|
||||||
var sourceNode = this.getNodeFromElement(info.source);
|
var sourceNode = this.getNodeFromElement(info.source);
|
||||||
sourceNode.conn = null;
|
sourceNode.conn = null;
|
||||||
@@ -1060,7 +1074,7 @@ HackingMission.prototype.initJsPlumb = function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Drops all connections where the specified node is the source
|
// Drops all connections where the specified node is the source
|
||||||
HackingMission.prototype.dropAllConnectionsFromNode = function(node) {
|
HackingMission.prototype.dropAllConnectionsFromNode = function(node) {
|
||||||
var allConns = this.jsplumbinstance.getAllConnections();
|
var allConns = this.jsplumbinstance.getAllConnections();
|
||||||
for (var i = allConns.length-1; i >= 0; --i) {
|
for (var i = allConns.length-1; i >= 0; --i) {
|
||||||
@@ -1070,7 +1084,7 @@ HackingMission.prototype.dropAllConnectionsFromNode = function(node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Drops all connections where the specified node is the target
|
// Drops all connections where the specified node is the target
|
||||||
HackingMission.prototype.dropAllConnectionsToNode = function(node) {
|
HackingMission.prototype.dropAllConnectionsToNode = function(node) {
|
||||||
var allConns = this.jsplumbinstance.getAllConnections();
|
var allConns = this.jsplumbinstance.getAllConnections();
|
||||||
for (var i = allConns.length-1; i >= 0; --i) {
|
for (var i = allConns.length-1; i >= 0; --i) {
|
||||||
@@ -1085,10 +1099,10 @@ var storedCycles = 0;
|
|||||||
HackingMission.prototype.process = function(numCycles=1) {
|
HackingMission.prototype.process = function(numCycles=1) {
|
||||||
if (!this.started) {return;}
|
if (!this.started) {return;}
|
||||||
storedCycles += numCycles;
|
storedCycles += numCycles;
|
||||||
if (storedCycles < 2) {return;} //Only process every 3 cycles minimum
|
if (storedCycles < 2) {return;} // Only process every 3 cycles minimum
|
||||||
|
|
||||||
var res = false;
|
var res = false;
|
||||||
//Process actions of all player nodes
|
// Process actions of all player nodes
|
||||||
this.playerCores.forEach((node)=>{
|
this.playerCores.forEach((node)=>{
|
||||||
res |= this.processNode(node, storedCycles);
|
res |= this.processNode(node, storedCycles);
|
||||||
});
|
});
|
||||||
@@ -1101,7 +1115,7 @@ HackingMission.prototype.process = function(numCycles=1) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Process actions of all enemy nodes
|
// Process actions of all enemy nodes
|
||||||
this.enemyCores.forEach((node)=>{
|
this.enemyCores.forEach((node)=>{
|
||||||
this.enemyAISelectAction(node);
|
this.enemyAISelectAction(node);
|
||||||
res |= this.processNode(node, storedCycles);
|
res |= this.processNode(node, storedCycles);
|
||||||
@@ -1116,7 +1130,7 @@ HackingMission.prototype.process = function(numCycles=1) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//The hp of enemy databases increases slowly
|
// The hp of enemy databases increases slowly
|
||||||
this.enemyDatabases.forEach((node)=>{
|
this.enemyDatabases.forEach((node)=>{
|
||||||
node.maxhp += (0.1 * storedCycles);
|
node.maxhp += (0.1 * storedCycles);
|
||||||
node.hp += (0.1 * storedCycles);
|
node.hp += (0.1 * storedCycles);
|
||||||
@@ -1127,19 +1141,19 @@ HackingMission.prototype.process = function(numCycles=1) {
|
|||||||
this.calculateDefenses();
|
this.calculateDefenses();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Win if all enemy databases are conquered
|
// Win if all enemy databases are conquered
|
||||||
if (this.enemyDatabases.length === 0) {
|
if (this.enemyDatabases.length === 0) {
|
||||||
this.finishMission(true);
|
this.finishMission(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Lose if all your cores are gone
|
// Lose if all your cores are gone
|
||||||
if (this.playerCores.length === 0) {
|
if (this.playerCores.length === 0) {
|
||||||
this.finishMission(false);
|
this.finishMission(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Defense/hp of misc nodes increases slowly over time
|
// Defense/hp of misc nodes increases slowly over time
|
||||||
this.miscNodes.forEach((node)=>{
|
this.miscNodes.forEach((node)=>{
|
||||||
node.def += (0.1 * storedCycles);
|
node.def += (0.1 * storedCycles);
|
||||||
node.maxhp += (0.05 * storedCycles);
|
node.maxhp += (0.05 * storedCycles);
|
||||||
@@ -1148,7 +1162,7 @@ HackingMission.prototype.process = function(numCycles=1) {
|
|||||||
this.updateNodeDomElement(node);
|
this.updateNodeDomElement(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Update timer and check if player lost
|
// Update timer and check if player lost
|
||||||
this.time -= (storedCycles * Engine._idleSpeed);
|
this.time -= (storedCycles * Engine._idleSpeed);
|
||||||
if (this.time <= 0) {
|
if (this.time <= 0) {
|
||||||
this.finishMission(false);
|
this.finishMission(false);
|
||||||
@@ -1159,7 +1173,7 @@ HackingMission.prototype.process = function(numCycles=1) {
|
|||||||
storedCycles = 0;
|
storedCycles = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns a bool representing whether defenses need to be re-calculated
|
// Returns a bool representing whether defenses need to be re-calculated
|
||||||
HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
||||||
if (nodeObj.action == null) {
|
if (nodeObj.action == null) {
|
||||||
return;
|
return;
|
||||||
@@ -1174,21 +1188,21 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetNode == null) {
|
if (targetNode == null) {
|
||||||
//Player is in the middle of dragging the connection,
|
// Player is in the middle of dragging the connection,
|
||||||
//so the target node is null. Do nothing here
|
// so the target node is null. Do nothing here
|
||||||
} else if (targetNode.plyrCtrl) {
|
} else if (targetNode.plyrCtrl) {
|
||||||
def = this.playerDef;
|
def = this.playerDef;
|
||||||
atk = this.enemyAtk;
|
atk = this.enemyAtk;
|
||||||
} else if (targetNode.enmyCtrl) {
|
} else if (targetNode.enmyCtrl) {
|
||||||
def = this.enemyDef;
|
def = this.enemyDef;
|
||||||
atk = this.playerAtk;
|
atk = this.playerAtk;
|
||||||
} else { //Misc Node
|
} else { // Misc Node
|
||||||
def = targetNode.def;
|
def = targetNode.def;
|
||||||
nodeObj.plyrCtrl ? atk = this.playerAtk : atk = this.enemyAtk;
|
nodeObj.plyrCtrl ? atk = this.playerAtk : atk = this.enemyAtk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculations are per second, so divide everything by 5
|
// Calculations are per second, so divide everything by 5
|
||||||
var calcStats = false, plyr = nodeObj.plyrCtrl;
|
var calcStats = false, plyr = nodeObj.plyrCtrl;
|
||||||
var enmyHacking = this.difficulty * CONSTANTS.HackingMissionDifficultyToHacking;
|
var enmyHacking = this.difficulty * CONSTANTS.HackingMissionDifficultyToHacking;
|
||||||
switch(nodeObj.action) {
|
switch(nodeObj.action) {
|
||||||
@@ -1229,13 +1243,13 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Stats can't go below 0
|
// Stats can't go below 0
|
||||||
if (nodeObj.atk < 0) {nodeObj.atk = 0;}
|
if (nodeObj.atk < 0) {nodeObj.atk = 0;}
|
||||||
if (nodeObj.def < 0) {nodeObj.def = 0;}
|
if (nodeObj.def < 0) {nodeObj.def = 0;}
|
||||||
if (targetNode && targetNode.atk < 0) {targetNode.atk = 0;}
|
if (targetNode && targetNode.atk < 0) {targetNode.atk = 0;}
|
||||||
if (targetNode && targetNode.def < 0) {targetNode.def = 0;}
|
if (targetNode && targetNode.def < 0) {targetNode.def = 0;}
|
||||||
|
|
||||||
//Conquering a node
|
// Conquering a node
|
||||||
if (targetNode && targetNode.hp <= 0) {
|
if (targetNode && targetNode.hp <= 0) {
|
||||||
var conqueredByPlayer = nodeObj.plyrCtrl;
|
var conqueredByPlayer = nodeObj.plyrCtrl;
|
||||||
targetNode.hp = targetNode.maxhp;
|
targetNode.hp = targetNode.maxhp;
|
||||||
@@ -1245,18 +1259,18 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
targetNode.deselect(this.actionButtons);
|
targetNode.deselect(this.actionButtons);
|
||||||
}
|
}
|
||||||
|
|
||||||
//The conquered node has its stats reduced
|
// The conquered node has its stats reduced
|
||||||
targetNode.atk /= 2;
|
targetNode.atk /= 2;
|
||||||
targetNode.def /= 3.5;
|
targetNode.def /= 3.5;
|
||||||
|
|
||||||
//Flag for whether the target node was a misc node
|
// Flag for whether the target node was a misc node
|
||||||
var isMiscNode = !targetNode.plyrCtrl && !targetNode.enmyCtrl;
|
var isMiscNode = !targetNode.plyrCtrl && !targetNode.enmyCtrl;
|
||||||
|
|
||||||
//Remove all connections from Node
|
// Remove all connections from Node
|
||||||
this.dropAllConnectionsToNode(targetNode);
|
this.dropAllConnectionsToNode(targetNode);
|
||||||
this.dropAllConnectionsFromNode(targetNode);
|
this.dropAllConnectionsFromNode(targetNode);
|
||||||
|
|
||||||
//Changes the css class and turn the node into a JsPlumb Source/Target
|
// Changes the css class and turn the node into a JsPlumb Source/Target
|
||||||
if (conqueredByPlayer) {
|
if (conqueredByPlayer) {
|
||||||
targetNode.setControlledByPlayer();
|
targetNode.setControlledByPlayer();
|
||||||
this.jsplumbinstance.unmakeTarget(targetNode.el);
|
this.jsplumbinstance.unmakeTarget(targetNode.el);
|
||||||
@@ -1268,7 +1282,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
targetNode.setControlledByEnemy();
|
targetNode.setControlledByEnemy();
|
||||||
nodeObj.conn = null; //Clear connection
|
nodeObj.conn = null; // Clear connection
|
||||||
this.jsplumbinstance.unmakeSource(targetNode.el);
|
this.jsplumbinstance.unmakeSource(targetNode.el);
|
||||||
this.jsplumbinstance.makeTarget(targetNode.el, {
|
this.jsplumbinstance.makeTarget(targetNode.el, {
|
||||||
maxConnections:-1,
|
maxConnections:-1,
|
||||||
@@ -1279,7 +1293,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
|
|
||||||
calcStats = true;
|
calcStats = true;
|
||||||
|
|
||||||
//Helper function to swap nodes between the respective enemyNodes/playerNodes arrays
|
// Helper function to swap nodes between the respective enemyNodes/playerNodes arrays
|
||||||
function swapNodes(orig, dest, targetNode) {
|
function swapNodes(orig, dest, targetNode) {
|
||||||
for (var i = 0; i < orig.length; ++i) {
|
for (var i = 0; i < orig.length; ++i) {
|
||||||
if (orig[i] == targetNode) {
|
if (orig[i] == targetNode) {
|
||||||
@@ -1319,7 +1333,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
case NodeTypes.Spam:
|
case NodeTypes.Spam:
|
||||||
if (conqueredByPlayer) {
|
if (conqueredByPlayer) {
|
||||||
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
|
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
|
||||||
//Conquering spam node increases time limit
|
// Conquering spam node increases time limit
|
||||||
this.time += CONSTANTS.HackingMissionSpamTimeIncrease;
|
this.time += CONSTANTS.HackingMissionSpamTimeIncrease;
|
||||||
} else {
|
} else {
|
||||||
swapNodes(isMiscNode ? this.miscNodes : this.playerNodes, this.enemyNodes, targetNode);
|
swapNodes(isMiscNode ? this.miscNodes : this.playerNodes, this.enemyNodes, targetNode);
|
||||||
@@ -1327,7 +1341,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case NodeTypes.Transfer:
|
case NodeTypes.Transfer:
|
||||||
//Conquering a Transfer node increases the attack of all cores by some percentages
|
// Conquering a Transfer node increases the attack of all cores by some percentages
|
||||||
if (conqueredByPlayer) {
|
if (conqueredByPlayer) {
|
||||||
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
|
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
|
||||||
this.playerCores.forEach(function(node) {
|
this.playerCores.forEach(function(node) {
|
||||||
@@ -1353,7 +1367,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If a misc node was conquered, the defense for all misc nodes increases by some fixed amount
|
// If a misc node was conquered, the defense for all misc nodes increases by some fixed amount
|
||||||
if (isMiscNode) { //&& conqueredByPlayer) {
|
if (isMiscNode) { //&& conqueredByPlayer) {
|
||||||
this.miscNodes.forEach((node)=>{
|
this.miscNodes.forEach((node)=>{
|
||||||
if (node.targetedCount === 0) {
|
if (node.targetedCount === 0) {
|
||||||
@@ -1363,23 +1377,25 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update node DOMs
|
// Update node DOMs
|
||||||
this.updateNodeDomElement(nodeObj);
|
this.updateNodeDomElement(nodeObj);
|
||||||
if (targetNode) {this.updateNodeDomElement(targetNode);}
|
if (targetNode) {this.updateNodeDomElement(targetNode);}
|
||||||
return calcStats;
|
return calcStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Enemy "AI" for CPU Core and Transfer Nodes
|
// Enemy "AI" for CPU Core and Transfer Nodes
|
||||||
HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
||||||
if (nodeObj == null) {return;}
|
if (nodeObj == null) {return;}
|
||||||
switch(nodeObj.type) {
|
switch(nodeObj.type) {
|
||||||
case NodeTypes.Core:
|
case NodeTypes.Core:
|
||||||
//Select a single RANDOM target from miscNodes and player's Nodes
|
/**
|
||||||
//If it is reachable, it will target it. If not, no target will
|
* Select a single RANDOM target from miscNodes and player's Nodes
|
||||||
//be selected for now, and the next time process() gets called this will repeat
|
* If it is reachable, it will target it. If not, no target will
|
||||||
|
* be selected for now, and the next time process() gets called this will repeat
|
||||||
|
*/
|
||||||
if (nodeObj.conn == null) {
|
if (nodeObj.conn == null) {
|
||||||
if (this.miscNodes.length === 0) {
|
if (this.miscNodes.length === 0) {
|
||||||
//Randomly pick a player node and attack it if its reachable
|
// Randomly pick a player node and attack it if its reachable
|
||||||
var rand = getRandomInt(0, this.playerNodes.length-1);
|
var rand = getRandomInt(0, this.playerNodes.length-1);
|
||||||
var node;
|
var node;
|
||||||
if (this.playerNodes.length === 0) {
|
if (this.playerNodes.length === 0) {
|
||||||
@@ -1388,23 +1404,23 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
|||||||
node = this.playerNodes[rand];
|
node = this.playerNodes[rand];
|
||||||
}
|
}
|
||||||
if (this.nodeReachableByEnemy(node)) {
|
if (this.nodeReachableByEnemy(node)) {
|
||||||
//Create connection
|
// Create connection
|
||||||
nodeObj.conn = this.jsplumbinstance.connect({
|
nodeObj.conn = this.jsplumbinstance.connect({
|
||||||
source:nodeObj.el,
|
source:nodeObj.el,
|
||||||
target:node.el
|
target:node.el
|
||||||
});
|
});
|
||||||
++node.targetedCount;
|
++node.targetedCount;
|
||||||
} else {
|
} else {
|
||||||
//Randomly pick a player core and attack it if its reachable
|
// Randomly pick a player core and attack it if its reachable
|
||||||
rand = getRandomInt(0, this.playerCores.length-1);
|
rand = getRandomInt(0, this.playerCores.length-1);
|
||||||
if (this.playerCores.length === 0) {
|
if (this.playerCores.length === 0) {
|
||||||
return; //No Misc Nodes, no player Nodes, no Player cores. Player lost
|
return; // No Misc Nodes, no player Nodes, no Player cores. Player lost
|
||||||
} else {
|
} else {
|
||||||
node = this.playerCores[rand];
|
node = this.playerCores[rand];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.nodeReachableByEnemy(node)) {
|
if (this.nodeReachableByEnemy(node)) {
|
||||||
//Create connection
|
// Create connection
|
||||||
nodeObj.conn = this.jsplumbinstance.connect({
|
nodeObj.conn = this.jsplumbinstance.connect({
|
||||||
source:nodeObj.el,
|
source:nodeObj.el,
|
||||||
target:node.el
|
target:node.el
|
||||||
@@ -1413,7 +1429,7 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Randomly pick a misc node and attack it if its reachable
|
// Randomly pick a misc node and attack it if its reachable
|
||||||
var rand = getRandomInt(0, this.miscNodes.length-1);
|
var rand = getRandomInt(0, this.miscNodes.length-1);
|
||||||
var node = this.miscNodes[rand];
|
var node = this.miscNodes[rand];
|
||||||
if (this.nodeReachableByEnemy(node)) {
|
if (this.nodeReachableByEnemy(node)) {
|
||||||
@@ -1425,10 +1441,10 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//If no connection was made, set the Core to Fortify
|
// If no connection was made, set the Core to Fortify
|
||||||
nodeObj.action = NodeActions.Fortify;
|
nodeObj.action = NodeActions.Fortify;
|
||||||
} else {
|
} else {
|
||||||
//If this node has a selected target
|
// If this node has a selected target
|
||||||
var targetNode;
|
var targetNode;
|
||||||
if (nodeObj.conn.target) {
|
if (nodeObj.conn.target) {
|
||||||
targetNode = this.getNodeFromElement(nodeObj.conn.target);
|
targetNode = this.getNodeFromElement(nodeObj.conn.target);
|
||||||
@@ -1453,7 +1469,7 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NodeTypes.Transfer:
|
case NodeTypes.Transfer:
|
||||||
//Switch between fortifying and overflowing as necessary
|
// Switch between fortifying and overflowing as necessary
|
||||||
if (nodeObj.def < 125) {
|
if (nodeObj.def < 125) {
|
||||||
nodeObj.action = NodeActions.Fortify;
|
nodeObj.action = NodeActions.Fortify;
|
||||||
} else {
|
} else {
|
||||||
@@ -1469,11 +1485,11 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hackEffWeightSelf = 130; //Weight for Node actions on self
|
var hackEffWeightSelf = 130; // Weight for Node actions on self
|
||||||
var hackEffWeightTarget = 25; //Weight for Node Actions against Target
|
var hackEffWeightTarget = 25; // Weight for Node Actions against Target
|
||||||
var hackEffWeightAttack = 80; //Weight for Attack action
|
var hackEffWeightAttack = 80; // Weight for Attack action
|
||||||
|
|
||||||
//Returns damage per cycle based on stats
|
// Returns damage per cycle based on stats
|
||||||
HackingMission.prototype.calculateAttackDamage = function(atk, def, hacking = 0) {
|
HackingMission.prototype.calculateAttackDamage = function(atk, def, hacking = 0) {
|
||||||
return Math.max(0.55 * (atk + (hacking / hackEffWeightAttack) - def), 1);
|
return Math.max(0.55 * (atk + (hacking / hackEffWeightAttack) - def), 1);
|
||||||
}
|
}
|
||||||
@@ -1494,11 +1510,11 @@ HackingMission.prototype.calculateOverflowEffect = function(hacking=0) {
|
|||||||
return 0.95 * hacking / hackEffWeightSelf;
|
return 0.95 * hacking / hackEffWeightSelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Updates timer display
|
// Updates timer display
|
||||||
HackingMission.prototype.updateTimer = function() {
|
HackingMission.prototype.updateTimer = function() {
|
||||||
var timer = document.getElementById("hacking-mission-timer");
|
var timer = document.getElementById("hacking-mission-timer");
|
||||||
|
|
||||||
//Convert time remaining to a string of the form mm:ss
|
// Convert time remaining to a string of the form mm:ss
|
||||||
var seconds = Math.round(this.time / 1000);
|
var seconds = Math.round(this.time / 1000);
|
||||||
var minutes = Math.trunc(seconds / 60);
|
var minutes = Math.trunc(seconds / 60);
|
||||||
seconds %= 60;
|
seconds %= 60;
|
||||||
@@ -1506,7 +1522,7 @@ HackingMission.prototype.updateTimer = function() {
|
|||||||
timer.innerText = "Time left: " + str;
|
timer.innerText = "Time left: " + str;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The 'win' argument is a bool for whether or not the player won
|
// The 'win' argument is a bool for whether or not the player won
|
||||||
HackingMission.prototype.finishMission = function(win) {
|
HackingMission.prototype.finishMission = function(win) {
|
||||||
inMission = false;
|
inMission = false;
|
||||||
currMission = null;
|
currMission = null;
|
||||||
@@ -1525,13 +1541,13 @@ HackingMission.prototype.finishMission = function(win) {
|
|||||||
dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation.");
|
dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Clear mission container
|
// Clear mission container
|
||||||
var container = document.getElementById("mission-container");
|
var container = document.getElementById("mission-container");
|
||||||
while(container.firstChild) {
|
while(container.firstChild) {
|
||||||
container.removeChild(container.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return to Faction page
|
// Return to Faction page
|
||||||
document.getElementById("mainmenu-container").style.visibility = "visible";
|
document.getElementById("mainmenu-container").style.visibility = "visible";
|
||||||
document.getElementById("character-overview-wrapper").style.visibility = "visible";
|
document.getElementById("character-overview-wrapper").style.visibility = "visible";
|
||||||
Engine.loadFactionContent();
|
Engine.loadFactionContent();
|
||||||
|
|||||||
72
src/Netscript/Environment.ts
Normal file
72
src/Netscript/Environment.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* The environment in which a script runs. The environment holds
|
||||||
|
* Netscript functions and arguments for that script.
|
||||||
|
*/
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export class Environment {
|
||||||
|
/**
|
||||||
|
* Parent environment. Used to implement "scope"
|
||||||
|
*/
|
||||||
|
parent: Environment | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the script that uses this Environment should stop running
|
||||||
|
*/
|
||||||
|
stopFlag: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment variables (currently only Netscript functions)
|
||||||
|
*/
|
||||||
|
vars: IMap<any> = {};
|
||||||
|
|
||||||
|
constructor(parent: Environment | null) {
|
||||||
|
if (parent instanceof Environment) {
|
||||||
|
this.vars = Object.assign({}, parent.vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the scope where the variable with the given name is defined
|
||||||
|
*/
|
||||||
|
lookup(name: string): Environment | null {
|
||||||
|
let scope: Environment | null = this;
|
||||||
|
while (scope) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(scope.vars, name)) {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
scope = scope.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the current value of a variable
|
||||||
|
get(name: string): any {
|
||||||
|
if (name in this.vars) {
|
||||||
|
return this.vars[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Undefined variable ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sets the value of a variable in any scope
|
||||||
|
set(name: string, value: any) {
|
||||||
|
var scope = this.lookup(name);
|
||||||
|
|
||||||
|
//If scope has a value, then this variable is already set in a higher scope, so
|
||||||
|
//set is there. Otherwise, create a new variable in the local scope
|
||||||
|
if (scope !== null) {
|
||||||
|
return scope.vars[name] = value;
|
||||||
|
} else {
|
||||||
|
return this.vars[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Creates (or overwrites) a variable in the current scope
|
||||||
|
def(name: string, value: any) {
|
||||||
|
return this.vars[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
332
src/Netscript/RamCostGenerator.ts
Normal file
332
src/Netscript/RamCostGenerator.ts
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
// TODO remember to update RamCalculations.js and WorkerScript.js
|
||||||
|
|
||||||
|
// RAM costs for Netscript functions
|
||||||
|
export const RamCostConstants: IMap<number> = {
|
||||||
|
ScriptBaseRamCost: 1.6,
|
||||||
|
ScriptDomRamCost: 25,
|
||||||
|
ScriptHackRamCost: 0.1,
|
||||||
|
ScriptHackAnalyzeRamCost: 1,
|
||||||
|
ScriptGrowRamCost: 0.15,
|
||||||
|
ScriptGrowthAnalyzeRamCost: 1,
|
||||||
|
ScriptWeakenRamCost: 0.15,
|
||||||
|
ScriptScanRamCost: 0.2,
|
||||||
|
ScriptPortProgramRamCost: 0.05,
|
||||||
|
ScriptRunRamCost: 1.0,
|
||||||
|
ScriptExecRamCost: 1.3,
|
||||||
|
ScriptSpawnRamCost: 2.0,
|
||||||
|
ScriptScpRamCost: 0.6,
|
||||||
|
ScriptKillRamCost: 0.5,
|
||||||
|
ScriptHasRootAccessRamCost: 0.05,
|
||||||
|
ScriptGetHostnameRamCost: 0.05,
|
||||||
|
ScriptGetHackingLevelRamCost: 0.05,
|
||||||
|
ScriptGetMultipliersRamCost: 4.0,
|
||||||
|
ScriptGetServerRamCost: 0.1,
|
||||||
|
ScriptFileExistsRamCost: 0.1,
|
||||||
|
ScriptIsRunningRamCost: 0.1,
|
||||||
|
ScriptHacknetNodesRamCost: 4.0,
|
||||||
|
ScriptHNUpgLevelRamCost: 0.4,
|
||||||
|
ScriptHNUpgRamRamCost: 0.6,
|
||||||
|
ScriptHNUpgCoreRamCost: 0.8,
|
||||||
|
ScriptGetStockRamCost: 2.0,
|
||||||
|
ScriptBuySellStockRamCost: 2.5,
|
||||||
|
ScriptGetPurchaseServerRamCost: 0.25,
|
||||||
|
ScriptPurchaseServerRamCost: 2.25,
|
||||||
|
ScriptGetPurchasedServerLimit: 0.05,
|
||||||
|
ScriptGetPurchasedServerMaxRam: 0.05,
|
||||||
|
ScriptRoundRamCost: 0.05,
|
||||||
|
ScriptReadWriteRamCost: 1.0,
|
||||||
|
ScriptArbScriptRamCost: 1.0,
|
||||||
|
ScriptGetScriptRamCost: 0.1,
|
||||||
|
ScriptGetHackTimeRamCost: 0.05,
|
||||||
|
ScriptGetFavorToDonate: 0.10,
|
||||||
|
ScriptCodingContractBaseRamCost: 10,
|
||||||
|
ScriptSleeveBaseRamCost: 4,
|
||||||
|
|
||||||
|
ScriptSingularityFn1RamCost: 2,
|
||||||
|
ScriptSingularityFn2RamCost: 3,
|
||||||
|
ScriptSingularityFn3RamCost: 5,
|
||||||
|
|
||||||
|
ScriptGangApiBaseRamCost: 4,
|
||||||
|
|
||||||
|
ScriptBladeburnerApiBaseRamCost: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RamCosts: IMap<any> = {
|
||||||
|
hacknet: {
|
||||||
|
numNodes: () => 0,
|
||||||
|
purchaseNode: () => 0,
|
||||||
|
getPurchaseNodeCost: () => 0,
|
||||||
|
getNodeStats: () => 0,
|
||||||
|
upgradeLevel: () => 0,
|
||||||
|
upgradeRam: () => 0,
|
||||||
|
upgradeCore: () => 0,
|
||||||
|
upgradeCache: () => 0,
|
||||||
|
getLevelUpgradeCost: () => 0,
|
||||||
|
getRamUpgradeCost: () => 0,
|
||||||
|
getCoreUpgradeCost: () => 0,
|
||||||
|
getCacheUpgradeCost: () => 0,
|
||||||
|
numHashes: () => 0,
|
||||||
|
hashCost: () => 0,
|
||||||
|
spendHashes: () => 0,
|
||||||
|
},
|
||||||
|
sprintf: () => 0,
|
||||||
|
vsprintf: () => 0,
|
||||||
|
scan: () => RamCostConstants.ScriptScanRamCost,
|
||||||
|
hack: () => RamCostConstants.ScriptHackRamCost,
|
||||||
|
hackAnalyzeThreads: () => RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||||
|
hackAnalyzePercent: () => RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||||
|
hackChance: () => RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||||
|
sleep: () => 0,
|
||||||
|
grow: () => RamCostConstants.ScriptGrowRamCost,
|
||||||
|
growthAnalyze: () => RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||||
|
weaken: () => RamCostConstants.ScriptWeakenRamCost,
|
||||||
|
print: () => 0,
|
||||||
|
tprint: () => 0,
|
||||||
|
clearLog: () => 0,
|
||||||
|
disableLog: () => 0,
|
||||||
|
enableLog: () => 0,
|
||||||
|
isLogEnabled: () => 0,
|
||||||
|
getScriptLogs: () => 0,
|
||||||
|
nuke: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
brutessh: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
ftpcrack: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
relaysmtp: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
httpworm: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
sqlinject: () => RamCostConstants.ScriptPortProgramRamCost,
|
||||||
|
run: () => RamCostConstants.ScriptRunRamCost,
|
||||||
|
exec: () => RamCostConstants.ScriptExecRamCost,
|
||||||
|
spawn: () => RamCostConstants.ScriptSpawnRamCost,
|
||||||
|
kill: () => RamCostConstants.ScriptKillRamCost,
|
||||||
|
killall: () => RamCostConstants.ScriptKillRamCost,
|
||||||
|
exit: () => 0,
|
||||||
|
scp: () => RamCostConstants.ScriptScpRamCost,
|
||||||
|
ls: () => RamCostConstants.ScriptScanRamCost,
|
||||||
|
ps: () => RamCostConstants.ScriptScanRamCost,
|
||||||
|
hasRootAccess: () => RamCostConstants.ScriptHasRootAccessRamCost,
|
||||||
|
getIp: () => RamCostConstants.ScriptGetHostnameRamCost,
|
||||||
|
getHostname: () => RamCostConstants.ScriptGetHostnameRamCost,
|
||||||
|
getHackingLevel: () => RamCostConstants.ScriptGetHackingLevelRamCost,
|
||||||
|
getHackingMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
|
||||||
|
getHacknetMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
|
||||||
|
getBitNodeMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
|
||||||
|
getServerMoneyAvailable: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerBaseSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerMinSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerRequiredHackingLevel: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerMaxMoney: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerGrowth: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerNumPortsRequired: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
getServerRam: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
serverExists: () => RamCostConstants.ScriptGetServerRamCost,
|
||||||
|
fileExists: () => RamCostConstants.ScriptFileExistsRamCost,
|
||||||
|
isRunning: () => RamCostConstants.ScriptIsRunningRamCost,
|
||||||
|
getStockSymbols: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockPrice: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockAskPrice: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockBidPrice: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockPosition: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockMaxShares: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockPurchaseCost: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
getStockSaleGain: () => RamCostConstants.ScriptGetStockRamCost,
|
||||||
|
buyStock: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
sellStock: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
shortStock: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
sellShort: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
placeOrder: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
cancelOrder: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
getOrders: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
getStockVolatility: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
getStockForecast: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
purchase4SMarketData: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
purchase4SMarketDataTixApi: () => RamCostConstants.ScriptBuySellStockRamCost,
|
||||||
|
getPurchasedServerLimit: () => RamCostConstants.ScriptGetPurchasedServerLimit,
|
||||||
|
getPurchasedServerMaxRam: () => RamCostConstants.ScriptGetPurchasedServerMaxRam,
|
||||||
|
getPurchasedServerCost: () => RamCostConstants.ScriptGetPurchaseServerRamCost,
|
||||||
|
purchaseServer: () => RamCostConstants.ScriptPurchaseServerRamCost,
|
||||||
|
deleteServer: () => RamCostConstants.ScriptPurchaseServerRamCost,
|
||||||
|
getPurchasedServers: () => RamCostConstants.ScriptPurchaseServerRamCost,
|
||||||
|
write: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
tryWrite: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
read: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
peek: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
clear: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
getPortHandle: () => RamCostConstants.ScriptReadWriteRamCost * 10,
|
||||||
|
rm: () => RamCostConstants.ScriptReadWriteRamCost,
|
||||||
|
scriptRunning: () => RamCostConstants.ScriptArbScriptRamCost,
|
||||||
|
scriptKill: () => RamCostConstants.ScriptArbScriptRamCost,
|
||||||
|
getScriptName: () => 0,
|
||||||
|
getScriptRam: () => RamCostConstants.ScriptGetScriptRamCost,
|
||||||
|
getHackTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||||
|
getGrowTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||||
|
getWeakenTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||||
|
getScriptIncome: () => RamCostConstants.ScriptGetScriptRamCost,
|
||||||
|
getScriptExpGain: () => RamCostConstants.ScriptGetScriptRamCost,
|
||||||
|
nFormat: () => 0,
|
||||||
|
getTimeSinceLastAug: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||||
|
prompt: () => 0,
|
||||||
|
wget: () => 0,
|
||||||
|
getFavorToDonate: () => RamCostConstants.ScriptGetFavorToDonate,
|
||||||
|
|
||||||
|
// Singularity Functions
|
||||||
|
universityCourse: () => RamCostConstants.ScriptSingularityFn1RamCost,
|
||||||
|
gymWorkout: () => RamCostConstants.ScriptSingularityFn1RamCost,
|
||||||
|
travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost,
|
||||||
|
purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost,
|
||||||
|
purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost,
|
||||||
|
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
|
||||||
|
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
|
||||||
|
isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
|
||||||
|
stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2,
|
||||||
|
upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
getUpgradeHomeRamCost: () => RamCostConstants.ScriptSingularityFn2RamCost / 2,
|
||||||
|
workForCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
applyToCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
getCompanyRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
|
||||||
|
getCompanyFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
|
||||||
|
getCompanyFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,
|
||||||
|
checkFactionInvitations: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
joinFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
workForFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,
|
||||||
|
getFactionRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
|
||||||
|
getFactionFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
|
||||||
|
getFactionFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,
|
||||||
|
donateToFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
createProgram: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
commitCrime: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getCrimeChance: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getOwnedAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getOwnedSourceFiles: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getAugmentationsFromFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getAugmentationPrereq: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
getAugmentationCost: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
purchaseAugmentation: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
installAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,
|
||||||
|
|
||||||
|
// Gang API
|
||||||
|
gang : {
|
||||||
|
getMemberNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||||
|
getGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getOtherGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getMemberInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
canRecruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||||
|
recruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getTaskNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||||
|
setMemberTask: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getEquipmentNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||||
|
getEquipmentCost: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getEquipmentType: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
purchaseEquipment: () => RamCostConstants.ScriptGangApiBaseRamCost,
|
||||||
|
ascendMember: () => RamCostConstants.ScriptGangApiBaseRamCost,
|
||||||
|
setTerritoryWarfare: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||||
|
getChanceToWinClash: () => RamCostConstants.ScriptGangApiBaseRamCost,
|
||||||
|
getBonusTime: () => 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bladeburner API
|
||||||
|
bladeburner : {
|
||||||
|
getContractNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||||
|
getOperationNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||||
|
getBlackOpNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||||
|
getBlackOpRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||||
|
getGeneralActionNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||||
|
getSkillNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||||
|
startAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
stopBladeburnerAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||||
|
getCurrentAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4,
|
||||||
|
getActionTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionEstimatedSuccessChance: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionRepGain: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionCountRemaining: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionMaxLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionCurrentLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
setActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
setActionLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getSkillPoints: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getSkillLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getSkillUpgradeCost: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
upgradeSkill: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
setTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getCityEstimatedPopulation: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getCityEstimatedCommunities: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getCityChaos: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
switchCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getStamina: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
joinBladeburnerFaction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
joinBladeburnerDivision: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||||
|
getBonusTime: () => 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Coding Contract API
|
||||||
|
codingcontract : {
|
||||||
|
attempt: () => RamCostConstants.ScriptCodingContractBaseRamCost,
|
||||||
|
getContractType: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||||
|
getData: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||||
|
getDescription: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||||
|
getNumTriesRemaining: () => RamCostConstants.ScriptCodingContractBaseRamCost / 5,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Duplicate Sleeve API
|
||||||
|
sleeve : {
|
||||||
|
getNumSleeves: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToShockRecovery: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToSynchronize: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToCommitCrime: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToUniversityCourse: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
travel: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToCompanyWork: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToFactionWork: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
setToGymWorkout: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
getSleeveStats: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
getTask: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
getInformation: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
getSleeveAugmentations: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
getSleevePurchasableAugs: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
|
},
|
||||||
|
|
||||||
|
heart: {
|
||||||
|
// Easter egg function
|
||||||
|
break : () => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRamCost(...args: string[]): number {
|
||||||
|
if (args.length === 0) {
|
||||||
|
console.warn(`No arguments passed to getRamCost()`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let curr = RamCosts[args[0]];
|
||||||
|
for (let i = 1; i < args.length; ++i) {
|
||||||
|
if (curr == null) {
|
||||||
|
console.warn(`Invalid function passed to getRamCost: ${args}`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currType = typeof curr;
|
||||||
|
if (currType === "function" || currType === "number") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
curr = curr[args[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
const currType = typeof curr;
|
||||||
|
if (currType === "function") {
|
||||||
|
return curr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currType === "number") {
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(`Unexpected type (${currType}) for value [${args}]`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
194
src/Netscript/WorkerScript.ts
Normal file
194
src/Netscript/WorkerScript.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/**
|
||||||
|
* The worker agent for running a script instance. Each running script instance
|
||||||
|
* has its own underlying WorkerScript object.
|
||||||
|
*
|
||||||
|
* Note that these objects are not saved and re-loaded when the game is refreshed.
|
||||||
|
* Instead, whenever the game is opened, WorkerScripts are re-created from
|
||||||
|
* RunningScript objects
|
||||||
|
*/
|
||||||
|
import { Environment } from "./Environment";
|
||||||
|
import { RamCostConstants } from "./RamCostGenerator";
|
||||||
|
|
||||||
|
import { RunningScript } from "../Script/RunningScript";
|
||||||
|
import { Script } from "../Script/Script";
|
||||||
|
import { AllServers } from "../Server/AllServers";
|
||||||
|
import { BaseServer } from "../Server/BaseServer";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export class WorkerScript {
|
||||||
|
/**
|
||||||
|
* Script's arguments
|
||||||
|
*/
|
||||||
|
args: any[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy of the script's code
|
||||||
|
*/
|
||||||
|
code: string = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the timeoutID (numeric value) for whenever this script is blocked by a
|
||||||
|
* timed Netscript function. i.e. Holds the return value of setTimeout()
|
||||||
|
*/
|
||||||
|
delay: number | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the Promise resolve() function for when the script is "blocked" by an async op
|
||||||
|
*/
|
||||||
|
delayResolve?: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores names of all functions that have logging disabled
|
||||||
|
*/
|
||||||
|
disableLogs: IMap<string> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for dynamic RAM calculation. Stores names of all functions that have
|
||||||
|
* already been checked by this script.
|
||||||
|
* TODO: Could probably just combine this with loadedFns?
|
||||||
|
*/
|
||||||
|
dynamicLoadedFns: IMap<string> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks dynamic RAM usage
|
||||||
|
*/
|
||||||
|
dynamicRamUsage: number = RamCostConstants.ScriptBaseRamCost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Netscript Environment for this script
|
||||||
|
*/
|
||||||
|
env: Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status message in case of script error. Currently unused I think
|
||||||
|
*/
|
||||||
|
errorMessage: string = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for static RAM calculation. Stores names of all functions that have
|
||||||
|
* already been checked by this script
|
||||||
|
*/
|
||||||
|
loadedFns: IMap<string> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filename of script
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script's output/return value. Currently not used or implemented
|
||||||
|
*/
|
||||||
|
output: string = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process ID. Must be an integer. Used for efficient script
|
||||||
|
* killing and removal.
|
||||||
|
*/
|
||||||
|
pid: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script's Static RAM usage. Equivalent to underlying script's RAM usage
|
||||||
|
*/
|
||||||
|
ramUsage: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not this workerScript is currently running
|
||||||
|
*/
|
||||||
|
running: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to underlying RunningScript object
|
||||||
|
*/
|
||||||
|
scriptRef: RunningScript;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP Address on which this script is running
|
||||||
|
*/
|
||||||
|
serverIp: string;
|
||||||
|
|
||||||
|
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => object) {
|
||||||
|
this.name = runningScriptObj.filename;
|
||||||
|
this.serverIp = runningScriptObj.server;
|
||||||
|
|
||||||
|
const sanitizedPid = Math.round(pid);
|
||||||
|
if (typeof sanitizedPid !== "number" || isNaN(sanitizedPid)) {
|
||||||
|
throw new Error(`Invalid PID when constructing WorkerScript: ${pid}`);
|
||||||
|
}
|
||||||
|
this.pid = sanitizedPid;
|
||||||
|
runningScriptObj.pid = sanitizedPid;
|
||||||
|
|
||||||
|
// Get the underlying script's code
|
||||||
|
const server = AllServers[this.serverIp];
|
||||||
|
if (server == null) {
|
||||||
|
throw new Error(`WorkerScript constructed with invalid server ip: ${this.serverIp}`);
|
||||||
|
}
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < server.scripts.length; ++i) {
|
||||||
|
if (server.scripts[i].filename === this.name) {
|
||||||
|
found = true;
|
||||||
|
this.code = server.scripts[i].code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.env = new Environment(null);
|
||||||
|
if (typeof nsFuncsGenerator === "function") {
|
||||||
|
this.env.vars = nsFuncsGenerator(this);
|
||||||
|
}
|
||||||
|
this.env.set("args", runningScriptObj.args.slice());
|
||||||
|
|
||||||
|
this.scriptRef = runningScriptObj;
|
||||||
|
this.args = runningScriptObj.args.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Server on which this script is running
|
||||||
|
*/
|
||||||
|
getServer() {
|
||||||
|
return AllServers[this.serverIp];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Script object for the underlying script.
|
||||||
|
* Returns null if it cannot be found (which would be a bug)
|
||||||
|
*/
|
||||||
|
getScript(): Script | null {
|
||||||
|
let server = this.getServer();
|
||||||
|
for (let i = 0; i < server.scripts.length; ++i) {
|
||||||
|
if (server.scripts[i].filename === this.name) {
|
||||||
|
return server.scripts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the script with the specified filename on the specified server,
|
||||||
|
* or null if it cannot be found
|
||||||
|
*/
|
||||||
|
getScriptOnServer(fn: string, server: BaseServer): Script | null {
|
||||||
|
if (server == null) {
|
||||||
|
server = this.getServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < server.scripts.length; ++i) {
|
||||||
|
if (server.scripts[i].filename === fn) {
|
||||||
|
return server.scripts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldLog(fn: string): boolean {
|
||||||
|
return (this.disableLogs.ALL == null && this.disableLogs[fn] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(txt: string): void {
|
||||||
|
this.scriptRef.log(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Event emitter that triggers when scripts are started/stopped
|
||||||
|
*/
|
||||||
|
import { EventEmitter } from "../utils/EventEmitter";
|
||||||
|
|
||||||
|
export const WorkerScriptStartStopEventEmitter = new EventEmitter();
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user