mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 14:28:36 +02:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15a324a946 | ||
|
|
94175877d7 | ||
|
|
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 | ||
|
|
2e9b028174 | ||
|
|
c56645c794 | ||
|
|
3ce2e83dd8 | ||
|
|
3241945452 | ||
|
|
fb857642e8 | ||
|
|
cc0e6548ff | ||
|
|
6c3c569a44 | ||
|
|
b5ebbba43d | ||
|
|
bf9b837e31 | ||
|
|
7f88ade30e | ||
|
|
36499ae9f2 | ||
|
|
4b95ba9ed1 | ||
|
|
804e4c23e3 | ||
|
|
b6b6d8e9fa | ||
|
|
c566c838be | ||
|
|
51d9274626 | ||
|
|
c8b478c208 | ||
|
|
18a3f061b4 | ||
|
|
3f8b9e4a32 | ||
|
|
e63ad76701 | ||
|
|
cb66ad9628 | ||
|
|
971bfbada4 | ||
|
|
b646c15521 | ||
|
|
92f7d12c0e | ||
|
|
7172f4e527 | ||
|
|
5592a8bc96 | ||
|
|
c4cb7daac5 | ||
|
|
75bc34208c | ||
|
|
83fc4d81b2 | ||
|
|
3cf18f100a | ||
|
|
8fbb072596 | ||
|
|
ea7f0752cb | ||
|
|
0f8f572519 | ||
|
|
52b6defebd | ||
|
|
34d749809a | ||
|
|
2ce4af2498 | ||
|
|
227bcf146e | ||
|
|
139a5add20 | ||
|
|
3a61a5cfa1 | ||
|
|
96db360a36 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,6 +3,6 @@ Netburner.txt
|
||||
/doc/build
|
||||
/node_modules
|
||||
/dist/*.map
|
||||
/tests/*.map
|
||||
/tests/*.bundle.*
|
||||
/tests/*.css
|
||||
/test/*.map
|
||||
/test/*.bundle.*
|
||||
/test/*.css
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Bitburner
|
||||
Bitburner is a cyberpunk hacking-themed incremental game. The game can be
|
||||
played at https://danielyxie.github.io/bitburner.
|
||||
Bitburner is a programming-based [incremental game](https://en.wikipedia.org/wiki/Incremental_game)
|
||||
that revolves around hacking and cyberpunk themes.
|
||||
The game can be played at https://danielyxie.github.io/bitburner.
|
||||
|
||||
# Documentation
|
||||
The game's official documentation can be found on [Read The
|
||||
|
||||
@@ -38,6 +38,7 @@ button {
|
||||
}
|
||||
|
||||
.a-link-button-inactive,
|
||||
.std-button-disabled,
|
||||
.std-button:disabled {
|
||||
text-decoration: none;
|
||||
background-color: #333;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Character Overview Panel (top-right)
|
||||
* Styling for the Character Overview Panel (top-right panel)
|
||||
*/
|
||||
|
||||
#character-overview-wrapper {
|
||||
|
||||
75
css/hacknetnodes.scss
Normal file
75
css/hacknetnodes.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
@import "mixins";
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Hacknet Nodes UI Page
|
||||
*/
|
||||
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.hacknet-general-info {
|
||||
margin: 10px;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
||||
@@ -138,81 +138,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Hacknet Nodes */
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-text,
|
||||
#hacknet-nodes-container li {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-page-text {
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
/* Pop-up boxes */
|
||||
.popup-box-container {
|
||||
display: none; /* Hidden by default */
|
||||
display: none; /* Initially hidden */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 10; /* Sit on top */
|
||||
left: 0;
|
||||
|
||||
@@ -68,13 +68,14 @@
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
background-color: #555;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
resize: none;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
#script-editor-status {
|
||||
|
||||
@@ -7,35 +7,45 @@
|
||||
p {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: $defaultFontSize * 0.875;
|
||||
}
|
||||
h2 {
|
||||
}
|
||||
|
||||
.stock-market-info-and-purchases {
|
||||
> h2 {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
> p {
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
> a, > button {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#stock-market-list li {
|
||||
button {
|
||||
font-size: $defaultFontSize;
|
||||
#stock-market-list {
|
||||
list-style: none;
|
||||
|
||||
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 {
|
||||
display: block;
|
||||
margin: 5px 5px 5px 10px;
|
||||
padding: 4px;
|
||||
width: 50%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.stock-market-input {
|
||||
@@ -47,14 +57,36 @@
|
||||
color: var(--my-font-color);
|
||||
}
|
||||
|
||||
.stock-market-price-movement-warning {
|
||||
border: 1px solid white;
|
||||
color: red;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.stock-market-position-text {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
display: block;
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-market-order-list {
|
||||
overflow-y: auto;
|
||||
max-height: 100px;
|
||||
|
||||
li {
|
||||
color: #fff;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-market-order-cancel-btn {
|
||||
|
||||
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],r[i]&&s.push(r[i][0]),r[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 u.push.apply(u,l||[]),o()}function o(){for(var n,t=0;t<u.length;t++){for(var o=u[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==r[c]&&(e=!1)}e&&(u.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},r={1:0},u=[];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;u.push([353,0]),o()}({300:function(n,t,o){},302:function(n,t,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){},353:function(n,t,o){"use strict";o.r(t);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),o(302),o(300)}});
|
||||
//# sourceMappingURL=engineStyle.bundle.js.map
|
||||
266
dist/engine.css → dist/engineStyle.css
vendored
266
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 */
|
||||
/* Attributes */
|
||||
/* COLORS */
|
||||
@@ -486,6 +443,7 @@ button {
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
|
||||
|
||||
.a-link-button-inactive,
|
||||
.std-button-disabled,
|
||||
.std-button:disabled {
|
||||
text-decoration: none;
|
||||
background-color: #333;
|
||||
@@ -501,11 +459,15 @@ button {
|
||||
.a-link-button-inactive:hover .tooltiptext,
|
||||
.a-link-button-inactive:hover .tooltiptexthigh,
|
||||
.a-link-button-inactive:hover .tooltiptextleft,
|
||||
.std-button-disabled:hover .tooltiptext,
|
||||
.std-button-disabled:hover .tooltiptexthigh,
|
||||
.std-button-disabled:hover .tooltiptextleft,
|
||||
.std-button:disabled:hover .tooltiptext,
|
||||
.std-button:disabled:hover .tooltiptexthigh,
|
||||
.std-button:disabled:hover .tooltiptextleft {
|
||||
visibility: visible; }
|
||||
.a-link-button-inactive:active,
|
||||
.std-button-disabled:active,
|
||||
.std-button:disabled:active {
|
||||
pointer-events: none; }
|
||||
|
||||
@@ -671,7 +633,7 @@ button {
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/**
|
||||
* Styling for the Character Overview Panel (top-right)
|
||||
* Styling for the Character Overview Panel (top-right panel)
|
||||
*/
|
||||
#character-overview-wrapper {
|
||||
position: relative; }
|
||||
@@ -881,13 +843,14 @@ button {
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
background-color: #555;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
resize: none;
|
||||
width: 60%; }
|
||||
|
||||
#script-editor-status {
|
||||
float: left;
|
||||
@@ -927,6 +890,107 @@ button {
|
||||
|
||||
/* 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 */
|
||||
/**
|
||||
* Styling for the Hacknet Nodes UI Page
|
||||
*/
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px; }
|
||||
|
||||
.hacknet-general-info {
|
||||
margin: 10px;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; }
|
||||
#hacknet-nodes-container li.hacknet-node {
|
||||
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw; }
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left; }
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right; }
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block; }
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table; }
|
||||
.hacknet-node-container .row {
|
||||
display: table-row;
|
||||
height: 30px; }
|
||||
.hacknet-node-container .row p {
|
||||
display: table-cell; }
|
||||
.hacknet-node-container .upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
/* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: 64px; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
||||
@@ -1044,64 +1108,6 @@ button {
|
||||
color: #fff;
|
||||
margin-left: 5%; }
|
||||
|
||||
/* Hacknet Nodes */
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px; }
|
||||
|
||||
#hacknet-nodes-text,
|
||||
#hacknet-nodes-container li {
|
||||
margin: 10px;
|
||||
padding: 10px; }
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; }
|
||||
#hacknet-nodes-container li.hacknet-node {
|
||||
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw; }
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left; }
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right; }
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block; }
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table; }
|
||||
.hacknet-node-container .row {
|
||||
display: table-row;
|
||||
height: 30px; }
|
||||
.hacknet-node-container .row p {
|
||||
display: table-cell; }
|
||||
.hacknet-node-container .upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
/* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: 64px; }
|
||||
|
||||
.menu-page-text {
|
||||
width: 70vw; }
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
@@ -1307,25 +1313,30 @@ button {
|
||||
font-size: 13px; }
|
||||
#stock-market-container a {
|
||||
font-size: 14px; }
|
||||
#stock-market-container h2 {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
display: block; }
|
||||
|
||||
#stock-market-list li button {
|
||||
font-size: 16px; }
|
||||
.stock-market-info-and-purchases > h2 {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px; }
|
||||
|
||||
#stock-market-container p {
|
||||
padding: 6px;
|
||||
margin: 6px;
|
||||
.stock-market-info-and-purchases > p {
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
width: 70%; }
|
||||
|
||||
#stock-market-container a {
|
||||
.stock-market-info-and-purchases > a, .stock-market-info-and-purchases > button {
|
||||
margin: 10px; }
|
||||
|
||||
#stock-market-list {
|
||||
list-style: none; }
|
||||
#stock-market-list li button {
|
||||
font-size: 16px; }
|
||||
|
||||
#stock-market-watchlist-filter {
|
||||
width: 50%;
|
||||
margin-left: 10px; }
|
||||
display: block;
|
||||
margin: 5px 5px 5px 10px;
|
||||
padding: 4px;
|
||||
width: 50%; }
|
||||
|
||||
.stock-market-input {
|
||||
display: inline-block;
|
||||
@@ -1335,13 +1346,28 @@ button {
|
||||
border: 1px solid #fff;
|
||||
color: var(--my-font-color); }
|
||||
|
||||
.stock-market-price-movement-warning {
|
||||
border: 1px solid white;
|
||||
color: red;
|
||||
margin: 2px;
|
||||
padding: 2px; }
|
||||
|
||||
.stock-market-position-text {
|
||||
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 {
|
||||
overflow-y: auto;
|
||||
max-height: 100px; }
|
||||
.stock-market-order-list li {
|
||||
color: #fff;
|
||||
padding: 4px; }
|
||||
|
||||
.stock-market-order-cancel-btn {
|
||||
background-color: #000;
|
||||
@@ -1402,7 +1428,7 @@ button {
|
||||
/* Pop-up boxes */
|
||||
.popup-box-container {
|
||||
display: none;
|
||||
/* Hidden by default */
|
||||
/* Initially hidden */
|
||||
position: fixed;
|
||||
/* Stay in place */
|
||||
z-index: 10;
|
||||
@@ -4860,4 +4886,4 @@ html {
|
||||
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
@@ -80,6 +80,19 @@ when you normally install Augmentations.
|
||||
The cost of purchasing an Augmentation for a Duplicate Sleeve is **not** affected
|
||||
by how many Augmentations you have purchased for yourself, and vice versa.
|
||||
|
||||
Memory
|
||||
~~~~~~
|
||||
Sleeve memory dictates what a sleeve's synchronization will be when its reset by
|
||||
switching BitNodes. For example, if a sleeve has a memory of 10, then when you
|
||||
switch BitNodes its synchronization will initially be set to 10, rather than 1.
|
||||
|
||||
Memory can only be increased by purchasing upgrades from The Covenant. Just like
|
||||
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.
|
||||
|
||||
Re-sleeving
|
||||
^^^^^^^^^^^
|
||||
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
||||
|
||||
@@ -40,7 +40,7 @@ solutions. Some may be numbers, others may be strings or arrays.
|
||||
If a contract asks for a specific solution format, then
|
||||
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
|
||||
marks that are part of the actual string solution should be included.
|
||||
* 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.
|
||||
|
||||
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.
|
||||
Fundamentals
|
||||
------------
|
||||
The Stock Market is not as simple as "buy at price X and sell at price Y". The following
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -21,16 +25,74 @@ 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'
|
||||
a stock.
|
||||
|
||||
NOTE: Shorting stocks is not available immediately, and must be unlocked later in the
|
||||
game.
|
||||
.. note:: Shorting stocks is not available immediately, and must be unlocked later in the
|
||||
game.
|
||||
|
||||
.. _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.
|
||||
|
||||
.. _gameplay_stock_market_spread_price_movement:
|
||||
|
||||
Transactions Influencing Stock Price
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Buying or selling a large number of shares of a stock will influence that stock's price.
|
||||
|
||||
Buying a stock in the long position will cause that stock's price to
|
||||
increase. Selling a stock in the long position will cause the stock's price to decrease.
|
||||
The reverse occurs for the short position. The effect of this depends on how many shares
|
||||
are being transacted. More shares will have a bigger effect on the stock price. If
|
||||
only a small number of shares are being transacted, the stock price may not be affected.
|
||||
|
||||
Note that this "influencing" of the stock price **can happen in the middle of a transaction**.
|
||||
For example, assume you are selling 1m shares of a stock. That stock's bid price
|
||||
is currently $100. However, selling 100k shares of the stock causes its price to drop
|
||||
by 1%. This means that for your transaction of 1m shares, the first 100k shares will be
|
||||
sold at $100. Then the next 100k shares will be sold at $99. Then the next 100k shares will
|
||||
be sold at $98.01, and so on.
|
||||
|
||||
This is an important concept to keep in mind if you are trying to purchase/sell a
|
||||
large number of shares, as **it can negatively affect your profits**.
|
||||
|
||||
.. note:: On the Stock Market UI, the displayed profit of a position does not take
|
||||
this "influencing" into account.
|
||||
|
||||
Transactions Influencing Stock Forecast
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
In addition to influencing stock price, buying or selling a large number of shares
|
||||
of a stock will also influence that stock's "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 on the
|
||||
stock forecast.
|
||||
|
||||
.. _gameplay_stock_market_order_types:
|
||||
|
||||
Order Types
|
||||
^^^^^^^^^^^
|
||||
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.
|
||||
|
||||
Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked
|
||||
later in the game.
|
||||
.. note:: Limit Orders and Stop Orders are not available immediately, and must be unlocked
|
||||
later in the game.
|
||||
|
||||
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
|
||||
@@ -71,3 +133,42 @@ 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 sell will execute if the stock's price >= order's price.
|
||||
|
||||
.. note:: Stop Orders do **not** take into account the fact that transactions can
|
||||
influence a stock's price. Limit Orders, however, do take this into account.
|
||||
|
||||
For example, assume you have a Limit Order set to purchase a stock at
|
||||
$5. Then, the stock's price drops to $5 and your Limit Order executes.
|
||||
However, the transaction causes the stock's price to increase before
|
||||
the order finishes executing all of the shares. Your Limit Order will
|
||||
stop executing, and will continue only when the stock's price drops back to
|
||||
$5 or below.
|
||||
|
||||
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
|
||||
* 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.
|
||||
|
||||
@@ -16,6 +16,85 @@ the terminal and enter::
|
||||
|
||||
nano .fconf
|
||||
|
||||
|
||||
.. _terminal_filesystem:
|
||||
|
||||
Filesystem (Directories)
|
||||
------------------------
|
||||
The Terminal contains a **very** basic filesystem that allows you to store and
|
||||
organize your files into different directories. Note that this is **not** a true
|
||||
filesystem implementation. Instead, it is done almost entirely using string manipulation.
|
||||
For this reason, many of the nice & useful features you'd find in a real
|
||||
filesystem do not exist.
|
||||
|
||||
Here are the Terminal commands you'll commonly use when dealing with the filesystem.
|
||||
|
||||
* :ref:`ls_terminal_command`
|
||||
* :ref:`cd_terminal_command`
|
||||
* :ref:`mv_terminal_command`
|
||||
|
||||
Directories
|
||||
^^^^^^^^^^^
|
||||
In order to create a directory, simply name a file using a full absolute Linux-style path::
|
||||
|
||||
/scripts/myScript.js
|
||||
|
||||
This will automatically create a "directory" called :code:`scripts`. This will also work
|
||||
for subdirectories::
|
||||
|
||||
/scripts/hacking/helpers/myHelperScripts.script
|
||||
|
||||
Files in the root directory do not need to begin with a forward slash::
|
||||
|
||||
thisIsAFileInTheRootDirectory.txt
|
||||
|
||||
Note that there is no way to manually create or remove directories. The creation and
|
||||
deletion of directories is automatically handled as you name/rename/delete
|
||||
files.
|
||||
|
||||
Absolute vs Relative Paths
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Many Terminal commands accept absolute both absolute and relative paths for specifying a
|
||||
file.
|
||||
|
||||
An absolute path specifies the location of the file from the root directory (/).
|
||||
Any path that begins with the forward slash is an absolute path::
|
||||
|
||||
$ nano /scripts/myScript.js
|
||||
$ cat /serverList.txt
|
||||
|
||||
A relative path specifies the location of the file relative to the current working directory.
|
||||
Any path that does **not** begin with a forward slash is a relative path. Note that the
|
||||
Linux-style dot symbols will work for relative paths::
|
||||
|
||||
. (a single dot) - represents the current directory
|
||||
.. (two dots) - represents the parent directory
|
||||
|
||||
$ cd ..
|
||||
$ nano ../scripts/myScript.js
|
||||
$ nano ../../helper.js
|
||||
|
||||
Netscript
|
||||
^^^^^^^^^
|
||||
Note that in order to reference a file, :ref:`netscript` functions require the
|
||||
**full** absolute file path. For example
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
run("/scripts/hacking/helpers.myHelperScripts.script");
|
||||
rm("/logs/myHackingLogs.txt");
|
||||
rm("thisIsAFileInTheRootDirectory.txt");
|
||||
|
||||
.. note:: A full file path **must** begin with a forward slash (/) if that file
|
||||
is not in the root directory.
|
||||
|
||||
Missing Features
|
||||
^^^^^^^^^^^^^^^^
|
||||
These features that are typically in Linux filesystems have not yet been added to the game:
|
||||
|
||||
* Tab autocompletion does not work with relative paths
|
||||
* :code:`mv` only accepts full filepaths for the destination argument. It does not accept directories
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
@@ -98,6 +177,25 @@ Display a message (.msg), literature (.lit), or text (.txt) file::
|
||||
$ cat foo.lit
|
||||
$ cat servers.txt
|
||||
|
||||
.. _cd_terminal_command:
|
||||
|
||||
cd
|
||||
^^
|
||||
|
||||
$ cd [dir]
|
||||
|
||||
Change to the specified directory.
|
||||
|
||||
See :ref:`terminal_filesystem` for details on directories.
|
||||
|
||||
Note that this command works even for directories that don't exist. If you change
|
||||
to a directory that doesn't exist, it will not be created. A directory is only created
|
||||
once there is a file in it::
|
||||
|
||||
$ cd scripts/hacking
|
||||
$ cd /logs
|
||||
$ cd ..
|
||||
|
||||
check
|
||||
^^^^^
|
||||
|
||||
@@ -234,27 +332,35 @@ killall
|
||||
|
||||
Kills all scripts on the current server.
|
||||
|
||||
.. _ls_terminal_command:
|
||||
|
||||
ls
|
||||
^^
|
||||
|
||||
$ ls [| grep pattern]
|
||||
$ ls [dir] [| grep pattern]
|
||||
|
||||
Prints files on the current server to the Terminal screen.
|
||||
Prints files and directories on the current server to the Terminal screen.
|
||||
|
||||
If this command is run with no arguments, then it prints all files on the current
|
||||
server to the Terminal screen. The files will be displayed in alphabetical
|
||||
order.
|
||||
If this command is run with no arguments, then it prints all files and directories on the current
|
||||
server to the Terminal screen. Directories will be printed first in alphabetical order,
|
||||
followed by the files (also in alphabetical order).
|
||||
|
||||
The '| grep pattern' is an optional parameter that can be used to only display files
|
||||
whose filenames match the specified pattern. For example, if you wanted to only display
|
||||
files with the .script extension, you could use::
|
||||
The :code:`dir` optional parameter allows you to specify the directory for which to display
|
||||
files.
|
||||
|
||||
The :code:`| grep pattern` optional parameter allows you to only display files and directories
|
||||
with a certain pattern in their names.
|
||||
|
||||
Examples::
|
||||
|
||||
// List files/directories with the '.script' extension in the current directory
|
||||
$ ls | grep .script
|
||||
|
||||
Alternatively, if you wanted to display all files with the word *purchase* in the filename,
|
||||
you could use::
|
||||
// List files/directories with the '.js' extension in the root directory
|
||||
$ ls / | grep .js
|
||||
|
||||
$ ls | grep purchase
|
||||
// List files/directories with the word 'purchase' in the name, in the :code:`scripts` directory
|
||||
$ ls scripts | grep purchase
|
||||
|
||||
|
||||
lscpu
|
||||
@@ -282,6 +388,28 @@ The first example above will print the amount of RAM needed to run 'foo.script'
|
||||
with a single thread. The second example above will print the amount of RAM needed
|
||||
to run 'foo.script' with 50 threads.
|
||||
|
||||
.. _mv_terminal_command:
|
||||
|
||||
mv
|
||||
^^
|
||||
|
||||
$ mv [source] [destination]
|
||||
|
||||
Move the source file to the specified destination in the filesystem.
|
||||
See :ref:`terminal_filesystem` for more details about the Terminal's filesystem.
|
||||
This command only works for scripts and text files (.txt). It cannot, however, be used
|
||||
to convert from script to text file, or vice versa.
|
||||
|
||||
This function can also be used to rename files.
|
||||
|
||||
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
|
||||
full filepath. It cannot be a directory.
|
||||
|
||||
Examples::
|
||||
|
||||
$ mv hacking.script scripts/hacking.script
|
||||
$ mv myScript.js myOldScript.js
|
||||
|
||||
nano
|
||||
^^^^
|
||||
|
||||
|
||||
@@ -3,6 +3,104 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
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
|
||||
------------------
|
||||
* Added BitNode-9: Hacktocracy
|
||||
* Changed BitNode-11's multipliers to make it slightly harder overall
|
||||
* Source-File 11 is now slightly stronger
|
||||
* Added several functions to Netscript Sleeve API for buying Sleeve augmentations (by hydroflame)
|
||||
* Added a new stat for Duplicate Sleeves: Memory
|
||||
* Increase baseline experience earned from Infiltration, but it now gives diminishing returns (on exp) as you get to higher difficulties/levels
|
||||
* In Bladeburner, stamina gained from Hyperbolic Regeneration Chamber is now a percentage of your max stamina
|
||||
|
||||
* Corporation Changes:
|
||||
* 'Demand' value of products decreases more slowly
|
||||
* Bug Fix: Fixed a Corporation issue that broke the Market-TA2 Research
|
||||
* Bug Fix: Issuing New Shares now works properly
|
||||
|
||||
* Bug Fix: Money Statistics tracker was incorrectly recording profits when selling stocks manually
|
||||
* Bug Fix: Fixed an issue with the job requirement tooltip for security jobs
|
||||
|
||||
v0.45.1 - 3/23/2019
|
||||
-------------------
|
||||
* Added two new Corporation Researches
|
||||
|
||||
@@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.45'
|
||||
version = '0.47'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.45.0'
|
||||
release = '0.47.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -2,10 +2,11 @@ Guides & Tips
|
||||
=============
|
||||
|
||||
Getting Started Guide for Intermediate Programmers
|
||||
What BitNode should I do?
|
||||
|
||||
Beginners FAQ
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
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!
|
||||
=====================================
|
||||
Bitburner is a cyberpunk-themed `incremental game <https://en.wikipedia.org/wiki/Incremental_game>`_ that is currently in the
|
||||
early beta stage of development. The game `can be played here <https://danielyxie.github.io/bitburner/>`_.
|
||||
Bitburner is a programming-based `incremental game <https://en.wikipedia.org/wiki/Incremental_game>`_
|
||||
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?
|
||||
------------------
|
||||
@@ -26,6 +27,7 @@ secrets that you've been searching for.
|
||||
Script Editors <scripteditors>
|
||||
Game Frozen or Stuck? <gamefrozen>
|
||||
Guides & Tips <guidesandtips>
|
||||
Tools & Resources <toolsandresources>
|
||||
Changelog <changelog>
|
||||
Donate <https://paypal.me/danielyxie>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
getScriptRam() Netscript Function
|
||||
===========================
|
||||
=================================
|
||||
|
||||
.. js:function:: getScriptRam(scriptname[, hostname/ip])
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
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 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 number by which the money on the server was multiplied for the growth
|
||||
:RAM cost: 0.15 GB
|
||||
|
||||
@@ -19,3 +24,4 @@ grow() Netscript Function
|
||||
Example::
|
||||
|
||||
grow("foodnstuff");
|
||||
grow("foodnstuff", { threads: 5 }); // Only use 5 threads to grow
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
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 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 of money stolen if the hack is successful, and zero otherwise
|
||||
:RAM cost: 0.1 GB
|
||||
|
||||
@@ -20,3 +25,4 @@ hack() Netscript Function
|
||||
|
||||
hack("foodnstuff");
|
||||
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
|
||||
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
|
||||
===========================
|
||||
|
||||
.. 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 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
|
||||
by the number of script threads
|
||||
:RAM cost: 0.15 GB
|
||||
@@ -18,3 +23,4 @@ weaken() Netscript Function
|
||||
Example::
|
||||
|
||||
weaken("foodnstuff");
|
||||
weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
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 string fn: Filename of the contract
|
||||
:param string hostname/ip: Hostname or IP of the server containing the contract.
|
||||
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.
|
||||
|
||||
: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.
|
||||
|
||||
Possible reasons for failure:
|
||||
|
||||
* Cannot currently recruit a new member
|
||||
* There already exists a member with the specified name
|
||||
|
||||
|
||||
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
getCacheUpgradeCost() Netscript Function
|
||||
========================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: getCacheUpgradeCost(i, n)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
:param number n: Number of times to upgrade cache. Must be positive. Rounded to nearest integer
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||
a Hacknet Node).
|
||||
|
||||
Returns the cost of upgrading the cache level of the specified Hacknet Server by *n*.
|
||||
|
||||
If an invalid value for *n* is provided, then this function returns 0. If the
|
||||
specified Hacknet Server is already at the max cache level, then Infinity is returned.
|
||||
@@ -1,6 +1,8 @@
|
||||
getNodeStats() Netscript Function
|
||||
=================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: getNodeStats(i)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
@@ -12,7 +14,12 @@ getNodeStats() Netscript Function
|
||||
level: Node's level,
|
||||
ram: Node's RAM,
|
||||
cores: Node's number of cores,
|
||||
production: Node's money earned per second,
|
||||
cache: Cache level. Only applicable for Hacknet Servers
|
||||
production: Node's production per second
|
||||
timeOnline: Number of seconds since Node has been purchased,
|
||||
totalProduction: Total number of money Node has produced
|
||||
totalProduction: Total amount that the Node has produced
|
||||
}
|
||||
|
||||
.. note:: Note that for Hacknet Nodes, production refers to the amount of money the node generates.
|
||||
For Hacknet Servers (the upgraded version of Hacknet Nodes), production refers to the amount
|
||||
of hashes the node generates.
|
||||
|
||||
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
hashCost() Netscript Function
|
||||
=============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: hashCost(upgName)
|
||||
|
||||
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes required for the specified upgrade. The name of the
|
||||
upgrade must be an exact match.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
var upgradeName = "Sell for Corporation Funds";
|
||||
if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||
hacknet.spendHashes(upgName);
|
||||
}
|
||||
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
numHashes() Netscript Function
|
||||
==============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: numHashes()
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes you have
|
||||
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
spendHashes() Netscript Function
|
||||
================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: spendHashes(upgName, upgTarget)
|
||||
|
||||
:param string upgName: Name of upgrade to spend hashes on. Must be an exact match
|
||||
:param string upgTarget: Object to which upgrade applies. Required for certain upgrades
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Spend the hashes generated by your Hacknet Servers on an upgrade. Returns a boolean value -
|
||||
true if the upgrade is successfully purchased, and false otherwise.
|
||||
|
||||
The name of the upgrade must be an exact match. The :code:`upgTarget` argument is used
|
||||
for upgrades such as :code:`Reduce Minimum Security`, which applies to a specific server.
|
||||
In this case, the :code:`upgTarget` argument must be the hostname of the server.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
hacknet.spendHashes("Sell for Corporation Funds");
|
||||
hacknet.spendHashes("Increase Maximum Money", "foodnstuff");
|
||||
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
upgradeCache() Netscript Function
|
||||
=================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: upgradeCache(i, n)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
:param number n: Number of cache levels to purchase. Must be positive. Rounded to nearest integer
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||
a Hacknet Node).
|
||||
|
||||
Tries to upgrade the specified Hacknet Server's cache *n* times.
|
||||
|
||||
Returns true if it successfully upgrades the Server's cache *n* times, or if
|
||||
it purchases some positive amount and the Server reaches its max cache level.
|
||||
|
||||
Returns false otherwise.
|
||||
@@ -8,8 +8,7 @@ later in 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
|
||||
where the Gang mechanic is accessible. This may change in the future
|
||||
The Gang mechanic and the Gang API are unlocked in BitNode-2.
|
||||
|
||||
**Gang API functions must be accessed through the 'gang' namespace**
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
Netscript Hacknet Node API
|
||||
==========================
|
||||
|
||||
.. warning:: Not all functions in the Hacknet Node API are immediately available.
|
||||
For this reason, the documentation for this API may contains spoilers
|
||||
for the game.
|
||||
|
||||
Netscript provides the following API for accessing and upgrading your Hacknet Nodes
|
||||
through scripts.
|
||||
|
||||
@@ -31,9 +35,14 @@ In :ref:`netscriptjs`::
|
||||
upgradeLevel() <hacknetnodeapi/upgradeLevel>
|
||||
upgradeRam() <hacknetnodeapi/upgradeRam>
|
||||
upgradeCore() <hacknetnodeapi/upgradeCore>
|
||||
upgradeCache() <hacknetnodeapi/upgradeCache>
|
||||
getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost>
|
||||
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
|
||||
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
|
||||
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
|
||||
numHashes() <hacknetnodeapi/numHashes>
|
||||
hashCost() <hacknetnodeapi/hashCost>
|
||||
spendHashes() <hacknetnodeapi/spendHashes>
|
||||
|
||||
.. _netscript_hacknetnodeapi_referencingahacknetnode:
|
||||
|
||||
@@ -68,23 +77,25 @@ The following is an example of one way a script can be used to automate the
|
||||
purchasing and upgrading of Hacknet Nodes.
|
||||
|
||||
This script attempts to purchase Hacknet Nodes until the player has a total of 8. Then
|
||||
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores::
|
||||
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
function myMoney() {
|
||||
return getServerMoneyAvailable("home");() <hacknetnodeapi/ return getServerMoneyAvailable("home");>
|
||||
}
|
||||
}() <hacknetnodeapi/>
|
||||
return getServerMoneyAvailable("home");
|
||||
}
|
||||
|
||||
disableLog("getServerMoneyAvailable");
|
||||
disableLog("sleep");
|
||||
|
||||
cnt = 8;
|
||||
var cnt = 8;
|
||||
|
||||
while(hacknet.numNodes() < cnt) {
|
||||
res = hacknet.purchaseNode();
|
||||
print("Purchased hacknet Node with index " + res);
|
||||
};
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
for (var i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).level <= 80) {
|
||||
var cost = hacknet.getLevelUpgradeCost(i, 10);
|
||||
while (myMoney() < cost) {
|
||||
@@ -95,9 +106,9 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to level 80");
|
||||
print("All nodes upgraded to level 80");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
for (var i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).ram < 16) {
|
||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
||||
while (myMoney() < cost) {
|
||||
@@ -108,43 +119,4 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 16GB RAM");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).level <= 140) {
|
||||
var cost = hacknet.getLevelUpgradeCost(i, 5);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeLevel(i, 5);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to level 140");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).ram < 64) {
|
||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeRam(i, 2);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 64GB RAM (MAX)");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknetnodes.getNodeStatsi(i).cores < 8) {
|
||||
var cost = hacknet.getCoreUpgradeCost(7);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeCore(i, 7);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 8 cores");
|
||||
print("All nodes upgraded to 16GB RAM");
|
||||
|
||||
@@ -15,11 +15,15 @@ access even after you 'reset' by installing Augmentations
|
||||
|
||||
.. toctree::
|
||||
:caption: API Functions:
|
||||
|
||||
|
||||
getStockSymbols() <tixapi/getStockSymbols>
|
||||
getStockPrice() <tixapi/getStockPrice>
|
||||
getStockAskPrice() <tixapi/getStockAskPrice>
|
||||
getStockBidPrice() <tixapi/getStockBidPrice>
|
||||
getStockPosition() <tixapi/getStockPosition>
|
||||
getStockMaxShares() <tixapi/getStockMaxShares>
|
||||
getStockPurchaseCost() <tixapi/getStockPurchaseCost>
|
||||
getStockSaleGain() <tixapi/getStockSaleGain>
|
||||
buyStock() <tixapi/buyStock>
|
||||
sellStock() <tixapi/sellStock>
|
||||
shortStock() <tixapi/shortStock>
|
||||
|
||||
@@ -41,6 +41,9 @@ In :ref:`netscriptjs`::
|
||||
setToUniversityCourse() <sleeveapi/setToUniversityCourse>
|
||||
setToGymWorkout() <sleeveapi/setToGymWorkout>
|
||||
travel() <sleeveapi/travel>
|
||||
getSleeveAugmentations() <sleeveapi/getSleeveAugmentations>
|
||||
getSleevePurchasableAugs() <sleeveapi/getSleevePurchasableAugs>
|
||||
purchaseSleeveAug() <sleeveapi/purchaseSleeveAug>
|
||||
|
||||
.. _netscript_sleeveapi_referencingaduplicatesleeve:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ workForFaction() Netscript Function
|
||||
|
||||
:param string factionName: Name of faction to work for. CASE-SENSITIVE
|
||||
:param string workType:
|
||||
Type of work to perform for the faction
|
||||
Type of work to perform for the faction:
|
||||
|
||||
* hacking/hacking contracts/hackingcontracts
|
||||
* field/fieldwork/field work
|
||||
|
||||
@@ -61,5 +61,5 @@ getInformation() Netscript Function
|
||||
workChaExpGain: charisma exp gained from work,
|
||||
workMoneyGain: money gained from work,
|
||||
},
|
||||
workRepGain: sl.getRepGain(),
|
||||
workRepGain: Reputation gain rate when working for factions or companies
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
getSleeveAugmentations() Netscript Function
|
||||
===========================================
|
||||
|
||||
.. js:function:: getSleeveAugmentations(sleeveNumber)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to retrieve augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
|
||||
Return a list of augmentation names that this sleeve has installed.
|
||||
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
getSleevePurchasableAugs() Netscript Function
|
||||
=============================================
|
||||
|
||||
.. js:function:: getSleevePurchasableAugs(sleeveNumber)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to retrieve purchasable augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
|
||||
Return a list of augmentations that the player can buy for this sleeve.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
[
|
||||
{
|
||||
name: string, // augmentation name
|
||||
cost: number, // augmentation cost
|
||||
}
|
||||
]
|
||||
@@ -10,8 +10,8 @@ getSleeveStats() Netscript Function
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
shock: current shock of the sleeve [0-1],
|
||||
sync: current sync of the sleeve [0-1],
|
||||
shock: current shock of the sleeve [0-100],
|
||||
sync: current sync of the sleeve [0-100],
|
||||
hacking_skill: current hacking skill of the sleeve,
|
||||
strength: current strength of the sleeve,
|
||||
defense: current defense of the sleeve,
|
||||
|
||||
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
purchaseSleeveAug() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: purchaseSleeveAug(sleeveNumber, augName)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to buy an aug for. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
:param string augName: Name of the aug to buy. Must be an exact match
|
||||
|
||||
Return true if the aug was purchased and installed on the sleeve.
|
||||
@@ -6,7 +6,11 @@ getOrders() Netscript Function
|
||||
:RAM cost: 2.5 GB
|
||||
|
||||
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::
|
||||
|
||||
|
||||
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
|
||||
:RAM cost: 2 GB
|
||||
|
||||
Returns the price of a stock, given its symbol (NOT the company name). The symbol is a sequence
|
||||
of two to four capital letters.
|
||||
Given a stock's symbol, returns the 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).
|
||||
|
||||
.. 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::
|
||||
|
||||
getStockPrice("FISG");
|
||||
getStockPrice("FSIG");
|
||||
|
||||
15
doc/source/netscript/tixapi/getStockPurchaseCost.rst
Normal file
15
doc/source/netscript/tixapi/getStockPurchaseCost.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
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>`,
|
||||
:ref:`large transactions influencing the price of the stock <gameplay_stock_market_spread_price_movement>`
|
||||
and commission fees.
|
||||
15
doc/source/netscript/tixapi/getStockSaleGain.rst
Normal file
15
doc/source/netscript/tixapi/getStockSaleGain.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
getStockSaleGain() Netscript Function
|
||||
=====================================
|
||||
|
||||
.. js:function:: getStockSaleGain(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 you would gain from selling a given number of
|
||||
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`,
|
||||
:ref:`large transactions influencing the price of the stock <gameplay_stock_market_spread_price_movement>`
|
||||
and commission fees.
|
||||
@@ -19,8 +19,10 @@ placeOrder() Netscript Function
|
||||
NOT case-sensitive.
|
||||
: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>`_.
|
||||
|
||||
The ability to place limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
|
||||
Places an order on the stock market. This function only works
|
||||
for :ref:`Limit and Stop Orders <gameplay_stock_market_order_types>`.
|
||||
|
||||
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/>`_.
|
||||
447
index.html
447
index.html
@@ -25,7 +25,7 @@
|
||||
ga('create', 'UA-100157497-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</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>
|
||||
<div id="entire-game-container" style="visibility:hidden;">
|
||||
<div id="mainmenu-container">
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<div id="script-editor-filename-wrapper">
|
||||
<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 id="ace-editor"></div>
|
||||
@@ -201,257 +201,9 @@
|
||||
|
||||
<!-- Hacknet Nodes -->
|
||||
<div id="hacknet-nodes-container" class="generic-menupage-container">
|
||||
<h1 id="hacknet-nodes-title"> Hacknet Nodes </h1>
|
||||
<p id="hacknet-nodes-text" class="menu-page-text">
|
||||
The Hacknet is a global, decentralized network of machines. It is used by hackers all around
|
||||
the world to anonymously share computing power and perform distributed cyberattacks without the
|
||||
fear of being traced.
|
||||
<br/><br/>
|
||||
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
|
||||
resources to the Hacknet network. This allows you to take a small percentage of profits
|
||||
from hacks performed on the network. Essentially, you are renting out your Node's computing power.
|
||||
<br/><br/>
|
||||
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
|
||||
in order to increase its computing power and thereby increase the profit you earn from it.
|
||||
</p>
|
||||
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
|
||||
<br/>
|
||||
<div id="hacknet-nodes-money-multipliers-div">
|
||||
<p id="hacknet-nodes-money">
|
||||
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br/>
|
||||
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
|
||||
</p>
|
||||
<span id="hacknet-nodes-multipliers">
|
||||
<a id="hacknet-nodes-1x-multiplier" class="a-link-button-inactive"> x1 </a>
|
||||
<a id="hacknet-nodes-5x-multiplier" class="a-link-button"> x5 </a>
|
||||
<a id="hacknet-nodes-10x-multiplier" class="a-link-button"> x10 </a>
|
||||
<a id="hacknet-nodes-max-multiplier" class="a-link-button"> MAX </a>
|
||||
</span>
|
||||
</div>
|
||||
<ul id="hacknet-nodes-list">
|
||||
</ul>
|
||||
<!-- React Component -->
|
||||
</div>
|
||||
|
||||
<!-- World -->
|
||||
<div id="world-container" class="generic-menupage-container">
|
||||
<h2 id="world-city-name"> </h2>
|
||||
<p id="world-city-desc"> </p>
|
||||
<ul id="aevum-locations-list">
|
||||
<li id="aevum-travelagency-li">
|
||||
<a id="aevum-travelagency" class="a-link-button"> Travel Agency </a>
|
||||
</li>
|
||||
<li id="aevum-hospital-li">
|
||||
<a id="aevum-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="aevum-summituniversity-li">
|
||||
<a id="aevum-summituniversity" class="a-link-button"> Summit University </a>
|
||||
</li>
|
||||
<li id="aevum-ecorp-li">
|
||||
<a id="aevum-ecorp" class="a-link-button"> ECorp </a>
|
||||
</li>
|
||||
<li id="aevum-bachmanandassociates-li">
|
||||
<a id="aevum-bachmanandassociates" class="a-link-button"> Bachman & Associates</a>
|
||||
</li>
|
||||
<li id="aevum-clarkeincorporated-li">
|
||||
<a id="aevum-clarkeincorporated" class="a-link-button"> Clarke Incorporated </a>
|
||||
</li>
|
||||
<li id="aevum-fulcrumtechnologies-li">
|
||||
<a id="aevum-fulcrumtechnologies" class="a-link-button"> Fulcrum Technologies </a>
|
||||
</li>
|
||||
<li id="aevum-aerocorp-li">
|
||||
<a id="aevum-aerocorp" class="a-link-button"> AeroCorp </a>
|
||||
</li>
|
||||
<li id="aevum-galacticcybersystems-li">
|
||||
<a id="aevum-galacticcybersystems" class="a-link-button"> Galactic Cybersystems </a>
|
||||
</li>
|
||||
<li id="aevum-watchdogsecurity-li">
|
||||
<a id="aevum-watchdogsecurity" class="a-link-button">Watchdog Security </a>
|
||||
</li>
|
||||
<li id="aevum-rhoconstruction-li">
|
||||
<a id="aevum-rhoconstruction" class="a-link-button">Rho Construction </a>
|
||||
</li>
|
||||
<li id="aevum-aevumpolice-li">
|
||||
<a id="aevum-aevumpolice" class="a-link-button">Aevum Police</a>
|
||||
</li>
|
||||
<li id="aevum-netlinktechnologies-li">
|
||||
<a id="aevum-netlinktechnologies" class="a-link-button">NetLink Technologies</a>
|
||||
</li>
|
||||
<li id="aevum-crushfitnessgym-li">
|
||||
<a id="aevum-crushfitnessgym" class="a-link-button">Crush Fitness Gym </a>
|
||||
</li>
|
||||
<li id="aevum-snapfitnessgym-li">
|
||||
<a id="aevum-snapfitnessgym" class="a-link-button">Snap Fitness Gym</a>
|
||||
</li>
|
||||
<li id="aevum-slums-li">
|
||||
<a id="aevum-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="chongqing-locations-list">
|
||||
<li id="chongqing-travelagency-li">
|
||||
<a id="chongqing-travelagency" class="a-link-button"> Travel Agency </a>
|
||||
</li>
|
||||
<li id="chongqing-hospital-li">
|
||||
<a id="chongqing-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="chonqging-kuaigonginternational-li">
|
||||
<a id="chongqing-kuaigonginternational" class="a-link-button">KuaiGong International </a>
|
||||
</li>
|
||||
<li id="chongqing-solarisspacesystems-li">
|
||||
<a id="chongqing-solarisspacesystems" class="a-link-button">Solaris Space Systems</a>
|
||||
</li>
|
||||
<li id="chongqing-slums-li">
|
||||
<a id="chongqing-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="sector12-locations-list">
|
||||
<li id="sector12-travelagency-li">
|
||||
<a id="sector12-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="sector12-hospital-li">
|
||||
<a id="sector12-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="sector12-rothmanuniversity-li">
|
||||
<a id="sector12-rothmanuniversity" class="a-link-button"> Rothman University</a>
|
||||
</li>
|
||||
<li id="sector12-megacorp-li">
|
||||
<a id="sector12-megacorp" class="a-link-button">MegaCorp</a>
|
||||
</li>
|
||||
<li id="sector12-bladeindustries-li">
|
||||
<a id="sector12-bladeindustries" class="a-link-button"> Blade Industries</a>
|
||||
</li>
|
||||
<li id="sector12-foursigma-li">
|
||||
<a id="sector12-foursigma" class="a-link-button">Four Sigma</a>
|
||||
</li>
|
||||
<li id="sector12-icarusmicrosystems-li">
|
||||
<a id="sector12-icarusmicrosystems" class="a-link-button"> Icarus Microsystems</a>
|
||||
</li>
|
||||
<li id="sector12-universalenergy-li">
|
||||
<a id="sector12-universalenergy" class="a-link-button">Universal Energy </a>
|
||||
</li>
|
||||
<li id="sector12-deltaone-li">
|
||||
<a id="sector12-deltaone" class="a-link-button">DeltaOne </a>
|
||||
</li>
|
||||
<li id="sector12-cia-li">
|
||||
<a id="sector12-cia" class="a-link-button">Central Intelligence Agency </a>
|
||||
</li>
|
||||
<li id="sector12-nsa-li">
|
||||
<a id="sector12-nsa" class="a-link-button">National Security Agency </a>
|
||||
</li>
|
||||
<li id="sector12-alphaenterprises-li">
|
||||
<a id="sector12-alphaenterprises" class="a-link-button">Alpha Enterprises</a>
|
||||
</li>
|
||||
<li id="sector12-carmichaelsecurity-li">
|
||||
<a id="sector12-carmichaelsecurity" class="a-link-button"> Carmichael Security</a>
|
||||
</li>
|
||||
<li id="sector12-foodnstuff-li">
|
||||
<a id="sector12-foodnstuff" class="a-link-button">FoodNStuff</a>
|
||||
</li>
|
||||
<li id="sector12-joesguns-li">
|
||||
<a id="sector12-joesguns" class="a-link-button"> Joe's Guns</a>
|
||||
</li>
|
||||
<li id="sector12-irongym-li">
|
||||
<a id="sector12-irongym" class="a-link-button">Iron Gym </a>
|
||||
</li>
|
||||
<li id="sector12-powerhousegym-li">
|
||||
<a id="sector12-powerhousegym" class="a-link-button">Powerhouse Gym</a>
|
||||
</li>
|
||||
<li id="sector12-slums-li">
|
||||
<a id="sector12-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
<li id="sector12-cityhall-li">
|
||||
<a id="sector12-cityhall" class="a-link-button">City Hall</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="newtokyo-locations-list">
|
||||
<li id="newtokyo-travelagency-li">
|
||||
<a id="newtokyo-travelagency" class="a-link-button"> Travel Agency</a>
|
||||
</li>
|
||||
<li id="newtokyo-hospital-li">
|
||||
<a id="newtokyo-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="newtokyo-defcomm-li">
|
||||
<a id="newtokyo-defcomm" class="a-link-button"> DefComm</a>
|
||||
</li>
|
||||
<li id="newtokyo-vitalife-li">
|
||||
<a id="newtokyo-vitalife" class="a-link-button">VitaLife </a>
|
||||
</li>
|
||||
<li id="newtokyo-globalpharmaceuticals-li">
|
||||
<a id="newtokyo-globalpharmaceuticals" class="a-link-button">Global Pharmaceuticals</a>
|
||||
</li>
|
||||
<li id="newtokyo-noodlebar-li">
|
||||
<a id="newtokyo-noodlebar" class="a-link-button">Noodle Bar </a>
|
||||
</li>
|
||||
<li id="newtokyo-slums-li">
|
||||
<a id="newtokyo-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="ishima-locations-list">
|
||||
<li id="ishima-travelagency-li">
|
||||
<a id="ishima-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="ishima-hospital-li">
|
||||
<a id="ishima-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="ishima-stormtechnologies-li">
|
||||
<a id="ishima-stormtechnologies" class="a-link-button">Storm Technologies</a>
|
||||
</li>
|
||||
<li id="ishima-novamedical-li">
|
||||
<a id="ishima-novamedical" class="a-link-button">Nova Medical</a>
|
||||
</li>
|
||||
<li id="ishima-omegasoftware-li">
|
||||
<a id="ishima-omegasoftware" class="a-link-button">Omega Software </a>
|
||||
</li>
|
||||
<li id="ishima-slums-li">
|
||||
<a id="ishima-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="volhaven-locations-list">
|
||||
<li id="volhaven-travelagency-li">
|
||||
<a id="volhaven-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="volhaven-hospital-li">
|
||||
<a id="volhaven-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="volhaven-zbinstituteoftechnology-li">
|
||||
<a id="volhaven-zbinstituteoftechnology" class="a-link-button">ZB Insitute of Technology</a>
|
||||
</li>
|
||||
<li id="volhaven-omnitekincorporated-li">
|
||||
<a id="volhaven-omnitekincorporated" class="a-link-button">OmniTek Incorporated </a>
|
||||
</li>
|
||||
<li id="volhaven-nwo-li">
|
||||
<a id="volhaven-nwo" class="a-link-button">NWO</a>
|
||||
</li>
|
||||
<li id="volhaven-helislabs-li">
|
||||
<a id="volhaven-helioslabs" class="a-link-button">Helios Labs</a>
|
||||
</li>
|
||||
<li id="volhaven-omniacybersystems-li">
|
||||
<a id="volhaven-omniacybersystems" class="a-link-button">Omnia Cybersystems</a>
|
||||
</li>
|
||||
<li id="volhaven-lexocorp-li">
|
||||
<a id="volhaven-lexocorp" class="a-link-button">LexoCorp</a>
|
||||
</li>
|
||||
<li id="volhaven-syscoresecurities-li">
|
||||
<a id="volhaven-syscoresecurities" class="a-link-button">SysCore Securities</a>
|
||||
</li>
|
||||
<li id="volhaven-computek-li">
|
||||
<a id="volhaven-computek" class="a-link-button">CompuTek</a>
|
||||
</li>
|
||||
<li id="volhaven-milleniumfitnessgym-li">
|
||||
<a id="volhaven-milleniumfitnessgym" class="a-link-button">Millenium Fitness Gym</a>
|
||||
</li>
|
||||
<li id="volhaven-slums-li">
|
||||
<a id="volhaven-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="generic-locations-list"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Create a program(executable) -->
|
||||
<div id="create-program-container" class="generic-menupage-container">
|
||||
<p id="create-program-page-text">
|
||||
@@ -469,8 +221,6 @@
|
||||
<!-- Single Faction info (when you select a faction from the Factions menu) -->
|
||||
<div id="faction-container" class="generic-menupage-container"></div>
|
||||
|
||||
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
|
||||
|
||||
<!-- Augmentations -->
|
||||
<div id="augmentations-container" class="generic-menupage-container"></div>
|
||||
|
||||
@@ -478,150 +228,31 @@
|
||||
<div id="tutorial-container" class="generic-menupage-container">
|
||||
<h1> Tutorial (AKA Links to Documentation) </h1>
|
||||
<a id="tutorial-getting-started-link" class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html">
|
||||
Getting Started
|
||||
</a><br><br>
|
||||
Getting Started</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html">
|
||||
Servers & Networking
|
||||
</a><br><br>
|
||||
Servers & Networking</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html">
|
||||
Hacking
|
||||
</a><br><br>
|
||||
Hacking</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html">
|
||||
Scripts
|
||||
</a><br><br>
|
||||
Scripts</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
|
||||
Netscript Programming Language
|
||||
</a><br><br>
|
||||
Netscript Programming Language</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html">
|
||||
Traveling
|
||||
</a><br><br>
|
||||
Traveling</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html">
|
||||
Companies
|
||||
</a><br><br>
|
||||
Companies</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html">
|
||||
Infiltration
|
||||
</a><br><br>
|
||||
Infiltration</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html">
|
||||
Factions
|
||||
</a><br><br>
|
||||
Factions</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html">
|
||||
Augmentations
|
||||
</a><br><br>
|
||||
Augmentations</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
|
||||
Keyboard Shortcuts
|
||||
</a>
|
||||
Keyboard Shortcuts</a>
|
||||
</div>
|
||||
|
||||
<!-- Location (visiting a location in World) -->
|
||||
<div id="location-container" class="generic-menupage-container">
|
||||
<a id="location-return-to-world-button" class="a-link-button"> Return to World </a>
|
||||
<h1 id="location-name"></h1>
|
||||
<p id="location-info"> </p>
|
||||
|
||||
<p id="location-job-title"> </p>
|
||||
<p id="location-text-divider-1"> --------------- </p>
|
||||
<p id="location-job-reputation" class="tooltip"> </p>
|
||||
<p id="location-text-divider-2"> --------------- </p>
|
||||
<p id="location-company-favor" class="tooltip"> </p>
|
||||
<p id="location-text-divider-3"> --------------- </p>
|
||||
|
||||
<!-- Jobs/Work at a company -->
|
||||
<a id="location-software-job" class="a-link-button tooltip"> Apply for Software Job</a>
|
||||
<a id="location-software-consultant-job" class="a-link-button tooltip"> Apply for Software Consultant Job</a>
|
||||
<a id="location-it-job" class="a-link-button tooltip"> Apply for IT Job </a>
|
||||
<a id="location-security-engineer-job" class="a-link-button tooltip"> Apply for Security Engineer Job</a>
|
||||
<a id="location-network-engineer-job" class="a-link-button tooltip"> Apply for Network Engineer Job</a>
|
||||
<a id="location-business-job" class="a-link-button tooltip"> Apply for Business Job</a>
|
||||
<a id="location-business-consultant-job" class="a-link-button tooltip"> Apply for Business Consultant Job </a>
|
||||
<a id="location-security-job" class="a-link-button tooltip"> Apply for Security Job</a>
|
||||
<a id="location-agent-job" class="a-link-button tooltip"> Apply to be an Agent</a>
|
||||
<a id="location-employee-job" class="a-link-button tooltip"> Apply to be an Employee </a>
|
||||
<a id="location-parttime-employee-job" class="a-link-button tooltip"> Apply to be a Part-time Employee </a>
|
||||
<a id="location-waiter-job" class="a-link-button tooltip"> Apply to be a Waiter</a>
|
||||
<a id="location-parttime-waiter-job" class="a-link-button tooltip"> Apply to be a Part-time Waiter</a>
|
||||
|
||||
<a id="location-work" class="a-link-button"> Work </a>
|
||||
|
||||
<!-- Gym -->
|
||||
<a id="location-gym-train-str" class="a-link-button">Train Strength</a>
|
||||
<a id="location-gym-train-def" class="a-link-button">Train Defense </a>
|
||||
<a id="location-gym-train-dex" class="a-link-button">Train Dexterity</a>
|
||||
<a id="location-gym-train-agi" class="a-link-button">Train Agility</a>
|
||||
|
||||
<!-- Study/Take classes at a university -->
|
||||
<a id="location-study-computer-science" class="a-link-button">Study Computer Science (free)</a>
|
||||
<a id="location-data-structures-class" class="a-link-button">Take Data Structures course</a>
|
||||
<a id="location-networks-class" class="a-link-button">Take Networks course</a>
|
||||
<a id="location-algorithms-class" class="a-link-button">Take Algorithms course</a>
|
||||
<a id="location-management-class" class="a-link-button">Take Management course</a>
|
||||
<a id="location-leadership-class" class="a-link-button">Take Leadership course</a>
|
||||
|
||||
<!-- Purchase servers -->
|
||||
<a id="location-purchase-2gb" class="a-link-button"> Purchase 2GB Server - $150,000</a>
|
||||
<a id="location-purchase-4gb" class="a-link-button"> Purchase 4GB Server - $300,000</a>
|
||||
<a id="location-purchase-8gb" class="a-link-button"> Purchase 8GB Server - $600,000</a>
|
||||
<a id="location-purchase-16gb" class="a-link-button"> Purchase 16GB Server - $1,200,000</a>
|
||||
<a id="location-purchase-32gb" class="a-link-button"> Purchase 32GB Server - $2,400,000</a>
|
||||
<a id="location-purchase-64gb" class="a-link-button"> Purchase 64GB Server - $4,800,000</a>
|
||||
<a id="location-purchase-128gb" class="a-link-button"> Purchase 128GB Server - $9,600,000</a>
|
||||
<a id="location-purchase-256gb" class="a-link-button"> Purchase 256GB Server - $19,200,000</a>
|
||||
<a id="location-purchase-512gb" class="a-link-button"> Purchase 512GB Server - $38,400,000</a>
|
||||
<a id="location-purchase-1tb" class="a-link-button"> Purchase 1TB Server - $75,000,000</a>
|
||||
<a id="location-purchase-tor" class="a-link-button"> Purchase TOR Router - $100,000</a>
|
||||
<a id="location-purchase-home-ram" class="a-link-button"> Purchase additional RAM for Home computer </a>
|
||||
<a id="location-purchase-home-cores" class="a-link-button"> Purchase additional Core for Home computer </a>
|
||||
|
||||
<!-- Infiltrate -->
|
||||
<a id="location-infiltrate" class="a-link-button tooltip"> Infiltrate Company
|
||||
<span class="tooltiptext">
|
||||
Infiltrate this company's facility to try and steal their classified secrets!
|
||||
Warning: You may end up hospitalized if you are unsuccessful!
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Hospital -->
|
||||
<a id="location-hospital-treatment" class="a-link-button"> Get Treatment for Wounds </a>
|
||||
|
||||
<!-- Travel agency -->
|
||||
<p id="location-travel-agency-text">
|
||||
From here, you can travel to any other city! A ticket costs $200,000.
|
||||
</p>
|
||||
<a id="location-travel-to-aevum" class="a-link-button"> Travel to Aevum </a>
|
||||
<a id="location-travel-to-chongqing" class="a-link-button"> Travel to Chongqing</a>
|
||||
<a id="location-travel-to-sector12" class="a-link-button"> Travel to Sector-12</a>
|
||||
<a id="location-travel-to-newtokyo" class="a-link-button"> Travel to New Tokyo</a>
|
||||
<a id="location-travel-to-ishima" class="a-link-button"> Travel to Ishima</a>
|
||||
<a id="location-travel-to-volhaven" class="a-link-button"> Travel to Volhaven</a>
|
||||
|
||||
<!-- Slums -->
|
||||
<p id="location-slums-description">
|
||||
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
|
||||
other shadowy entities. The city's government and police have neglected this area for years...
|
||||
<br/><br/><br/>
|
||||
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
|
||||
successful. Your chance at successfully committing a crime is determined by your stats.
|
||||
</p>
|
||||
<a class="a-link-button tooltip" id="location-slums-shoplift"> Shoplift </a>
|
||||
<a id="location-slums-rob-store" class="a-link-button tooltip"> Rob a store </a>
|
||||
<a id="location-slums-mug" class="a-link-button tooltip"> Mug someone </a>
|
||||
<a id="location-slums-larceny" class="a-link-button tooltip"> Commit Larceny </a>
|
||||
<a id="location-slums-deal-drugs" class="a-link-button tooltip"> Deal Drugs </a>
|
||||
<a id="location-slums-bond-forgery" class="a-link-button tooltip">Bond Forgery</a>
|
||||
<a id="location-slums-traffic-arms" class="a-link-button tooltip">Traffick Illegal Arms</a>
|
||||
<a id="location-slums-homicide" class="a-link-button tooltip">Homicide</a>
|
||||
<a id="location-slums-gta" class="a-link-button tooltip"> Grand Theft Auto </a>
|
||||
<a id="location-slums-kidnap" class="a-link-button tooltip"> Kidnap and Ransom </a>
|
||||
<a id="location-slums-assassinate" class="a-link-button tooltip"> Assassinate </a>
|
||||
<a id="location-slums-heist" class="a-link-button tooltip"> Heist </a>
|
||||
|
||||
<!-- City Hall -->
|
||||
<a id="location-cityhall-create-corporation" class="a-link-button">Create a Corporation</a>
|
||||
|
||||
<!-- Bladeburner @ NSA -->
|
||||
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
|
||||
|
||||
<!-- Re-sleeving @ VitaLife -->
|
||||
<a id="location-vitalife-resleeve" class="a-link-button">Re-Sleeve</a>
|
||||
</div>
|
||||
|
||||
<div id="infiltration-container" class="generic-menupage-container">
|
||||
@@ -646,53 +277,7 @@
|
||||
</div>
|
||||
|
||||
<div id="stock-market-container" class="generic-menupage-container">
|
||||
<p>
|
||||
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>
|
||||
<!-- React Component -->
|
||||
</div>
|
||||
|
||||
<!-- Log Box -->
|
||||
@@ -1003,7 +588,7 @@
|
||||
<p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p>
|
||||
</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 -->
|
||||
<script src="src/ThirdParty/raphael.min.js"></script>
|
||||
|
||||
2320
package-lock.json
generated
2320
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -9,8 +9,9 @@
|
||||
"@types/numeral": "0.0.25",
|
||||
"@types/react": "^16.8.6",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"acorn": "^5.0.0",
|
||||
"acorn": "^5.7.3",
|
||||
"acorn-dynamic-import": "^2.0.0",
|
||||
"acorn-walk": "^6.1.1",
|
||||
"ajv": "^5.1.5",
|
||||
"ajv-keywords": "^2.0.0",
|
||||
"async": "^2.6.1",
|
||||
@@ -48,8 +49,7 @@
|
||||
"beautify-lint": "^1.0.3",
|
||||
"benchmark": "^2.1.1",
|
||||
"bundle-loader": "~0.5.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"es6-promise-polyfill": "^1.1.1",
|
||||
"eslint": "^4.19.1",
|
||||
@@ -59,15 +59,18 @@
|
||||
"i18n-webpack-plugin": "^1.0.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"js-beautify": "^1.5.10",
|
||||
"jsdom": "^15.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"json5": "^1.0.1",
|
||||
"less": "^3.0.4",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^4.1.0",
|
||||
"lodash": "^4.17.10",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-lcov-reporter": "^1.0.0",
|
||||
"mocha": "^6.1.4",
|
||||
"mochapack": "^1.1.1",
|
||||
"node-sass": "^4.10.0",
|
||||
"null-loader": "^1.0.0",
|
||||
"raw-loader": "~0.5.0",
|
||||
"sass-loader": "^7.0.3",
|
||||
"script-loader": "~0.7.0",
|
||||
@@ -79,7 +82,7 @@
|
||||
"stylelint": "^9.2.1",
|
||||
"stylelint-declaration-use-variable": "^1.6.1",
|
||||
"stylelint-order": "^0.8.1",
|
||||
"ts-loader": "^4.4.1",
|
||||
"ts-loader": "^4.5.0",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.2",
|
||||
"uglify-es": "^3.3.9",
|
||||
@@ -89,7 +92,7 @@
|
||||
"webpack": "^4.12.0",
|
||||
"webpack-cli": "^3.0.4",
|
||||
"webpack-dev-middleware": "^3.1.3",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -106,13 +109,15 @@
|
||||
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
||||
"build": "webpack --mode production",
|
||||
"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:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js",
|
||||
"lint:style": "stylelint ./css/*",
|
||||
"lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts",
|
||||
"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:dev": "webpack --watch --mode development"
|
||||
},
|
||||
"version": "0.45.0"
|
||||
"version": "0.47.0"
|
||||
}
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
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";
|
||||
// TODO - Convert this to React
|
||||
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
|
||||
import { Player } from "./Player";
|
||||
import { getServer } from "./Server/ServerHelpers";
|
||||
|
||||
/* {
|
||||
* serverName: {
|
||||
* header: Server Header Element
|
||||
* panel: Server Panel List (ul) element
|
||||
* scripts: {
|
||||
* script id: Ref to Script information
|
||||
* }
|
||||
* }
|
||||
* ...
|
||||
import { Page, routing } from "./ui/navigationTracking";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { logBoxCreate } from "../utils/LogBox";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { getElementById } from "../utils/uiHelpers/getElementById";
|
||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../utils/uiHelpers/removeElement";
|
||||
|
||||
|
||||
/**
|
||||
* {
|
||||
* serverName: {
|
||||
* header: Server Header Element
|
||||
* panel: Server Panel List (ul) element
|
||||
* scripts: {
|
||||
* script id: Ref to Script information
|
||||
* }
|
||||
* }
|
||||
* ...
|
||||
*/
|
||||
let ActiveScriptsUI = {};
|
||||
let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements
|
||||
const ActiveScriptsUI = {};
|
||||
const ActiveScriptsTasks = []; // Sequentially schedule the creation/deletion of UI elements
|
||||
|
||||
const getHeaderHtml = (server) => {
|
||||
// TODO: calculate the longest hostname length rather than hard coding it
|
||||
@@ -48,7 +51,7 @@ const updateHeaderHtml = (server) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert it to a string, as that's how it's stored it will come out of the data attributes
|
||||
// Convert it to a string, as that's how it's stored it will come out of the data attributes
|
||||
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
|
||||
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
||||
accordion.header.dataset.ramPercentage = ramPercentage;
|
||||
@@ -81,16 +84,18 @@ function createActiveScriptsServerPanel(server) {
|
||||
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
|
||||
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
|
||||
/**
|
||||
* 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) {
|
||||
@@ -98,9 +103,9 @@ function deleteActiveScriptsServerPanel(server) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Make sure it's empty
|
||||
// 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");
|
||||
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,7 +117,7 @@ function deleteActiveScriptsServerPanel(server) {
|
||||
function addActiveScriptsItem(workerscript) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.log("ERROR: Invalid server IP for workerscript in addActiveScriptsItem()");
|
||||
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
@@ -122,7 +127,7 @@ function addActiveScriptsItem(workerscript) {
|
||||
createActiveScriptsServerPanel(server);
|
||||
}
|
||||
|
||||
//Create the unique identifier (key) for this script
|
||||
// Create the unique identifier (key) for this script
|
||||
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
||||
itemNameArray.push(String(workerscript.args[i]));
|
||||
@@ -139,8 +144,10 @@ function addActiveScriptsItem(workerscript) {
|
||||
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
|
||||
/**
|
||||
* 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)
|
||||
@@ -173,7 +180,7 @@ function addActiveScriptsItem(workerscript) {
|
||||
}
|
||||
}));
|
||||
|
||||
//Append element to list
|
||||
// Append element to list
|
||||
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
||||
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
||||
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
||||
@@ -218,11 +225,13 @@ function deleteActiveScriptsItem(workerscript) {
|
||||
}.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);
|
||||
/**
|
||||
* Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
|
||||
* We'll limit this to 150 at a time for performance (in case someone decides to start a
|
||||
* bunch of scripts all at once...)
|
||||
*/
|
||||
const numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
||||
for (let i = 0; i < numTasks; ++i) {
|
||||
let task = ActiveScriptsTasks.shift();
|
||||
try {
|
||||
@@ -233,8 +242,7 @@ function updateActiveScriptsItems(maxTasks=150) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!routing.isOn(Page.ActiveScripts)) {return;}
|
||||
var total = 0;
|
||||
let total = 0;
|
||||
for (var i = 0; i < workerScripts.length; ++i) {
|
||||
try {
|
||||
total += updateActiveScriptsItemContent(workerScripts[i]);
|
||||
@@ -243,13 +251,14 @@ function updateActiveScriptsItems(maxTasks=150) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!routing.isOn(Page.ActiveScripts)) { return total; }
|
||||
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
|
||||
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
|
||||
getElementById("active-scripts-total-prod-aug-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) {
|
||||
@@ -258,7 +267,7 @@ function updateActiveScriptsItemContent(workerscript) {
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
if (ActiveScriptsUI[hostname] == null) {
|
||||
return; //Hasn't been created yet. We'll skip it
|
||||
return; // Hasn't been created yet. We'll skip it
|
||||
}
|
||||
|
||||
updateHeaderHtml(server);
|
||||
@@ -270,11 +279,11 @@ function updateActiveScriptsItemContent(workerscript) {
|
||||
var itemName = itemNameArray.join("-");
|
||||
|
||||
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
||||
return; //Hasn't been fully added yet. We'll skip it
|
||||
return; // Hasn't been fully added yet. We'll skip it
|
||||
}
|
||||
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
||||
|
||||
//Update the text if necessary. This fn returns the online $/s production
|
||||
// Update the text if necessary. This fn returns the online $/s production
|
||||
return updateActiveScriptsText(workerscript, item, itemName);
|
||||
}
|
||||
|
||||
@@ -293,7 +302,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
updateHeaderHtml(server);
|
||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
||||
|
||||
//Only update if the item is visible
|
||||
// Only update if the item is visible
|
||||
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
||||
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
||||
|
||||
@@ -302,7 +311,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
||||
|
||||
//Online
|
||||
// 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, " ");
|
||||
|
||||
@@ -310,7 +319,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
||||
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||
|
||||
//Offline
|
||||
// Offline
|
||||
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
|
||||
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ");
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import { IMap } from "./types";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
|
||||
let Aliases = {};
|
||||
let GlobalAliases = {};
|
||||
export let Aliases: IMap<string> = {};
|
||||
export let GlobalAliases: IMap<string> = {};
|
||||
|
||||
function loadAliases(saveString) {
|
||||
export function loadAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
Aliases = {};
|
||||
} else {
|
||||
@@ -11,7 +12,7 @@ function loadAliases(saveString) {
|
||||
}
|
||||
}
|
||||
|
||||
function loadGlobalAliases(saveString) {
|
||||
export function loadGlobalAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
GlobalAliases = {};
|
||||
} else {
|
||||
@@ -19,8 +20,8 @@ function loadGlobalAliases(saveString) {
|
||||
}
|
||||
}
|
||||
|
||||
//Print all aliases to terminal
|
||||
function printAliases() {
|
||||
// Prints all aliases to terminal
|
||||
export function printAliases(): void {
|
||||
for (var name in Aliases) {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
post("alias " + name + "=" + Aliases[name]);
|
||||
@@ -33,8 +34,8 @@ function printAliases() {
|
||||
}
|
||||
}
|
||||
|
||||
//True if successful, false otherwise
|
||||
function parseAliasDeclaration(dec,global=false) {
|
||||
// Returns true if successful, false otherwise
|
||||
export function parseAliasDeclaration(dec: string, global: boolean=false) {
|
||||
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
||||
var matches = dec.match(re);
|
||||
if (matches == null || matches.length != 3) {return false;}
|
||||
@@ -46,50 +47,55 @@ function parseAliasDeclaration(dec,global=false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function addAlias(name, value) {
|
||||
if (name in GlobalAliases){
|
||||
function addAlias(name: string, value: string): void {
|
||||
if (name in GlobalAliases) {
|
||||
delete GlobalAliases[name];
|
||||
}
|
||||
Aliases[name] = value;
|
||||
}
|
||||
|
||||
function addGlobalAlias(name, value) {
|
||||
function addGlobalAlias(name: string, value: string): void {
|
||||
if (name in Aliases){
|
||||
delete Aliases[name];
|
||||
}
|
||||
GlobalAliases[name] = value;
|
||||
}
|
||||
|
||||
function getAlias(name) {
|
||||
function getAlias(name: string): string | null {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
return Aliases[name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGlobalAlias(name) {
|
||||
function getGlobalAlias(name: string): string | null {
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
return GlobalAliases[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeAlias(name) {
|
||||
export function removeAlias(name: string): boolean {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
delete Aliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
delete GlobalAliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Returns the original string with any aliases substituted in
|
||||
//Aliases only applied to "whole words", one level deep
|
||||
function substituteAliases(origCommand) {
|
||||
var commandArray = origCommand.split(" ");
|
||||
/**
|
||||
* 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 {
|
||||
const commandArray = origCommand.split(" ");
|
||||
if (commandArray.length > 0){
|
||||
// For the unalias command, dont substite
|
||||
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
||||
@@ -112,6 +118,3 @@ function substituteAliases(origCommand) {
|
||||
}
|
||||
return commandArray.join(" ");
|
||||
}
|
||||
|
||||
export {Aliases, GlobalAliases, printAliases, parseAliasDeclaration,
|
||||
removeAlias, substituteAliases, loadAliases, loadGlobalAliases};
|
||||
@@ -10,6 +10,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
|
||||
|
||||
interface IConstructorParams {
|
||||
info: string;
|
||||
isSpecial?: boolean;
|
||||
moneyCost: number;
|
||||
name: string;
|
||||
prereqs?: string[];
|
||||
@@ -62,6 +63,9 @@ export class Augmentation {
|
||||
// Description of what this Aug is and what it does
|
||||
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
|
||||
level: number = 0;
|
||||
|
||||
@@ -90,6 +94,10 @@ export class Augmentation {
|
||||
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
this.startingCost = this.baseCost;
|
||||
|
||||
if (params.isSpecial) {
|
||||
this.isSpecial = true;
|
||||
}
|
||||
|
||||
this.level = 0;
|
||||
|
||||
// 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",
|
||||
ADRPheromone1: "ADR-V1 Pheromone Gene",
|
||||
ADRPheromone2: "ADR-V2 Pheromone Gene",
|
||||
ShadowsSimulacrum: "The Shadow's Simulacrum",
|
||||
HacknetNodeCPUUpload: "Hacknet Node CPU Architecture Neural-Upload",
|
||||
HacknetNodeCacheUpload: "Hacknet Node Cache Architecture Neural-Upload",
|
||||
HacknetNodeNICUpload: "Hacknet Node NIC Architecture Neural-Upload",
|
||||
|
||||
@@ -57,8 +57,9 @@ export function initBitNodes() {
|
||||
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
||||
"You will no longer gain passive reputation with Factions<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File increases the player's crime success rate, " +
|
||||
"crime money, and charisma multipliers by:<br><br>" +
|
||||
"upgrade its level up to a maximum of 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:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
@@ -81,7 +82,8 @@ export function initBitNodes() {
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine", "The Singularity has arrived. The human race is gone, replaced " +
|
||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
|
||||
"The Singularity has arrived. The human race is gone, replaced " +
|
||||
"by artificially superintelligent beings that are more machine than man. <br><br>" +
|
||||
"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>" +
|
||||
@@ -92,7 +94,8 @@ export function initBitNodes() {
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
||||
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
||||
"that you can use.");
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman", "They said it couldn't be done. They said the human brain, " +
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
|
||||
"They said it couldn't be done. They said the human brain, " +
|
||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||
"by 1's and 0's. They were wrong.<br><br>" +
|
||||
@@ -173,7 +176,24 @@ export function initBitNodes() {
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Do Androids Dream?", "COMING SOON");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"You cannnot purchase additional servers<br>" +
|
||||
"Hacking is significantly less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"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 " +
|
||||
@@ -183,7 +203,7 @@ export function initBitNodes() {
|
||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased.<br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||
@@ -198,8 +218,9 @@ export function initBitNodes() {
|
||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking stat and experience gain are halved<br>" +
|
||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||
"The growth rate of servers is halved<br>" +
|
||||
"The growth rate of servers is significantly reduced<br>" +
|
||||
"Weakening a server is twice as effective<br>" +
|
||||
"Company wages are decreased by 50%<br>" +
|
||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||
@@ -210,9 +231,9 @@ export function initBitNodes() {
|
||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%");
|
||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||
"To iterate is human, to recurse divine.<br><br>" +
|
||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||
@@ -245,9 +266,9 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
}
|
||||
|
||||
switch (p.bitNodeN) {
|
||||
case 1: //Source Genesis (every multiplier is 1)
|
||||
case 1: // Source Genesis (every multiplier is 1)
|
||||
break;
|
||||
case 2: //Rise of the Underworld
|
||||
case 2: // Rise of the Underworld
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.8;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.2;
|
||||
@@ -257,7 +278,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
break;
|
||||
case 3: //Corporatocracy
|
||||
case 3: // Corporatocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.RepToDonateToFaction = 0.5;
|
||||
BitNodeMultipliers.AugmentationRepCost = 3;
|
||||
@@ -272,7 +293,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||
break;
|
||||
case 4: //The Singularity
|
||||
case 4: // The Singularity
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
||||
@@ -286,7 +307,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||
break;
|
||||
case 5: //Artificial intelligence
|
||||
case 5: // Artificial intelligence
|
||||
BitNodeMultipliers.ServerMaxMoney = 2;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@@ -299,7 +320,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
break;
|
||||
case 6: //Bladeburner
|
||||
case 6: // Bladeburner
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.4;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@@ -314,7 +335,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.25;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 7: //Bladeburner 2079
|
||||
case 7: // Bladeburner 2079
|
||||
BitNodeMultipliers.BladeburnerRank = 0.6;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 3;
|
||||
@@ -334,7 +355,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 8: //Ghost of Wall Street
|
||||
case 8: // Ghost of Wall Street
|
||||
BitNodeMultipliers.ScriptHackMoney = 0;
|
||||
BitNodeMultipliers.ManualHackMoney = 0;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0;
|
||||
@@ -345,6 +366,27 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 5;
|
||||
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.1;
|
||||
BitNodeMultipliers.HackExpGain = 0.05;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 5;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
||||
@@ -369,9 +411,11 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.5;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.2;
|
||||
BitNodeMultipliers.ServerWeakenRate = 2;
|
||||
BitNodeMultipliers.CrimeMoney = 3;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
||||
@@ -379,8 +423,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||
BitNodeMultipliers.InfiltrationMoney = 2.5;
|
||||
BitNodeMultipliers.InfiltrationRep = 2.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.01;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.1;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.25;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
break;
|
||||
|
||||
@@ -120,7 +120,8 @@ interface IBitNodeMultipliers {
|
||||
HackingLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences how much money each Hacknet node can generate.
|
||||
* Influences how much money is produced by Hacknet Nodes.
|
||||
* Influeces the hash rate of Hacknet Servers (unlocked in BitNode-9)
|
||||
*/
|
||||
HacknetNodeMoney: number;
|
||||
|
||||
|
||||
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 {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
||||
import {isString} from "../utils/helpers/isString";
|
||||
import { Engine } from "./engine";
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
|
||||
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
|
||||
function writeCinematicText(lines) {
|
||||
export let cinematicTextFlag = false;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
if (lines.constructor !== Array) {
|
||||
throw new Error("Invalid non-array argument passed into writeCinematicText()");
|
||||
}
|
||||
|
||||
//We'll reuse the 'Red Pill' content
|
||||
// Reuse the 'Red Pill' content
|
||||
Engine.loadCinematicTextContent();
|
||||
var container = document.getElementById("cinematic-text-container");
|
||||
const container = document.getElementById("cinematic-text-container");
|
||||
container.style.width = "75%";
|
||||
if (container == null) {throw new Error("Could not find cinematic-text-container for writeCinematicText()");}
|
||||
removeChildrenFromElement(container);
|
||||
|
||||
for (var i = 0; i < lines.length; ++i) {
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
if (!isString(lines[i])) {
|
||||
throw new Error("Invalid non-string element in 'lines' argument. writeCinematicText() failed");
|
||||
}
|
||||
@@ -45,11 +51,11 @@ function writeCinematicTextRecurse(lines, lineNumber=0) {
|
||||
|
||||
function writeCinematicTextLine(line) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var container = document.getElementById("cinematic-text-container");
|
||||
var pElem = document.createElement("p");
|
||||
const container = document.getElementById("cinematic-text-container");
|
||||
const pElem = document.createElement("p");
|
||||
container.appendChild(pElem);
|
||||
|
||||
var promise = writeCinematicTextLetter(pElem, line, 0);
|
||||
const promise = writeCinematicTextLetter(pElem, line, 0);
|
||||
promise.then(function(res) {
|
||||
resolve(res);
|
||||
}, function(e) {
|
||||
@@ -61,14 +67,15 @@ function writeCinematicTextLine(line) {
|
||||
function writeCinematicTextLetter(pElem, line, i=0) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
setTimeoutRef(function() {
|
||||
const textToShow = line.substring(0, i);
|
||||
|
||||
if (i >= line.length) {
|
||||
var textToShow = line.substring(0, i);
|
||||
pElem.innerHTML = textToShow;
|
||||
return resolve(true);
|
||||
}
|
||||
var textToShow = line.substring(0, i);
|
||||
|
||||
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) {
|
||||
resolve(res);
|
||||
}, function(e) {
|
||||
@@ -96,5 +103,3 @@ function cinematicTextEnd() {
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
export {cinematicTextFlag, writeCinematicText};
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { CodingContract,
|
||||
CodingContractRewardType,
|
||||
CodingContractTypes } from "./CodingContracts";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { AllServers } from "./Server/AllServers";
|
||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||
import {
|
||||
CodingContract,
|
||||
CodingContractRewardType,
|
||||
CodingContractTypes
|
||||
} from "./CodingContracts";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
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() {
|
||||
// First select a random problem type
|
||||
@@ -127,14 +131,15 @@ function getRandomReward() {
|
||||
});
|
||||
|
||||
switch (reward.type) {
|
||||
case CodingContractRewardType.FactionReputation:
|
||||
case CodingContractRewardType.FactionReputation: {
|
||||
// Get a random faction that player is a part of. That
|
||||
// faction must allow hacking contracts
|
||||
var numFactions = factionsThatAllowHacking.length;
|
||||
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
|
||||
reward.name = randFaction;
|
||||
break;
|
||||
case CodingContractRewardType.CompanyReputation:
|
||||
}
|
||||
case CodingContractRewardType.CompanyReputation: {
|
||||
const allJobs = Object.keys(Player.jobs);
|
||||
if (allJobs.length > 0) {
|
||||
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
|
||||
@@ -142,6 +147,7 @@ function getRandomReward() {
|
||||
reward.type = CodingContractRewardType.Money;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -157,7 +163,9 @@ function getRandomServer() {
|
||||
// An infinite loop shouldn't ever happen, but to be safe we'll use
|
||||
// a for loop with a limited number of tries
|
||||
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);
|
||||
randServer = AllServers[servers[randIndex]];
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import { codingContractTypesMetadata,
|
||||
DescriptionFunc,
|
||||
GeneratorFunc,
|
||||
SolverFunc } from "./data/codingcontracttypes";
|
||||
import {
|
||||
codingContractTypesMetadata,
|
||||
DescriptionFunc,
|
||||
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 { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
|
||||
|
||||
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
||||
|
||||
/* Represents different types of problems that a Coding Contract can have */
|
||||
@@ -198,6 +202,7 @@ export class CodingContract {
|
||||
}
|
||||
},
|
||||
placeholder: "Enter Solution here",
|
||||
width: "50%",
|
||||
}) as HTMLInputElement;
|
||||
solveBtn = createElement("a", {
|
||||
class: "a-link-button",
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { CompanyPosition } from "./CompanyPosition";
|
||||
import * as posNames from "./data/companypositionnames";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export interface IConstructorParams {
|
||||
name: string;
|
||||
info: string;
|
||||
@@ -93,6 +96,43 @@ export class Company {
|
||||
}
|
||||
}
|
||||
|
||||
hasAgentPositions(): boolean {
|
||||
return (this.companyPositions[posNames.AgentCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasBusinessConsultantPositions(): boolean {
|
||||
return (this.companyPositions[posNames.BusinessConsultantCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasBusinessPositions(): boolean {
|
||||
return (this.companyPositions[posNames.BusinessCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasEmployeePositions(): boolean {
|
||||
return (this.companyPositions[posNames.MiscCompanyPositions[1]] != null);
|
||||
}
|
||||
|
||||
hasITPositions(): boolean {
|
||||
return (this.companyPositions[posNames.ITCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasSecurityPositions(): boolean {
|
||||
return (this.companyPositions[posNames.SecurityCompanyPositions[2]] != null);
|
||||
}
|
||||
|
||||
hasSoftwareConsultantPositions(): boolean {
|
||||
return (this.companyPositions[posNames.SoftwareConsultantCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasSoftwarePositions(): boolean {
|
||||
return (this.companyPositions[posNames.SoftwareCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasWaiterPositions(): boolean {
|
||||
return (this.companyPositions[posNames.MiscCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
|
||||
gainFavor(): void {
|
||||
if (this.favor == null) { this.favor = 0; }
|
||||
if (this.rolloverRep == null) { this.rolloverRep = 0; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import * as names from "./data/CompanyPositionNames";
|
||||
import * as names from "./data/companypositionnames";
|
||||
|
||||
/* tslint:disable:completed-docs */
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { CompanyPosition } from "./CompanyPosition";
|
||||
import { CompanyPositions } from "./CompanyPositions";
|
||||
|
||||
export function getNextCompanyPosition(currPos: CompanyPosition | null): CompanyPosition | null {
|
||||
export function getNextCompanyPositionHelper(currPos: CompanyPosition | null): CompanyPosition | null {
|
||||
if (currPos == null) { return null; }
|
||||
|
||||
const nextPosName: string | null = currPos.nextPosition;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IConstructorParams } from "../Company";
|
||||
import { Locations } from "../../Locations";
|
||||
import * as posNames from "./CompanyPositionNames";
|
||||
import { IMap } from "../../types";
|
||||
import * as posNames from "./companypositionnames";
|
||||
import { IConstructorParams } from "../Company";
|
||||
|
||||
import { IMap } from "../../types";
|
||||
import { LocationName } from "../../Locations/data/LocationNames";
|
||||
|
||||
// Create Objects containing Company Positions by category
|
||||
// Will help in metadata construction later
|
||||
@@ -89,7 +90,7 @@ CEOOnly[posNames.BusinessCompanyPositions[5]] = true;
|
||||
// Metadata
|
||||
export const companiesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
name: Locations.AevumECorp,
|
||||
name: LocationName.AevumECorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -101,7 +102,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12MegaCorp,
|
||||
name: LocationName.Sector12MegaCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -113,7 +114,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumBachmanAndAssociates,
|
||||
name: LocationName.AevumBachmanAndAssociates,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -125,7 +126,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12BladeIndustries,
|
||||
name: LocationName.Sector12BladeIndustries,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -137,7 +138,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenNWO,
|
||||
name: LocationName.VolhavenNWO,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -149,7 +150,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumClarkeIncorporated,
|
||||
name: LocationName.AevumClarkeIncorporated,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -161,7 +162,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenOmniTekIncorporated,
|
||||
name: LocationName.VolhavenOmniTekIncorporated,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -173,7 +174,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12FourSigma,
|
||||
name: LocationName.Sector12FourSigma,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -185,7 +186,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.ChongqingKuaiGongInternational,
|
||||
name: LocationName.ChongqingKuaiGongInternational,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -197,7 +198,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumFulcrumTechnologies,
|
||||
name: LocationName.AevumFulcrumTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -208,7 +209,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaStormTechnologies,
|
||||
name: LocationName.IshimaStormTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -220,7 +221,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoDefComm,
|
||||
name: LocationName.NewTokyoDefComm,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -232,7 +233,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenHeliosLabs,
|
||||
name: LocationName.VolhavenHeliosLabs,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -244,7 +245,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoVitaLife,
|
||||
name: LocationName.NewTokyoVitaLife,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -256,7 +257,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12IcarusMicrosystems,
|
||||
name: LocationName.Sector12IcarusMicrosystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -268,7 +269,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12UniversalEnergy,
|
||||
name: LocationName.Sector12UniversalEnergy,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -280,7 +281,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumGalacticCybersystems,
|
||||
name: LocationName.AevumGalacticCybersystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -292,7 +293,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumAeroCorp,
|
||||
name: LocationName.AevumAeroCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -305,7 +306,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenOmniaCybersystems,
|
||||
name: LocationName.VolhavenOmniaCybersystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -318,7 +319,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.ChongqingSolarisSpaceSystems,
|
||||
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -331,7 +332,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12DeltaOne,
|
||||
name: LocationName.Sector12DeltaOne,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -344,7 +345,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoGlobalPharmaceuticals,
|
||||
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -357,7 +358,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaNovaMedical,
|
||||
name: LocationName.IshimaNovaMedical,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -370,7 +371,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12CIA,
|
||||
name: LocationName.Sector12CIA,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -385,7 +386,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 149,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12NSA,
|
||||
name: LocationName.Sector12NSA,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -400,7 +401,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 149,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumWatchdogSecurity,
|
||||
name: LocationName.AevumWatchdogSecurity,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -415,7 +416,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 124,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenLexoCorp,
|
||||
name: LocationName.VolhavenLexoCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -428,7 +429,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumRhoConstruction,
|
||||
name: LocationName.AevumRhoConstruction,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToLeadDeveloper,
|
||||
@@ -439,7 +440,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 49,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12AlphaEnterprises,
|
||||
name: LocationName.Sector12AlphaEnterprises,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToLeadDeveloper,
|
||||
@@ -451,7 +452,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumPolice,
|
||||
name: LocationName.AevumPolice,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllSecurityPositions,
|
||||
@@ -462,7 +463,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenSysCoreSecurities,
|
||||
name: LocationName.VolhavenSysCoreSecurities,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -472,7 +473,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 124,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenCompuTek,
|
||||
name: LocationName.VolhavenCompuTek,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -482,7 +483,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 74,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumNetLinkTechnologies,
|
||||
name: LocationName.AevumNetLinkTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -492,7 +493,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12CarmichaelSecurity,
|
||||
name: LocationName.Sector12CarmichaelSecurity,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -505,7 +506,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 74,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12FoodNStuff,
|
||||
name: LocationName.Sector12FoodNStuff,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
EmployeeOnly, PartTimeEmployeeOnly
|
||||
@@ -515,7 +516,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 0,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12JoesGuns,
|
||||
name: LocationName.Sector12JoesGuns,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
EmployeeOnly, PartTimeEmployeeOnly
|
||||
@@ -525,7 +526,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 0,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaOmegaSoftware,
|
||||
name: LocationName.IshimaOmegaSoftware,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllSoftwarePositions,
|
||||
@@ -537,7 +538,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 49,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoNoodleBar,
|
||||
name: LocationName.NewTokyoNoodleBar,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
WaiterOnly, PartTimeWaiterOnly
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Metadata used for constructing Company Positions
|
||||
import { IConstructorParams } from "../CompanyPosition";
|
||||
import * as posNames from "./CompanyPositionNames";
|
||||
import * as posNames from "./companypositionnames";
|
||||
|
||||
export const companyPositionMetadata: IConstructorParams[] = [
|
||||
{
|
||||
|
||||
249
src/Constants.ts
249
src/Constants.ts
@@ -1,110 +1,46 @@
|
||||
import {IMap} from "./types";
|
||||
/**
|
||||
* Generic Game Constants
|
||||
*
|
||||
* Constants for specific mechanics or features will NOT be here.
|
||||
*/
|
||||
import { IMap } from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.45.1",
|
||||
Version: "0.47.0",
|
||||
|
||||
//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
|
||||
//the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
|
||||
/** 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
|
||||
* the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
|
||||
*/
|
||||
MaxSkillLevel: 975,
|
||||
|
||||
//Milliseconds per game cycle
|
||||
// Milliseconds per game cycle
|
||||
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,
|
||||
|
||||
/* Base costs */
|
||||
// Base RAM costs
|
||||
BaseCostFor1GBOfRamHome: 32000,
|
||||
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
||||
BaseCostFor1GBOfRamHacknetNode: 30000,
|
||||
|
||||
// Cost to travel to another city
|
||||
TravelCost: 200e3,
|
||||
|
||||
BaseCostForHacknetNode: 1000,
|
||||
BaseCostForHacknetNodeCore: 500000,
|
||||
|
||||
/* Hacknet Node constants */
|
||||
HacknetNodeMoneyGainPerLevel: 1.6,
|
||||
HacknetNodePurchaseNextMult: 1.85, //Multiplier when purchasing an additional hacknet node
|
||||
HacknetNodeUpgradeLevelMult: 1.04, //Multiplier for cost when upgrading level
|
||||
HacknetNodeUpgradeRamMult: 1.28, //Multiplier for cost when upgrading RAM
|
||||
HacknetNodeUpgradeCoreMult: 1.48, //Multiplier for cost when buying another core
|
||||
|
||||
HacknetNodeMaxLevel: 200,
|
||||
HacknetNodeMaxRam: 64,
|
||||
HacknetNodeMaxCores: 16,
|
||||
|
||||
/* Faction and Company favor */
|
||||
BaseFavorToDonate: 150,
|
||||
DonateMoneyToRepDivisor: 1e6,
|
||||
// Faction and Company favor-related things
|
||||
BaseFavorToDonate: 150,
|
||||
DonateMoneyToRepDivisor: 1e6,
|
||||
FactionReputationToFavorBase: 500,
|
||||
FactionReputationToFavorMult: 1.02,
|
||||
CompanyReputationToFavorBase: 500,
|
||||
CompanyReputationToFavorMult: 1.02,
|
||||
|
||||
/* Augmentation */
|
||||
//NeuroFlux Governor cost multiplier as you level up
|
||||
// NeuroFlux Governor Augmentation cost multiplier
|
||||
NeuroFluxGovernorLevelMult: 1.14,
|
||||
|
||||
/* Netscript Constants */
|
||||
//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,
|
||||
NumNetscriptPorts: 20,
|
||||
|
||||
ScriptSingularityFn1RamCost: 1,
|
||||
ScriptSingularityFn2RamCost: 2,
|
||||
ScriptSingularityFn3RamCost: 3,
|
||||
|
||||
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
|
||||
|
||||
ScriptGangApiBaseRamCost: 4,
|
||||
|
||||
ScriptBladeburnerApiBaseRamCost: 4,
|
||||
|
||||
NumNetscriptPorts: 20,
|
||||
|
||||
//Server constants
|
||||
// Server-related constants
|
||||
HomeComputerMaxRam: 1073741824, // 2 ^ 30
|
||||
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
|
||||
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
|
||||
@@ -112,48 +48,50 @@ export let CONSTANTS: IMap<any> = {
|
||||
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
|
||||
|
||||
PurchasedServerLimit: 25,
|
||||
PurchasedServerMaxRam: 1048576, //2^20
|
||||
PurchasedServerMaxRam: 1048576, // 2^20
|
||||
|
||||
//Augmentation Constants
|
||||
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
|
||||
MultipleAugMultiplier: 1.9,
|
||||
// Augmentation Constants
|
||||
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
|
||||
MultipleAugMultiplier: 1.9,
|
||||
|
||||
//How much a TOR router costs
|
||||
TorRouterCost: 200000,
|
||||
// TOR Router
|
||||
TorRouterCost: 200e3,
|
||||
|
||||
//Infiltration constants
|
||||
// Infiltration
|
||||
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
|
||||
InfiltrationExpPow: 0.8,
|
||||
|
||||
//Stock market constants
|
||||
WSEAccountCost: 200e6,
|
||||
TIXAPICost: 5e9,
|
||||
MarketData4SCost: 1e9,
|
||||
// Stock market
|
||||
WSEAccountCost: 200e6,
|
||||
TIXAPICost: 5e9,
|
||||
MarketData4SCost: 1e9,
|
||||
MarketDataTixApi4SCost: 25e9,
|
||||
StockMarketCommission: 100e3,
|
||||
StockMarketCommission: 100e3,
|
||||
|
||||
//Hospital/Health
|
||||
// Hospital/Health
|
||||
HospitalCostPerHp: 100e3,
|
||||
|
||||
//Intelligence-related constants
|
||||
IntelligenceCrimeWeight: 0.05, //Weight for how much int affects crime success rates
|
||||
IntelligenceInfiltrationWeight: 0.1, //Weight for how much int affects infiltration success rates
|
||||
// Intelligence-related constants
|
||||
IntelligenceCrimeWeight: 0.05, // Weight for how much int affects crime success rates
|
||||
IntelligenceInfiltrationWeight: 0.1, // Weight for how much int affects infiltration success rates
|
||||
IntelligenceCrimeBaseExpGain: 0.001,
|
||||
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
|
||||
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
|
||||
IntelligenceSingFnBaseExpGain: 0.002,
|
||||
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
|
||||
HackingMissionRepToDiffConversion: 10000, //Faction rep is divided by this to get mission difficulty
|
||||
HackingMissionRepToRewardConversion: 7, //Faction rep divided byt his to get mission rep reward
|
||||
HackingMissionSpamTimeIncrease: 25000, //How much time limit increase is gained when conquering a Spam Node (ms)
|
||||
HackingMissionTransferAttackIncrease: 1.05, //Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
|
||||
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)
|
||||
// Hacking Missions
|
||||
// TODO Move this into Hacking Mission implementation
|
||||
HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty
|
||||
HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward
|
||||
HackingMissionSpamTimeIncrease: 25000, // How much time limit increase is gained when conquering a Spam Node (ms)
|
||||
HackingMissionTransferAttackIncrease: 1.05, // Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
|
||||
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>" +
|
||||
"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 " +
|
||||
@@ -201,7 +139,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
|
||||
"-Nodes slowly regenerate health over time.",
|
||||
|
||||
/* Time Constants */
|
||||
// Time-related constants
|
||||
MillisecondsPer20Hours: 72000000,
|
||||
GameCyclesPer20Hours: 72000000 / 200,
|
||||
|
||||
@@ -229,7 +167,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
MillisecondsPerFiveMinutes: 300000,
|
||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||
|
||||
/* Player Work / Action related Constants */
|
||||
// Player Work & Action
|
||||
FactionWorkHacking: "Faction Hacking Work",
|
||||
FactionWorkField: "Faction Field Work",
|
||||
FactionWorkSecurity: "Faction Security Work",
|
||||
@@ -272,47 +210,58 @@ export let CONSTANTS: IMap<any> = {
|
||||
CrimeAssassination: "assassinate a high-profile target",
|
||||
CrimeHeist: "pull off the ultimate heist",
|
||||
|
||||
/* Coding Contract Constants */
|
||||
CodingContractBaseFactionRepGain: 2500,
|
||||
CodingContractBaseCompanyRepGain: 4000,
|
||||
CodingContractBaseMoneyGain: 75e6,
|
||||
// Coding Contract
|
||||
// TODO Move this into Coding contract impelmentation?
|
||||
CodingContractBaseFactionRepGain: 2500,
|
||||
CodingContractBaseCompanyRepGain: 4000,
|
||||
CodingContractBaseMoneyGain: 75e6,
|
||||
|
||||
// BitNode/Source-File related stuff
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.45.1
|
||||
* Added two new Corporation Researches
|
||||
* General UI improvements (by hydroflame and koriar)
|
||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
||||
v0.47.0
|
||||
* 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
|
||||
|
||||
v0.45.0
|
||||
* Corporation changes:
|
||||
** Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
||||
** This means that each Corporation 'state' will now only take 2 seconds, rather than 3
|
||||
** Increased initial salaries for newly-hired employees
|
||||
** Increased the cost multiplier for upgrading office size (the cost will increase faster)
|
||||
** The stats of your employees now has a slightly larger effect on production & sales
|
||||
** Added several new Research upgrades
|
||||
** Market-TA research now allows you to automatically set sale price at optimal values
|
||||
** Market-TA research now works for Products (not just Materials)
|
||||
** Reduced the amount of Scientific Research needed to unlock the Hi-Tech R&D Laboratory from 10k to 5k
|
||||
** Energy Material requirement of the Software industry reduced from 1 to 0.5
|
||||
** It is now slightly easier to increase the Software industry's production multiplier
|
||||
** Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
|
||||
** You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
|
||||
** Significantly changed the effects of the different employee positions. See updated descriptions
|
||||
** Reduced the amount of money you gain from private investors
|
||||
** Training employees is now 3x more effective
|
||||
** Bug Fix: An industry's products are now properly separated between different cities
|
||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
||||
* Modified the multipliers of BitNode-3 and BitNode-8 to make them slightly harder
|
||||
* After installing Augmentations, Duplicate Sleeves will now default to Synchronize if their Shock is 0
|
||||
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
|
||||
* Bug Fix: growthAnalyze() function now properly accounts for BitNode multipliers
|
||||
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
|
||||
* 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
|
||||
`
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import { BitNodeMultipliers } from "../BitNode/BitNode
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { showLiterature } from "../Literature";
|
||||
import { Locations } from "../Locations";
|
||||
import { createCityMap } from "../Locations/Cities";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { Player } from "../Player";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
@@ -113,15 +113,15 @@ $(document).mousedown(function(event) {
|
||||
var empManualAssignmentModeActive = false;
|
||||
function Industry(params={}) {
|
||||
this.offices = { //Maps locations to offices. 0 if no office at that location
|
||||
[Locations.Aevum]: 0,
|
||||
[Locations.Chongqing]: 0,
|
||||
[Locations.Sector12]: new OfficeSpace({
|
||||
loc:Locations.Sector12,
|
||||
[CityName.Aevum]: 0,
|
||||
[CityName.Chongqing]: 0,
|
||||
[CityName.Sector12]: new OfficeSpace({
|
||||
loc:CityName.Sector12,
|
||||
size:OfficeInitialSize,
|
||||
}),
|
||||
[Locations.NewTokyo]: 0,
|
||||
[Locations.Ishima]: 0,
|
||||
[Locations.Volhaven]: 0
|
||||
[CityName.NewTokyo]: 0,
|
||||
[CityName.Ishima]: 0,
|
||||
[CityName.Volhaven]: 0
|
||||
};
|
||||
|
||||
this.name = params.name ? params.name : 0;
|
||||
@@ -172,17 +172,17 @@ function Industry(params={}) {
|
||||
this.newInd = true;
|
||||
|
||||
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
|
||||
[Locations.Aevum]: 0,
|
||||
[Locations.Chonqing]: 0,
|
||||
[Locations.Sector12]: new Warehouse({
|
||||
[CityName.Aevum]: 0,
|
||||
[CityName.Chonqing]: 0,
|
||||
[CityName.Sector12]: new Warehouse({
|
||||
corp: params.corp,
|
||||
industry: this,
|
||||
loc: Locations.Sector12,
|
||||
loc: CityName.Sector12,
|
||||
size: WarehouseInitialSize,
|
||||
}),
|
||||
[Locations.NewTokyo]: 0,
|
||||
[Locations.Ishima]: 0,
|
||||
[Locations.Volhaven]: 0
|
||||
[CityName.NewTokyo]: 0,
|
||||
[CityName.Ishima]: 0,
|
||||
[CityName.Volhaven]: 0
|
||||
};
|
||||
|
||||
this.init();
|
||||
@@ -521,15 +521,18 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
|
||||
}
|
||||
|
||||
// Process production, purchase, and import/export of materials
|
||||
var res = this.processMaterials(marketCycles, company);
|
||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||
let res = this.processMaterials(marketCycles, company);
|
||||
if (Array.isArray(res)) {
|
||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||
}
|
||||
|
||||
// Process creation, production & sale of products
|
||||
res = this.processProducts(marketCycles, company);
|
||||
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
|
||||
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
|
||||
|
||||
if (Array.isArray(res)) {
|
||||
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
|
||||
@@ -563,13 +566,15 @@ Industry.prototype.processMaterialMarket = function(marketCycles=1) {
|
||||
}
|
||||
}
|
||||
|
||||
//Process change in demand and competition for this industry's products
|
||||
// Process change in demand and competition for this industry's products
|
||||
Industry.prototype.processProductMarket = function(marketCycles=1) {
|
||||
//Demand gradually decreases, and competition gradually increases
|
||||
for (var name in this.products) {
|
||||
// Demand gradually decreases, and competition gradually increases
|
||||
for (const name in this.products) {
|
||||
if (this.products.hasOwnProperty(name)) {
|
||||
var product = this.products[name];
|
||||
var change = getRandomInt(1, 3) * 0.0004;
|
||||
const product = this.products[name];
|
||||
let change = getRandomInt(0, 3) * 0.0004;
|
||||
if (change === 0) { continue; }
|
||||
|
||||
if (this.type === Industries.Pharmaceutical || this.type === Industries.Software ||
|
||||
this.type === Industries.Robotics) {
|
||||
change *= 3;
|
||||
@@ -770,7 +775,17 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
||||
* advertisingFactor
|
||||
* this.getSalesMultiplier());
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
const optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = mat.bCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
}
|
||||
|
||||
// We'll store this "Optimal Price" in a property so that we don't have
|
||||
// to re-calculate it for the UI
|
||||
@@ -834,7 +849,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
||||
sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles);
|
||||
sellAmt = Math.min(mat.qty, sellAmt);
|
||||
if (sellAmt < 0) {
|
||||
console.log("sellAmt calculated to be negative");
|
||||
console.warn(`sellAmt calculated to be negative for ${matName} in ${city}`);
|
||||
mat.sll = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -887,9 +902,11 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
||||
break;
|
||||
}
|
||||
|
||||
//Make sure theres enough space in warehouse
|
||||
// Make sure theres enough space in warehouse
|
||||
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 {
|
||||
var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]);
|
||||
amt = Math.min(maxAmt, amt);
|
||||
@@ -1089,7 +1106,12 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
optimalPrice = 0;
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = product.pCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + product.pCost;
|
||||
}
|
||||
@@ -1251,7 +1273,7 @@ Industry.prototype.getAdvertisingFactors = function() {
|
||||
|
||||
//Returns a multiplier based on a materials demand and competition that affects sales
|
||||
Industry.prototype.getMarketFactor = function(mat) {
|
||||
return mat.dmd * (100 - mat.cmp)/100;
|
||||
return Math.max(0.1, mat.dmd * (100 - mat.cmp) / 100);
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether this Industry has the specified Research
|
||||
@@ -1446,7 +1468,6 @@ function Employee(params={}) {
|
||||
this.hap = params.happiness ? params.happiness : 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.cha = params.charisma ? params.charisma : getRandomInt(10, 50);
|
||||
this.exp = params.experience ? params.experience : getRandomInt(10, 50);
|
||||
@@ -1465,13 +1486,7 @@ function Employee(params={}) {
|
||||
Employee.prototype.process = function(marketCycles=1, office) {
|
||||
var gain = 0.003 * marketCycles,
|
||||
det = gain * Math.random();
|
||||
this.age += gain;
|
||||
this.exp += gain;
|
||||
if (this.age > 150) {
|
||||
this.int -= det;
|
||||
this.eff -= det;
|
||||
this.cha -= det;
|
||||
}
|
||||
|
||||
// Employee salaries slowly go up over time
|
||||
this.cyclesUntilRaise -= marketCycles;
|
||||
@@ -1560,7 +1575,6 @@ Employee.prototype.createUI = function(panel, corporation, industry) {
|
||||
innerHTML:"Morale: " + formatNumber(this.mor, 3) + "<br>" +
|
||||
"Happiness: " + formatNumber(this.hap, 3) + "<br>" +
|
||||
"Energy: " + formatNumber(this.ene, 3) + "<br>" +
|
||||
"Age: " + formatNumber(this.age, 3) + "<br>" +
|
||||
"Intelligence: " + formatNumber(effInt, 3) + "<br>" +
|
||||
"Charisma: " + formatNumber(effCha, 3) + "<br>" +
|
||||
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
|
||||
|
||||
@@ -86,7 +86,7 @@ export class Material {
|
||||
this.mku = 6;
|
||||
break;
|
||||
case "Energy":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 80; this.cmpR = [65, 95];
|
||||
this.bCost = 2000; this.mv = 0.2;
|
||||
this.mku = 6;
|
||||
@@ -122,26 +122,26 @@ export class Material {
|
||||
this.mku = 2;
|
||||
break;
|
||||
case "Real Estate":
|
||||
this.dmd = 50; this.dmdR = [5, 100];
|
||||
this.dmd = 50; this.dmdR = [5, 99];
|
||||
this.cmp = 50; this.cmpR = [25, 75];
|
||||
this.bCost = 80e3; this.mv = 1.5; //Less mv bc its processed twice
|
||||
this.mku = 1.5;
|
||||
break;
|
||||
case "Drugs":
|
||||
this.dmd = 60; this.dmdR = [45, 75];
|
||||
this.cmp = 70; this.cmpR = [40, 100];
|
||||
this.cmp = 70; this.cmpR = [40, 99];
|
||||
this.bCost = 40e3; this.mv = 1.6;
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "Robots":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 9];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 75e3; this.mv = 0.5; //Less mv bc its processed twice
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "AI Cores":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 15e3; this.mv = 0.8; //Less mv bc its processed twice
|
||||
this.mku = 0.5;
|
||||
break;
|
||||
|
||||
@@ -3,11 +3,9 @@ import { MaterialSizes } from "./MaterialSizes";
|
||||
import { ProductRatingWeights,
|
||||
IProductRatingWeight } from "./ProductRatingWeights";
|
||||
|
||||
import { Cities } from "../Locations/Cities";
|
||||
import { createCityMap } from "../Locations/createCityMap";
|
||||
import { IMap } from "../types";
|
||||
|
||||
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
@@ -31,6 +31,7 @@ import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
@@ -780,7 +781,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
@@ -1052,7 +1058,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
@@ -1406,7 +1417,7 @@ export class CorporationEventHandler {
|
||||
}
|
||||
|
||||
// Array of all cities. Used later
|
||||
const cities = Object.values(Cities);
|
||||
const cities = Object.keys(Cities);
|
||||
|
||||
// Parse quantity
|
||||
if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
|
||||
|
||||
@@ -300,8 +300,8 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
<br />
|
||||
|
||||
<p>Avg Employee Morale: {numeralWrapper.format(avgMorale, "0.000")}</p>
|
||||
<p>Avg Happiness Morale: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Energy Morale: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Avg Employee Happiness: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Employee Energy: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
||||
{
|
||||
vechain &&
|
||||
@@ -495,8 +495,6 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
<br />
|
||||
Energy: {numeralWrapper.format(this.state.employee.ene, nf)}
|
||||
<br />
|
||||
Age: {numeralWrapper.format(this.state.employee.age, nf)}
|
||||
<br />
|
||||
Intelligence: {numeralWrapper.format(effInt, nf)}
|
||||
<br />
|
||||
Charisma: {numeralWrapper.format(effCha, nf)}
|
||||
|
||||
@@ -218,7 +218,7 @@ function MaterialComponent(props) {
|
||||
mat.buy === 0 && mat.imp === 0;
|
||||
|
||||
// Purchase material button
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
|
||||
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
||||
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
||||
|
||||
@@ -229,9 +229,9 @@ function MaterialComponent(props) {
|
||||
let sellButtonText;
|
||||
if (mat.sllman[0]) {
|
||||
if (isString(mat.sllman[1])) {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${mat.sllman[1]})`
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${mat.sllman[1]})`
|
||||
} else {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${numeralWrapper.format(mat.sllman[1], nfB)})`;
|
||||
}
|
||||
|
||||
if (mat.marketTa2) {
|
||||
@@ -469,7 +469,7 @@ export class IndustryWarehouse extends BaseReactComponent {
|
||||
return (
|
||||
<div className={"cmpy-mgmt-warehouse-panel"}>
|
||||
<p className={"tooltip"} style={sizeUsageStyle}>
|
||||
Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")}
|
||||
Storage: {numeralWrapper.formatBigNumber(warehouse.sizeUsed)} / {numeralWrapper.formatBigNumber(warehouse.size)}
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { overviewPage } from "./Routing";
|
||||
|
||||
import { OfficeSpace } from "../Corporation";
|
||||
|
||||
import { Cities } from "../../Locations/Cities";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
|
||||
export class MainPanel extends BaseReactComponent {
|
||||
constructor(props) {
|
||||
@@ -19,13 +19,13 @@ export class MainPanel extends BaseReactComponent {
|
||||
|
||||
this.state = {
|
||||
division: "",
|
||||
city: Cities.Sector12,
|
||||
city: CityName.Sector12,
|
||||
}
|
||||
}
|
||||
|
||||
// We can pass this setter to child components
|
||||
changeCityState(newCity) {
|
||||
if (Object.values(Cities).includes(newCity)) {
|
||||
if (Object.values(CityName).includes(newCity)) {
|
||||
this.state.city = newCity;
|
||||
} else {
|
||||
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
|
||||
@@ -45,7 +45,7 @@ export class MainPanel extends BaseReactComponent {
|
||||
const currentDivision = this.routing().current();
|
||||
if (currentDivision !== this.state.division) {
|
||||
this.state.division = currentDivision;
|
||||
this.state.city = Cities.Sector12;
|
||||
this.state.city = CityName.Sector12;
|
||||
}
|
||||
|
||||
return this.renderDivisionPage();
|
||||
|
||||
@@ -54,7 +54,7 @@ export class Overview extends BaseReactComponent {
|
||||
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
||||
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
||||
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br>`;
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
|
||||
}
|
||||
|
||||
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
|
||||
|
||||
189
src/DevMenu.jsx
189
src/DevMenu.jsx
@@ -1,38 +1,44 @@
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { CodingContractTypes } from "./CodingContracts";
|
||||
import { generateContract,
|
||||
generateRandomContract,
|
||||
generateRandomContractOnHome } from "./CodingContractGenerator";
|
||||
import { Companies } from "./Company/Companies";
|
||||
import { Company } from "./Company/Company";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { AllServers } from "./Server/AllServers";
|
||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { StockMarket,
|
||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { CodingContractTypes } from "./CodingContracts";
|
||||
import {
|
||||
generateContract,
|
||||
generateRandomContract,
|
||||
generateRandomContractOnHome
|
||||
} from "./CodingContractGenerator";
|
||||
import { Companies } from "./Company/Companies";
|
||||
import { Company } from "./Company/Company";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { AllServers } from "./Server/AllServers";
|
||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { StockMarket, SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
|
||||
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 {
|
||||
constructor(props) {
|
||||
@@ -41,7 +47,7 @@ class ValueAdjusterComponent extends Component {
|
||||
this.setValue = this.setValue.bind(this);
|
||||
}
|
||||
setValue(event) {
|
||||
this.setState({ value: event.target.value });
|
||||
this.setState({ value: parseFloat(event.target.value) });
|
||||
}
|
||||
render() {
|
||||
const { title, add, subtract, reset } = this.props;
|
||||
@@ -124,7 +130,6 @@ class DevMenuComponent extends Component {
|
||||
this.setState({ codingcontract: event.target.value });
|
||||
}
|
||||
|
||||
|
||||
addMoney(n) {
|
||||
return function() {
|
||||
Player.gainMoney(n);
|
||||
@@ -186,14 +191,20 @@ class DevMenuComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
modifyKarma(modifier) {
|
||||
return function(amt) {
|
||||
Player.karma += (amt * modifier);
|
||||
}
|
||||
}
|
||||
|
||||
tonsOfExp() {
|
||||
Player.gainHackingExp(1e27);
|
||||
Player.gainStrengthExp(1e27);
|
||||
Player.gainDefenseExp(1e27);
|
||||
Player.gainDexterityExp(1e27);
|
||||
Player.gainAgilityExp(1e27);
|
||||
Player.gainCharismaExp(1e27);
|
||||
Player.gainIntelligenceExp(1e27);
|
||||
Player.gainHackingExp(tonsPP);
|
||||
Player.gainStrengthExp(tonsPP);
|
||||
Player.gainDefenseExp(tonsPP);
|
||||
Player.gainDexterityExp(tonsPP);
|
||||
Player.gainAgilityExp(tonsPP);
|
||||
Player.gainCharismaExp(tonsPP);
|
||||
Player.gainIntelligenceExp(tonsPP);
|
||||
Player.updateSkillLevels();
|
||||
}
|
||||
|
||||
@@ -237,6 +248,12 @@ class DevMenuComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
resetKarma() {
|
||||
return function() {
|
||||
Player.karma = 0;
|
||||
}
|
||||
}
|
||||
|
||||
enableIntelligence() {
|
||||
if(Player.intelligence === 0) {
|
||||
Player.intelligence = 1;
|
||||
@@ -296,7 +313,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
tonsOfRep() {
|
||||
for (const i in Factions) {
|
||||
Factions[i].playerReputation = 1e27;
|
||||
Factions[i].playerReputation = tonsPP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +325,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
tonsOfFactionFavor() {
|
||||
for (const i in Factions) {
|
||||
Factions[i].favor = 1e27;
|
||||
Factions[i].favor = tonsPP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +471,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
tonsOfRepCompanies() {
|
||||
for (const c in Companies) {
|
||||
Companies[c].playerReputation = 1e12;
|
||||
Companies[c].playerReputation = tonsP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +483,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
tonsOfFavorCompanies() {
|
||||
for (const c in Companies) {
|
||||
Companies[c].favor = 1e12;
|
||||
Companies[c].favor = tonsP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +508,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
addTonsBladeburnerRank() {
|
||||
if (!!Player.bladeburner) {
|
||||
Player.bladeburner.changeRank(1e12);
|
||||
Player.bladeburner.changeRank(tonsP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,13 +528,13 @@ class DevMenuComponent extends Component {
|
||||
|
||||
addTonsBladeburnerCycles() {
|
||||
if (!!Player.bladeburner) {
|
||||
Player.bladeburner.storedCycles += 1e12;
|
||||
Player.bladeburner.storedCycles += tonsP;
|
||||
}
|
||||
}
|
||||
|
||||
addTonsGangCycles() {
|
||||
if (!!Player.gang) {
|
||||
Player.gang.storedCycles = 1e12;
|
||||
Player.gang.storedCycles = tonsP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +554,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
addTonsCorporationCycles() {
|
||||
if (!!Player.corporation) {
|
||||
Player.corporation.storedCycles = 1e12;
|
||||
Player.corporation.storedCycles = tonsP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,16 +661,16 @@ class DevMenuComponent extends Component {
|
||||
|
||||
let sourceFiles = [];
|
||||
validSFN.forEach( i => sourceFiles.push(
|
||||
<tr key={'sf-'+i}>
|
||||
<td><span className="text">SF-{i}:</span></td>
|
||||
<td>
|
||||
<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, 2)}>2</button>
|
||||
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
|
||||
</td>
|
||||
</tr>
|
||||
));
|
||||
<tr key={'sf-'+i}>
|
||||
<td><span className="text">SF-{i}:</span></td>
|
||||
<td>
|
||||
<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, 2)}>2</button>
|
||||
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
|
||||
</td>
|
||||
</tr>
|
||||
));
|
||||
|
||||
|
||||
|
||||
@@ -713,11 +730,11 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Hacking:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
title="hacking exp"
|
||||
add={this.modifyExp('hacking', 1)}
|
||||
subtract={this.modifyExp('hacking', -1)}
|
||||
reset={this.resetExperience('hacking')}
|
||||
<ValueAdjusterComponent
|
||||
title="hacking exp"
|
||||
add={this.modifyExp('hacking', 1)}
|
||||
subtract={this.modifyExp('hacking', -1)}
|
||||
reset={this.resetExperience('hacking')}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -726,7 +743,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Strength:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="strength exp"
|
||||
add={this.modifyExp('strength', 1)}
|
||||
subtract={this.modifyExp('strength', -1)}
|
||||
@@ -739,7 +756,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Defense:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="defense exp"
|
||||
add={this.modifyExp('defense', 1)}
|
||||
subtract={this.modifyExp('defense', -1)}
|
||||
@@ -752,7 +769,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Dexterity:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="dexterity exp"
|
||||
add={this.modifyExp('dexterity', 1)}
|
||||
subtract={this.modifyExp('dexterity', -1)}
|
||||
@@ -765,7 +782,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Agility:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="agility exp"
|
||||
add={this.modifyExp('agility', 1)}
|
||||
subtract={this.modifyExp('agility', -1)}
|
||||
@@ -778,7 +795,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Charisma:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="charisma exp"
|
||||
add={this.modifyExp('charisma', 1)}
|
||||
subtract={this.modifyExp('charisma', -1)}
|
||||
@@ -791,7 +808,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text text-center">Intelligence:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="intelligence exp"
|
||||
add={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>
|
||||
</td>
|
||||
</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>
|
||||
</table>
|
||||
</div>
|
||||
@@ -832,7 +862,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text">Reputation:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="reputation"
|
||||
add={this.modifyFactionRep(1)}
|
||||
subtract={this.modifyFactionRep(-1)}
|
||||
@@ -845,7 +875,7 @@ class DevMenuComponent extends Component {
|
||||
<span className="text">Favor:</span>
|
||||
</td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="favor"
|
||||
add={this.modifyFactionFavor(1)}
|
||||
subtract={this.modifyFactionFavor(-1)}
|
||||
@@ -979,7 +1009,7 @@ class DevMenuComponent extends Component {
|
||||
<tr>
|
||||
<td><span className="text">Reputation:</span></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="reputation"
|
||||
add={this.modifyCompanyRep(1)}
|
||||
subtract={this.modifyCompanyRep(-1)}
|
||||
@@ -990,7 +1020,7 @@ class DevMenuComponent extends Component {
|
||||
<tr>
|
||||
<td><span className="text">Favor:</span></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="favor"
|
||||
add={this.modifyCompanyFavor(1)}
|
||||
subtract={this.modifyCompanyFavor(-1)}
|
||||
@@ -1028,7 +1058,7 @@ class DevMenuComponent extends Component {
|
||||
<td><span className="text">Rank:</span></td>
|
||||
<td><button className="std-button" onClick={this.addTonsBladeburnerRank}>Tons</button></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="rank"
|
||||
add={this.modifyBladeburnerRank(1)}
|
||||
subtract={this.modifyBladeburnerRank(-1)}
|
||||
@@ -1040,7 +1070,7 @@ class DevMenuComponent extends Component {
|
||||
<td><span className="text">Cycles:</span></td>
|
||||
<td><button className="std-button" onClick={this.addTonsBladeburnerCycles}>Tons</button></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="cycles"
|
||||
add={this.modifyBladeburnerCycles(1)}
|
||||
subtract={this.modifyBladeburnerCycles(-1)}
|
||||
@@ -1064,7 +1094,7 @@ class DevMenuComponent extends Component {
|
||||
<td><span className="text">Cycles:</span></td>
|
||||
<td><button className="std-button" onClick={this.addTonsGangCycles}>Tons</button></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="cycles"
|
||||
add={this.modifyGangCycles(1)}
|
||||
subtract={this.modifyGangCycles(-1)}
|
||||
@@ -1088,7 +1118,7 @@ class DevMenuComponent extends Component {
|
||||
<td><span className="text">Cycles:</span></td>
|
||||
<td><button className="std-button" onClick={this.addTonsCorporationCycles}>Tons</button></td>
|
||||
<td>
|
||||
<ValueAdjusterComponent
|
||||
<ValueAdjusterComponent
|
||||
title="cycles"
|
||||
add={this.modifyCorporationCycles(1)}
|
||||
subtract={this.modifyCorporationCycles(-1)}
|
||||
@@ -1121,7 +1151,7 @@ class DevMenuComponent extends Component {
|
||||
{contractTypes}
|
||||
</select>
|
||||
<button className="std-button" onClick={this.specificContract}>Generate Specified Contract Type on Home Comp</button>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -1185,7 +1215,6 @@ class DevMenuComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const devMenuContainerId = "dev-menu-container";
|
||||
|
||||
export function createDevMenu() {
|
||||
@@ -1199,11 +1228,11 @@ export function createDevMenu() {
|
||||
id: devMenuContainerId,
|
||||
});
|
||||
|
||||
const entireGameContainer = document.getElementById("entire-game-container");
|
||||
if (entireGameContainer == null) {
|
||||
throw new Error("Could not find entire-game-container DOM element");
|
||||
}
|
||||
entireGameContainer.appendChild(devMenuContainer);
|
||||
const entireGameContainer = document.getElementById("entire-game-container");
|
||||
if (entireGameContainer == null) {
|
||||
throw new Error("Could not find entire-game-container DOM element");
|
||||
}
|
||||
entireGameContainer.appendChild(devMenuContainer);
|
||||
|
||||
ReactDOM.render(<DevMenuComponent />, devMenuContainer);
|
||||
}
|
||||
|
||||
@@ -97,16 +97,6 @@ export class Faction {
|
||||
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.
|
||||
*/
|
||||
|
||||
7
src/Faction/FactionHelpers.d.ts
vendored
Normal file
7
src/Faction/FactionHelpers.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
|
||||
export declare function getNextNeurofluxLevel(): number;
|
||||
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
|
||||
export declare function purchaseAugmentationBoxCreate(aug: Augmentation, fac: Faction): void;
|
||||
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;
|
||||
@@ -1,695 +0,0 @@
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Engine } from "../engine";
|
||||
import { Faction } from "./Faction";
|
||||
import { Factions } from "./Factions";
|
||||
import { FactionInfos } from "./FactionInfo";
|
||||
import { Locations} from "../Location";
|
||||
import { HackingMission, setInMission } from "../Missions";
|
||||
import { Player } from "../Player";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
|
||||
import { createPurchaseSleevesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
|
||||
import {Page, routing} from "../ui/navigationTracking";
|
||||
import {numeralWrapper} from "../ui/numeralFormat";
|
||||
import {dialogBoxCreate} from "../../utils/DialogBox";
|
||||
import {factionInvitationBoxCreate} from "../../utils/FactionInvitationBox";
|
||||
import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import {createElement} from "../../utils/uiHelpers/createElement";
|
||||
import {Reviver, Generic_toJSON,
|
||||
Generic_fromJSON} from "../../utils/JSONReviver";
|
||||
import {formatNumber} from "../../utils/StringHelperFunctions";
|
||||
import {yesNoBoxCreate, yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton, yesNoBoxClose} from "../../utils/YesNoBox";
|
||||
|
||||
function inviteToFaction(faction) {
|
||||
if (Settings.SuppressFactionInvites) {
|
||||
faction.alreadyInvited = true;
|
||||
Player.factionInvitations.push(faction.name);
|
||||
if (routing.isOn(Page.Factions)) {
|
||||
Engine.loadFactionsContent();
|
||||
}
|
||||
} else {
|
||||
factionInvitationBoxCreate(faction);
|
||||
}
|
||||
}
|
||||
|
||||
function joinFaction(faction) {
|
||||
faction.isMember = true;
|
||||
Player.factions.push(faction.name);
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
//Determine what factions you are banned from now that you have joined this faction
|
||||
for(const i in factionInfo.enemies) {
|
||||
const enemy = factionInfo.enemies[i];
|
||||
if (Factions[enemy] instanceof Faction) {
|
||||
Factions[enemy].isBanned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Displays the HTML content for a specific faction
|
||||
function displayFactionContent(factionName) {
|
||||
var faction = Factions[factionName];
|
||||
if (faction == null) {
|
||||
throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
|
||||
}
|
||||
if (!faction.isMember) {
|
||||
throw new Error("Not a member of this faction, cannot display faction information");
|
||||
}
|
||||
var factionInfo = faction.getInfo();
|
||||
|
||||
removeChildrenFromElement(Engine.Display.factionContent);
|
||||
var elements = [];
|
||||
|
||||
//Header and faction info
|
||||
elements.push(createElement("h1", {
|
||||
innerText:factionName
|
||||
}));
|
||||
elements.push(createElement("pre", {
|
||||
innerHTML:"<i>" + factionInfo.infoText + "</i>"
|
||||
}));
|
||||
elements.push(createElement("p", {
|
||||
innerText:"---------------",
|
||||
}));
|
||||
|
||||
//Faction reputation and favor
|
||||
var favorGain = faction.getFavorGain();
|
||||
if (favorGain.length != 2) {favorGain = 0;}
|
||||
favorGain = favorGain[0];
|
||||
elements.push(createElement("p", {
|
||||
innerText: "Reputation: " + formatNumber(faction.playerReputation, 4),
|
||||
tooltip:"You will earn " + formatNumber(favorGain, 0) +
|
||||
" faction favor upon resetting after installing an Augmentation"
|
||||
}))
|
||||
elements.push(createElement("p", {
|
||||
innerText:"---------------",
|
||||
}));
|
||||
elements.push(createElement("p", {
|
||||
innerText:"Faction Favor: " + formatNumber(faction.favor, 0),
|
||||
tooltip:"Faction favor increases the rate at which " +
|
||||
"you earn reputation for this faction by 1% per favor. Faction favor " +
|
||||
"is gained whenever you reset after installing an Augmentation. The amount of " +
|
||||
"favor you gain depends on how much reputation you have with the faction"
|
||||
}));
|
||||
elements.push(createElement("p", {
|
||||
innerText:"---------------",
|
||||
}));
|
||||
|
||||
//Faction Work Description Text
|
||||
elements.push(createElement("pre", {
|
||||
id:"faction-work-description-text",
|
||||
innerText:"Perform work/carry out assignments for your faction to help further its cause! By doing so " +
|
||||
"you will earn reputation for your faction. You will also gain reputation passively over time, " +
|
||||
"although at a very slow rate. Earning reputation will allow you to purchase Augmentations " +
|
||||
"through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot " +
|
||||
"use your terminal or create scripts when you are performing a task!"
|
||||
}));
|
||||
elements.push(createElement("br"));
|
||||
|
||||
//Hacking Mission Option
|
||||
var hackMissionDiv = createElement("div", { class:"faction-work-div" });
|
||||
var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
hackMissionDiv.appendChild(hackMissionDivWrapper);
|
||||
hackMissionDivWrapper.appendChild(createElement("a", {
|
||||
class:"a-link-button", innerText:"Hacking Mission",
|
||||
clickListener:()=>{
|
||||
Engine.loadMissionContent();
|
||||
var mission = new HackingMission(faction.playerReputation, faction);
|
||||
setInMission(true, mission); //Sets inMission flag to true
|
||||
mission.init();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
hackMissionDivWrapper.appendChild(createElement("p", {
|
||||
innerText:"Attempt a hacking mission for your faction. " +
|
||||
"A mission is a mini game that, if won, earns you " +
|
||||
"significant reputation with this faction. (Recommended hacking level: 200+)"
|
||||
}));
|
||||
elements.push(hackMissionDiv);
|
||||
|
||||
//Hacking Contracts Option
|
||||
var hackDiv = createElement("div", { class:"faction-work-div", });
|
||||
var hackDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
hackDiv.appendChild(hackDivWrapper);
|
||||
hackDivWrapper.appendChild(createElement("a", {
|
||||
class:"std-button", innerText:"Hacking Contracts",
|
||||
clickListener:()=>{
|
||||
Player.startFactionHackWork(faction);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
hackDivWrapper.appendChild(createElement("p", {
|
||||
innerText:"Complete hacking contracts for your faction. " +
|
||||
"Your effectiveness, which determines how much " +
|
||||
"reputation you gain for this faction, is based on your hacking skill. " +
|
||||
"You will gain hacking exp."
|
||||
}));
|
||||
elements.push(hackDiv);
|
||||
|
||||
//Field Work Option
|
||||
var fieldWorkDiv = createElement("div", { class:"faction-work-div" });
|
||||
var fieldWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
fieldWorkDiv.appendChild(fieldWorkDivWrapper);
|
||||
fieldWorkDivWrapper.appendChild(createElement("a", {
|
||||
class:"std-button", innerText:"Field Work",
|
||||
clickListener:()=>{
|
||||
Player.startFactionFieldWork(faction);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
fieldWorkDivWrapper.appendChild(createElement("p", {
|
||||
innerText:"Carry out field missions for your faction. " +
|
||||
"Your effectiveness, which determines how much " +
|
||||
"reputation you gain for this faction, is based on all of your stats. " +
|
||||
"You will gain exp for all stats."
|
||||
}));
|
||||
elements.push(fieldWorkDiv);
|
||||
|
||||
//Security Work Option
|
||||
var securityWorkDiv = createElement("div", { class:"faction-work-div" });
|
||||
var securityWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
securityWorkDiv.appendChild(securityWorkDivWrapper);
|
||||
securityWorkDivWrapper.appendChild(createElement("a", {
|
||||
class:"std-button", innerText:"Security Work",
|
||||
clickListener:()=>{
|
||||
Player.startFactionSecurityWork(faction);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
securityWorkDivWrapper.appendChild(createElement("p", {
|
||||
innerText:"Serve in a security detail for your faction. " +
|
||||
"Your effectiveness, which determines how much " +
|
||||
"reputation you gain for this faction, is based on your combat stats. " +
|
||||
"You will gain exp for all combat stats."
|
||||
}));
|
||||
elements.push(securityWorkDiv);
|
||||
|
||||
//Donate for reputation
|
||||
var donateDiv = createElement("div", { class:"faction-work-div" });
|
||||
var donateDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
donateDiv.appendChild(donateDivWrapper);
|
||||
var donateRepGain = createElement("p", {
|
||||
innerText:"This donation will result in 0.000 reputation gain"
|
||||
});
|
||||
var donateAmountInput = createElement("input", {
|
||||
class: "text-input", placeholder:"Donation amount",
|
||||
inputListener:()=>{
|
||||
let amt = 0;
|
||||
if(donateAmountInput.value !== "") {
|
||||
amt = parseFloat(donateAmountInput.value);
|
||||
}
|
||||
if (isNaN(amt)) {
|
||||
donateRepGain.innerText = "Invalid donate amount entered!";
|
||||
} else {
|
||||
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
|
||||
donateRepGain.innerText = "This donation will result in " +
|
||||
formatNumber(repGain, 3) + " reputation gain";
|
||||
}
|
||||
},
|
||||
});
|
||||
donateDivWrapper.appendChild(createElement("a", {
|
||||
class:"std-button", innerText:"Donate Money",
|
||||
clickListener:()=>{
|
||||
var amt = parseFloat(donateAmountInput.value);
|
||||
if (isNaN(amt) || amt < 0) {
|
||||
dialogBoxCreate("Invalid amount entered!");
|
||||
} else if (Player.money.lt(amt)) {
|
||||
dialogBoxCreate("You cannot afford to donate this much money!");
|
||||
} else {
|
||||
Player.loseMoney(amt);
|
||||
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
|
||||
faction.playerReputation += repGain;
|
||||
dialogBoxCreate("You just donated " + numeralWrapper.format(amt, "$0.000a") + " to " +
|
||||
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
|
||||
displayFactionContent(factionName);
|
||||
}
|
||||
}
|
||||
}));
|
||||
donateDivWrapper.appendChild(donateAmountInput);
|
||||
donateDivWrapper.appendChild(donateRepGain);
|
||||
elements.push(donateDiv);
|
||||
|
||||
//Purchase Augmentations
|
||||
const purchaseAugmentationsDiv = createElement("div", { class: "faction-work-div", display: "inline" });
|
||||
const purchaseAugmentationsDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
|
||||
purchaseAugmentationsDiv.appendChild(purchaseAugmentationsDivWrapper);
|
||||
purchaseAugmentationsDivWrapper.appendChild(createElement("a", {
|
||||
class:"std-button",
|
||||
innerText:"Purchase Augmentations",
|
||||
margin: "5px",
|
||||
clickListener:()=>{
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.factionAugmentationsContent.style.display = "block";
|
||||
|
||||
displayFactionAugmentations(factionName);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
purchaseAugmentationsDivWrapper.appendChild(createElement("pre", {
|
||||
innerHTML: "<br>As your reputation with this faction rises, you will " +
|
||||
"unlock Augmentations, which you can purchase to enhance " +
|
||||
"your abilities.<br><br>"
|
||||
}));
|
||||
elements.push(purchaseAugmentationsDiv);
|
||||
|
||||
//Gang (BitNode-2)
|
||||
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
|
||||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
|
||||
factionName == "NiteSec" || factionName == "The Black Hand")) {
|
||||
//Set everything else to invisible
|
||||
hackMissionDiv.style.display = "none";
|
||||
hackDiv.style.display = "none";
|
||||
fieldWorkDiv.style.display = "none";
|
||||
securityWorkDiv.style.display = "none";
|
||||
donateDiv.style.display = "none";
|
||||
|
||||
//Create the 'Manage Gang' button
|
||||
var gangDiv = createElement("div", {
|
||||
id:"faction-gang-div", class:"faction-work-div", display:"inline"
|
||||
});
|
||||
var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
|
||||
gangDiv.appendChild(gangDivWrapper);
|
||||
gangDivWrapper.appendChild(createElement("a", {
|
||||
class:"a-link-button", innerText:"Manage Gang",
|
||||
clickListener: () => {
|
||||
if (!Player.inGang()) {
|
||||
// Determine whether this is a hacking gang
|
||||
let hacking = false;
|
||||
if (factionName === "NiteSec" || factionName === "The Black Hand") { hacking = true; }
|
||||
|
||||
// Configure Yes/No buttons for the pop-up
|
||||
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
|
||||
yesBtn.innerHTML = "Create Gang";
|
||||
noBtn.innerHTML = "Cancel";
|
||||
yesBtn.addEventListener("click", () => {
|
||||
Player.startGang(factionName, hacking);
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
Engine.loadGangContent();
|
||||
yesNoBoxClose();
|
||||
});
|
||||
noBtn.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
// Pop-up text
|
||||
let gangTypeText = "";
|
||||
if (hacking) {
|
||||
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
|
||||
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
|
||||
"is not as important.<br><br>";
|
||||
} else {
|
||||
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
|
||||
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
|
||||
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
|
||||
}
|
||||
yesNoBoxCreate(`Would you like to create a new Gang with ${factionName}?<br><br>` +
|
||||
"Note that this will prevent you from creating a Gang with any other Faction until " +
|
||||
"this BitNode is destroyed.<br><br>" +
|
||||
gangTypeText +
|
||||
"Other than hacking vs combat, there are NO differences between the Factions you can " +
|
||||
"create a Gang with, and each of these Factions have all Augmentations available.");
|
||||
} else {
|
||||
Engine.loadGangContent();
|
||||
}
|
||||
}
|
||||
}));
|
||||
gangDivWrapper.appendChild(createElement("p", {
|
||||
innerText:"Create and manage a gang for this Faction. " +
|
||||
"Gangs will earn you money and faction reputation."
|
||||
}));
|
||||
//Manage Gang button goes before Faction work stuff
|
||||
elements.splice(7, 1, gangDiv);
|
||||
|
||||
if (Player.inGang() && Player.gang.facName != factionName) {
|
||||
//If the player has a gang but its not for this faction
|
||||
gangDiv.style.display = "none";
|
||||
}
|
||||
//Display all elements
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
Engine.Display.factionContent.appendChild(elements[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Purchase Sleeves from Covenant
|
||||
if (factionName === "The Covenant" && Player.bitNodeN >= 10 && SourceFileFlags[10]) {
|
||||
const covenantPurchaseSleevesDiv = createElement("div", { class: "faction-work-div", display: "inline" });
|
||||
const covenantPurchaseSleevesDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
|
||||
covenantPurchaseSleevesDiv.appendChild(covenantPurchaseSleevesDivWrapper);
|
||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("a", {
|
||||
class: "std-button",
|
||||
innerText: "Purchase Duplicate Sleeves",
|
||||
clickListener: () => {
|
||||
createPurchaseSleevesFromCovenantPopup(Player);
|
||||
}
|
||||
}));
|
||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
|
||||
innerText: "Purchase Duplicate Sleeves. These are permanent! You can purchase up to 5 total.",
|
||||
}));
|
||||
|
||||
elements.push(covenantPurchaseSleevesDiv);
|
||||
}
|
||||
|
||||
// Determine if actions should be possible
|
||||
donateDiv.style.display = faction.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none";
|
||||
|
||||
hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none";
|
||||
hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none";
|
||||
fieldWorkDiv.style.display = factionInfo.offerFieldWork ? "inline" : "none";
|
||||
securityWorkDiv.style.display = factionInfo.offerSecurityWork ? "inline" : "none";
|
||||
|
||||
//Display all elements
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
Engine.Display.factionContent.appendChild(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function displayFactionAugmentations(factionName) {
|
||||
var faction = Factions[factionName];
|
||||
if (faction == null) {
|
||||
throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
|
||||
}
|
||||
|
||||
removeChildrenFromElement(Engine.Display.factionAugmentationsContent);
|
||||
var elements = [];
|
||||
|
||||
//Back button
|
||||
elements.push(createElement("a", {
|
||||
innerText:"Back", class:"a-link-button",
|
||||
clickListener:()=>{
|
||||
Engine.loadFactionContent();
|
||||
displayFactionContent(factionName);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
//Header text
|
||||
elements.push(createElement("h1", {innerText:"Faction Augmentations"}));
|
||||
elements.push(createElement("p", {
|
||||
id:"faction-augmentations-page-desc",
|
||||
innerHTML:"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
|
||||
"Augmentations are powerful upgrades that will enhance your abilities."
|
||||
}));
|
||||
|
||||
elements.push(createElement("br"));
|
||||
elements.push(createElement("br"));
|
||||
|
||||
//Augmentations List
|
||||
var augmentationsList = createElement("ul");
|
||||
|
||||
//Sort buttons
|
||||
const sortByCostBtn = createElement("a", {
|
||||
innerText:"Sort by Cost", class:"a-link-button",
|
||||
clickListener:()=>{
|
||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Cost;
|
||||
var augs = faction.augmentations.slice();
|
||||
augs.sort((augName1, augName2)=>{
|
||||
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
return aug1.baseCost - aug2.baseCost;
|
||||
});
|
||||
removeChildrenFromElement(augmentationsList);
|
||||
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
|
||||
}
|
||||
});
|
||||
const sortByRepBtn = createElement("a", {
|
||||
innerText:"Sort by Reputation", class:"a-link-button",
|
||||
clickListener:()=>{
|
||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Reputation;
|
||||
var augs = faction.augmentations.slice();
|
||||
augs.sort((augName1, augName2)=>{
|
||||
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
||||
});
|
||||
removeChildrenFromElement(augmentationsList);
|
||||
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
|
||||
}
|
||||
});
|
||||
const defaultSortBtn = createElement("a", {
|
||||
innerText:"Sort by Default Order", class:"a-link-button",
|
||||
clickListener:()=>{
|
||||
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Default;
|
||||
removeChildrenFromElement(augmentationsList);
|
||||
createFactionAugmentationDisplayElements(augmentationsList, faction.augmentations, faction);
|
||||
}
|
||||
});
|
||||
elements.push(sortByCostBtn);
|
||||
elements.push(sortByRepBtn);
|
||||
elements.push(defaultSortBtn);
|
||||
switch(Settings.PurchaseAugmentationsOrder) {
|
||||
case PurchaseAugmentationsOrderSetting.Cost:
|
||||
sortByCostBtn.click();
|
||||
break;
|
||||
case PurchaseAugmentationsOrderSetting.Reputation:
|
||||
sortByRepBtn.click();
|
||||
break;
|
||||
default:
|
||||
defaultSortBtn.click();
|
||||
break;
|
||||
}
|
||||
|
||||
elements.push(augmentationsList);
|
||||
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
Engine.Display.factionAugmentationsContent.appendChild(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Takes in an array of Augmentation Names, constructs DOM elements
|
||||
//to list them on the faction page, and appends them to the given
|
||||
//DOM element
|
||||
// @augmentationsList DOM List to append Aug DOM elements to
|
||||
// @augs Array of Aug names
|
||||
// @faction Faction for which to display Augmentations
|
||||
function createFactionAugmentationDisplayElements(augmentationsList, augs, faction) {
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
for (var i = 0; i < augs.length; ++i) {
|
||||
(function () {
|
||||
var aug = Augmentations[augs[i]];
|
||||
if (aug == null) {
|
||||
throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
|
||||
}
|
||||
var owned = false;
|
||||
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
|
||||
if (Player.queuedAugmentations[j].name == aug.name) {
|
||||
owned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < Player.augmentations.length; ++j) {
|
||||
if (Player.augmentations[j].name == aug.name) {
|
||||
owned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var item = createElement("li");
|
||||
var span = createElement("span", { display:"inline-block", margin: "4px", padding: "4px" });
|
||||
var aDiv = createElement("div", {tooltip:aug.info});
|
||||
var aElem = createElement("a", {
|
||||
innerText:aug.name, display:"inline",
|
||||
clickListener:()=>{
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
purchaseAugmentationBoxCreate(aug, faction);
|
||||
} else {
|
||||
purchaseAugmentation(aug, faction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
aElem.innerText += " - Level " + (getNextNeurofluxLevel());
|
||||
}
|
||||
var pElem = createElement("p", {
|
||||
display:"inline",
|
||||
})
|
||||
var req = aug.baseRepRequirement * factionInfo.augmentationRepRequirementMult;
|
||||
var hasPrereqs = hasAugmentationPrereqs(aug);
|
||||
if (!hasPrereqs) {
|
||||
aElem.setAttribute("class", "a-link-button-inactive");
|
||||
pElem.innerHTML = "LOCKED (Requires " + aug.prereqs.join(",") + " as prerequisite(s))";
|
||||
pElem.style.color = "red";
|
||||
} else if (aug.name != AugmentationNames.NeuroFluxGovernor && (aug.owned || owned)) {
|
||||
aElem.setAttribute("class", "a-link-button-inactive");
|
||||
pElem.innerHTML = "ALREADY OWNED";
|
||||
} else if (faction.playerReputation >= req) {
|
||||
aElem.setAttribute("class", "a-link-button");
|
||||
pElem.innerHTML = "UNLOCKED - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
|
||||
} else {
|
||||
aElem.setAttribute("class", "a-link-button-inactive");
|
||||
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
|
||||
pElem.style.color = "red";
|
||||
}
|
||||
aDiv.appendChild(aElem);
|
||||
span.appendChild(aDiv);
|
||||
span.appendChild(pElem);
|
||||
item.appendChild(span);
|
||||
augmentationsList.appendChild(item);
|
||||
}()); //Immediate invocation closure
|
||||
}
|
||||
}
|
||||
|
||||
function purchaseAugmentationBoxCreate(aug, fac) {
|
||||
const factionInfo = fac.getInfo();
|
||||
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
|
||||
yesBtn.innerHTML = "Purchase";
|
||||
noBtn.innerHTML = "Cancel";
|
||||
yesBtn.addEventListener("click", function() {
|
||||
purchaseAugmentation(aug, fac);
|
||||
});
|
||||
noBtn.addEventListener("click", function() {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
|
||||
aug.info + "<br><br>" +
|
||||
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
|
||||
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
|
||||
}
|
||||
|
||||
//Returns a boolean indicating whether the player has the prerequisites for the
|
||||
//specified Augmentation
|
||||
function hasAugmentationPrereqs(aug) {
|
||||
var hasPrereqs = true;
|
||||
if (aug.prereqs && aug.prereqs.length > 0) {
|
||||
for (var i = 0; i < aug.prereqs.length; ++i) {
|
||||
var prereqAug = Augmentations[aug.prereqs[i]];
|
||||
if (prereqAug == null) {
|
||||
console.log("ERROR: Invalid prereq Augmentation: " + aug.prereqs[i]);
|
||||
continue;
|
||||
}
|
||||
if (prereqAug.owned === false) {
|
||||
hasPrereqs = false;
|
||||
|
||||
//Check if the aug is purchased
|
||||
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
|
||||
if (Player.queuedAugmentations[j].name === prereqAug.name) {
|
||||
hasPrereqs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasPrereqs;
|
||||
}
|
||||
|
||||
function purchaseAugmentation(aug, fac, sing=false) {
|
||||
const factionInfo = fac.getInfo();
|
||||
var hasPrereqs = hasAugmentationPrereqs(aug);
|
||||
if (!hasPrereqs) {
|
||||
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
|
||||
"purchase this one.";
|
||||
if (sing) {return txt;} else {dialogBoxCreate(txt);}
|
||||
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
||||
let txt = "You don't have enough money to purchase " + aug.name;
|
||||
if (sing) {return txt;}
|
||||
dialogBoxCreate(txt);
|
||||
} else if (fac.playerReputation < aug.baseRepRequirement) {
|
||||
let txt = "You don't have enough faction reputation to purchase " + aug.name;
|
||||
if (sing) {return txt;}
|
||||
dialogBoxCreate(txt);
|
||||
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
|
||||
if (Player.firstAugPurchased === false) {
|
||||
Player.firstAugPurchased = true;
|
||||
document.getElementById("augmentations-tab").style.display = "list-item";
|
||||
document.getElementById("character-menu-header").click();
|
||||
document.getElementById("character-menu-header").click();
|
||||
}
|
||||
|
||||
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
queuedAugmentation.level = getNextNeurofluxLevel();
|
||||
}
|
||||
Player.queuedAugmentations.push(queuedAugmentation);
|
||||
|
||||
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
|
||||
|
||||
//If you just purchased Neuroflux Governor, recalculate the cost
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
var nextLevel = getNextNeurofluxLevel();
|
||||
--nextLevel;
|
||||
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
|
||||
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
|
||||
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
|
||||
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
for (var name in Augmentations) {
|
||||
if (Augmentations.hasOwnProperty(name)) {
|
||||
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if (sing) {
|
||||
return "You purchased " + aug.name;
|
||||
} else {
|
||||
if(!Settings.SuppressBuyAugmentationConfirmation){
|
||||
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
|
||||
"effect until they are installed. To install your augmentations, go to the " +
|
||||
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
|
||||
"augmentations will now be more expensive.");
|
||||
}
|
||||
}
|
||||
|
||||
displayFactionAugmentations(fac.name);
|
||||
} else {
|
||||
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
|
||||
"Please report this to the game developer with an explanation of how to " +
|
||||
"reproduce this.");
|
||||
}
|
||||
yesNoBoxClose();
|
||||
}
|
||||
|
||||
function getNextNeurofluxLevel() {
|
||||
// Get current Neuroflux level based on Player's augmentations
|
||||
let currLevel = 0;
|
||||
for (var i = 0; i < Player.augmentations.length; ++i) {
|
||||
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||
currLevel = Player.augmentations[i].level;
|
||||
}
|
||||
}
|
||||
|
||||
// Account for purchased but uninstalled Augmentations
|
||||
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||
++currLevel;
|
||||
}
|
||||
}
|
||||
return currLevel + 1;
|
||||
}
|
||||
|
||||
function processPassiveFactionRepGain(numCycles) {
|
||||
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
|
||||
for (var name in Factions) {
|
||||
if (Factions.hasOwnProperty(name)) {
|
||||
var faction = Factions[name];
|
||||
|
||||
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
|
||||
//maybe later make this based on
|
||||
//a player's 'status' like how powerful they are and how much money they have
|
||||
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {getNextNeurofluxLevel, inviteToFaction,
|
||||
joinFaction, displayFactionContent, processPassiveFactionRepGain,
|
||||
purchaseAugmentation};
|
||||
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
|
||||
balance point to keep them from running out of control
|
||||
/**
|
||||
* TODO
|
||||
* Add police clashes
|
||||
* balance point to keep them from running out of control
|
||||
*/
|
||||
|
||||
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
|
||||
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
|
||||
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
|
||||
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
|
||||
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) {
|
||||
var boxId = "gang-member-upgrade-popup-box";
|
||||
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",
|
||||
"NiteSec", "The Black Hand"];
|
||||
const GangNames = [
|
||||
"Slum Snakes",
|
||||
"Tetrads",
|
||||
"The Syndicate",
|
||||
"The Dark Army",
|
||||
"Speakers for the Dead",
|
||||
"NiteSec",
|
||||
"The Black Hand"
|
||||
];
|
||||
|
||||
export let AllGangs = {
|
||||
"Slum Snakes" : {
|
||||
power: 1,
|
||||
@@ -137,12 +153,12 @@ export function loadAllGangs(saveString) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param facName - Name of corresponding faction
|
||||
* @param hacking - Boolean indicating whether or not its a hacking gang
|
||||
* @param facName {string} Name of corresponding faction
|
||||
* @param hacking {bollean} Whether or not its a hacking gang
|
||||
*/
|
||||
export function Gang(facName, hacking=false) {
|
||||
this.facName = facName;
|
||||
this.members = []; //Array of GangMembers
|
||||
this.members = [];
|
||||
this.wanted = 1;
|
||||
this.respect = 1;
|
||||
|
||||
@@ -197,7 +213,7 @@ Gang.prototype.process = 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;
|
||||
for (var i = 0; i < this.members.length; ++i) {
|
||||
respectGains += (this.members[i].calculateRespectGain(this));
|
||||
@@ -291,9 +307,9 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
||||
}
|
||||
|
||||
// Then process territory
|
||||
for (var i = 0; i < GangNames.length; ++i) {
|
||||
for (let i = 0; i < GangNames.length; ++i) {
|
||||
const others = GangNames.filter((e) => {
|
||||
return e !== i;
|
||||
return e !== GangNames[i];
|
||||
});
|
||||
const other = getRandomInt(0, others.length - 1);
|
||||
|
||||
@@ -318,6 +334,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
||||
AllGangs[otherGang].territory -= 0.0001;
|
||||
if (thisGang === gangName) {
|
||||
this.clash(true); // Player won
|
||||
AllGangs[otherGang].power *= (1 / 1.01);
|
||||
} else if (otherGang === gangName) {
|
||||
this.clash(false); // Player lost
|
||||
} else {
|
||||
@@ -333,6 +350,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
|
||||
this.clash(false); // Player lost
|
||||
} else if (otherGang === gangName) {
|
||||
this.clash(true); // Player won
|
||||
AllGangs[thisGang].power *= (1 / 1.01);
|
||||
} else {
|
||||
AllGangs[thisGang].power *= (1 / 1.01);
|
||||
}
|
||||
@@ -562,10 +580,9 @@ Gang.fromJSON = function(value) {
|
||||
|
||||
Reviver.constructors.Gang = Gang;
|
||||
|
||||
/*** Gang Member object ***/
|
||||
function GangMember(name) {
|
||||
this.name = name;
|
||||
this.task = "Unassigned"; //GangMemberTask object
|
||||
this.task = "Unassigned";
|
||||
|
||||
this.earnedRespect = 0;
|
||||
|
||||
@@ -597,11 +614,11 @@ function GangMember(name) {
|
||||
this.agi_asc_mult = 1;
|
||||
this.cha_asc_mult = 1;
|
||||
|
||||
this.upgrades = []; //Names of upgrades
|
||||
this.augmentations = []; //Names only
|
||||
this.upgrades = []; // Names of upgrades
|
||||
this.augmentations = []; // Names of augmentations only
|
||||
}
|
||||
|
||||
//Same formula for Player
|
||||
// Same skill calculation formula as Player
|
||||
GangMember.prototype.calculateSkill = function(exp, mult=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"];
|
||||
}
|
||||
|
||||
//Gains are per cycle
|
||||
// Gains are per cycle
|
||||
GangMember.prototype.calculateRespectGain = function(gang) {
|
||||
const task = this.getTask();
|
||||
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) {
|
||||
const task = this.getTask();
|
||||
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) {return 0;}
|
||||
var statWeight = (task.hackWeight/100) * this.hack +
|
||||
(task.strWeight/100) * this.str +
|
||||
(task.defWeight/100) * this.def +
|
||||
(task.dexWeight/100) * this.dex +
|
||||
(task.agiWeight/100) * this.agi +
|
||||
(task.chaWeight/100) * this.cha;
|
||||
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) { return 0; }
|
||||
let statWeight = (task.hackWeight / 100) * this.hack +
|
||||
(task.strWeight / 100) * this.str +
|
||||
(task.defWeight / 100) * this.def +
|
||||
(task.dexWeight / 100) * this.dex +
|
||||
(task.agiWeight / 100) * this.agi +
|
||||
(task.chaWeight / 100) * this.cha;
|
||||
statWeight -= (3.5 * task.difficulty);
|
||||
if (statWeight <= 0) { return 0; }
|
||||
const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100;
|
||||
if (isNaN(territoryMult) || territoryMult <= 0) { return 0; }
|
||||
if (task.baseWanted < 0) {
|
||||
return 0.5 * task.baseWanted * statWeight * territoryMult;
|
||||
return 0.4 * task.baseWanted * statWeight * territoryMult;
|
||||
} 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
|
||||
GangMember.prototype.getAscensionResults = function() {
|
||||
// Calculate ascension bonus to stat multipliers.
|
||||
// This is based on the current number of multipliers from Non-Augmentation upgrades
|
||||
// + Ascension Bonus = N% of current bonus from Augmentations
|
||||
/**
|
||||
* Calculate ascension bonus to stat multipliers.
|
||||
* 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 str = 1;
|
||||
let def = 1;
|
||||
@@ -837,7 +860,7 @@ GangMember.fromJSON = function(value) {
|
||||
|
||||
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,
|
||||
params={baseRespect: 0, baseWanted: 0, baseMoney: 0,
|
||||
hackWeight: 0, strWeight: 0, defWeight: 0,
|
||||
@@ -935,7 +958,7 @@ GangMemberUpgrade.prototype.createDescription = function() {
|
||||
this.desc = lines.join("<br>");
|
||||
}
|
||||
|
||||
//Passes in a GangMember object
|
||||
// Passes in a GangMember object
|
||||
GangMemberUpgrade.prototype.apply = function(member) {
|
||||
if (this.mults.str != null) { member.str_mult *= this.mults.str; }
|
||||
if (this.mults.def != null) { member.def_mult *= this.mults.def; }
|
||||
@@ -971,7 +994,7 @@ gangMemberUpgradesMetadata.forEach((e) => {
|
||||
Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
||||
const boxId = "gang-member-upgrade-popup-box";
|
||||
if (UIElems.gangMemberUpgradeBoxOpened) {
|
||||
//Already opened, refreshing
|
||||
// Already opened, refreshing
|
||||
if (UIElems.gangMemberUpgradeBoxElements == null || UIElems.gangMemberUpgradeBox == null || UIElems.gangMemberUpgradeBoxContent == null) {
|
||||
console.error("Refreshing Gang member upgrade box throws error because required elements are null");
|
||||
return;
|
||||
@@ -991,7 +1014,7 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//New popup
|
||||
// New popup
|
||||
UIElems.gangMemberUpgradeBoxFilter = createElement("input", {
|
||||
type:"text", placeholder:"Filter gang members",
|
||||
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) {
|
||||
var container = createElement("div", {
|
||||
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",
|
||||
});
|
||||
|
||||
//Already purchased upgrades
|
||||
// Already purchased upgrades
|
||||
const ownedUpgradesElements = [];
|
||||
function pushOwnedUpgrade(upgName) {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
@@ -1071,7 +1094,7 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
|
||||
container.appendChild(ownedUpgrades);
|
||||
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 armorUpgrades = [];
|
||||
const vehicleUpgrades = [];
|
||||
@@ -1203,18 +1226,18 @@ Gang.prototype.displayGangContent = function(player) {
|
||||
if (!UIElems.gangContentCreated || UIElems.gangContainer == null) {
|
||||
UIElems.gangContentCreated = true;
|
||||
|
||||
//Create gang container
|
||||
// Create gang container
|
||||
UIElems.gangContainer = createElement("div", {
|
||||
id:"gang-container", class:"generic-menupage-container",
|
||||
});
|
||||
|
||||
//Get variables
|
||||
// Get variables
|
||||
var facName = this.facName,
|
||||
members = this.members,
|
||||
wanted = this.wanted,
|
||||
respect = this.respect;
|
||||
|
||||
//Back button
|
||||
// Back button
|
||||
UIElems.gangContainer.appendChild(createElement("a", {
|
||||
class:"a-link-button", display:"inline-block", innerText:"Back",
|
||||
clickListener:()=>{
|
||||
@@ -1224,7 +1247,7 @@ Gang.prototype.displayGangContent = function(player) {
|
||||
}
|
||||
}));
|
||||
|
||||
//Buttons to switch between panels
|
||||
// Buttons to switch between panels
|
||||
UIElems.managementButton = createElement("a", {
|
||||
id:"gang-management-subpage-button", class:"a-link-button-inactive",
|
||||
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.territoryButton);
|
||||
|
||||
//Subpage for managing gang members
|
||||
// Subpage for managing gang members
|
||||
UIElems.gangManagementSubpage = createElement("div", {
|
||||
display:"block", id:"gang-management-subpage",
|
||||
});
|
||||
@@ -1340,6 +1363,7 @@ Gang.prototype.displayGangContent = function(player) {
|
||||
innerText: "Cancel",
|
||||
});
|
||||
createPopup(popupId, [txt, br, nameInput, yesBtn, noBtn]);
|
||||
nameInput.focus();
|
||||
}
|
||||
});
|
||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitMemberButton);
|
||||
@@ -1352,7 +1376,7 @@ Gang.prototype.displayGangContent = function(player) {
|
||||
});
|
||||
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.gangExpandAllButton = createElement("a", {
|
||||
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.gangManageEquipmentButton);
|
||||
|
||||
//Gang Member list
|
||||
// Gang Member list
|
||||
UIElems.gangMemberList = createElement("ul", {id:"gang-member-list"});
|
||||
this.displayGangMemberList();
|
||||
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberList);
|
||||
|
||||
//Subpage for seeing gang territory information
|
||||
// Subpage for seeing gang territory information
|
||||
UIElems.gangTerritorySubpage = createElement("div", {
|
||||
id:"gang-territory-subpage", display:"none"
|
||||
});
|
||||
|
||||
//Info text for territory page
|
||||
// Info text for territory page
|
||||
UIElems.gangTerritoryDescText = createElement("p", {
|
||||
width:"70%",
|
||||
innerHTML:
|
||||
@@ -1575,7 +1599,7 @@ Gang.prototype.updateGangContent = function() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Update information for overall gang
|
||||
// Update information for overall gang
|
||||
if (UIElems.gangInfo instanceof Element) {
|
||||
var faction = Factions[this.facName];
|
||||
var rep;
|
||||
@@ -1585,7 +1609,7 @@ Gang.prototype.updateGangContent = function() {
|
||||
rep = faction.playerReputation;
|
||||
}
|
||||
removeChildrenFromElement(UIElems.gangInfo);
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Respect
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Respect
|
||||
display: "inline-block",
|
||||
innerText: "Respect: " + formatNumber(this.respect, 6) +
|
||||
" (" + formatNumber(5*this.respectGainRate, 6) + " / sec)",
|
||||
@@ -1596,7 +1620,7 @@ Gang.prototype.updateGangContent = function() {
|
||||
}));
|
||||
UIElems.gangInfo.appendChild(createElement("br"));
|
||||
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
|
||||
display: "inline-block",
|
||||
innerText: "Wanted Level: " + formatNumber(this.wanted, 6) +
|
||||
" (" + formatNumber(5*this.wantedGainRate, 6) + " / sec)",
|
||||
@@ -1608,20 +1632,20 @@ Gang.prototype.updateGangContent = function() {
|
||||
|
||||
var wantedPenalty = this.getWantedPenalty();
|
||||
wantedPenalty = (1 - wantedPenalty) * 100;
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
|
||||
display: "inline-block",
|
||||
innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`,
|
||||
tooltip: "Penalty for respect and money gain rates due to Wanted Level"
|
||||
}));
|
||||
UIElems.gangInfo.appendChild(createElement("br"));
|
||||
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
|
||||
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
|
||||
display: "inline-block",
|
||||
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`,
|
||||
}));
|
||||
UIElems.gangInfo.appendChild(createElement("br"));
|
||||
|
||||
//Fix some rounding issues graphically
|
||||
// Fix some rounding issues graphically
|
||||
var territoryMult = AllGangs[this.facName].territory * 100;
|
||||
let displayNumber;
|
||||
if (territoryMult <= 0) {
|
||||
@@ -1656,7 +1680,7 @@ Gang.prototype.updateGangContent = function() {
|
||||
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 respectCost = this.getRespectNeededToRecruitMember();
|
||||
|
||||
@@ -1674,14 +1698,14 @@ Gang.prototype.updateGangContent = function() {
|
||||
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) {
|
||||
this.updateGangMemberDisplayElement(this.members[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Takes in a GangMember object
|
||||
// Takes in a GangMember object
|
||||
Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
||||
if (!UIElems.gangContentCreated) { return; }
|
||||
const name = memberObj.name;
|
||||
@@ -1822,7 +1846,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
|
||||
taskDiv.appendChild(taskSelector);
|
||||
taskDiv.appendChild(gainInfo);
|
||||
|
||||
//Panel for Description of task
|
||||
// Panel for Description of task
|
||||
var taskDescDiv = createElement("div", {
|
||||
class:"gang-member-info-div",
|
||||
id: name + "gang-member-task-desc",
|
||||
|
||||
1
src/Hacking/README.md
Normal file
1
src/Hacking/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Implementation of underlying Hacking mechanics
|
||||
53
src/Hacking/netscriptCanHack.ts
Normal file
53
src/Hacking/netscriptCanHack.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Functions used to determine whether the target can be hacked (or grown/weakened).
|
||||
* Meant to be used for Netscript implementation
|
||||
*
|
||||
* The returned status object's message should be used for logging in Netscript
|
||||
*/
|
||||
import { IReturnStatus } from "../types";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Server } from "../Server/Server";
|
||||
|
||||
function baseCheck(server: Server, fnName: string): IReturnStatus {
|
||||
const hostname = server.hostname;
|
||||
|
||||
if (!("requiredHackingSkill" in server)) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot ${fnName} ${hostname} server because it is a Hacknet Node`
|
||||
}
|
||||
}
|
||||
|
||||
if (server.hasAdminRights === false) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot ${fnName} ${hostname} server because you do not have root access`,
|
||||
}
|
||||
}
|
||||
|
||||
return { res: true }
|
||||
}
|
||||
|
||||
export function netscriptCanHack(server: Server, p: IPlayer): IReturnStatus {
|
||||
const initialCheck = baseCheck(server, "hack");
|
||||
if (!initialCheck.res) { return initialCheck; }
|
||||
|
||||
let s = server;
|
||||
if (s.requiredHackingSkill > p.hacking_skill) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot hack ${server.hostname} server because your hacking skill is not high enough`,
|
||||
}
|
||||
}
|
||||
|
||||
return { res: true }
|
||||
}
|
||||
|
||||
export function netscriptCanGrow(server: Server): IReturnStatus {
|
||||
return baseCheck(server, "grow");
|
||||
}
|
||||
|
||||
export function netscriptCanWeaken(server: Server): IReturnStatus {
|
||||
return baseCheck(server, "weaken");
|
||||
}
|
||||
587
src/Hacknet/HacknetHelpers.jsx
Normal file
587
src/Hacknet/HacknetHelpers.jsx
Normal file
@@ -0,0 +1,587 @@
|
||||
import {
|
||||
HacknetNode,
|
||||
BaseCostForHacknetNode,
|
||||
HacknetNodePurchaseNextMult,
|
||||
HacknetNodeMaxLevel,
|
||||
HacknetNodeMaxRam,
|
||||
HacknetNodeMaxCores
|
||||
} from "./HacknetNode";
|
||||
import {
|
||||
HacknetServer,
|
||||
BaseCostForHacknetServer,
|
||||
HacknetServerPurchaseMult,
|
||||
HacknetServerMaxLevel,
|
||||
HacknetServerMaxRam,
|
||||
HacknetServerMaxCores,
|
||||
HacknetServerMaxCache,
|
||||
MaxNumberHacknetServers
|
||||
} from "./HacknetServer";
|
||||
import { HashManager } from "./HashManager";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { generateRandomContract } from "../CodingContractGenerator";
|
||||
import {
|
||||
iTutorialSteps,
|
||||
iTutorialNextStep,
|
||||
ITutorial
|
||||
} from "../InteractiveTutorial";
|
||||
import { Player } from "../Player";
|
||||
import { AddToAllServers, AllServers } from "../Server/AllServers";
|
||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
|
||||
import { getElementById } from "../../utils/uiHelpers/getElementById";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { HacknetRoot } from "./ui/Root";
|
||||
|
||||
let hacknetNodesDiv;
|
||||
function hacknetNodesInit() {
|
||||
hacknetNodesDiv = document.getElementById("hacknet-nodes-container");
|
||||
document.removeEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
|
||||
// Returns a boolean indicating whether the player has Hacknet Servers
|
||||
// (the upgraded form of Hacknet Nodes)
|
||||
export function hasHacknetServers() {
|
||||
return (Player.bitNodeN === 9 || SourceFileFlags[9] > 0);
|
||||
}
|
||||
|
||||
export function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* END INTERACTIVE TUTORIAL */
|
||||
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
if (hasHacknetServers()) {
|
||||
const cost = getCostOfNextHacknetServer();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetServer is NaN`)
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
Player.loseMoney(cost);
|
||||
const server = Player.createHacknetServer();
|
||||
updateHashManagerCapacity();
|
||||
|
||||
return numOwned;
|
||||
} else {
|
||||
const cost = getCostOfNextHacknetNode();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetNode is NaN`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
|
||||
// Auto generate a name for the Node
|
||||
const name = "hacknet-node-" + numOwned;
|
||||
const node = new HacknetNode(name, Player.hacknet_node_money_mult);
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(node);
|
||||
|
||||
return numOwned;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasMaxNumberHacknetServers() {
|
||||
return hasHacknetServers() && Player.hacknetNodes.length >= MaxNumberHacknetServers;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetNode() {
|
||||
// Cost increases exponentially based on how many you own
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetNodePurchaseNextMult;
|
||||
|
||||
return BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetServer() {
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetServerPurchaseMult;
|
||||
|
||||
if (numOwned > MaxNumberHacknetServers) { return Infinity; }
|
||||
|
||||
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node
|
||||
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player.hacknet_node_level_cost_mult))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
let levelsToMax = maxLevel - nodeObj.level;
|
||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.hacknet_node_level_cost_mult))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
while (min <= max) {
|
||||
var curr = (min + max) / 2 | 0;
|
||||
if (curr !== maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult)) &&
|
||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player.hacknet_node_level_cost_mult))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player.hacknet_node_ram_cost_mult))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let levelsToMax;
|
||||
if (nodeObj instanceof HacknetServer) {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.maxRam));
|
||||
} else {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
|
||||
}
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player.hacknet_node_ram_cost_mult))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//We'll just loop until we find the max
|
||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player.hacknet_node_ram_cost_mult))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player.hacknet_node_core_cost_mult))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cores;
|
||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.hacknet_node_core_cost_mult))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult)) &&
|
||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player.hacknet_node_core_cost_mult))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cache;
|
||||
if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
// Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) &&
|
||||
!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
max = curr -1 ;
|
||||
} else if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!routing.isOn(Page.HacknetNodes)) { return; }
|
||||
|
||||
ReactDOM.render(<HacknetRoot />, hacknetNodesDiv);
|
||||
}
|
||||
|
||||
export function clearHacknetNodesUI() {
|
||||
if (hacknetNodesDiv instanceof HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(hacknetNodesDiv);
|
||||
}
|
||||
|
||||
hacknetNodesDiv.style.display = "none";
|
||||
}
|
||||
|
||||
export function processHacknetEarnings(numCycles) {
|
||||
// Determine if player has Hacknet Nodes or Hacknet Servers, then
|
||||
// call the appropriate function
|
||||
if (Player.hacknetNodes.length === 0) { return 0; }
|
||||
if (hasHacknetServers()) {
|
||||
return processAllHacknetServerEarnings(numCycles);
|
||||
} else if (Player.hacknetNodes[0] instanceof HacknetNode) {
|
||||
return processAllHacknetNodeEarnings(numCycles);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function processAllHacknetNodeEarnings(numCycles) {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function processSingleHacknetNodeEarnings(numCycles, nodeObj) {
|
||||
const totalEarnings = nodeObj.process(numCycles);
|
||||
Player.gainMoney(totalEarnings);
|
||||
Player.recordMoneySource(totalEarnings, "hacknetnode");
|
||||
|
||||
return totalEarnings;
|
||||
}
|
||||
|
||||
function processAllHacknetServerEarnings(numCycles) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`)
|
||||
}
|
||||
|
||||
let hashes = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
Player.hashManager.storeHashes(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) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
console.error(`Player does not have a HashManager`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// HashManager handles the transaction. This just needs to actually implement
|
||||
// the effects of the upgrade
|
||||
if (Player.hashManager.upgrade(upgName)) {
|
||||
const upg = HashUpgrades[upgName];
|
||||
|
||||
switch (upgName) {
|
||||
case "Sell for Money": {
|
||||
Player.gainMoney(upg.value);
|
||||
Player.recordMoneySource(upg.value, "hacknetnode");
|
||||
break;
|
||||
}
|
||||
case "Sell for Corporation Funds": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
Player.corporation.funds = Player.corporation.funds.plus(upg.value);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Reduce Minimum Security": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMinimumSecurity(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Increase Maximum Money": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMaximumMoney(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Improve Studying": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Improve Gym Training": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Exchange for Corporation Research": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
for (const division of Player.corporation.divisions) {
|
||||
division.sciResearch.qty += upg.value;
|
||||
}
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Exchange for Bladeburner Rank": {
|
||||
// This will throw if player isnt in Bladeburner
|
||||
try {
|
||||
Player.bladeburner.changeRank(upg.value);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Exchange for Bladeburner SP": {
|
||||
// This will throw if player isn't in Bladeburner
|
||||
try {
|
||||
// As long as we don't change `Bladeburner.totalSkillPoints`, this
|
||||
// shouldn't affect anything else
|
||||
Player.bladeburner.skillPoints += upg.value;
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Generate Coding Contract": {
|
||||
generateRandomContract();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`)
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
211
src/Hacknet/HacknetNode.ts
Normal file
211
src/Hacknet/HacknetNode.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Hacknet Node Class
|
||||
*
|
||||
* Hacknet Nodes are specialized machines that passively earn the player money over time.
|
||||
* They can be upgraded to increase their production
|
||||
*/
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Node production
|
||||
export const HacknetNodeMoneyGainPerLevel: number = 1.6; // Base production per level
|
||||
|
||||
// Constants for Hacknet Node purchase/upgrade costs
|
||||
export const BaseCostForHacknetNode: number = 1000;
|
||||
export const BaseCostFor1GBOfRamHacknetNode: number = 30e3;
|
||||
export const BaseCostForHacknetNodeCore: number = 500e3;
|
||||
export const HacknetNodePurchaseNextMult: number = 1.85; // Multiplier when purchasing an additional hacknet node
|
||||
export const HacknetNodeUpgradeLevelMult: number = 1.04; // Multiplier for cost when upgrading level
|
||||
export const HacknetNodeUpgradeRamMult: number = 1.28; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetNodeUpgradeCoreMult: number = 1.48; // Multiplier for cost when buying another core
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Nodes
|
||||
export const HacknetNodeMaxLevel: number = 200;
|
||||
export const HacknetNodeMaxRam: number = 64;
|
||||
export const HacknetNodeMaxCores: number = 16;
|
||||
|
||||
export class HacknetNode implements IHacknetNode {
|
||||
/**
|
||||
* Initiatizes a HacknetNode object from a JSON save state.
|
||||
*/
|
||||
static fromJSON(value: any): HacknetNode {
|
||||
return Generic_fromJSON(HacknetNode, value.data);
|
||||
}
|
||||
|
||||
// Node's number of cores
|
||||
cores: number = 1;
|
||||
|
||||
// Node's Level
|
||||
level: number = 1;
|
||||
|
||||
// Node's production per second
|
||||
moneyGainRatePerSecond: number = 0;
|
||||
|
||||
// Identifier for Node. Includes the full "name" (hacknet-node-N)
|
||||
name: string;
|
||||
|
||||
// How long this Node has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Node's RAM (GB)
|
||||
ram: number = 1;
|
||||
|
||||
// Total money earned by this Node
|
||||
totalMoneyGenerated: number = 0;
|
||||
|
||||
constructor(name: string="", prodMult: number=1) {
|
||||
this.name = name;
|
||||
|
||||
this.updateMoneyGainRate(prodMult);
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's number of cores
|
||||
calculateCoreUpgradeCost(levels: number=1, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetNodeMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const coreBaseCost = BaseCostForHacknetNodeCore;
|
||||
const mult = HacknetNodeUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
|
||||
++currentCores;
|
||||
}
|
||||
|
||||
totalCost *= costMult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's level
|
||||
calculateLevelUpgradeCost(levels: number=1, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetNodeMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetNodeUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return BaseCostForHacknetNode / 2 * totalMultiplier * costMult;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's RAM
|
||||
calculateRamUpgradeCost(levels: number=1, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.ram >= HacknetNodeMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.ram));
|
||||
let currentRam = this.ram;
|
||||
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBOfRamHacknetNode;
|
||||
let mult = Math.pow(HacknetNodeUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
|
||||
totalCost *= costMult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Node in the game loop.
|
||||
// Returns the amount of money generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
let gain = this.moneyGainRatePerSecond * seconds;
|
||||
if (isNaN(gain)) {
|
||||
console.error(`Hacknet Node ${this.name} calculated earnings of NaN`);
|
||||
gain = 0;
|
||||
}
|
||||
|
||||
this.totalMoneyGenerated += gain;
|
||||
this.onlineTimeSeconds += seconds;
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
||||
// Upgrade this Node's number of cores, if possible
|
||||
// Returns a boolean indicating whether new cores were successfully bought
|
||||
upgradeCore(levels: number=1, prodMult: number): void {
|
||||
this.cores = Math.min(HacknetNodeMaxCores, Math.round(this.cores + levels));
|
||||
this.updateMoneyGainRate(prodMult);
|
||||
}
|
||||
|
||||
// Upgrade this Node's level, if possible
|
||||
// Returns a boolean indicating whether the level was successfully updated
|
||||
upgradeLevel(levels: number=1, prodMult: number): void {
|
||||
this.level = Math.min(HacknetNodeMaxLevel, Math.round(this.level + levels));
|
||||
this.updateMoneyGainRate(prodMult);
|
||||
}
|
||||
|
||||
// Upgrade this Node's RAM, if possible
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
upgradeRam(levels: number=1, prodMult: number): void {
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
this.ram *= 2; // Ram is always doubled
|
||||
}
|
||||
this.ram = Math.round(this.ram); // Handle any floating point precision issues
|
||||
this.updateMoneyGainRate(prodMult);
|
||||
}
|
||||
|
||||
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
|
||||
updateMoneyGainRate(prodMult: number): void {
|
||||
//How much extra $/s is gained per level
|
||||
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
|
||||
|
||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||
Math.pow(1.035, this.ram - 1) *
|
||||
((this.cores + 5) / 6) *
|
||||
prodMult *
|
||||
BitNodeMultipliers.HacknetNodeMoney;
|
||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetNode", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetNode = HacknetNode;
|
||||
252
src/Hacknet/HacknetServer.ts
Normal file
252
src/Hacknet/HacknetServer.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Hacknet Servers - Reworked Hacknet Node mechanic for BitNode-9
|
||||
*/
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
|
||||
import {
|
||||
Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver
|
||||
} from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Server stats/production
|
||||
export const HacknetServerHashesPerLevel: number = 0.001;
|
||||
|
||||
// Constants for Hacknet Server purchase/upgrade costs
|
||||
export const BaseCostForHacknetServer: number = 50e3;
|
||||
export const BaseCostFor1GBHacknetServerRam: number = 200e3;
|
||||
export const BaseCostForHacknetServerCore: number = 1e6;
|
||||
export const BaseCostForHacknetServerCache: number = 10e6;
|
||||
|
||||
export const HacknetServerPurchaseMult: number = 3.2; // Multiplier for puchasing an additional Hacknet Server
|
||||
export const HacknetServerUpgradeLevelMult: number = 1.1; // Multiplier for cost when upgrading level
|
||||
export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
|
||||
export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
|
||||
|
||||
export const MaxNumberHacknetServers: number = 20; // Max number of Hacknet Servers you can own
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Server
|
||||
export const HacknetServerMaxLevel: number = 300;
|
||||
export const HacknetServerMaxRam: number = 8192;
|
||||
export const HacknetServerMaxCores: number = 128;
|
||||
export const HacknetServerMaxCache: number = 15;
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
organizationName?: string;
|
||||
}
|
||||
|
||||
export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
// Initializes a HacknetServer Object from a JSON save state
|
||||
static fromJSON(value: any): HacknetServer {
|
||||
return Generic_fromJSON(HacknetServer, value.data);
|
||||
}
|
||||
|
||||
// Cache level. Affects hash Capacity
|
||||
cache: number = 1;
|
||||
|
||||
// Number of cores. Improves hash production
|
||||
cores: number = 1;
|
||||
|
||||
// Number of hashes that can be stored by this Hacknet Server
|
||||
hashCapacity: number = 0;
|
||||
|
||||
// Hashes produced per second
|
||||
hashRate: number = 0;
|
||||
|
||||
// Similar to Node level. Improves hash production
|
||||
level: number = 1;
|
||||
|
||||
// How long this HacknetServer has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Total number of hashes earned by this
|
||||
totalHashesGenerated: number = 0;
|
||||
|
||||
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
|
||||
super(params);
|
||||
|
||||
this.maxRam = 1;
|
||||
this.updateHashCapacity();
|
||||
}
|
||||
|
||||
calculateCacheUpgradeCost(levels: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cache >= HacknetServerMaxCache) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCacheMult;
|
||||
let totalCost = 0;
|
||||
let currentCache = this.cache;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCache - 1);
|
||||
++currentCache;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCache;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateCoreUpgradeCost(levels: number, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetServerMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCores-1);
|
||||
++currentCores;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCore;
|
||||
totalCost *= costMult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateLevelUpgradeCost(levels: number, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetServerMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return 10 * BaseCostForHacknetServer * totalMultiplier * costMult;
|
||||
}
|
||||
|
||||
calculateRamUpgradeCost(levels: number, costMult: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.maxRam >= HacknetServerMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.maxRam));
|
||||
let currentRam = this.maxRam;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBHacknetServerRam;
|
||||
let mult = Math.pow(HacknetServerUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
totalCost *= costMult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Server in the game loop.
|
||||
// Returns the number of hashes generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
|
||||
return this.hashRate * seconds;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the cache was successfully upgraded
|
||||
upgradeCache(levels: number): void {
|
||||
this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels));
|
||||
this.updateHashCapacity();
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the number of cores was successfully upgraded
|
||||
upgradeCore(levels: number, prodMult: number): void {
|
||||
this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels));
|
||||
this.updateHashRate(prodMult);
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the level was successfully upgraded
|
||||
upgradeLevel(levels: number, prodMult: number): void {
|
||||
this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels));
|
||||
this.updateHashRate(prodMult);
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
upgradeRam(levels: number, prodMult: number): boolean {
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
this.maxRam *= 2;
|
||||
}
|
||||
this.maxRam = Math.min(HacknetServerMaxRam, Math.round(this.maxRam));
|
||||
this.updateHashRate(prodMult);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever a script is run, we must update this server's hash rate
|
||||
*/
|
||||
runScript(script: RunningScript, prodMult?: number): void {
|
||||
super.runScript(script);
|
||||
if (prodMult != null && typeof prodMult === "number") {
|
||||
this.updateHashRate(prodMult);
|
||||
}
|
||||
}
|
||||
|
||||
updateHashCapacity(): void {
|
||||
this.hashCapacity = 32 * Math.pow(2, this.cache);
|
||||
}
|
||||
|
||||
updateHashRate(prodMult: number): void {
|
||||
const baseGain = HacknetServerHashesPerLevel * this.level;
|
||||
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
|
||||
const coreMultiplier = 1 + (this.cores - 1) / 5;
|
||||
const ramRatio = (1 - this.ramUsed / this.maxRam);
|
||||
|
||||
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
|
||||
|
||||
this.hashRate = hashRate * prodMult * BitNodeMultipliers.HacknetNodeMoney;
|
||||
|
||||
if (isNaN(this.hashRate)) {
|
||||
this.hashRate = 0;
|
||||
console.error(`Error calculating Hacknet Server hash production. This is a bug. Please report to game dev`, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetServer", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetServer = HacknetServer;
|
||||
154
src/Hacknet/HashManager.ts
Normal file
154
src/Hacknet/HashManager.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* This is a central class for storing and managing the player's hashes,
|
||||
* which are generated by Hacknet Servers
|
||||
*
|
||||
* It is also used to keep track of what upgrades the player has bought with
|
||||
* his hashes, and contains method for grabbing the data/multipliers from
|
||||
* those upgrades
|
||||
*/
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { IMap } from "../types";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export class HashManager {
|
||||
// Initiatizes a HashManager object from a JSON save state.
|
||||
static fromJSON(value: any): HashManager {
|
||||
return Generic_fromJSON(HashManager, value.data);
|
||||
}
|
||||
|
||||
// Max number of hashes this can hold. Equal to the sum of capacities of
|
||||
// all Hacknet Servers
|
||||
capacity: number = 0;
|
||||
|
||||
// Number of hashes currently in storage
|
||||
hashes: number = 0;
|
||||
|
||||
// Map of Hash Upgrade Name -> levels in that upgrade
|
||||
upgrades: IMap<number> = {};
|
||||
|
||||
constructor() {
|
||||
for (const name in HashUpgrades) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic helper function for getting a multiplier from a HashUpgrade
|
||||
*/
|
||||
getMult(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Could not find Hash Study upgrade`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1 + ((upg.value * currLevel) / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves studying. This returns that multiplier
|
||||
*/
|
||||
getStudyMult(): number {
|
||||
const upgName = "Improve Studying";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves gym training. This returns that multiplier
|
||||
*/
|
||||
getTrainingMult(): number {
|
||||
const upgName = "Improve Gym Training";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cost (in hashes) of an upgrade
|
||||
*/
|
||||
getUpgradeCost(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.getUpgradeCost(): ${upgName}`);
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return upg.getCost(currLevel);
|
||||
}
|
||||
|
||||
prestige(): void {
|
||||
for (const name in HashUpgrades) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
this.hashes = 0;
|
||||
|
||||
// When prestiging, player's hacknet nodes are always reset. So capacity = 0
|
||||
this.updateCapacity(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts an upgrade and refunds the hashes used to buy it
|
||||
*/
|
||||
refundUpgrade(upgName: string): void {
|
||||
const upg = HashUpgrades[upgName];
|
||||
|
||||
// Reduce the level first, so we get the right cost
|
||||
--this.upgrades[upgName];
|
||||
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null || currLevel < 0) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const cost = upg.getCost(currLevel);
|
||||
this.hashes += cost;
|
||||
}
|
||||
|
||||
storeHashes(numHashes: number): void {
|
||||
this.hashes += numHashes;
|
||||
this.hashes = Math.min(this.hashes, this.capacity);
|
||||
}
|
||||
|
||||
updateCapacity(newCap: number): void {
|
||||
if (newCap < 0) {
|
||||
this.capacity = 0;
|
||||
}
|
||||
this.capacity = Math.max(newCap, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean indicating whether or not the upgrade was successfully purchased
|
||||
* Note that this does NOT actually implement the effect
|
||||
*/
|
||||
upgrade(upgName: string): boolean {
|
||||
const upg = HashUpgrades[upgName];
|
||||
if (upg == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = this.getUpgradeCost(upgName);
|
||||
|
||||
if (this.hashes < cost) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hashes -= cost;
|
||||
++this.upgrades[upgName];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Serialize the current object to a JSON save state.
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HashManager", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HashManager = HashManager;
|
||||
61
src/Hacknet/HashUpgrade.ts
Normal file
61
src/Hacknet/HashUpgrade.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Object representing an upgrade that can be purchased with hashes
|
||||
*/
|
||||
export interface IConstructorParams {
|
||||
cost?: number;
|
||||
costPerLevel: number;
|
||||
desc: string;
|
||||
hasTargetServer?: boolean;
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export class HashUpgrade {
|
||||
/**
|
||||
* If the upgrade has a flat cost (never increases), it goes here
|
||||
* Otherwise, this property should be undefined
|
||||
*
|
||||
* This property overrides the 'costPerLevel' property
|
||||
*/
|
||||
cost?: number;
|
||||
|
||||
/**
|
||||
* Base cost for this upgrade. Every time the upgrade is purchased,
|
||||
* its cost increases by this same amount (so its 1x, 2x, 3x, 4x, etc.)
|
||||
*/
|
||||
costPerLevel: number = 0;
|
||||
|
||||
/**
|
||||
* Description of what the upgrade does
|
||||
*/
|
||||
desc: string = "";
|
||||
|
||||
/**
|
||||
* Boolean indicating that this upgrade's effect affects a single server,
|
||||
* the "target" server
|
||||
*/
|
||||
hasTargetServer: boolean = false;
|
||||
|
||||
// Name of upgrade
|
||||
name: string = "";
|
||||
|
||||
// Generic value used to indicate the potency/amount of this upgrade's effect
|
||||
// The meaning varies between different upgrades
|
||||
value: number = 0;
|
||||
|
||||
constructor(p: IConstructorParams) {
|
||||
if (p.cost != null) { this.cost = p.cost; }
|
||||
|
||||
this.costPerLevel = p.costPerLevel;
|
||||
this.desc = p.desc;
|
||||
this.hasTargetServer = p.hasTargetServer ? p.hasTargetServer : false;
|
||||
this.name = p.name;
|
||||
this.value = p.value;
|
||||
}
|
||||
|
||||
getCost(levels: number): number {
|
||||
if (typeof this.cost === "number") { return this.cost; }
|
||||
|
||||
return Math.round((levels + 1) * this.costPerLevel);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user