Compare commits

...

100 Commits

Author SHA1 Message Date
danielyxie
59cf1d5baf v0.47.2 2019-07-15 21:40:43 -07:00
danielyxie
916ef06913 isBusy() now returns true if you are in a hacking mission 2019-07-15 21:40:43 -07:00
danielyxie
91ee65a101 Converted everything to use acorn npm package. Updated acorn packages to latest version. Updated acorn parsing to use ES9 2019-07-15 21:40:43 -07:00
danielyxie
042f926700 Minor bugfixes with killing Netscript scripts, and cleaned up text 2019-07-15 21:40:43 -07:00
danielyxie
c0432359c3 Implemented 'kill by PID' functionality 2019-07-15 21:40:43 -07:00
danielyxie
fbf5545708 Color-coded BitNode selection screen and added SF information 2019-07-15 21:40:43 -07:00
danielyxie
6ae7b0136c Minor bugfixes for a variety of NS functions. After infiltration, UI returns to corp page rather than city page 2019-07-15 21:40:43 -07:00
danielyxie
200ccd3ad0 Renamed getNoSuchRunningScriptErrorMessage() function 2019-07-15 21:40:43 -07:00
danielyxie
31f97f74fd Fix GH Issue #621: workForFaction() now properly accounts for disabled/enabled logs 2019-07-15 21:40:43 -07:00
danielyxie
4cabd2e4ed Implement GH Issue #620: Add tail() Netscript function 2019-07-15 21:40:43 -07:00
danielyxie
8be7fa9157 Implemented GH Issue #599: Added 'solarized dark' theme to CodeMirror 2019-07-15 21:40:43 -07:00
danielyxie
8ddf7dfbd4 Fix GH Issue #616: Stock Market UI throws error for certain locales because the price format length is too high 2019-07-15 21:40:43 -07:00
danielyxie
571ddb109a Fix GH Issue #632: 'Create Program' link visibility should now be properly dynamically evaluated 2019-07-15 21:40:43 -07:00
danielyxie
b2772bbfc1 Fixed GH Issue #641 2019-07-15 21:40:43 -07:00
Papayaman1000
b82d7c12af Removed outdated information
Firefox added flagless support for dynamic imports in version 67, so it no longer needs special mention.
2019-06-28 13:23:24 -07:00
danielyxie
4476d6b258 Merge pull request #642 from danielyxie/dev
Dev
2019-06-28 13:22:53 -07:00
danielyxie
433b399de9 v0.47.1 2019-06-28 09:28:08 -07:00
danielyxie
58d04c0cbb Updated documentation changelog 2019-06-27 00:01:46 -07:00
danielyxie
e3a74f23a1 ps and top Terminal commands now show script pid. Updated version and changelog 2019-06-27 00:01:06 -07:00
danielyxie
3a374de210 killWorkerScript() now takes an optional argument for whether to rerenderUI. This is used to batch UI updates on killall() 2019-06-24 22:48:54 -07:00
danielyxie
4cc6437408 Updated WorkerScript-related code for the workerScripts array->map change 2019-06-24 22:48:54 -07:00
danielyxie
821725cf4d Initial commit for converting workerScripts pool to Map data structure 2019-06-24 22:48:54 -07:00
danielyxie
931de230ae Minor rebalancing to stock market. Updated documentation and tests for recent changes 2019-06-11 00:18:14 -07:00
danielyxie
7301946236 Added and Updated Stock Market tests for the new changes 2019-06-09 21:23:48 -07:00
danielyxie
a15041da75 More rebalancing for stock market changes. Transactions now affect second-order forecast (very slightly) 2019-06-09 15:12:33 -07:00
danielyxie
00f8c0a51f Removed debug stuff for beta branch 2019-06-04 21:31:45 -07:00
danielyxie
63483837bc Fixed version in changelog 2019-06-03 23:10:10 -07:00
danielyxie
dc5f4e6694 Updated documentation for new Stock Market changes. More rebalancing for recent stock market changes 2019-06-03 23:05:25 -07:00
danielyxie
35f8a5115a Finished implementing player influencing on stock 2nd-order forecasts. Balanced recent stock market changes 2019-06-03 22:21:36 -07:00
danielyxie
8398fd47f0 Changed the way stock market cycles occur. Stock second-order forecast now changes normally like before 2019-06-02 23:29:56 -07:00
danielyxie
9d7c869c0a Merge branch 'dev' of https://github.com/danielyxie/bitburner into dev 2019-06-02 20:57:57 -07:00
danielyxie
74587f269e Updated changelog 2019-06-02 20:57:39 -07:00
danielyxie
6a3ffff3ad Merge pull request #630 from jaguilar/ns2_recompile_when_script_write
Recompile scripts when updated via ns.write
2019-06-02 20:56:21 -07:00
J
2f8eac07ee fix indentation 2019-06-02 23:53:07 -04:00
J
3eaefa01f9 Merge branch 'dev' into ns2_recompile_when_script_write 2019-06-02 23:50:57 -04:00
danielyxie
b250af913d Merge pull request #629 from jaguilar/ns2_recompile_stale_deps
recompile ns2 scripts if dependencies change
2019-06-02 20:46:32 -07:00
danielyxie
0b4968d148 Re-added the getStockPurchaseCost() and getStockSaleGain() functions so we don't break user scripts 2019-06-02 20:28:02 -07:00
J
8817d179c6 Merge branch 'dev' into ns2_recompile_stale_deps 2019-06-02 23:03:20 -04:00
James Aguilar
e5e3fec1a9 close dialog boxes on escape 2019-06-02 20:00:14 -07:00
Heikki Aitakangas
eecb0c0f01 Update script.module immediately, so we avoid accidentally evaling the same module several times 2019-06-02 19:58:01 -07:00
James Aguilar
d45689c7df use sequence numbers rather than timestamps for script expiration 2019-06-02 20:26:06 -04:00
James Aguilar
2201dfc371 fix typo 2019-06-02 15:40:13 -04:00
James Aguilar
3ef9042051 fix two cases where markUpdated was not properly called 2019-06-02 15:38:45 -04:00
James Aguilar
1236ad252b markUpdated() for Script 2019-06-02 15:32:35 -04:00
James Aguilar
65331ab22e recompile ns2 scripts if dependencies change 2019-06-02 15:21:08 -04:00
danielyxie
c485fdfa87 Removed stock market price movement. Now only forecast is influenced by big transactions 2019-05-22 19:12:06 -07:00
danielyxie
6effda29a9 Implemented second-order forecasts for stocks 2019-05-22 17:23:30 -07:00
danielyxie
7035154454 Merge branch 'master' of https://github.com/danielyxie/bitburner into dev 2019-05-20 14:45:30 -07:00
danielyxie
d7f3ab9177 Tempered the affect that stock market txs have on stock forecasts 2019-05-20 14:44:51 -07:00
danielyxie
3660dde75f Merge branch 'master' of https://github.com/danielyxie/bitburner into dev 2019-05-19 14:07:16 -07:00
danielyxie
99688b78c7 Temporary rebalancing for v0.47.0 2019-05-19 14:06:53 -07:00
danielyxie
6841f24932 Optimized Largest Prime factor coding contract solver. 2019-05-19 13:56:49 -07:00
Sotisi
9f94d0838a removed faulty else, was left prior by accident. 2019-05-17 15:54:36 -07:00
Sotisi
086fc67ecc Fixed O(n) runtime of "Find Largest Prime Factor" for high primes to 0(sqrt(n)). 2019-05-17 15:54:36 -07:00
danielyxie
a2551f98c2 Fixed documentation typos in v0.47.0 2019-05-17 15:51:28 -07:00
danielyxie
95c928afc9 Merge branch 'dev' of https://github.com/danielyxie/bitburner into dev 2019-05-17 13:51:48 -07:00
danielyxie
8a00e6e532 Merge pull request #614 from danielyxie/active-scripts-ui-and-implementation-rework
Active scripts ui and implementation rework
2019-05-17 13:50:55 -07:00
danielyxie
287a97aea6 Fixed merge conflcits with dev 2019-05-17 13:50:27 -07:00
danielyxie
664267bff0 Removed unused imports in engine 2019-05-17 13:47:35 -07:00
danielyxie
2597b33f81 Finished refactoring augmentations page UI to use react 2019-05-17 13:47:35 -07:00
danielyxie
9442b348e6 Refactored SourceFile-related code to TypeScript 2019-05-17 13:47:35 -07:00
danielyxie
3b7f9c9fb0 Fixed issues with Active Scripts UI. Implemented event emitter for Active Scripts UI 2019-05-17 13:41:16 -07:00
danielyxie
20ca7533b0 Merge pull request #612 from danielyxie/dev
v0.47.0
2019-05-17 13:09:04 -07:00
danielyxie
15a324a946 v0.47.0 release 2019-05-17 13:07:11 -07:00
danielyxie
94175877d7 Changed stock market price movements so that upward and downward movements use different trackers. Forecast can no longer be inverted due to price movements. Updated stock market unit tests 2019-05-16 23:55:21 -07:00
danielyxie
c1ec3c5eba Finished refactoring Active Scripts UI into React/TypeScript. Currently untested 2019-05-16 23:44:59 -07:00
danielyxie
42804b0cd3 Refactored 'workerScripts' array and killWorkerScript() fn to be their own modules in TypeScript 2019-05-15 23:05:36 -07:00
danielyxie
b1248521f3 Removed unused imports in engine 2019-05-15 00:37:11 -07:00
danielyxie
b744997c72 Finished refactoring augmentations page UI to use react 2019-05-15 00:15:07 -07:00
danielyxie
2d37409392 Refactored SourceFile-related code to TypeScript 2019-05-14 20:56:59 -07:00
danielyxie
bd02e724e5 Fixed several more bugs. Rebalanced stock market changes to make the effects a bit less potent for now 2019-05-14 04:23:55 -07:00
danielyxie
fef7aaba8f Adding more directory-related unit tests. Several more bug fixes and QoL improvements 2019-05-14 01:35:37 -07:00
danielyxie
1775ea86ff Fixed corporation bug 2019-05-11 20:03:36 -07:00
danielyxie
b0918d7bd3 Fixed numerous reported bugs. Refactored some of the directory-related code. Added documentation for MasonDs changes to hack/grow/weaken 2019-05-11 19:20:20 -07:00
danielyxie
29e0ce5f96 Fixed merge conflicts 2019-05-11 02:20:09 -07:00
Mason Dechaineux
44c26165f4 (feat) optional threads argument to netscript functions hack, weaken, grow 2019-05-11 02:05:40 -07:00
danielyxie
9dd68947f1 Added Dynamic RAM calculation unit tests 2019-05-10 02:24:50 -07:00
danielyxie
db5fdb1fcb Last fixes to unit test build configuration 2019-05-09 19:51:56 -07:00
danielyxie
74e72854d8 Configured unit test webpack build to work 2019-05-09 19:36:04 -07:00
danielyxie
ece246b391 Converted mocha unit tests to run using mocha-webpack (mochapack) package 2019-05-09 19:03:13 -07:00
danielyxie
cdb5dfec62 Resolved more circular dependencies. Installed acorn-walk and imported it in RamCalculations using ES6 modules. Fixed bugs in stock market rework 2019-05-06 18:01:06 -07:00
danielyxie
8a5b6f6cbc Refactored stock buying/selling code into its own file. Refactored WorkerScript & NetscriptEnvironment into their own Typescript classes. Refactored a ton of code to remove circular dependencies 2019-05-04 21:03:40 -07:00
danielyxie
585e1ac7aa Stock transactions can now influence forecast in addition to price. Several more minor bug/UI fixes 2019-05-01 15:20:14 -07:00
danielyxie
8726946d4a Merge branch 'dev' of https://github.com/danielyxie/bitburner into dev 2019-04-30 02:27:58 -07:00
danielyxie
064008d200 Removed 'age' stat from Corporation employees 2019-04-30 02:27:48 -07:00
danielyxie
d955280f90 Re-sleeves can no longer have the Neuroflux Governor aug. This is to prevent bugs 2019-04-30 02:27:13 -07:00
danielyxie
580a7fac24 Bug fixes for v0.47.0. Fixed the BUY MAX feature for new stock market. Added collapse/expand tickers buttons for new stock market UI 2019-04-29 20:54:20 -07:00
danielyxie
9df054dd0c Merge branch 'dev' of https://github.com/danielyxie/bitburner into dev 2019-04-28 23:22:02 -07:00
Mason Dechaineux
8fa7b112e1 Correctly throw error when ns.run() is called with 0 threads 2019-04-28 23:21:55 -07:00
danielyxie
dd9df0a18c Fixed Stock Market UI. Added documentation for stock market changes 2019-04-28 23:21:32 -07:00
danielyxie
3a601a015d Added unit tests for Stock Market. Removed dependencies from package.json, since they're sourced directly in tests/index.html 2019-04-28 23:21:32 -07:00
danielyxie
87b4698d5b Fixed issues relating to stock price movements 2019-04-28 23:21:32 -07:00
danielyxie
67632ced09 Fixed Stock Market UI issues. Added warnings for price movements 2019-04-28 23:21:32 -07:00
danielyxie
d7fb335815 Fixed Stock market UI compilation errors and removed refactored code 2019-04-28 23:21:32 -07:00
danielyxie
4809a21e38 Finished React components for new Stock Market UI 2019-04-28 23:21:32 -07:00
danielyxie
6b3646e981 Added spread and price movement properties to stocks. Refactored Stock Market implementation code 2019-04-28 23:21:32 -07:00
danielyxie
0c64bf470a Merge branch 'master' of https://github.com/danielyxie/bitburner into dev 2019-04-20 23:37:51 -07:00
danielyxie
99e034921e Updated docuemntation changelog for v0.46.3 2019-04-20 23:37:37 -07:00
danielyxie
7a3a3de7d1 v0.46.3 2019-04-20 23:37:37 -07:00
danielyxie
f91c5bd7b9 Updated docuemntation changelog for v0.46.3 2019-04-19 22:29:33 -07:00
219 changed files with 15412 additions and 11181 deletions

6
.gitignore vendored
View File

@@ -3,6 +3,6 @@ Netburner.txt
/doc/build /doc/build
/node_modules /node_modules
/dist/*.map /dist/*.map
/tests/*.map /test/*.map
/tests/*.bundle.* /test/*.bundle.*
/tests/*.css /test/*.css

126
css/activescripts.scss Normal file
View File

@@ -0,0 +1,126 @@
@import "theme";
.active-scripts-list {
list-style-type: none;
}
#active-scripts-container {
position: fixed;
padding-top: 10px;
> p {
width: 70%;
margin: 6px;
padding: 4px;
}
.accordion-header {
> pre {
color: white;
}
}
}
.active-scripts-server-header {
background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none;
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&.active, &:hover {
background-color: #555;
}
}
.active-scripts-server-header.active {
&:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&:hover {
background-color: #666;
}
}
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none;
div, ul, ul > li {
background-color: #555;
}
}
.active-scripts-script-header {
background-color: #555;
border: none;
color: var(--my-font-color);
cursor: pointer;
display: block;
outline: none;
padding: 4px 25px 4px 10px;
position: relative;
text-align: left;
width: auto;
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
&:hover,
&.active:hover {
background-color: #666;
}
&.active {
background-color: #555;
}
}
.active-scripts-script-panel {
background-color: #555;
display: none;
font-size: 14px;
margin-bottom: 6px;
padding: 0 18px;
width: auto;
pre, h2, ul, li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}

31
css/augmentations.scss Normal file
View File

@@ -0,0 +1,31 @@
/**
* Styling for the Augmentations UI. This is the page that displays all of the
* player's owned and purchased Augmentations and Source-Files. It also allows
* the player to install Augmentations
*/
@import "theme";
#augmentations-container {
position: fixed;
padding-top: 10px;
}
#augmentations-content {
> p {
font-size: $defaultFontSize * 0.875;
width: 70%;
}
}
.augmentations-list {
button,
div {
color: var(--my-font-color);
text-decoration: none;
}
button {
padding: 4px;
}
}

View File

@@ -18,126 +18,6 @@
position: fixed; position: fixed;
} }
/* Active scripts */
.active-scripts-list {
list-style-type: none;
}
#active-scripts-container {
position: fixed;
padding-top: 10px;
}
#active-scripts-text,
#active-scripts-total-prod {
width: 70%;
margin: 6px;
padding: 4px;
}
.active-scripts-server-header {
background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none;
}
.active-scripts-server-header.active,
.active-scripts-server-header:hover {
background-color: #555;
}
.active-scripts-server-header.active:hover {
background-color: #666;
}
.active-scripts-server-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
.active-scripts-server-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none;
}
.active-scripts-server-panel div,
.active-scripts-server-panel ul,
.active-scripts-server-panel ul > li {
background-color: #555;
}
.active-scripts-script-header {
background-color: #555;
color: var(--my-font-color);
padding: 4px 25px 4px 10px;
cursor: pointer;
width: auto;
text-align: left;
border: none;
outline: none;
position: relative;
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
&:hover,
&.active:hover {
background-color: #666;
}
&.active {
background-color: #555;
}
}
.active-scripts-script-panel {
padding: 0 18px;
background-color: #555;
width: auto;
display: none;
margin-bottom: 6px;
p, h2, ul, li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}
/* World */ /* World */
#world-container { #world-container {
position: fixed; position: fixed;
@@ -185,19 +65,6 @@
width: 70%; width: 70%;
} }
#faction-donate-amount-txt,
#faction-donate-input {
padding: 6px;
margin: 6px;
display: inline-block;
color: var(--my-font-color);
background-color: #000;
}
#faction-donate-amount-txt {
width: 50%;
}
#faction-container p, #faction-container p,
#faction-container pre { #faction-container pre {
padding: 4px 6px; padding: 4px 6px;
@@ -213,45 +80,12 @@
word-wrap: break-word; /* Internet Explorer 5.5+ */ word-wrap: break-word; /* Internet Explorer 5.5+ */
} }
/* Faction Augmentations */
#faction-augmentations-container {
position: fixed;
padding-top: 10px;
p, a, ul, h1 {
margin: 8px;
padding: 4px;
}
}
/* World */ /* World */
#world-container li { #world-container li {
margin: 0 0 15px 0; margin: 0 0 15px 0;
list-style-type: none; list-style-type: none;
} }
/* Augmentations */
#augmentations-container {
position: fixed;
padding-top: 10px;
}
.augmentations-list {
button,
div {
color: var(--my-font-color);
text-decoration: none;
}
button {
padding: 2px 5px;
}
div {
padding: 6px;
}
}
/* Tutorial */ /* Tutorial */
#tutorial-container { #tutorial-container {
position: fixed; position: fixed;

View File

@@ -7,16 +7,28 @@
position: fixed; position: fixed;
} }
.bitnode { .bitnode {
color: #00f; &.level-0 {
} color: red;
}
.bitnode-destroyed { &.level-1 {
color: #f00; color: yellow;
} }
.bitnode:hover, &.level-2 {
.bitnode-destroyed:hover { color: #48D1CC;
color: #fff; }
&.level-3 {
color: blue;
}
&.unimplemented {
color: gray;
}
&:hover {
color: #fff;
}
} }

View File

@@ -7,35 +7,45 @@
p { p {
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
} }
a { a {
font-size: $defaultFontSize * 0.875; font-size: $defaultFontSize * 0.875;
} }
h2 { }
.stock-market-info-and-purchases {
> h2 {
display: block;
margin-top: 10px; margin-top: 10px;
margin-left: 10px; margin-left: 10px;
}
> p {
display: block; display: block;
margin-left: 10px;
width: 70%;
}
> a, > button {
margin: 10px;
} }
} }
#stock-market-list li { #stock-market-list {
button { list-style: none;
font-size: $defaultFontSize;
li {
button {
font-size: $defaultFontSize;
}
} }
} }
#stock-market-container p {
padding: 6px;
margin: 6px;
width: 70%;
}
#stock-market-container a {
margin: 10px;
}
#stock-market-watchlist-filter { #stock-market-watchlist-filter {
display: block;
margin: 5px 5px 5px 10px;
padding: 4px;
width: 50%; width: 50%;
margin-left: 10px;
} }
.stock-market-input { .stock-market-input {
@@ -47,14 +57,36 @@
color: var(--my-font-color); color: var(--my-font-color);
} }
.stock-market-price-movement-warning {
border: 1px solid white;
color: red;
margin: 2px;
padding: 2px;
}
.stock-market-position-text { .stock-market-position-text {
color: #fff; color: #fff;
display: inline-block; display: block;
p {
color: #fff;
display: inline-block;
margin: 4px;
}
h3 {
margin: 4px;
}
} }
.stock-market-order-list { .stock-market-order-list {
overflow-y: auto; overflow-y: auto;
max-height: 100px; max-height: 100px;
li {
color: #fff;
padding: 4px;
}
} }
.stock-market-order-cancel-btn { .stock-market-order-cancel-btn {

View File

@@ -243,8 +243,8 @@ a:visited {
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
.accordion-header { .accordion-header {
background-color: #444; background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
font-size: $defaultFontSize * 1.25;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 4px 6px; padding: 4px 6px;
cursor: pointer; cursor: pointer;

20
dist/engine.bundle.js vendored

File diff suppressed because one or more lines are too long

2
dist/engineStyle.bundle.js vendored Normal file
View File

@@ -0,0 +1,2 @@
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([362,0]),o()}({305:function(n,t,o){},307:function(n,t,o){},309:function(n,t,o){},311:function(n,t,o){},313:function(n,t,o){},315:function(n,t,o){},317:function(n,t,o){},319:function(n,t,o){},321:function(n,t,o){},323:function(n,t,o){},325:function(n,t,o){},327:function(n,t,o){},329:function(n,t,o){},331:function(n,t,o){},333:function(n,t,o){},335:function(n,t,o){},337:function(n,t,o){},339:function(n,t,o){},341:function(n,t,o){},343:function(n,t,o){},345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},362:function(n,t,o){"use strict";o.r(t);o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345),o(343),o(341),o(339),o(337),o(335),o(333),o(331),o(329),o(327),o(325),o(323),o(321),o(319),o(317),o(315),o(313),o(311),o(309),o(307),o(305)}});
//# sourceMappingURL=engineStyle.bundle.js.map

View File

@@ -1,46 +1,3 @@
/* COLORS */
/* Attributes */
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0px 0px 6px; }
.CodeMirror {
height: 100%;
width: 100%;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
font-size: 16px; }
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8F908A; }
.CodeMirror-selection-highlight-scrollbar {
background-color: #8F908A; }
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404F7D; }
/**
* Vim command display
*/
#codemirror-vim-command-display-wrapper {
background-color: white;
font-size: 13px;
height: 30px;
margin-left: 6px; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/* COLORS */ /* COLORS */
@@ -304,8 +261,8 @@ a:visited {
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
.accordion-header { .accordion-header {
background-color: #444; background-color: #444;
font-size: 20px;
color: #fff; color: #fff;
font-size: 20px;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 4px 6px; padding: 4px 6px;
cursor: pointer; cursor: pointer;
@@ -933,6 +890,147 @@ button {
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */ /* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
/* COLORS */
/* Attributes */
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0px 0px 6px; }
.CodeMirror {
height: 100%;
width: 100%;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
font-size: 16px; }
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8F908A; }
.CodeMirror-selection-highlight-scrollbar {
background-color: #8F908A; }
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404F7D; }
/**
* Vim command display
*/
#codemirror-vim-command-display-wrapper {
background-color: white;
font-size: 13px;
height: 30px;
margin-left: 6px; }
/* COLORS */
/* Attributes */
.active-scripts-list {
list-style-type: none; }
#active-scripts-container {
position: fixed;
padding-top: 10px; }
#active-scripts-container > p {
width: 70%;
margin: 6px;
padding: 4px; }
#active-scripts-container .accordion-header > pre {
color: white; }
.active-scripts-server-header {
background-color: #444;
font-size: 20px;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none; }
.active-scripts-server-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.active-scripts-server-header.active, .active-scripts-server-header:hover {
background-color: #555; }
.active-scripts-server-header.active:after {
content: "\2796";
/* "minus" sign (-) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.active-scripts-server-header.active:hover {
background-color: #666; }
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none; }
.active-scripts-server-panel div, .active-scripts-server-panel ul, .active-scripts-server-panel ul > li {
background-color: #555; }
.active-scripts-script-header {
background-color: #555;
border: none;
color: var(--my-font-color);
cursor: pointer;
display: block;
outline: none;
padding: 4px 25px 4px 10px;
position: relative;
text-align: left;
width: auto; }
.active-scripts-script-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px; }
.active-scripts-script-header.active:after {
content: "\2796";
/* "minus" sign (-) */ }
.active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
background-color: #666; }
.active-scripts-script-header.active {
background-color: #555; }
.active-scripts-script-panel {
background-color: #555;
display: none;
font-size: 14px;
margin-bottom: 6px;
padding: 0 18px;
width: auto; }
.active-scripts-script-panel pre, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/** /**
@@ -1007,107 +1105,6 @@ button {
padding-top: 10px; padding-top: 10px;
position: fixed; } position: fixed; }
/* Active scripts */
.active-scripts-list {
list-style-type: none; }
#active-scripts-container {
position: fixed;
padding-top: 10px; }
#active-scripts-text,
#active-scripts-total-prod {
width: 70%;
margin: 6px;
padding: 4px; }
.active-scripts-server-header {
background-color: #444;
font-size: 20px;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none; }
.active-scripts-server-header.active,
.active-scripts-server-header:hover {
background-color: #555; }
.active-scripts-server-header.active:hover {
background-color: #666; }
.active-scripts-server-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.active-scripts-server-header.active:after {
content: "\2796";
/* "minus" sign (-) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none; }
.active-scripts-server-panel div,
.active-scripts-server-panel ul,
.active-scripts-server-panel ul > li {
background-color: #555; }
.active-scripts-script-header {
background-color: #555;
color: var(--my-font-color);
padding: 4px 25px 4px 10px;
cursor: pointer;
width: auto;
text-align: left;
border: none;
outline: none;
position: relative; }
.active-scripts-script-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px; }
.active-scripts-script-header.active:after {
content: "\2796";
/* "minus" sign (-) */ }
.active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
background-color: #666; }
.active-scripts-script-header.active {
background-color: #555; }
.active-scripts-script-panel {
padding: 0 18px;
background-color: #555;
width: auto;
display: none;
margin-bottom: 6px; }
.active-scripts-script-panel p, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%; }
/* World */ /* World */
#world-container { #world-container {
position: fixed; position: fixed;
@@ -1147,17 +1144,6 @@ button {
margin: 6px; margin: 6px;
width: 70%; } width: 70%; }
#faction-donate-amount-txt,
#faction-donate-input {
padding: 6px;
margin: 6px;
display: inline-block;
color: var(--my-font-color);
background-color: #000; }
#faction-donate-amount-txt {
width: 50%; }
#faction-container p, #faction-container p,
#faction-container pre { #faction-container pre {
padding: 4px 6px; padding: 4px 6px;
@@ -1176,35 +1162,11 @@ button {
word-wrap: break-word; word-wrap: break-word;
/* Internet Explorer 5.5+ */ } /* Internet Explorer 5.5+ */ }
/* Faction Augmentations */
#faction-augmentations-container {
position: fixed;
padding-top: 10px; }
#faction-augmentations-container p, #faction-augmentations-container a, #faction-augmentations-container ul, #faction-augmentations-container h1 {
margin: 8px;
padding: 4px; }
/* World */ /* World */
#world-container li { #world-container li {
margin: 0 0 15px 0; margin: 0 0 15px 0;
list-style-type: none; } list-style-type: none; }
/* Augmentations */
#augmentations-container {
position: fixed;
padding-top: 10px; }
.augmentations-list button,
.augmentations-list div {
color: var(--my-font-color);
text-decoration: none; }
.augmentations-list button {
padding: 2px 5px; }
.augmentations-list div {
padding: 6px; }
/* Tutorial */ /* Tutorial */
#tutorial-container { #tutorial-container {
position: fixed; position: fixed;
@@ -1286,6 +1248,29 @@ button {
display: inline; display: inline;
width: 25%; } width: 25%; }
/**
* Styling for the Augmentations UI. This is the page that displays all of the
* player's owned and purchased Augmentations and Source-Files. It also allows
* the player to install Augmentations
*/
/* COLORS */
/* Attributes */
#augmentations-container {
position: fixed;
padding-top: 10px; }
#augmentations-content > p {
font-size: 14px;
width: 70%; }
.augmentations-list button,
.augmentations-list div {
color: var(--my-font-color);
text-decoration: none; }
.augmentations-list button {
padding: 4px; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/** /**
@@ -1294,14 +1279,22 @@ button {
#red-pill-container { #red-pill-container {
position: fixed; } position: fixed; }
.bitnode { .bitnode.level-0 {
color: #00f; } color: red; }
.bitnode-destroyed { .bitnode.level-1 {
color: #f00; } color: yellow; }
.bitnode:hover, .bitnode.level-2 {
.bitnode-destroyed:hover { color: #48D1CC; }
.bitnode.level-3 {
color: blue; }
.bitnode.unimplemented {
color: gray; }
.bitnode:hover {
color: #fff; } color: #fff; }
/* COLORS */ /* COLORS */
@@ -1313,25 +1306,30 @@ button {
font-size: 13px; } font-size: 13px; }
#stock-market-container a { #stock-market-container a {
font-size: 14px; } font-size: 14px; }
#stock-market-container h2 {
margin-top: 10px;
margin-left: 10px;
display: block; }
#stock-market-list li button { .stock-market-info-and-purchases > h2 {
font-size: 16px; } display: block;
margin-top: 10px;
margin-left: 10px; }
#stock-market-container p { .stock-market-info-and-purchases > p {
padding: 6px; display: block;
margin: 6px; margin-left: 10px;
width: 70%; } width: 70%; }
#stock-market-container a { .stock-market-info-and-purchases > a, .stock-market-info-and-purchases > button {
margin: 10px; } margin: 10px; }
#stock-market-list {
list-style: none; }
#stock-market-list li button {
font-size: 16px; }
#stock-market-watchlist-filter { #stock-market-watchlist-filter {
width: 50%; display: block;
margin-left: 10px; } margin: 5px 5px 5px 10px;
padding: 4px;
width: 50%; }
.stock-market-input { .stock-market-input {
display: inline-block; display: inline-block;
@@ -1341,13 +1339,28 @@ button {
border: 1px solid #fff; border: 1px solid #fff;
color: var(--my-font-color); } color: var(--my-font-color); }
.stock-market-price-movement-warning {
border: 1px solid white;
color: red;
margin: 2px;
padding: 2px; }
.stock-market-position-text { .stock-market-position-text {
color: #fff; color: #fff;
display: inline-block; } display: block; }
.stock-market-position-text p {
color: #fff;
display: inline-block;
margin: 4px; }
.stock-market-position-text h3 {
margin: 4px; }
.stock-market-order-list { .stock-market-order-list {
overflow-y: auto; overflow-y: auto;
max-height: 100px; } max-height: 100px; }
.stock-market-order-list li {
color: #fff;
padding: 4px; }
.stock-market-order-cancel-btn { .stock-market-order-cancel-btn {
background-color: #000; background-color: #000;
@@ -4866,4 +4879,4 @@ html {
margin-right: 0px; } margin-right: 0px; }
/*# sourceMappingURL=engine.css.map*/ /*# sourceMappingURL=engineStyle.css.map*/

623
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -86,8 +86,11 @@ Sleeve memory dictates what a sleeve's synchronization will be when its reset by
switching BitNodes. For example, if a sleeve has a memory of 10, then when you switching BitNodes. For example, if a sleeve has a memory of 10, then when you
switch BitNodes its synchronization will initially be set to 10, rather than 1. switch BitNodes its synchronization will initially be set to 10, rather than 1.
Memory can only be increased by purchasing upgrades from The Covenant. Memory can only be increased by purchasing upgrades from The Covenant. Just like
It is a persistent stat, meaning it never gets reset back to 1. the ability to purchase additional sleeves, this is only available in BitNodes-10
and above, and is only available after defeating BitNode-10 at least once.
Memory is a persistent stat, meaning it never gets reset back to 1.
The maximum possible value for a sleeve's memory is 100. The maximum possible value for a sleeve's memory is 100.
Re-sleeving Re-sleeving

View File

@@ -204,8 +204,8 @@ The list contains the name of (i.e. the value returned by
| | | the string, the result should be an array with only an empty string. | | | | the string, the result should be an array with only an empty string. |
| | | | | | | |
| | | Examples: | | | | Examples: |
| | | ()())() -> ["()()()", "(())()"] | | | | ()())() -> [()()(), (())()] |
| | | (a)())() -> ["(a)()()", "(a())()"] | | | | (a)())() -> [(a)()(), (a())()] |
| | | )( -> [""] | | | | )( -> [""] |
+------------------------------------+------------------------------------------------------------------------------------------+ +------------------------------------+------------------------------------------------------------------------------------------+
| Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target | | Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target |
@@ -214,10 +214,12 @@ The list contains the name of (i.e. the value returned by
| | | | | | | |
| | | The answer should be provided as an array of strings containing the valid expressions. | | | | The answer should be provided as an array of strings containing the valid expressions. |
| | | | | | | |
| | | NOTE: Numbers in an expression cannot have leading 0's |
| | | |
| | | Examples: | | | | Examples: |
| | | Input: digits = "123", target = 6 | | | | Input: digits = "123", target = 6 |
| | | Output: ["1+2+3", "1*2*3"] | | | | Output: [1+2+3, 1*2*3] |
| | | | | | | |
| | | Input: digits = "105", target = 5 | | | | Input: digits = "105", target = 5 |
| | | Output: ["1*0+5", "10-5"] | | | | Output: [1*0+5, 10-5] |
+------------------------------------+------------------------------------------------------------------------------------------+ +------------------------------------+------------------------------------------------------------------------------------------+

View File

@@ -7,10 +7,14 @@ buy and sell stocks in order to make money.
The WSE can be found in the 'City' tab, and is accessible in every city. The WSE can be found in the 'City' tab, and is accessible in every city.
Automating the Stock Market Fundamentals
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------
You can write scripts to perform automatic and algorithmic trading on the Stock Market. The Stock Market is not as simple as "buy at price X and sell at price Y". The following
See :ref:`netscript_tixapi` for more details. are several fundamental concepts you need to understand about the stock market.
.. note:: For those that have experience with finance/trading/investing, please be aware
that the game's stock market does not function exactly like it does in the real
world. So these concepts below should seem similar, but won't be exactly the same.
Positions: Long vs Short Positions: Long vs Short
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,16 +25,73 @@ is the exact opposite. In a Short position you purchase shares of a stock and
earn a profit if the price of that stock decreases. This is also called 'shorting' earn a profit if the price of that stock decreases. This is also called 'shorting'
a stock. a stock.
NOTE: Shorting stocks is not available immediately, and must be unlocked later in the .. note:: Shorting stocks is not available immediately, and must be unlocked later in the
game. game.
Forecast & Second-Order Forecast
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A stock's forecast is its likelihood of increasing or decreasing in value. The
forecast is typically represented by its probability of increasing in either
a decimal or percentage form. For example, a forecast of 70% means the stock
has a 70% chance of increasing and a 30% chance of decreasing.
A stock's second-order forecast is the target value that its forecast trends towards.
For example, if a stock has a forecast of 60% and a second-order forecast of 70%,
then the stock's forecast should slowly trend towards 70% over time. However, this is
determined by RNG so there is a chance that it may never reach 70%.
Both the forecast and the second-order forecast change over time.
A stock's forecast can be viewed after purchasing Four Sigma (4S) Market Data
access. This lets you see the forecast info on the Stock Market UI. If you also
purchase access to the 4S Market Data TIX API, then you can view a stock's forecast
using the :js:func:`getStockForecast` function.
A stock's second-order forecast is always hidden.
.. _gameplay_stock_market_spread:
Spread (Bid Price & Ask Price)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The **bid price** is the maximum price at which someone will buy a stock on the
stock market.
The **ask price** is the minimum price that a seller is willing to receive for a stock
on the stock market
The ask price will always be higher than the bid price (This is because if a seller
is willing to receive less than the bid price, that transaction is guaranteed to
happen). The difference between the bid and ask price is known as the **spread**.
A stock's "price" will be the average of the bid and ask price.
The bid and ask price are important because these are the prices at which a
transaction actually occurs. If you purchase a stock in the long position, the cost
of your purchase depends on that stock's ask price. If you then try to sell that
stock (still in the long position), the price at which you sell is the stock's
bid price. Note that this is reversed for a short position. Purchasing a stock
in the short position will occur at the stock's bid price, and selling a stock
in the short position will occur at the stock's ask price.
Transactions Influencing Stock Forecast
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Buying or selling a large number of shares
of a stock will influence that stock's forecast & second-order forecast.
The forecast is the likelihood that the stock will increase or decrease in price.
The magnitude of this effect depends on the number of shares being transacted.
More shares will have a bigger effect.
The effect that transactions have on a stock's second-order forecast is
significantly smaller than the effect on its forecast.
.. _gameplay_stock_market_order_types:
Order Types Order Types
^^^^^^^^^^^ ^^^^^^^^^^^
There are three different types of orders you can make to buy or sell stocks on the exchange: There are three different types of orders you can make to buy or sell stocks on the exchange:
Market Order, Limit Order, and Stop Order. Market Order, Limit Order, and Stop Order.
Note that Limit Orders and Stop Orders are not available immediately, and must be unlocked .. note:: Limit Orders and Stop Orders are not available immediately, and must be unlocked
later in the game. later in the game.
When you place a Market Order to buy or sell a stock, the order executes immediately at When you place a Market Order to buy or sell a stock, the order executes immediately at
whatever the current price of the stock is. For example if you choose to short a stock whatever the current price of the stock is. For example if you choose to short a stock
@@ -71,3 +132,77 @@ A Limit Order to sell will execute if the stock's price <= order's price
A Stop Order to buy will execute if the stock's price <= order's price A Stop Order to buy will execute if the stock's price <= order's price
A Stop Order to sell will execute if the stock's price >= order's price. A Stop Order to sell will execute if the stock's price >= order's price.
.. _gameplay_stock_market_player_actions_influencing_stock:
Player Actions Influencing Stocks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is possible for your actions elsewhere in the game to influence the stock market.
Hacking
If a server has a corresponding stock (e.g. *foodnstuff* server -> FoodNStuff
stock), then hacking that server can decrease the stock's second-order
forecast. This causes the corresponding stock's forecast to trend downwards in value
over time.
This effect only occurs if you set the *stock* option to
true when calling the :js:func:`hack` function. The chance that hacking a
server will cause this effect is based on what percentage of the
server's total money you steal.
A single hack will have a minor
effect, but continuously hacking a server for lots of money over time
will have a noticeable effect in making the stock's forecast trend downwards.
Growing
If a server has a corresponding stock (e.g. *foodnstuff* server -> FoodNStuff
stock), then growing that server's money can increase the stock's
second-order forecast. This causes the corresponding stock's
forecast to trend upwards in value over time.
This effect only occurs if you set the *stock* option to true when calling the
:js:func:`grow` function. The chance that growing a server will cause this
effect is based on what percentage of the server's total money to add to it.
A single grow operation will have a minor effect, but continuously growing
a server for lots of money over time will have a noticeable effect in making
the stock's forecast trend upwards.
Working for a Company
If a company has a corresponding stock, then working for that company will
increase the corresponding stock's second-order forecast. This will
cause the stock's forecast to (slowly) trend upwards in value
over time.
The potency of this effect is based on how "effective" you are when you work
(i.e. its based on your stats and multipliers).
Automating the Stock Market
---------------------------
You can write scripts to perform automatic and algorithmic trading on the Stock Market.
See :ref:`netscript_tixapi` for more details.
Under the Hood
--------------
Stock prices are updated very ~6 seconds.
Whether a stock's price moves up or down is determined by RNG. However,
stocks have properties that can influence the way their price moves. These properties
are hidden, although some of them can be made visible by purchasing the
Four Sigma (4S) Market Data upgrade. Some examples of these properties are:
* Volatility
* Likelihood of increasing or decreasing (i.e. the stock's forecast)
* Likelihood of forecast increasing or decreasing (i.e. the stock's second-order forecast)
* How easily a stock's price/forecast is influenced by transactions
* Spread percentage
* Maximum price (not a real maximum, more of a "soft cap")
Each stock has its own unique values for these properties.
Offline Progression
-------------------
The Stock Market does not change or process anything while the game has closed.
However, it does accumulate time when offline. This accumulated time allows
the stock market to run 50% faster when the game is opened again. This means
that stock prices will update every ~4 seconds instead of 6.

View File

@@ -312,9 +312,12 @@ kill
^^^^ ^^^^
$ kill [script name] [args...] $ kill [script name] [args...]
$ kill [pid]
Kill the script specified by the script name and arguments. Each argument must Kill the script specified by the script filename and arguments OR by its PID.
be separated by a space. Remember that a running script is uniquely identified
If you are killing the script using its filename and arguments, then each argument
must be separated by a space. Remember that a running script is uniquely identified
by both its name and the arguments that are used to start it. So, if a script by both its name and the arguments that are used to start it. So, if a script
was ran with the following arguments:: was ran with the following arguments::
@@ -324,8 +327,7 @@ Then to kill this script the same arguments would have to be used::
$ kill foo.script 50e3 sigma-cosmetics $ kill foo.script 50e3 sigma-cosmetics
Note that after issuing the 'kill' command for a script, it may take a few seconds for If you are killing the script using its PID, then the PID argument must be numeric.
the script to actually stop running.
killall killall
^^^^^^^ ^^^^^^^
@@ -403,7 +405,7 @@ to convert from script to text file, or vice versa.
This function can also be used to rename files. This function can also be used to rename files.
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the .. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
full filepath. It cannot be a directory. full filepath. It cannot be a directory.
Examples:: Examples::
@@ -511,6 +513,8 @@ sudov
Prints whether or not you have root access to the current server. Prints whether or not you have root access to the current server.
.. _tail_terminal_command:
tail tail
^^^^ ^^^^

View File

@@ -3,11 +3,113 @@
Changelog Changelog
========= =========
v0.47.2 - 7/15/2019
-------------------
**Netscript Changes**
* Added tail() Netscript function
* hacknet.getNodeStats() function now returns an additional property for Hacknet Servers: hashCapacity
* When writing to a file, the write() function now casts the data being written to a string (using String())
* BitNode-selection page now shows what Source-File level you have for each BitNode
* Overloaded kill() function so that you can kill a script by its PID
* spawn() now only takes 10 seconds to run (decreased from 20 seconds)
* run() and exec() now return the PID of the newly-executed scripts, rather than a boolean
* (A PID is just a positive integer)
* run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS
* Script parsing and RAM calculations now support ES9
* installAugmentations() no longer has a return value since it causes all scripts to die
* isBusy() now returns true if you are in a Hacking Mission
* Bug fix: workForFaction() function now properly accounts for disabled logs
* Bug fix: RAM should now be properly calculated when running a callback script with installAugmentations()
* Bug fix: Fixed bug that caused scripts killed by exit()/spawn() to "clean up" twice
**Misc Changes**
* The 'kill' Terminal command can now kill a script by its PID
* Added 'Solarized Dark' theme to CodeMirror editor
* After Infiltration, you will now return to the company page rather than the city page
* Bug fix: Stock Market UI should no longer crash for certain locale settings
* Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files)
* Bug fix: Fixed an issue that allowed you to increase money on servers with a 'maxMoney' of 0 (like CSEC)
* Bug fix: Scripts no longer persist if they were started with syntax/import errors
* Bug fix: 'hack' and 'analyze' Terminal commands are now blocking
* Bug fix: Exp earned by duplicate sleeves at universities/gyms now takes hash upgrades into account
v0.47.1 - 6/27/2019
-------------------
* Stock Market changes:
* Transactions no longer influence stock prices (but they still influence forecast)
* Changed the way stocks behave, particularly with regard to how the stock forecast occasionally "flips"
* Hacking & growing a server can potentially affect the way the corresponding stock's forecast changes
* Working for a company positively affects the way the corresponding stock's forecast changes
* Scripts now start/stop instantly
* Improved performance when starting up many copies of a new NetscriptJS script (by Ornedan)
* Improved performance when killing scripts
* Dialog boxes can now be closed with the ESC key (by jaguilar)
* NetscriptJS scripts should now be "re-compiled" if their dependencies change (by jaguilar)
* write() function should now properly cause NetscriptJS scripts to "re-compile" (by jaguilar)
v0.47.0 - 5/17/2019
-------------------
* Stock Market changes:
* Implemented spread. Stock's now have bid and ask prices at which transactions occur
* Large transactions will now influence a stock's price and forecast
* This "influencing" can take effect in the middle of a transaction
* See documentation for more details on these changes
* Added getStockAskPrice(), getStockBidPrice() Netscript functions to the TIX API
* Added getStockPurchaseCost(), getStockSaleGain() Netscript functions to the TIX API
* Re-sleeves can no longer have the NeuroFlux Governor augmentation
* This is just a temporary patch until the mechanic gets re-worked
* hack(), grow(), and weaken() functions now take optional arguments for number of threads to use (by MasonD)
* codingcontract.attempt() now takes an optional argument that allows you to configure the function to return a contract's reward
* Adjusted RAM costs of Netscript Singularity functions (mostly increased)
* Adjusted RAM cost of codingcontract.getNumTriesRemaining() Netscript function
* Netscript Singularity functions no longer cost extra RAM outside of BitNode-4
* Corporation employees no longer have an "age" stat
* Gang Wanted level gain rate capped at 100 (per employee)
* Script startup/kill is now processed every 3 seconds, instead of 6 seconds
* getHackTime(), getGrowTime(), and getWeakenTime() now return Infinity if called on a Hacknet Server
* Money/Income tracker now displays money lost from hospitalizations
* Exported saves now have a unique filename based on current BitNode and timestamp
* Maximum number of Hacknet Servers decreased from 25 to 20
* Bug Fix: Corporation employees stats should no longer become negative
* Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios
* Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server
* Bug Fix: Duplicate Sleeves now properly have access to all Augmentations if you have a gang
* Bug Fix: getAugmentationsFromFaction() & purchaseAugmentation() functions should now work properly if you have a gang
* Bug Fix: Fixed issue that caused messages (.msg) to be sent when refreshing/reloading the game
* Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes
* Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads
* Bug Fix: Faction UI should now automatically update reputation
* Bug Fix: Fixed purchase4SMarketData()
* Bug Fix: Netscript1.0 now works properly for multiple 'namespace' imports (import * as namespace from "script")
* Bug Fix: Terminal 'wget' command now correctly evaluates directory paths
* Bug Fix: wget(), write(), and scp() Netscript functions now fail if an invalid filepath is passed in
* Bug Fix: Having Corporation warehouses at full capacity should no longer freeze game in certain conditions
* Bug Fix: Prevented an exploit that allows you to buy multiple copies of an Augmentation by holding the 'Enter' button
* Bug Fix: gang.getOtherGangInformation() now properly returns a deep copy
* Bug Fix: Fixed getScriptIncome() returning an undefined value
* Bug Fix: Fixed an issue with Hacknet Server hash rate not always updating
v0.46.3 - 4/20/2019
-------------------
* Added a new Augmentation: The Shadow's Simulacrum
* Improved tab autocompletion feature in Terminal so that it works better with directories
* Bug Fix: Tech vendor location UI now properly refreshed when purchasing a TOR router
* Bug Fix: Fixed UI issue with faction donations
* Bug Fix: The money statistics & breakdown should now properly track money earned from Hacknet Server (hashes -> money)
* Bug Fix: Fixed issue with changing input in 'Minimum Path Sum in a Triangle' coding contract problem
* Fixed several typos in various places
v0.46.2 - 4/14/2019 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 * 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) * (Karma is a hidden stat and is lowered by committing crimes)
* Gang changes: * Gang changes:
* Bug Fix: Gangs can no longer clash with themselve * Bug Fix: Gangs can no longer clash with themselve
* Bug Fix: Winning against another gang should properly reduce their power * Bug Fix: Winning against another gang should properly reduce their power

View File

@@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.46' version = '0.47'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.46.2' release = '0.47.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@@ -27,6 +27,7 @@ secrets that you've been searching for.
Script Editors <scripteditors> Script Editors <scripteditors>
Game Frozen or Stuck? <gamefrozen> Game Frozen or Stuck? <gamefrozen>
Guides & Tips <guidesandtips> Guides & Tips <guidesandtips>
Tools & Resources <toolsandresources>
Changelog <changelog> Changelog <changelog>
Donate <https://paypal.me/danielyxie> Donate <https://paypal.me/danielyxie>

View File

@@ -6,7 +6,7 @@ clear() Netscript Function
:param string/number port/fn: Port or text file to clear :param string/number port/fn: Port or text file to clear
:RAM cost: 1 GB :RAM cost: 1 GB
This function is used to clear data in a `Netscript Ports <http://bitburner.wikia.com/wiki/Netscript_Ports>`_ or a text file. This function is used to clear data in a :ref:`Netscript Port <netscript_ports>` or a text file.
If the *port/fn* argument is a number between 1 and 20, then it specifies a port and will clear it (deleting all data from the underlying queue). If the *port/fn* argument is a number between 1 and 20, then it specifies a port and will clear it (deleting all data from the underlying queue).

View File

@@ -14,10 +14,15 @@ exec() Netscript Function
Run a script as a separate process on a specified server. This is similar to the *run* function except Run a script as a separate process on a specified server. This is similar to the *run* function except
that it can be used to run a script on any server, instead of just the current server. that it can be used to run a script on any server, instead of just the current server.
Returns true if the script is successfully started, and false otherwise. If the script was successfully started, then this functions returns the PID
of that script. Otherwise, it returns 0.
Running this function with a *numThreads* argument of 0 will return false without running the script. .. note:: PID stands for Process ID. The PID is a unique identifier for each script.
However, running this function with a negative *numThreads* argument will cause a runtime error. The PID will always be a positive integer.
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
running the script. However, running this function with a negative *numThreads*
argument will cause a runtime error.
The simplest way to use the *exec* command is to call it with just the script name and the target server. The simplest way to use the *exec* command is to call it with just the script name and the target server.
The following example will try to run *generic-hack.script* on the *foodnstuff* server:: The following example will try to run *generic-hack.script* on the *foodnstuff* server::

View File

@@ -11,3 +11,6 @@ getGrowTime() Netscript Function
The function takes in an optional *hackLvl* parameter that can be specified The function takes in an optional *hackLvl* parameter that can be specified
to see what the grow time would be at different hacking levels. to see what the grow time would be at different hacking levels.
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
return :code:`Infinity`.

View File

@@ -11,3 +11,6 @@ getHackTime() Netscript Function
The function takes in an optional *hackLvl* parameter that can be specified The function takes in an optional *hackLvl* parameter that can be specified
to see what the hack time would be at different hacking levels. to see what the hack time would be at different hacking levels.
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
return :code:`Infinity`.

View File

@@ -11,3 +11,6 @@ getWeakenTime() Netscript Function
The function takes in an optional *hackLvl* parameter that can be specified The function takes in an optional *hackLvl* parameter that can be specified
to see what the weaken time would be at different hacking levels. to see what the weaken time would be at different hacking levels.
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
return :code:`Infinity`.

View File

@@ -1,9 +1,16 @@
grow() Netscript Function grow() Netscript Function
========================= =========================
.. js:function:: grow(hostname/ip) .. js:function:: grow(hostname/ip[, opts={}])
:param string hostname/ip: IP or hostname of the target server to grow :param string hostname/ip: IP or hostname of the target server to grow
:param object opts: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
* stock (*boolean*) - If true, the function can affect the stock market. See
:ref:`gameplay_stock_market_player_actions_influencing_stock`
:returns: The number by which the money on the server was multiplied for the growth :returns: The number by which the money on the server was multiplied for the growth
:RAM cost: 0.15 GB :RAM cost: 0.15 GB
@@ -19,3 +26,4 @@ grow() Netscript Function
Example:: Example::
grow("foodnstuff"); grow("foodnstuff");
grow("foodnstuff", { threads: 5 }); // Only use 5 threads to grow

View File

@@ -4,7 +4,7 @@ growthAnalyze() Netscript Function
.. js:function:: growthAnalyze(hostname/ip, growthAmount) .. js:function:: growthAnalyze(hostname/ip, growthAmount)
:param string hostname/ip: IP or hostname of server to analyze :param string hostname/ip: IP or hostname of server to analyze
:param number growthAmount: Multiplicative factor by which the server is grown. Decimal form. :param number growthAmount: Multiplicative factor by which the server is grown. Decimal form. Must be >= 1.
:returns: The amount of grow() calls needed to grow the specified server by the specified amount :returns: The amount of grow() calls needed to grow the specified server by the specified amount
:RAM cost: 1 GB :RAM cost: 1 GB

View File

@@ -1,9 +1,16 @@
hack() Netscript Function hack() Netscript Function
========================= =========================
.. js:function:: hack(hostname/ip) .. js:function:: hack(hostname/ip[, opts={}])
:param string hostname/ip: IP or hostname of the target server to hack :param string hostname/ip: IP or hostname of the target server to hack
:param object opts: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
* stock (*boolean*) - If true, the function can affect the stock market. See
:ref:`gameplay_stock_market_player_actions_influencing_stock`
:returns: The amount of money stolen if the hack is successful, and zero otherwise :returns: The amount of money stolen if the hack is successful, and zero otherwise
:RAM cost: 0.1 GB :RAM cost: 0.1 GB
@@ -20,3 +27,4 @@ hack() Netscript Function
hack("foodnstuff"); hack("foodnstuff");
hack("10.1.2.3"); hack("10.1.2.3");
hack("foodnstuff", { threads: 5 }); // Only use 5 threads to hack

View File

@@ -21,4 +21,13 @@ hackAnalyzeThreads() Netscript Function
If this function returns 50, this means that if your next `hack()` call If this function returns 50, this means that if your next `hack()` call
is run on a script with 50 threads, it will steal $1m from the `foodnstuff` server. is run on a script with 50 threads, it will steal $1m from the `foodnstuff` server.
**Warning**: The value returned by this function isn't necessarily a whole number. .. warning:: The value returned by this function isn't necessarily a whole number.
.. warning:: It is possible for this function to return :code:`Infinity` or :code:`NaN` in
certain uncommon scenarios. This is because in JavaScript:
* :code:`0 / 0 = NaN`
* :code:`N / 0 = Infinity` for 0 < N < Infinity.
For example, if a server has no money available and you want to hack some positive
amount from it, then the function would return :code:`Infinity` because
this would be impossible.

View File

@@ -27,3 +27,22 @@ kill() Netscript Function
The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff":: The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff"::
kill("foo.script", getHostname(), 1, "foodnstuff"); kill("foo.script", getHostname(), 1, "foodnstuff");
.. js:function:: kill(scriptPid)
:param number scriptPid: PID of the script to kill
:RAM cost: 0.5 GB
Kills the script with the specified PID. Killing a script by its PID will typically
have better performance, especially if you have many scripts running.
If this function successfully kills the specified script, then it will return true.
Otherwise, it will return false.
*Examples:*
The following example will try to kill the script with the PID 10::
if (kill(10)) {
print("Killed script with PID 10!");
}

View File

@@ -13,10 +13,15 @@ run() Netscript Function
Run a script as a separate process. This function can only be used to run scripts located on the current server (the server Run a script as a separate process. This function can only be used to run scripts located on the current server (the server
running the script that calls this function). running the script that calls this function).
Returns true if the script is successfully started, and false otherwise. If the script was successfully started, then this functions returns the PID
of that script. Otherwise, it returns 0.
Running this function with a *numThreads* argument of 0 will return false without running the script. .. note:: PID stands for Process ID. The PID is a unique identifier for each script.
However, running this function with a negative *numThreads* argument will cause a runtime error. The PID will always be a positive integer.
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
running the script. However, running this function with a negative *numThreads*
argument will cause a runtime error.
The simplest way to use the *run* command is to call it with just the script name. The following example will run The simplest way to use the *run* command is to call it with just the script name. The following example will run
'foo.script' single-threaded with no arguments:: 'foo.script' single-threaded with no arguments::

View File

@@ -0,0 +1,29 @@
tail() Netscript Function
==================================
.. js:function:: tail([fn], [hostname/ip=current ip], [...args])
:param string fn: Optional. Filename of script to get logs from.
:param string ip: Optional. IP or hostname of the server that the script is on
:param args...: Arguments to identify which scripts to get logs for
:RAM cost: 0 GB
Opens a script's logs. This is functionally the same as the
:ref:`tail_terminal_command` Terminal command.
If the function is called with no arguments, it will open the current script's logs.
Otherwise, the `fn`, `hostname/ip,` and `args...` arguments can be used to get the logs
from another script. Remember that scripts are uniquely identified by both
their names and arguments.
Examples::
// Open logs from foo.script on the current server that was run with no args
tail("foo.script");
// Open logs from foo.script on the foodnstuff server that was run with no args
tail("foo.script", "foodnstuff");
// Open logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
tail("foo.script", "foodnstuff", 1, "test");

View File

@@ -1,9 +1,14 @@
weaken() Netscript Function weaken() Netscript Function
=========================== ===========================
.. js:function:: weaken(hostname/ip) .. js:function:: weaken(hostname/ip[, opts={}])
:param string hostname/ip: IP or hostname of the target server to weaken :param string hostname/ip: IP or hostname of the target server to weaken
:param object opts: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
:returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied :returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied
by the number of script threads by the number of script threads
:RAM cost: 0.15 GB :RAM cost: 0.15 GB
@@ -18,3 +23,4 @@ weaken() Netscript Function
Example:: Example::
weaken("foodnstuff"); weaken("foodnstuff");
weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken

View File

@@ -1,13 +1,20 @@
attempt() Netscript Function attempt() Netscript Function
============================ ============================
.. js:function:: attempt(answer, fn[, hostname/ip=current ip]) .. js:function:: attempt(answer, fn[, hostname/ip=current ip, opts={}])
:param answer: Solution for the contract :param answer: Solution for the contract
:param string fn: Filename of the contract :param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract. :param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided Optional. Defaults to current server if not provided
:param object opts: Optional parameters for configuring function behavior. Properties:
* returnReward (*boolean*) If truthy, then the function will return a string
that states the contract's reward when it is successfully solved.
Attempts to solve the Coding Contract with the provided solution. Attempts to solve the Coding Contract with the provided solution.
:returns: Boolean indicating whether the solution was correct :returns: Boolean indicating whether the solution was correct. If the :code:`returnReward`
option is configured, then the function will instead return a string. If the
contract is successfully solved, the string will contain a description of the
contract's reward. Otherwise, it will be an empty string.

View File

@@ -15,6 +15,7 @@ getNodeStats() Netscript Function
ram: Node's RAM, ram: Node's RAM,
cores: Node's number of cores, cores: Node's number of cores,
cache: Cache level. Only applicable for Hacknet Servers cache: Cache level. Only applicable for Hacknet Servers
hashCapacity: Hash Capacity provided by this Node. Only applicable for Hacknet Servers
production: Node's production per second production: Node's production per second
timeOnline: Number of seconds since Node has been purchased, timeOnline: Number of seconds since Node has been purchased,
totalProduction: Total amount that the Node has produced totalProduction: Total amount that the Node has produced

View File

@@ -24,6 +24,7 @@ This includes information such as function signatures, what they do, and their r
enableLog() <basicfunctions/enableLog> enableLog() <basicfunctions/enableLog>
isLogEnabled() <basicfunctions/isLogEnabled> isLogEnabled() <basicfunctions/isLogEnabled>
getScriptLogs() <basicfunctions/getScriptLogs> getScriptLogs() <basicfunctions/getScriptLogs>
tail() <basicfunctions/tail>
scan() <basicfunctions/scan> scan() <basicfunctions/scan>
nuke() <basicfunctions/nuke> nuke() <basicfunctions/nuke>
brutessh() <basicfunctions/brutessh> brutessh() <basicfunctions/brutessh>

View File

@@ -8,8 +8,7 @@ later in the game
.. warning:: This page contains spoilers for the game .. warning:: This page contains spoilers for the game
The Gang API is unlocked in BitNode-2. Currently, BitNode-2 is the only location The Gang mechanic and the Gang API are unlocked in BitNode-2.
where the Gang mechanic is accessible. This may change in the future
**Gang API functions must be accessed through the 'gang' namespace** **Gang API functions must be accessed through the 'gang' namespace**

View File

@@ -15,11 +15,15 @@ access even after you 'reset' by installing Augmentations
.. toctree:: .. toctree::
:caption: API Functions: :caption: API Functions:
getStockSymbols() <tixapi/getStockSymbols> getStockSymbols() <tixapi/getStockSymbols>
getStockPrice() <tixapi/getStockPrice> getStockPrice() <tixapi/getStockPrice>
getStockAskPrice() <tixapi/getStockAskPrice>
getStockBidPrice() <tixapi/getStockBidPrice>
getStockPosition() <tixapi/getStockPosition> getStockPosition() <tixapi/getStockPosition>
getStockMaxShares() <tixapi/getStockMaxShares> getStockMaxShares() <tixapi/getStockMaxShares>
getStockPurchaseCost() <tixapi/getStockPurchaseCost>
getStockSaleGain() <tixapi/getStockSaleGain>
buyStock() <tixapi/buyStock> buyStock() <tixapi/buyStock>
sellStock() <tixapi/sellStock> sellStock() <tixapi/sellStock>
shortStock() <tixapi/shortStock> shortStock() <tixapi/shortStock>

View File

@@ -16,7 +16,7 @@ there is plenty of documentation on Javascript available on the web.
Browser compatibility Browser compatibility
--------------------- ---------------------
As of the time of writing this, Mozilla Firefox and a few other browsers do not support `dynamic import <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import>`_ functionality and therefore cannot run NetscriptJS scripts. These browsers will thus only be capable of using Netscript 1.0. As of the time of writing this, a few browsers do not support `dynamic import <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import>`_ functionality and therefore cannot run NetscriptJS scripts. These browsers will thus only be capable of using Netscript 1.0.
How to use NetscriptJS How to use NetscriptJS
---------------------- ----------------------
@@ -51,8 +51,6 @@ Here is a summary of all rules you need to follow when writing Netscript JS code
* grow * grow
* weaken * weaken
* sleep * sleep
* run
* exec
* prompt * prompt
* wget * wget

View File

@@ -57,6 +57,10 @@ And the data in port 1 will look like::
[3, 4, 5, 6, 7, 8, 9] [3, 4, 5, 6, 7, 8, 9]
.. warning:: In :ref:`netscriptjs`, do not trying writing base
`Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_
to a port.
**Port Handles** **Port Handles**
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1` WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
@@ -213,16 +217,6 @@ to specify a namespace for the import::
keyword should **NOT** be used in :ref:`netscript1`, as this will break the script. keyword should **NOT** be used in :ref:`netscript1`, as this will break the script.
It can, however, be used in :ref:`netscriptjs` (but it's not required). It can, however, be used in :ref:`netscriptjs` (but it's not required).
Importing in NetscriptJS
^^^^^^^^^^^^^^^^^^^^^^^^
There is a minor annoyance when using the `import` feature in :ref:`netscriptjs`.
If you make a change in a NetscriptJS script, then you have to manually "refresh" all other
scripts that import from that script.
The easiest way to do this is to simply save and then refresh the game. Alternatively,
you can open up all the scripts that need to be "refreshed" in the script editor
and then simply re-save them.
Standard, Built-In JavaScript Objects Standard, Built-In JavaScript Objects
------------------------------------- -------------------------------------
Standard built-in JavaScript objects such as Standard built-in JavaScript objects such as

View File

@@ -16,8 +16,6 @@ You can use the Singularity Functions in other BitNodes if and only if you have
Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to
level 3, then you will be able to access all of the Singularity Functions. level 3, then you will be able to access all of the Singularity Functions.
Note that Singularity Functions require twice as much RAM outside of BitNode-4
.. toctree:: .. toctree::
:caption: Functions: :caption: Functions:

View File

@@ -63,17 +63,17 @@ Examples
**Basic example usage**:: **Basic example usage**::
for (var i = 0; i < sleeve.getNumSleeves(); i++) { for (var i = 0; i < sleeve.getNumSleeves(); i++) {
sleeve.shockRecovery(i); sleeve.setToShockRecovery(i);
}
sleep(10 * 60 * 60); // wait 10h
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
sleeve.setToSynchronize(i);
} }
sleep(10*60*60); // wait 10h sleep(10*60*60); // wait 10h
for (var i = 0; i < sleeve.getNumSleeves(); i++) { for (var i = 0; i < sleeve.getNumSleeves(); i++) {
sleeve.synchronize(i); sleeve.setToCommitCrime(i, 'shoplift');
}
sleep(10*60*60); // wait 10h
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
sleeve.commitCrime(i, 'shoplift');
} }

View File

@@ -11,4 +11,7 @@ installAugmentations() Netscript Function
This function will automatically install your Augmentations, resetting the game as usual. This function will automatically install your Augmentations, resetting the game as usual.
It will return true if successful, and false otherwise. This function will return false if it was not able to install Augmentations.
If this function successfully installs Augmentations, then it has no return value because
all scripts are immediately terminated.

View File

@@ -5,5 +5,12 @@ isBusy() Netscript Function
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function. If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.
Returns a boolean indicating whether or not the player is currently performing an 'action'. These actions include Returns a boolean indicating whether or not the player is currently performing an 'action'.
working for a company/faction, studying at a univeristy, working out at a gym, creating a program, or committing a crime. These actions include:
* Working for a company/faction
* Studying at a univeristy
* Working out at a gym
* Creating a program
* Committing a crime
* Carrying out a Hacking Mission

View File

@@ -5,7 +5,7 @@ workForFaction() Netscript Function
:param string factionName: Name of faction to work for. CASE-SENSITIVE :param string factionName: Name of faction to work for. CASE-SENSITIVE
:param string workType: :param string workType:
Type of work to perform for the faction Type of work to perform for the faction:
* hacking/hacking contracts/hackingcontracts * hacking/hacking contracts/hackingcontracts
* field/fieldwork/field work * field/fieldwork/field work

View File

@@ -61,5 +61,5 @@ getInformation() Netscript Function
workChaExpGain: charisma exp gained from work, workChaExpGain: charisma exp gained from work,
workMoneyGain: money gained from work, workMoneyGain: money gained from work,
}, },
workRepGain: sl.getRepGain(), workRepGain: Reputation gain rate when working for factions or companies
} }

View File

@@ -1,5 +1,5 @@
getSleeveAugmentations() Netscript Function getSleeveAugmentations() Netscript Function
======================================= ===========================================
.. js:function:: getSleeveAugmentations(sleeveNumber) .. js:function:: getSleeveAugmentations(sleeveNumber)

View File

@@ -6,7 +6,11 @@ getOrders() Netscript Function
:RAM cost: 2.5 GB :RAM cost: 2.5 GB
Returns your order book for the stock market. This is an object containing information Returns your order book for the stock market. This is an object containing information
for all the Limit and Stop Orders you have in the stock market. for all the :ref:`Limit and Stop Orders <gameplay_stock_market_order_types>`
you have in the stock market.
.. note:: This function isn't accessible until you have unlocked the ability to use
Limit and Stop Orders.
The object has the following structure:: The object has the following structure::

View 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.

View 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.

View File

@@ -6,9 +6,12 @@ getStockPrice() Netscript Function
:param string sym: Stock symbol :param string sym: Stock symbol
:RAM cost: 2 GB :RAM cost: 2 GB
Returns the price of a stock, given its symbol (NOT the company name). The symbol is a sequence Given a stock's symbol, returns the price of that stock (the symbol is a sequence
of two to four capital letters. of two to four capital letters, **not** the name of the company to which that stock belongs).
.. note:: The stock's price is the average of its bid and ask price.
See :ref:`gameplay_stock_market_spread` for details on what this means.
Example:: Example::
getStockPrice("FISG"); getStockPrice("FSIG");

View File

@@ -0,0 +1,14 @@
getStockPurchaseCost() Netscript Function
=========================================
.. js:function:: getStockPurchaseCost(sym, shares, posType)
:param string sym: Stock symbol
:param number shares: Number of shares to purchase
:param string posType: Specifies whether the order is a "Long" or "Short" position.
The values "L" or "S" can also be used.
:RAM cost: 2 GB
Calculates and returns how much it would cost to buy a given number of
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`
and commission fees.

View File

@@ -0,0 +1,14 @@
getStockSaleGain() Netscript Function
=====================================
.. js:function:: getStockSaleGain(sym, shares, posType)
:param string sym: Stock symbol
:param number shares: Number of shares to sell
:param string posType: Specifies whether the order is a "Long" or "Short" position.
The values "L" or "S" can also be used.
:RAM cost: 2 GB
Calculates and returns how much you would gain from selling a given number of
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`
and commission fees.

View File

@@ -19,8 +19,10 @@ placeOrder() Netscript Function
NOT case-sensitive. NOT case-sensitive.
:RAM cost: 2.5 GB :RAM cost: 2.5 GB
Places an order on the stock market. This function only works for `Limit and Stop Orders <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_. Places an order on the stock market. This function only works
for :ref:`Limit and Stop Orders <gameplay_stock_market_order_types>`.
The ability to place limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
Returns true if the order is successfully placed, and false otherwise. Returns true if the order is successfully placed, and false otherwise.
.. note:: The ability to place limit and stop orders is **not** immediately available to
the player and must be unlocked later on in the game.

View 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/>`_.

View File

@@ -25,7 +25,7 @@
ga('create', 'UA-100157497-1', 'auto'); ga('create', 'UA-100157497-1', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="dist/engine.css" rel="stylesheet"></head> <link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="dist/engineStyle.css" rel="stylesheet"></head>
<body> <body>
<div id="entire-game-container" style="visibility:hidden;"> <div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container"> <div id="mainmenu-container">
@@ -277,53 +277,7 @@
</div> </div>
<div id="stock-market-container" class="generic-menupage-container"> <div id="stock-market-container" class="generic-menupage-container">
<p> <!-- React Component -->
Welcome to the World Stock Exchange (WSE)! <br/><br/>
To begin trading, you must first purchase an account. WSE accounts will persist
after you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-account" class="a-link-button-inactive"> Buy WSE Account </a>
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
<h2> Trade Information eXchange (TIX) API </h2>
<p>
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies.
<br/><br/>
If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
<h2> Four Sigma (4S) Market Data Feed </h2>
<p>
Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies.
<br/><br/>
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
retain that access even after you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-4s-data" class="a-link-button-inactive tooltip">
Buy 4S Market Data Feed
</a>
<div class="help-tip-big" id="stock-market-4s-data-help-tip">?</div>
<a id="stock-market-buy-4s-tix-api" class="a-link-button-inactive tooltip">
Buy 4S Market Data TIX API Access
</a>
<p id="stock-market-commission"> </p>
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br/><br/>
<input id="stock-market-watchlist-filter" class="text-input" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;">
</ul>
</div> </div>
<!-- Log Box --> <!-- Log Box -->
@@ -634,7 +588,7 @@
<p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p> <p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p>
</div> </div>
</div> </div>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script></body> <script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script><script type="text/javascript" src="dist/engineStyle.bundle.js"></script></body>
<!-- Misc Scripts --> <!-- Misc Scripts -->
<script src="src/ThirdParty/raphael.min.js"></script> <script src="src/ThirdParty/raphael.min.js"></script>

1253
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,8 @@
"@types/numeral": "0.0.25", "@types/numeral": "0.0.25",
"@types/react": "^16.8.6", "@types/react": "^16.8.6",
"@types/react-dom": "^16.8.2", "@types/react-dom": "^16.8.2",
"acorn": "^5.0.0", "acorn": "^6.2.0",
"acorn-dynamic-import": "^2.0.0", "acorn-walk": "^6.2.0",
"ajv": "^5.1.5", "ajv": "^5.1.5",
"ajv-keywords": "^2.0.0", "ajv-keywords": "^2.0.0",
"async": "^2.6.1", "async": "^2.6.1",
@@ -44,12 +44,13 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.3.4", "@babel/core": "^7.3.4",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"beautify-lint": "^1.0.3", "beautify-lint": "^1.0.3",
"benchmark": "^2.1.1", "benchmark": "^2.1.1",
"bundle-loader": "~0.5.0", "bundle-loader": "~0.5.0",
"chai": "^4.1.2", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"css-loader": "^0.28.11", "css-loader": "^0.28.11",
"es6-promise-polyfill": "^1.1.1", "es6-promise-polyfill": "^1.1.1",
"eslint": "^4.19.1", "eslint": "^4.19.1",
@@ -59,15 +60,18 @@
"i18n-webpack-plugin": "^1.0.0", "i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"js-beautify": "^1.5.10", "js-beautify": "^1.5.10",
"jsdom": "^15.0.0",
"jsdom-global": "^3.0.2",
"json5": "^1.0.1", "json5": "^1.0.1",
"less": "^3.9.0", "less": "^3.9.0",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"lodash": "^4.17.10", "lodash": "^4.17.10",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^5.2.0", "mocha": "^6.1.4",
"mocha-lcov-reporter": "^1.0.0", "mochapack": "^1.1.1",
"node-sass": "^4.10.0", "node-sass": "^4.10.0",
"null-loader": "^1.0.0",
"raw-loader": "~0.5.0", "raw-loader": "~0.5.0",
"sass-loader": "^7.0.3", "sass-loader": "^7.0.3",
"script-loader": "~0.7.0", "script-loader": "~0.7.0",
@@ -106,13 +110,15 @@
"start:dev": "webpack-dev-server --progress --env.devServer --mode development", "start:dev": "webpack-dev-server --progress --env.devServer --mode development",
"build": "webpack --mode production", "build": "webpack --mode production",
"build:dev": "webpack --mode development", "build:dev": "webpack --mode development",
"build:test": "webpack --config webpack.config-test.js",
"lint": "npm run lint:typescript & npm run lint:javascript & npm run lint:style", "lint": "npm run lint:typescript & npm run lint:javascript & npm run lint:style",
"lint:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js", "lint:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js",
"lint:style": "stylelint ./css/*", "lint:style": "stylelint ./css/*",
"lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts", "lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts",
"preinstall": "node ./scripts/engines-check.js", "preinstall": "node ./scripts/engines-check.js",
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
"watch": "webpack --watch --mode production", "watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development" "watch:dev": "webpack --watch --mode development"
}, },
"version": "0.46.2" "version": "0.47.0"
} }

View File

@@ -1,337 +0,0 @@
// TODO - Convert this to React
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
import { Player } from "./Player";
import { getServer } from "./Server/ServerHelpers";
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
* }
* }
* ...
*/
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
const longestHostnameLength = 18;
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
const barOptions = {
progress: server.ramUsed / server.maxRam,
totalTicks: 30
};
return `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, '&nbsp;');
};
const updateHeaderHtml = (server) => {
const accordion = ActiveScriptsUI[server.hostname];
if (accordion === null || accordion === undefined) {
return;
}
// Convert it to a string, as that's how it's stored it will come out of the data attributes
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
accordion.header.dataset.ramPercentage = ramPercentage;
accordion.header.innerHTML = getHeaderHtml(server);
}
}
function createActiveScriptsServerPanel(server) {
let hostname = server.hostname;
var activeScriptsList = document.getElementById("active-scripts-list");
let res = createAccordionElement({
hdrText: getHeaderHtml(server)
});
let li = res[0];
var hdr = res[1];
let panel = res[2];
if (ActiveScriptsUI[hostname] != null) {
console.log("WARNING: Tried to create already-existing Active Scripts Server panel. This is most likely fine. It probably means many scripts just got started up on a new server. Aborting");
return;
}
var panelScriptList = createElement("ul");
panel.appendChild(panelScriptList);
activeScriptsList.appendChild(li);
ActiveScriptsUI[hostname] = {
header: hdr,
panel: panel,
panelList: panelScriptList,
scripts: {}, // Holds references to li elements for each active script
scriptHdrs: {}, // Holds references to header elements for each active script
scriptStats: {}, // Holds references to the p elements containing text for each active script
};
return li;
}
/**
* Deletes the info for a particular server (Dropdown header + Panel with all info)
* in the Active Scripts page if it exists
*/
function deleteActiveScriptsServerPanel(server) {
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null) {
console.log("WARNING: Tried to delete non-existent Active Scripts Server panel. Aborting");
return;
}
// Make sure it's empty
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
return;
}
removeElement(ActiveScriptsUI[hostname].panel);
removeElement(ActiveScriptsUI[hostname].header);
delete ActiveScriptsUI[hostname];
}
function addActiveScriptsItem(workerscript) {
var server = getServer(workerscript.serverIp);
if (server == null) {
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
return;
}
let hostname = server.hostname;
ActiveScriptsTasks.push(function(workerscript, hostname) {
if (ActiveScriptsUI[hostname] == null) {
createActiveScriptsServerPanel(server);
}
// Create the unique identifier (key) for this script
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i]));
}
var itemName = itemNameArray.join("-");
let res = createAccordionElement({hdrText:workerscript.name});
let li = res[0];
let hdr = res[1];
let panel = res[2];
hdr.classList.remove("accordion-header");
hdr.classList.add("active-scripts-script-header");
panel.classList.remove("accordion-panel");
panel.classList.add("active-scripts-script-panel");
/**
* Handle the constant elements on the panel that don't change after creation:
* Threads, args, kill/log button
*/
panel.appendChild(createElement("p", {
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
"Args: " + arrayToString(workerscript.args)
}));
var panelText = createElement("p", {
innerText: "Loading...",
fontSize: "14px",
});
panel.appendChild(panelText);
panel.appendChild(createElement("br"));
panel.appendChild(createElement("span", {
innerText: "Log",
class: "accordion-button",
margin: "4px",
padding: "4px",
clickListener: () => {
logBoxCreate(workerscript.scriptRef);
return false;
}
}));
panel.appendChild(createElement("span", {
innerText: "Kill Script",
class: "accordion-button",
margin: "4px",
padding: "4px",
clickListener: () => {
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.server);
dialogBoxCreate("Killing script, may take a few minutes to complete...");
return false;
}
}));
// Append element to list
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
ActiveScriptsUI[hostname].scripts[itemName] = li;
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
ActiveScriptsUI[hostname].scriptStats[itemName] = panelText;
}.bind(null, workerscript, hostname));
}
function deleteActiveScriptsItem(workerscript) {
ActiveScriptsTasks.push(function(workerscript) {
var server = getServer(workerscript.serverIp);
if (server == null) {
throw new Error("ERROR: Invalid server IP for workerscript. This most likely occurred because " +
"you tried to delete a large number of scripts and also deleted servers at the " +
"same time. It's not a big deal, just save and refresh the game.");
return;
}
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null) {
console.log("ERROR: Trying to delete Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
return;
}
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i]));
}
var itemName = itemNameArray.join("-");
let li = ActiveScriptsUI[hostname].scripts[itemName];
if (li == null) {
console.log("ERROR: Cannot find Active Script UI element for workerscript: ");
console.log(workerscript);
return;
}
removeElement(li);
delete ActiveScriptsUI[hostname].scripts[itemName];
delete ActiveScriptsUI[hostname].scriptHdrs[itemName];
delete ActiveScriptsUI[hostname].scriptStats[itemName];
if (Object.keys(ActiveScriptsUI[hostname].scripts).length === 0) {
deleteActiveScriptsServerPanel(server);
}
}.bind(null, workerscript));
}
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 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 {
task();
} catch(e) {
exceptionAlert(e);
console.log(task);
}
}
if (!routing.isOn(Page.ActiveScripts)) { return; }
let total = 0;
for (var i = 0; i < workerScripts.length; ++i) {
try {
total += updateActiveScriptsItemContent(workerScripts[i]);
} catch(e) {
exceptionAlert(e);
}
}
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
return total;
}
function updateActiveScriptsItemContent(workerscript) {
var server = getServer(workerscript.serverIp);
if (server == null) {
console.log("ERROR: Invalid server IP for workerscript in updateActiveScriptsItemContent().");
return;
}
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null) {
return; // Hasn't been created yet. We'll skip it
}
updateHeaderHtml(server);
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i]));
}
var itemName = itemNameArray.join("-");
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
return; // Hasn't been fully added yet. We'll skip it
}
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
// Update the text if necessary. This fn returns the online $/s production
return updateActiveScriptsText(workerscript, item, itemName);
}
function updateActiveScriptsText(workerscript, item, itemName) {
var server = getServer(workerscript.serverIp);
if (server == null) {
console.log("ERROR: Invalid server IP for workerscript for updateActiveScriptsText()");
return;
}
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null || ActiveScriptsUI[hostname].scriptHdrs[itemName] == null) {
console.log("ERROR: Trying to update Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
return;
}
updateHeaderHtml(server);
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
// Only update if the item is visible
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
removeChildrenFromElement(item);
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
// Online
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, "&nbsp;");
var onlineMpsText = "Online production rate: " + numeralWrapper.formatMoney(onlineMps) + " / second";
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, "&nbsp;");
// 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, "&nbsp;");
var offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
var offlineMpsText = "Offline production rate: " + numeralWrapper.formatMoney(offlineMps) + " / second";
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
var offlineEpsText = (Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, "&nbsp;");
item.innerHTML = onlineTime + "<br>" + offlineTime + "<br>" + onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
onlineMpsText + "<br>" + onlineEpsText + "<br>" + offlineTotalMoneyMade + "<br>" + offlineTotalExpEarned + "<br>" +
offlineMpsText + "<br>" + offlineEpsText + "<br>";
return onlineMps;
}
export {addActiveScriptsItem, deleteActiveScriptsItem, updateActiveScriptsItems};

View File

@@ -1,34 +1,39 @@
import { Augmentation } from "./Augmentation"; import { Augmentation } from "./Augmentation";
import { Augmentations } from "./Augmentations"; import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { AugmentationsRoot } from "./ui/Root";
import { CONSTANTS } from "../Constants";
import { Factions,
factionExists } from "../Faction/Factions";
import { hasBladeburnerSF } from "../NetscriptFunctions";
import { addWorkerScript } from "../NetscriptWorker";
import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige";
import { saveObject } from "../SaveObject";
import { RunningScript } from "../Script/RunningScript";
import { Script } from "../Script/Script";
import { Server } from "../Server/Server";
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { SourceFiles } from "../SourceFile"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { CONSTANTS } from "../Constants";
import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement"; import { Factions, factionExists } from "../Faction/Factions";
import { Reviver, Generic_toJSON, import { startWorkerScript } from "../NetscriptWorker";
Generic_fromJSON } from "../../utils/JSONReviver"; import { Player } from "../Player";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { prestigeAugmentation } from "../Prestige";
import { clearObject } from "../../utils/helpers/clearObject"; import { saveObject } from "../SaveObject";
import { createElement } from "../../utils/uiHelpers/createElement"; import { RunningScript } from "../Script/RunningScript";
import { isString } from "../../utils/helpers/isString"; import { Script } from "../Script/Script";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement"; import { Server } from "../Server/Server";
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions";
import { clearObject } from "../../utils/helpers/clearObject";
import { createElement } from "../../utils/uiHelpers/createElement";
import { isString } from "../../utils/helpers/isString";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
import React from "react";
import ReactDOM from "react-dom";
function AddToAugmentations(aug) { function AddToAugmentations(aug) {
var name = aug.name; var name = aug.name;
@@ -2042,17 +2047,6 @@ function applyAugmentation(aug, reapply=false) {
} }
} }
/*
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
for (var i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
//Already have this aug, just upgrade the level
return;
}
}
}
*/
// Push onto Player's Augmentation list // Push onto Player's Augmentation list
if (!reapply) { if (!reapply) {
var ownedAug = new PlayerOwnedAugmentation(aug.name); var ownedAug = new PlayerOwnedAugmentation(aug.name);
@@ -2084,18 +2078,17 @@ function installAugmentations(cbScript=null) {
//Run a script after prestiging //Run a script after prestiging
if (cbScript && isString(cbScript)) { if (cbScript && isString(cbScript)) {
var home = Player.getHomeComputer(); var home = Player.getHomeComputer();
for (var i = 0; i < home.scripts.length; ++i) { for (const script of home.scripts) {
if (home.scripts[i].filename === cbScript) { if (script.filename === cbScript) {
var script = home.scripts[i]; const ramUsage = script.ramUsage;
var ramUsage = script.ramUsage; const ramAvailable = home.maxRam - home.ramUsed;
var ramAvailable = home.maxRam - home.ramUsed;
if (ramUsage > ramAvailable) { if (ramUsage > ramAvailable) {
return; //Not enough RAM return; // Not enough RAM
} }
var runningScriptObj = new RunningScript(script, []); //No args const runningScriptObj = new RunningScript(script, []); // No args
runningScriptObj.threads = 1; //Only 1 thread runningScriptObj.threads = 1; // Only 1 thread
home.runScript(runningScriptObj, Player);
addWorkerScript(runningScriptObj, home); startWorkerScript(runningScriptObj, home);
} }
} }
} }
@@ -2105,225 +2098,30 @@ function augmentationExists(name) {
return Augmentations.hasOwnProperty(name); return Augmentations.hasOwnProperty(name);
} }
//Used for testing balance export function displayAugmentationsContent(contentEl) {
function giveAllAugmentations() { if (!routing.isOn(Page.Augmentations)) { return; }
for (var name in Augmentations) { if (!(contentEl instanceof HTMLElement)) { return; }
var aug = Augmentations[name];
if (aug == null) {continue;} ReactDOM.render(
var ownedAug = new PlayerOwnedAugmentation(name); <AugmentationsRoot
Player.augmentations.push(ownedAug); exportGameFn={saveObject.exportGame.bind(saveObject)}
} installAugmentationsFn={installAugmentations}
Player.reapplyAllAugmentations(); />,
contentEl
);
} }
function displayAugmentationsContent(contentEl) { export function isRepeatableAug(aug) {
removeChildrenFromElement(contentEl); const augName = (aug instanceof Augmentation) ? aug.name : aug;
contentEl.appendChild(createElement("h1", {
innerText:"Purchased Augmentations",
}));
contentEl.appendChild(createElement("pre", { if (augName === AugmentationNames.NeuroFluxGovernor) { return true; }
width:"70%", whiteSpace:"pre-wrap", display:"block",
innerText:"Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.\n" +
"WARNING: Installing your Augmentations resets most of your progress, including:\n\n" +
"Stats/Skill levels and Experience\n" +
"Money\n" +
"Scripts on every computer but your home computer\n" +
"Purchased servers\n" +
"Hacknet Nodes\n" +
"Faction/Company reputation\n" +
"Stocks\n" +
"Installing Augmentations lets you start over with the perks and benefits granted by all " +
"of the Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades " +
"on your home computer (but you will lose all programs besides NUKE.exe)."
}));
//Install Augmentations button return false;
contentEl.appendChild(createElement("a", {
class:"a-link-button", innerText:"Install Augmentations",
tooltip:"'I never asked for this'",
clickListener:()=>{
installAugmentations();
return false;
}
}));
//Backup button
contentEl.appendChild(createElement("a", {
class:"a-link-button flashing-button", innerText:"Backup Save (Export)",
tooltip:"It's always a good idea to backup/export your save!",
clickListener:()=>{
saveObject.exportGame();
return false;
}
}));
//Purchased/queued augmentations list
var queuedAugmentationsList = createElement("ul", {class:"augmentations-list"});
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
var augName = Player.queuedAugmentations[i].name;
var aug = Augmentations[augName];
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (Player.queuedAugmentations[i].level);
}
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
queuedAugmentationsList.appendChild(accordion[0]);
}
contentEl.appendChild(queuedAugmentationsList);
//Installed augmentations list
contentEl.appendChild(createElement("h1", {
innerText:"Installed Augmentations", marginTop:"8px",
}));
contentEl.appendChild(createElement("p", {
width:"70%", whiteSpace:"pre-wrap",
innerText:"List of all Augmentations (including Source Files) that have been " +
"installed. You have gained the effects of these Augmentations."
}));
var augmentationsList = createElement("ul", {class:"augmentations-list"});
//Expand/Collapse All buttons
contentEl.appendChild(createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Expand All", display:"inline-block",
clickListener:()=>{
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
if (!allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
}
}
}));
contentEl.appendChild(createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Collapse All", display:"inline-block",
clickListener:()=>{
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
if (allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
}
}
}));
//Sort Buttons
const sortInOrderButton = createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Sort in Order",
tooltip:"Sorts the Augmentations alphabetically and Source Files in numerical order (1, 2, 3,...)",
clickListener:()=>{
removeChildrenFromElement(augmentationsList);
//Create a copy of Player's Source Files and augs array and sort them
var sourceFiles = Player.sourceFiles.slice();
var augs = Player.augmentations.slice();
sourceFiles.sort((sf1, sf2)=>{
return sf1.n - sf2.n;
});
augs.sort((aug1, aug2)=>{
return aug1.name <= aug2.name ? -1 : 1;
});
displaySourceFiles(augmentationsList, sourceFiles);
displayAugmentations(augmentationsList, augs);
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
}
});
contentEl.appendChild(sortInOrderButton);
const sortByAcquirementTimeButton = createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Sort by Acquirement Time",
tooltip:"Sorts the Augmentations and Source Files based on when you acquired them (same as default)",
clickListener:()=>{
removeChildrenFromElement(augmentationsList);
displaySourceFiles(augmentationsList, Player.sourceFiles);
displayAugmentations(augmentationsList, Player.augmentations);
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
}
});
contentEl.appendChild(sortByAcquirementTimeButton);
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sortInOrderButton.click();
} else {
sortByAcquirementTimeButton.click();
}
contentEl.appendChild(augmentationsList);
// Display multiplier information at the bottom
contentEl.appendChild(createElement("p", {
display: "block",
innerHTML:
`<br><br><strong><u>Total Multipliers:</u></strong><br>` +
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
'Hacking Money multiplier: ' + formatNumber(Player.hacking_money_mult * 100, 2) + '%<br>' +
'Hacking Growth multiplier: ' + formatNumber(Player.hacking_grow_mult * 100, 2) + '%<br><br>' +
'Hacking Level multiplier: ' + formatNumber(Player.hacking_mult * 100, 2) + '%<br>' +
'Hacking Experience multiplier: ' + formatNumber(Player.hacking_exp_mult * 100, 2) + '%<br><br>' +
'Strength Level multiplier: ' + formatNumber(Player.strength_mult * 100, 2) + '%<br>' +
'Strength Experience multiplier: ' + formatNumber(Player.strength_exp_mult * 100, 2) + '%<br><br>' +
'Defense Level multiplier: ' + formatNumber(Player.defense_mult * 100, 2) + '%<br>' +
'Defense Experience multiplier: ' + formatNumber(Player.defense_exp_mult * 100, 2) + '%<br><br>' +
'Dexterity Level multiplier: ' + formatNumber(Player.dexterity_mult * 100, 2) + '%<br>' +
'Dexterity Experience multiplier: ' + formatNumber(Player.dexterity_exp_mult * 100, 2) + '%<br><br>' +
'Agility Level multiplier: ' + formatNumber(Player.agility_mult * 100, 2) + '%<br>' +
'Agility Experience multiplier: ' + formatNumber(Player.agility_exp_mult * 100, 2) + '%<br><br>' +
'Charisma Level multiplier: ' + formatNumber(Player.charisma_mult * 100, 2) + '%<br>' +
'Charisma Experience multiplier: ' + formatNumber(Player.charisma_exp_mult * 100, 2) + '%<br><br>' +
'Hacknet Node production multiplier: ' + formatNumber(Player.hacknet_node_money_mult * 100, 2) + '%<br>' +
'Hacknet Node purchase cost multiplier: ' + formatNumber(Player.hacknet_node_purchase_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node RAM upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_ram_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node Core purchase cost multiplier: ' + formatNumber(Player.hacknet_node_core_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node level upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_level_cost_mult * 100, 2) + '%<br><br>' +
'Company reputation gain multiplier: ' + formatNumber(Player.company_rep_mult * 100, 2) + '%<br>' +
'Faction reputation gain multiplier: ' + formatNumber(Player.faction_rep_mult * 100, 2) + '%<br>' +
'Salary multiplier: ' + formatNumber(Player.work_money_mult * 100, 2) + '%<br>' +
'Crime success multiplier: ' + formatNumber(Player.crime_success_mult * 100, 2) + '%<br>' +
'Crime money multiplier: ' + formatNumber(Player.crime_money_mult * 100, 2) + '%<br><br><br>',
}))
} }
//Creates the accordion elements to display Augmentations export {
// @listElement - List DOM element to append accordion elements to installAugmentations,
// @augs - Array of Augmentation objects initAugmentations,
function displayAugmentations(listElement, augs) { applyAugmentation,
for (var i = 0; i < augs.length; ++i) { augmentationExists,
var augName = augs[i].name; };
var aug = Augmentations[augName];
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (augs[i].level);
}
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
listElement.appendChild(accordion[0]);
}
}
//Creates the accordion elements to display Source Files
// @listElement - List DOM element to append accordion elements to
// @sourceFiles - Array of Source File objects
function displaySourceFiles(listElement, sourceFiles) {
for (var i = 0; i < sourceFiles.length; ++i) {
var srcFileKey = "SourceFile" + sourceFiles[i].n;
var sourceFileObject = SourceFiles[srcFileKey];
if (sourceFileObject == null) {
console.log("ERROR: Invalid source file number: " + sourceFiles[i].n);
continue;
}
const maxLevel = sourceFiles[i].n == 12 ? "∞" : "3";
var accordion = createAccordionElement({
hdrText:sourceFileObject.name + "<br>" + "Level " + (sourceFiles[i].lvl) + " / "+maxLevel,
panelText:sourceFileObject.info
});
listElement.appendChild(accordion[0]);
}
}
export {installAugmentations,
initAugmentations, applyAugmentation, augmentationExists,
displayAugmentationsContent};

View File

@@ -0,0 +1,42 @@
/**
* React Component for displaying a list of the player's installed Augmentations
* on the Augmentations UI
*/
import * as React from "react";
import { Player } from "../../Player";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
export function InstalledAugmentations(): React.ReactElement {
const sourceAugs = Player.augmentations.slice();
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceAugs.sort((aug1, aug2) => {
return aug1.name <= aug2.name ? -1 : 1;
});
}
const augs = sourceAugs.map((e) => {
const aug = Augmentations[e.name];
let level = null;
if (e.name === AugmentationNames.NeuroFluxGovernor) {
level = e.level;
}
return (
<li key={e.name}>
<AugmentationAccordion aug={aug} level={level} />
</li>
)
});
return (
<>{augs}</>
)
}

View File

@@ -0,0 +1,107 @@
/**
* React Component for displaying all of the player's installed Augmentations and
* Source-Files.
*
* It also contains 'configuration' buttons that allow you to change how the
* Augs/SF's are displayed
*/
import * as React from "react";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { ListConfiguration } from "./ListConfiguration";
import { OwnedSourceFiles } from "./OwnedSourceFiles";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
type IProps = {}
type IState = {
rerenderFlag: boolean;
}
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
listRef: React.RefObject<HTMLUListElement>;
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
}
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
this.expandAllHeaders = this.expandAllHeaders.bind(this);
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
this.sortInOrder = this.sortInOrder.bind(this);
this.listRef = React.createRef();
}
collapseAllHeaders() {
const ul = this.listRef.current;
if (ul == null) { return; }
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (ticker.classList.contains("active")) {
ticker.click();
}
}
}
expandAllHeaders() {
const ul = this.listRef.current;
if (ul == null) { return; }
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (!ticker.classList.contains("active")) {
ticker.click();
}
}
}
rerender() {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
}
});
}
sortByAcquirementTime() {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
this.rerender();
}
sortInOrder() {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically
this.rerender();
}
render() {
return (
<>
<ListConfiguration
collapseAllButtonsFn={this.collapseAllHeaders}
expandAllButtonsFn={this.expandAllHeaders}
sortByAcquirementTimeFn={this.sortByAcquirementTime}
sortInOrderFn={this.sortInOrder}
/>
<ul className="augmentations-list" ref={this.listRef}>
<OwnedSourceFiles />
<InstalledAugmentations />
</ul>
</>
)
}
}

View File

@@ -0,0 +1,39 @@
/**
* React Component for configuring the way installed augmentations and
* Source-Files are displayed in the Augmentations UI
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
collapseAllButtonsFn: () => void;
expandAllButtonsFn: () => void;
sortByAcquirementTimeFn: () => void;
sortInOrderFn: () => void;
}
export function ListConfiguration(props: IProps): React.ReactElement {
return (
<>
<StdButton
onClick={props.expandAllButtonsFn}
text="Expand All"
/>
<StdButton
onClick={props.collapseAllButtonsFn}
text="Collapse All"
/>
<StdButton
onClick={props.sortInOrderFn}
text="Sort in Order"
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
/>
<StdButton
onClick={props.sortByAcquirementTimeFn}
text="Sort by Acquirement Time"
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
/>
</>
)
}

View File

@@ -0,0 +1,41 @@
/**
* React Component for displaying a list of the player's Source-Files
* on the Augmentations UI
*/
import * as React from "react";
import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { SourceFiles } from "../../SourceFile/SourceFiles";
import { SourceFileAccordion } from "../../ui/React/SourceFileAccordion";
export function OwnedSourceFiles(): React.ReactElement {
const sourceSfs = Player.sourceFiles.slice();
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceSfs.sort((sf1, sf2) => {
return sf1.n - sf2.n;
});
}
const sfs = sourceSfs.map((e) => {
const srcFileKey = "SourceFile" + e.n;
const sfObj = SourceFiles[srcFileKey];
if (sfObj == null) {
console.error(`Invalid source file number: ${e.n}`);
return null;
}
return (
<li key={e.n}>
<SourceFileAccordion level={e.lvl} sf={sfObj} />
</li>
)
});
return (
<>{sfs}</>
);
}

View File

@@ -0,0 +1,96 @@
/**
* React component for displaying the player's multipliers on the Augmentation UI page
*/
import * as React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
export function PlayerMultipliers(): React.ReactElement {
return (
<>
<p><strong><u>Total Multipliers:</u></strong></p>
<pre>
{'Hacking Chance multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_chance_mult)}
</pre>
<pre>
{'Hacking Speed multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_speed_mult)}
</pre>
<pre>
{'Hacking Money multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_money_mult)}
</pre>
<pre>
{'Hacking Growth multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_grow_mult)}
</pre><br />
<pre>
{'Hacking Level multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_mult)}
</pre>
<pre>
{'Hacking Experience multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_exp_mult)}
</pre>
<br />
<pre>
{'Strength Level multiplier: ' + numeralWrapper.formatPercentage(Player.strength_mult)}
</pre>
<pre>
{'Strength Experience multiplier: ' + numeralWrapper.formatPercentage(Player.strength_exp_mult)}
</pre>
<br />
<pre>
{'Defense Level multiplier: ' + numeralWrapper.formatPercentage(Player.defense_mult)}
</pre>
<pre>
{'Defense Experience multiplier: ' + numeralWrapper.formatPercentage(Player.defense_exp_mult)}
</pre><br />
<pre>
{'Dexterity Level multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_mult)}
</pre>
<pre>
{'Dexterity Experience multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_exp_mult)}
</pre><br />
<pre>
{'Agility Level multiplier: ' + numeralWrapper.formatPercentage(Player.agility_mult)}
</pre>
<pre>
{'Agility Experience multiplier: ' + numeralWrapper.formatPercentage(Player.agility_exp_mult)}
</pre><br />
<pre>
{'Charisma Level multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_mult)}
</pre>
<pre>
{'Charisma Experience multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_exp_mult)}
</pre><br />
<pre>
{'Hacknet Node production multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_money_mult)}
</pre>
<pre>
{'Hacknet Node purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_purchase_cost_mult)}
</pre>
<pre>
{'Hacknet Node RAM upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_ram_cost_mult)}
</pre>
<pre>
{'Hacknet Node Core purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_core_cost_mult)}
</pre>
<pre>
{'Hacknet Node level upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_level_cost_mult)}
</pre><br />
<pre>
{'Company reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.company_rep_mult)}
</pre>
<pre>
{'Faction reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.faction_rep_mult)}
</pre>
<pre>
{'Salary multiplier: ' + numeralWrapper.formatPercentage(Player.work_money_mult)}
</pre><br />
<pre>
{'Crime success multiplier: ' + numeralWrapper.formatPercentage(Player.crime_success_mult)}
</pre>
<pre>
{'Crime money multiplier: ' + numeralWrapper.formatPercentage(Player.crime_money_mult)}
</pre>
</>
)
}

View File

@@ -0,0 +1,32 @@
/**
* React component for displaying all of the player's purchased (but not installed)
* Augmentations on the Augmentations UI.
*/
import * as React from "react";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
export function PurchasedAugmentations(): React.ReactElement {
const augs: React.ReactElement[] = [];
for (const ownedAug of Player.queuedAugmentations) {
const aug = Augmentations[ownedAug.name];
let level = null;
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
level = ownedAug.level;
}
augs.push(
<li key={`${ownedAug.name}${ownedAug.level}`}>
<AugmentationAccordion aug={aug} level={level} />
</li>
)
}
return (
<ul className="augmentations-list">{augs}</ul>
)
}

View File

@@ -0,0 +1,83 @@
/**
* Root React component for the Augmentations UI page that display all of your
* owned and purchased Augmentations and Source-Files.
*/
import * as React from "react";
import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
import { PlayerMultipliers } from "./PlayerMultipliers";
import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { Player } from "../../Player";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
exportGameFn: () => void;
installAugmentationsFn: () => void;
}
type IState = {
}
export class AugmentationsRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
}
render() {
return (
<div id="augmentations-content">
<h1>Purchased Augmentations</h1>
<p>
Below is a list of all Augmentations you have purchased but not
yet installed. Click the button below to install them.
</p>
<p>
WARNING: Installing your Augmentations resets most of your progress,
including:
</p><br />
<p>- Stats/Skill levels and Experience</p>
<p>- Money</p>
<p>- Scripts on every computer but your home computer</p>
<p>- Purchased servers</p>
<p>- Hacknet Nodes</p>
<p>- Faction/Company reputation</p>
<p>- Stocks</p><br />
<p>
Installing Augmentations lets you start over with the perks and
benefits granted by all of the Augmentations you have ever
installed. Also, you will keep any scripts and RAM/Core upgrades
on your home computer (but you will lose all programs besides
NUKE.exe)
</p>
<StdButton
onClick={this.props.installAugmentationsFn}
text="Install Augmentations"
tooltip="'I never asked for this'"
/>
<StdButton
addClasses="flashing-button"
onClick={this.props.exportGameFn}
text="Backup Save (Export)"
tooltip="It's always a good idea to backup/export your save!"
/>
<PurchasedAugmentations />
<h1>Installed Augmentations</h1>
<p>
{
`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
`that have been installed. You have gained the effects of these.`
}
</p>
<InstalledAugmentationsAndSourceFiles />
<br /> <br />
<PlayerMultipliers />
</div>
)
}
}

View File

@@ -25,235 +25,232 @@ class BitNode {
} }
export let BitNodes: IMap<BitNode> = {}; export const BitNodes: IMap<BitNode> = {};
export function initBitNodes() { BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
BitNodes = {}; "The first BitNode created by the Enders to imprison the minds of humans. It became " +
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode", "the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
"The first BitNode created by the Enders to imprison the minds of humans. It became " + "This is the first BitNode that you play through. It has no special " +
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" + "modifications or mechanics.<br><br>" +
"This is the first BitNode that you play through. It has no special " + "Destroying this BitNode will give you Source-File 1, or if you already have " +
"modifications or mechanics.<br><br>" + "this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
"Destroying this BitNode will give you Source-File 1, or if you already have " + "lets the player start with 32GB of RAM on his/her home computer when entering a " +
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " + "new BitNode, and also increases all of the player's multipliers by:<br><br>" +
"lets the player start with 32GB of RAM on his/her home computer when entering a " + "Level 1: 16%<br>" +
"new BitNode, and also increases all of the player's multipliers by:<br><br>" + "Level 2: 24%<br>" +
"Level 1: 16%<br>" + "Level 3: 28%");
"Level 2: 24%<br>" + BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
"Level 3: 28%"); "From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs "left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " + "people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " + "factions quickly rose to the top of the modern world.<br><br>" +
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " + "In this BitNode:<br><br>" +
"factions quickly rose to the top of the modern world.<br><br>" + "Your hacking level is reduced by 20%<br>" +
"In this BitNode:<br><br>" + "The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
"Your hacking level is reduced by 20%<br>" + "The amount of money gained from crimes and Infiltration is tripled<br>" +
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" + "Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
"The amount of money gained from crimes and Infiltration is tripled<br>" + "NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " + "will earn the player money and reputation with the corresponding Faction<br>" +
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " + "Every Augmentation in the game will be available through the Factions listed above<br>" +
"will earn the player money and reputation with the corresponding Faction<br>" + "For every Faction NOT listed above, reputation gains are halved<br>" +
"Every Augmentation in the game will be available through the Factions listed above<br>" + "You will no longer gain passive reputation with Factions<br><br>" +
"For every Faction NOT listed above, reputation gains are halved<br>" + "Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
"You will no longer gain passive reputation with Factions<br><br>" + "upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " + "once your karma decreases to a certain value. " +
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " + "It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
"once your karma decreases to a certain value. " + "Level 1: 24%<br>" +
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" + "Level 2: 36%<br>" +
"Level 1: 24%<br>" + "Level 3: 42%");
"Level 2: 36%<br>" + BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
"Level 3: 42%"); "Our greatest illusion is that a healthy society can revolve around a " +
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization", "single-minded pursuit of wealth.<br><br>" +
"Our greatest illusion is that a healthy society can revolve around a " + "Sometime in the early 21st century economic and political globalization turned " +
"single-minded pursuit of wealth.<br><br>" + "the world into a corporatocracy, and it never looked back. Now, the privileged " +
"Sometime in the early 21st century economic and political globalization turned " + "elite will happily bankrupt their own countrymen, decimate their own community, " +
"the world into a corporatocracy, and it never looked back. Now, the privileged " + "and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" +
"elite will happily bankrupt their own countrymen, decimate their own community, " + "In this BitNode you can create and manage your own corporation. Running a successful corporation " +
"and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" + "has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
"In this BitNode you can create and manage your own corporation. Running a successful corporation " + "The price and reputation cost of all Augmentations is tripled<br>" +
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" + "The starting and maximum amount of money on servers is reduced by 75%<br>" +
"The price and reputation cost of all Augmentations is tripled<br>" + "Server growth rate is reduced by 80%<br>" +
"The starting and maximum amount of money on servers is reduced by 75%<br>" + "You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
"Server growth rate is reduced by 80%<br>" + "Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" + "upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " + "some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " + "Level 1: 8%<br>" +
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" + "Level 2: 12%<br>" +
"Level 1: 8%<br>" + "Level 3: 14%");
"Level 2: 12%<br>" + BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
"Level 3: 14%"); "The Singularity has arrived. The human race is gone, replaced " +
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine", "by artificially superintelligent beings that are more machine than man. <br><br>" +
"The Singularity has arrived. The human race is gone, replaced " + "In this BitNode, progressing is significantly harder. Experience gain rates " +
"by artificially superintelligent beings that are more machine than man. <br><br>" + "for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
"In this BitNode, progressing is significantly harder. Experience gain rates " + "In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " +
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" + "These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " +
"In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " + "purchasing/installing Augmentations, and creating programs.<br><br>" +
"These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " + "Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
"purchasing/installing Augmentations, and creating programs.<br><br>" + "upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " + "Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " + "that you can use.");
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " + BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
"that you can use."); "They said it couldn't be done. They said the human brain, " +
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman", "along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
"They said it couldn't be done. They said the human brain, " + "of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " + "by 1's and 0's. They were wrong.<br><br>" +
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " + "In this BitNode:<br><br>" +
"by 1's and 0's. They were wrong.<br><br>" + "The base security level of servers is doubled<br>" +
"In this BitNode:<br><br>" + "The starting money on servers is halved, but the maximum money remains the same<br>" +
"The base security level of servers is doubled<br>" + "Most methods of earning money now give significantly less<br>" +
"The starting money on servers is halved, but the maximum money remains the same<br>" + "Infiltration gives 50% more reputation and money<br>" +
"Most methods of earning money now give significantly less<br>" + "Corporations have 50% lower valuations and are therefore less profitable<br>" +
"Infiltration gives 50% more reputation and money<br>" + "Augmentations are more expensive<br>" +
"Corporations have 50% lower valuations and are therefore less profitable<br>" + "Hacking experience gain rates are reduced<br><br>" +
"Augmentations are more expensive<br>" + "Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
"Hacking experience gain rates are reduced<br><br>" + "upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " + "Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " + "gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " + "when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " + "in the game. <br><br>" +
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " + "In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " +
"in the game. <br><br>" + "and will also raise all of your hacking-related multipliers by:<br><br>" +
"In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " + "Level 1: 8%<br>" +
"and will also raise all of your hacking-related multipliers by:<br><br>" + "Level 2: 12%<br>" +
"Level 1: 8%<br>" + "Level 3: 14%");
"Level 2: 12%<br>" + BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
"Level 3: 14%"); "In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain", "androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " + "of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " + "the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " + "than the humans that had created them.<br><br>" +
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " + "In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
"than the humans that had created them.<br><br>" + "for progression. Furthermore:<br><br>" +
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " + "Hacking and Hacknet Nodes will be less profitable<br>" +
"for progression. Furthermore:<br><br>" + "Your hacking level is reduced by 65%<br>" +
"Hacking and Hacknet Nodes will be less profitable<br>" + "Hacking experience gain from scripts is reduced by 75%<br>" +
"Your hacking level is reduced by 65%<br>" + "Corporations have 80% lower valuations and are therefore less profitable<br>" +
"Hacking experience gain from scripts is reduced by 75%<br>" + "Working for companies is 50% less profitable<br>" +
"Corporations have 80% lower valuations and are therefore less profitable<br>" + "Crimes and Infiltration are 25% less profitable<br><br>" +
"Working for companies is 50% less profitable<br>" + "Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
"Crimes and Infiltration are 25% less profitable<br><br>" + "its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " + "BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " + "Level 1: 8%<br>" +
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" + "Level 2: 12%<br>" +
"Level 1: 8%<br>" + "Level 3: 14%");
"Level 2: 12%<br>" + BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
"Level 3: 14%"); "In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans", "for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " + "breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " + "Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " + "and more intelligent than the humans that had created them.<br><br>" +
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " + "In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
"and more intelligent than the humans that had created them.<br><br>" + "functionality through Netscript. Furthermore: <br><br>" +
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " + "The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
"functionality through Netscript. Furthermore: <br><br>" + "Bladeburner skills cost twice as many skill points<br>" +
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" + "Augmentations are 3x more expensive<br>" +
"Bladeburner skills cost twice as many skill points<br>" + "Hacking and Hacknet Nodes will be significantly less profitable<br>" +
"Augmentations are 3x more expensive<br>" + "Your hacking level is reduced by 65%<br>" +
"Hacking and Hacknet Nodes will be significantly less profitable<br>" + "Hacking experience gain from scripts is reduced by 75%<br>" +
"Your hacking level is reduced by 65%<br>" + "Corporations have 80% lower valuations and are therefore less profitable<br>" +
"Hacking experience gain from scripts is reduced by 75%<br>" + "Working for companies is 50% less profitable<br>" +
"Corporations have 80% lower valuations and are therefore less profitable<br>" + "Crimes and Infiltration are 25% less profitable<br><br>" +
"Working for companies is 50% less profitable<br>" + "Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
"Crimes and Infiltration are 25% less profitable<br><br>" + "its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " + "BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " + "Level 1: 8%<br>" +
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" + "Level 2: 12%<br>" +
"Level 1: 8%<br>" + "Level 3: 14%");
"Level 2: 12%<br>" + BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
"Level 3: 14%"); "You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps", "In this BitNode:<br><br>" +
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" + "You start with $250 million<br>" +
"In this BitNode:<br><br>" + "The only way to earn money is by trading on the stock market<br>" +
"You start with $250 million<br>" + "You start with a WSE membership and access to the TIX API<br>" +
"The only way to earn money is by trading on the stock market<br>" + "You are able to short stocks and place different types of orders (limit/stop)<br>" +
"You start with a WSE membership and access to the TIX API<br>" + "You can immediately donate to factions to gain reputation<br><br>" +
"You are able to short stocks and place different types of orders (limit/stop)<br>" + "Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
"You can immediately donate to factions to gain reputation<br><br>" + "upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " + "Level 1: Permanent access to WSE and TIX API<br>" +
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" + "Level 2: Ability to short stocks in other BitNodes<br>" +
"Level 1: Permanent access to WSE and TIX API<br>" + "Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"Level 2: Ability to short stocks in other BitNodes<br>" + "This Source-File also increases your hacking growth multipliers by: " +
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" + "<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
"This Source-File also increases your hacking growth multipliers by: " + BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%"); "When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed", "became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " + "powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " + "abandoned the project and dissociated themselves from it.<br><br>" +
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " + "This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
"abandoned the project and dissociated themselves from it.<br><br>" + "hashes, which can be spent on a variety of different upgrades.<br><br>" +
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " + "In this BitNode:<br><br>" +
"hashes, which can be spent on a variety of different upgrades.<br><br>" + "Your stats are significantly decreased<br>" +
"In this BitNode:<br><br>" + "You cannnot purchase additional servers<br>" +
"Your stats are significantly decreased<br>" + "Hacking is significantly less profitable<br><br>" +
"You cannnot purchase additional servers<br>" + "Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
"Hacking is significantly less profitable<br><br>" + "upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " + "Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" + "Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" + "Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" + "(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" + "when installing Augmentations)");
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " + BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
"when installing Augmentations)"); "In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are", "to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " + "or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " + "human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " + "This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" + "1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" + "2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" + "In this BitNode:<br><br>" +
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" + "Your stats are significantly decreased<br>" +
"In this BitNode:<br><br>" + "All methods of gaining money are half as profitable (except Stock Market)<br>" +
"Your stats are significantly decreased<br>" + "Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
"All methods of gaining money are half as profitable (except Stock Market)<br>" + "Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" + "Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" + "upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " + "Each level of this Source-File also grants you a Duplicate Sleeve");
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " + BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"Each level of this Source-File also grants you a Duplicate Sleeve"); "The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.", "of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " + "the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " + "In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers " +
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" + "were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
"In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers " + "governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " + "In this BitNode:<br><br>" +
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" + "Your hacking stat and experience gain are halved<br>" +
"In this BitNode:<br><br>" + "The starting and maximum amount of money available on servers is significantly decreased<br>" +
"Your hacking stat and experience gain are halved<br>" + "The growth rate of servers is significantly reduced<br>" +
"The starting and maximum amount of money available on servers is significantly decreased<br>" + "Weakening a server is twice as effective<br>" +
"The growth rate of servers is significantly reduced<br>" + "Company wages are decreased by 50%<br>" +
"Weakening a server is twice as effective<br>" + "Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
"Company wages are decreased by 50%<br>" + "Hacknet Node production is significantly decreased<br>" +
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" + "Crime and Infiltration are more lucrative<br>" +
"Hacknet Node production is significantly decreased<br>" + "Augmentations are twice as expensive<br><br>" +
"Crime and Infiltration are more lucrative<br>" + "Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
"Augmentations are twice as expensive<br><br>" + "upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " + "the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " + "This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " + "Level 1: 32%<br>" +
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" + "Level 2: 48%<br>" +
"Level 1: 32%<br>" + "Level 3: 56%");
"Level 2: 48%<br>" + BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
"Level 3: 56%"); "To iterate is human, to recurse divine.<br><br>" +
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.", "Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
"To iterate is human, to recurse divine.<br><br>" + "if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " + "of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " + "In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " + // Books: Frontera, Shiner
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)"); BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
//Books: Frontera, Shiner BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON"); BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON"); BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON"); BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON"); BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON"); BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON"); BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON"); BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON"); BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON"); BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
}
export function initBitNodeMultipliers(p: IPlayer) { export function initBitNodeMultipliers(p: IPlayer) {
if (p.bitNodeN == null) { if (p.bitNodeN == null) {

View File

@@ -4173,7 +4173,7 @@ function initBladeburner() {
"results would be catastrophic.<br><br>" + "results would be catastrophic.<br><br>" +
"We do not have the power or jurisdiction to shutdown this down " + "We do not have the power or jurisdiction to shutdown this down " +
"through legal or political means, so we must resort to a covert " + "through legal or political means, so we must resort to a covert " +
"operation. Your goal is to destroy this technology and eliminate" + "operation. Your goal is to destroy this technology and eliminate " +
"anyone who was involved in its creation.", "anyone who was involved in its creation.",
baseDifficulty:15e3, reqdRank:30e3, baseDifficulty:15e3, reqdRank:30e3,
rankGain:750, rankLoss:60, hpLoss:1000, rankGain:750, rankLoss:60, hpLoss:1000,

View File

@@ -7,6 +7,7 @@ import { Factions } from "./Faction/Factions";
import { Player } from "./Player"; import { Player } from "./Player";
import { AllServers } from "./Server/AllServers"; import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers"; import { GetServerByHostname } from "./Server/ServerHelpers";
import { SpecialServerNames } from "./Server/SpecialServerIps";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
@@ -162,7 +163,9 @@ function getRandomServer() {
// An infinite loop shouldn't ever happen, but to be safe we'll use // An infinite loop shouldn't ever happen, but to be safe we'll use
// a for loop with a limited number of tries // a for loop with a limited number of tries
for (let i = 0; i < 200; ++i) { for (let i = 0; i < 200; ++i) {
if (randServer.purchasedByPlayer === false) { break; } if (!randServer.purchasedByPlayer && randServer.hostname !== SpecialServerNames.WorldDaemon) {
break;
}
randIndex = getRandomInt(0, servers.length - 1); randIndex = getRandomInt(0, servers.length - 1);
randServer = AllServers[servers[randIndex]]; randServer = AllServers[servers[randIndex]];
} }

View File

@@ -17,9 +17,6 @@ import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup"; import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */ /* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
/* Represents different types of problems that a Coding Contract can have */ /* Represents different types of problems that a Coding Contract can have */
@@ -205,6 +202,7 @@ export class CodingContract {
} }
}, },
placeholder: "Enter Solution here", placeholder: "Enter Solution here",
width: "50%",
}) as HTMLInputElement; }) as HTMLInputElement;
solveBtn = createElement("a", { solveBtn = createElement("a", {
class: "a-link-button", class: "a-link-button",

View File

@@ -6,7 +6,7 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.46.3", Version: "v0.47.2",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -38,60 +38,6 @@ export let CONSTANTS: IMap<any> = {
// NeuroFlux Governor Augmentation cost multiplier // NeuroFlux Governor Augmentation cost multiplier
NeuroFluxGovernorLevelMult: 1.14, NeuroFluxGovernorLevelMult: 1.14,
// RAM Costs for Netscript functions
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,
ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05,
ScriptGetHackingLevelRamCost: 0.05,
ScriptGetMultipliersRamCost: 4.0,
ScriptGetServerRamCost: 0.1,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptHacknetNodesRamCost: 4.0,
ScriptHNUpgLevelRamCost: 0.4,
ScriptHNUpgRamRamCost: 0.6,
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
ScriptGetPurchaseServerRamCost: 0.25,
ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05,
ScriptGetPurchasedServerMaxRam: 0.05,
ScriptRoundRamCost: 0.05,
ScriptReadWriteRamCost: 1.0,
ScriptArbScriptRamCost: 1.0,
ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4,
ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2,
ScriptSingularityFn3RamCost: 3,
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
ScriptGangApiBaseRamCost: 4,
ScriptBladeburnerApiBaseRamCost: 4,
NumNetscriptPorts: 20, NumNetscriptPorts: 20,
// Server-related constants // Server-related constants
@@ -251,6 +197,13 @@ export let CONSTANTS: IMap<any> = {
ClassLeadershipBaseCost: 320, ClassLeadershipBaseCost: 320,
ClassGymBaseCost: 120, ClassGymBaseCost: 120,
ClassStudyComputerScienceBaseExp: 0.5,
ClassDataStructuresBaseExp: 1,
ClassNetworksBaseExp: 2,
ClassAlgorithmsBaseExp: 4,
ClassManagementBaseExp: 2,
ClassLeadershipBaseExp: 4,
CrimeShoplift: "shoplift", CrimeShoplift: "shoplift",
CrimeRobStore: "rob a store", CrimeRobStore: "rob a store",
CrimeMug: "mug someone", CrimeMug: "mug someone",
@@ -275,35 +228,35 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.46.3 v0.47.2
* 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 Netscript Changes
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value * Added tail() Netscript function
** (Karma is a hidden stat and is lowered by committing crimes) * hacknet.getNodeStats() function now returns an additional property for Hacknet Servers: hashCapacity
* Gang changes: * When writing to a file, the write() function now casts the data being written to a string (using String())
** Bug Fix: Gangs can no longer clash with themselve * BitNode-selection page now shows what Source-File level you have for each BitNode
** Bug Fix: Winning against another gang should properly reduce their power * Overloaded kill() function so that you can kill a script by its PID
* spawn() now only takes 10 seconds to run (decreased from 20 seconds)
* run() and exec() now return the PID of the newly-executed scripts, rather than a boolean
** (A PID is just a positive integer)
* run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS
* Script parsing and RAM calculations now support ES9
* installAugmentations() no longer has a return value since it causes all scripts to die
* isBusy() now returns true if you are in a Hacking Mission
* Bug fix: workForFaction() function now properly accounts for disabled logs
* Bug fix: RAM should now be properly calculated when running a callback script with installAugmentations()
* Bug fix: Fixed bug that caused scripts killed by exit()/spawn() to "clean up" twice
* Bug Fix: Terminal 'wget' command now works properly Misc Changes
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes * The 'kill' Terminal command can now kill a script by its PID
* Bug Fix: Fixed button for creating Corporations * Added 'Solarized Dark' theme to CodeMirror editor
* After Infiltration, you will now return to the company page rather than the city page
v0.46.1 * Bug fix: Stock Market UI should no longer crash for certain locale settings
* Added a very rudimentary directory system to the Terminal * Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files)
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories * Bug fix: Fixed an issue that allowed you to increase money on servers with a 'maxMoney' of 0 (like CSEC)
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API * Bug fix: Scripts no longer persist if they were started with syntax/import errors
* 'Generate Coding Contract' hash upgrade is now more expensive * Bug fix: 'hack' and 'analyze' Terminal commands are now blocking
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer * Bug fix: Exp earned by duplicate sleeves at universities/gyms now takes hash upgrades into account
* 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
` `
} }

View File

@@ -521,15 +521,18 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
} }
// Process production, purchase, and import/export of materials // Process production, purchase, and import/export of materials
var res = this.processMaterials(marketCycles, company); let res = this.processMaterials(marketCycles, company);
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]); if (Array.isArray(res)) {
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]); this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
}
// Process creation, production & sale of products // Process creation, production & sale of products
res = this.processProducts(marketCycles, company); res = this.processProducts(marketCycles, company);
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]); if (Array.isArray(res)) {
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]); this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
}
} }
// Process change in demand and competition for this industry's materials // Process change in demand and competition for this industry's materials
@@ -846,7 +849,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles); sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles);
sellAmt = Math.min(mat.qty, sellAmt); sellAmt = Math.min(mat.qty, sellAmt);
if (sellAmt < 0) { if (sellAmt < 0) {
console.log("sellAmt calculated to be negative"); console.warn(`sellAmt calculated to be negative for ${matName} in ${city}`);
mat.sll = 0; mat.sll = 0;
continue; continue;
} }
@@ -899,9 +902,11 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
break; break;
} }
//Make sure theres enough space in warehouse // Make sure theres enough space in warehouse
if (expWarehouse.sizeUsed >= expWarehouse.size) { if (expWarehouse.sizeUsed >= expWarehouse.size) {
return; //Warehouse at capacity // Warehouse at capacity. Exporting doesnt
// affect revenue so just return 0's
return [0, 0];
} else { } else {
var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]); var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]);
amt = Math.min(maxAmt, amt); amt = Math.min(maxAmt, amt);
@@ -1463,7 +1468,6 @@ function Employee(params={}) {
this.hap = params.happiness ? params.happiness : getRandomInt(50, 100); this.hap = params.happiness ? params.happiness : getRandomInt(50, 100);
this.ene = params.energy ? params.energy : getRandomInt(50, 100); this.ene = params.energy ? params.energy : getRandomInt(50, 100);
this.age = params.age ? params.age : getRandomInt(20, 50);
this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50); this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50);
this.cha = params.charisma ? params.charisma : getRandomInt(10, 50); this.cha = params.charisma ? params.charisma : getRandomInt(10, 50);
this.exp = params.experience ? params.experience : getRandomInt(10, 50); this.exp = params.experience ? params.experience : getRandomInt(10, 50);
@@ -1482,13 +1486,7 @@ function Employee(params={}) {
Employee.prototype.process = function(marketCycles=1, office) { Employee.prototype.process = function(marketCycles=1, office) {
var gain = 0.003 * marketCycles, var gain = 0.003 * marketCycles,
det = gain * Math.random(); det = gain * Math.random();
this.age += gain;
this.exp += gain; this.exp += gain;
if (this.age > 150) {
this.int -= det;
this.eff -= det;
this.cha -= det;
}
// Employee salaries slowly go up over time // Employee salaries slowly go up over time
this.cyclesUntilRaise -= marketCycles; this.cyclesUntilRaise -= marketCycles;
@@ -1577,7 +1575,6 @@ Employee.prototype.createUI = function(panel, corporation, industry) {
innerHTML:"Morale: " + formatNumber(this.mor, 3) + "<br>" + innerHTML:"Morale: " + formatNumber(this.mor, 3) + "<br>" +
"Happiness: " + formatNumber(this.hap, 3) + "<br>" + "Happiness: " + formatNumber(this.hap, 3) + "<br>" +
"Energy: " + formatNumber(this.ene, 3) + "<br>" + "Energy: " + formatNumber(this.ene, 3) + "<br>" +
"Age: " + formatNumber(this.age, 3) + "<br>" +
"Intelligence: " + formatNumber(effInt, 3) + "<br>" + "Intelligence: " + formatNumber(effInt, 3) + "<br>" +
"Charisma: " + formatNumber(effCha, 3) + "<br>" + "Charisma: " + formatNumber(effCha, 3) + "<br>" +
"Experience: " + formatNumber(this.exp, 3) + "<br>" + "Experience: " + formatNumber(this.exp, 3) + "<br>" +

View File

@@ -495,8 +495,6 @@ export class IndustryOffice extends BaseReactComponent {
<br /> <br />
Energy: {numeralWrapper.format(this.state.employee.ene, nf)} Energy: {numeralWrapper.format(this.state.employee.ene, nf)}
<br /> <br />
Age: {numeralWrapper.format(this.state.employee.age, nf)}
<br />
Intelligence: {numeralWrapper.format(effInt, nf)} Intelligence: {numeralWrapper.format(effInt, nf)}
<br /> <br />
Charisma: {numeralWrapper.format(effCha, nf)} Charisma: {numeralWrapper.format(effCha, nf)}

View File

@@ -4,6 +4,7 @@ import ReactDOM from "react-dom";
import { FactionRoot } from "./ui/Root"; import { FactionRoot } from "./ui/Root";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { isRepeatableAug } from "../Augmentation/AugmentationHelpers";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@@ -93,7 +94,12 @@ export function purchaseAugmentationBoxCreate(aug, fac) {
const yesBtn = yesNoBoxGetYesButton(); const yesBtn = yesNoBoxGetYesButton();
yesBtn.innerHTML = "Purchase"; yesBtn.innerHTML = "Purchase";
yesBtn.addEventListener("click", function() { yesBtn.addEventListener("click", function() {
if (!isRepeatableAug(aug) && Player.hasAugmentation(aug)) {
return;
}
purchaseAugmentation(aug, fac); purchaseAugmentation(aug, fac);
yesNoBoxClose();
}); });
const noBtn = yesNoBoxGetNoButton(); const noBtn = yesNoBoxGetNoButton();
@@ -204,7 +210,6 @@ export function purchaseAugmentation(aug, fac, sing=false) {
"Please report this to the game developer with an explanation of how to " + "Please report this to the game developer with an explanation of how to " +
"reproduce this."); "reproduce this.");
} }
yesNoBoxClose();
} }
export function getNextNeurofluxLevel() { export function getNextNeurofluxLevel() {

View File

@@ -30,10 +30,24 @@ const infoStyleMarkup = {
} }
export class Info extends React.Component<IProps, any> { export class Info extends React.Component<IProps, any> {
render() { constructor(props: IProps) {
super(props);
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a"); this.getFavorGainText = this.getFavorGainText.bind(this);
this.getReputationText = this.getReputationText.bind(this);
}
getFavorGainText(): string {
const favorGain = this.props.faction.getFavorGain()[0]; 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 " + 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 " + "this faction by 1% per favor. Faction favor is gained whenever you " +
"reset after installing an Augmentation. The amount of " + "reset after installing an Augmentation. The amount of " +
@@ -51,8 +65,8 @@ export class Info extends React.Component<IProps, any> {
<p style={blockStyleMarkup}>-------------------------</p> <p style={blockStyleMarkup}>-------------------------</p>
<AutoupdatingParagraph <AutoupdatingParagraph
intervalTime={5e3} intervalTime={5e3}
text={`Reputation: ${formattedRep}`} getText={this.getReputationText}
tooltip={`You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`} getTooltip={this.getFavorGainText}
/> />
<p style={blockStyleMarkup}>-------------------------</p> <p style={blockStyleMarkup}>-------------------------</p>
<ParagraphWithTooltip <ParagraphWithTooltip

View File

@@ -279,7 +279,7 @@ export class FactionRoot extends React.Component<IProps, IState> {
{ {
canPurchaseSleeves && canPurchaseSleeves &&
<Option <Option
buttonText={"Purchase Duplicate Sleeves"} buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo} infoText={sleevePurchasesInfo}
onClick={this.sleevePurchases} onClick={this.sleevePurchases}
/> />

View File

@@ -1,6 +1,6 @@
import { FconfSettings } from "./FconfSettings"; import { FconfSettings } from "./FconfSettings";
import { parse, Node } from "../../utils/acorn"; import { parse, Node } from "acorn";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
var FconfComments = { var FconfComments = {

View File

@@ -682,21 +682,25 @@ GangMember.prototype.calculateRespectGain = function(gang) {
GangMember.prototype.calculateWantedLevelGain = function(gang) { GangMember.prototype.calculateWantedLevelGain = function(gang) {
const task = this.getTask(); const task = this.getTask();
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) {return 0;} if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) { return 0; }
var statWeight = (task.hackWeight/100) * this.hack + let statWeight = (task.hackWeight / 100) * this.hack +
(task.strWeight/100) * this.str + (task.strWeight / 100) * this.str +
(task.defWeight/100) * this.def + (task.defWeight / 100) * this.def +
(task.dexWeight/100) * this.dex + (task.dexWeight / 100) * this.dex +
(task.agiWeight/100) * this.agi + (task.agiWeight / 100) * this.agi +
(task.chaWeight/100) * this.cha; (task.chaWeight / 100) * this.cha;
statWeight -= (3.5 * task.difficulty); statWeight -= (3.5 * task.difficulty);
if (statWeight <= 0) { return 0; } if (statWeight <= 0) { return 0; }
const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100; const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100;
if (isNaN(territoryMult) || territoryMult <= 0) { return 0; } if (isNaN(territoryMult) || territoryMult <= 0) { return 0; }
if (task.baseWanted < 0) { if (task.baseWanted < 0) {
return 0.5 * task.baseWanted * statWeight * territoryMult; return 0.4 * task.baseWanted * statWeight * territoryMult;
} else { } else {
return 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8)); const calc = 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8));
// Put an arbitrary cap on this to prevent wanted level from rising too fast if the
// denominator is very small. Might want to rethink formula later
return Math.min(100, calc);
} }
} }

View File

@@ -6,34 +6,34 @@
*/ */
import { IReturnStatus } from "../types"; import { IReturnStatus } from "../types";
import { HacknetServer } from "../Hacknet/HacknetServer";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
function baseCheck(server: Server | HacknetServer, fnName: string): IReturnStatus { function baseCheck(server: Server, fnName: string): IReturnStatus {
if (server instanceof HacknetServer) { const hostname = server.hostname;
if (!("requiredHackingSkill" in server)) {
return { return {
res: false, res: false,
msg: `Cannot ${fnName} ${server.hostname} server because it is a Hacknet Node` msg: `Cannot ${fnName} ${hostname} server because it is a Hacknet Node`
} }
} }
if (server.hasAdminRights === false) { if (server.hasAdminRights === false) {
return { return {
res: false, res: false,
msg: `Cannot ${fnName} ${server.hostname} server because you do not have root access`, msg: `Cannot ${fnName} ${hostname} server because you do not have root access`,
} }
} }
return { res: true } return { res: true }
} }
export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IReturnStatus { export function netscriptCanHack(server: Server, p: IPlayer): IReturnStatus {
const initialCheck = baseCheck(server, "hack"); const initialCheck = baseCheck(server, "hack");
if (!initialCheck.res) { return initialCheck; } if (!initialCheck.res) { return initialCheck; }
let s = <Server>server; let s = server;
if (s.requiredHackingSkill > p.hacking_skill) { if (s.requiredHackingSkill > p.hacking_skill) {
return { return {
res: false, res: false,
@@ -44,10 +44,10 @@ export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IR
return { res: true } return { res: true }
} }
export function netscriptCanGrow(server: Server | HacknetServer): IReturnStatus { export function netscriptCanGrow(server: Server): IReturnStatus {
return baseCheck(server, "grow"); return baseCheck(server, "grow");
} }
export function netscriptCanWeaken(server: Server | HacknetServer): IReturnStatus { export function netscriptCanWeaken(server: Server): IReturnStatus {
return baseCheck(server, "weaken"); return baseCheck(server, "weaken");
} }

View File

@@ -0,0 +1,4 @@
/**
* Utility functions for calculating the maximum number of Hacknet upgrades the player
* can purchase for a Node with his/her current money
*/

View File

@@ -1,35 +1,51 @@
import { HacknetNode, /**
BaseCostForHacknetNode, * Generic helper/utility functions for the Hacknet mechanic:
HacknetNodePurchaseNextMult, * - Purchase nodes/upgrades
HacknetNodeMaxLevel, * - Calculating maximum number of upgrades
HacknetNodeMaxRam, * - Processing Hacknet earnings
HacknetNodeMaxCores } from "./HacknetNode"; * - Updating Hash Manager capacity
import { HacknetServer, * - Purchasing hash upgrades
BaseCostForHacknetServer, *
HacknetServerPurchaseMult, * TODO Should probably split the different types of functions into their own modules
HacknetServerMaxLevel, */
HacknetServerMaxRam, import {
HacknetServerMaxCores, HacknetNode,
HacknetServerMaxCache, BaseCostForHacknetNode,
MaxNumberHacknetServers } from "./HacknetServer"; HacknetNodePurchaseNextMult,
import { HashManager } from "./HashManager"; HacknetNodeMaxLevel,
import { HashUpgrades } from "./HashUpgrades"; HacknetNodeMaxRam,
HacknetNodeMaxCores
} from "./HacknetNode";
import {
HacknetServer,
BaseCostForHacknetServer,
HacknetServerPurchaseMult,
HacknetServerMaxLevel,
HacknetServerMaxRam,
HacknetServerMaxCores,
HacknetServerMaxCache,
MaxNumberHacknetServers
} from "./HacknetServer";
import { HashManager } from "./HashManager";
import { HashUpgrades } from "./HashUpgrades";
import { generateRandomContract } from "../CodingContractGenerator"; import { generateRandomContract } from "../CodingContractGenerator";
import { iTutorialSteps, iTutorialNextStep, import {
ITutorial} from "../InteractiveTutorial"; iTutorialSteps,
import { Player } from "../Player"; iTutorialNextStep,
import { AddToAllServers, ITutorial
AllServers } from "../Server/AllServers"; } from "../InteractiveTutorial";
import { GetServerByHostname } from "../Server/ServerHelpers"; import { Player } from "../Player";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; import { AllServers } from "../Server/AllServers";
import { Page, routing } from "../ui/navigationTracking"; import { GetServerByHostname } from "../Server/ServerHelpers";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { Page, routing } from "../ui/navigationTracking";
import {getElementById} from "../../utils/uiHelpers/getElementById"; import { getElementById } from "../../utils/uiHelpers/getElementById";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { HacknetRoot } from "./ui/Root"; import { HacknetRoot } from "./ui/Root";
let hacknetNodesDiv; let hacknetNodesDiv;
function hacknetNodesInit() { function hacknetNodesInit() {
@@ -66,7 +82,7 @@ export function purchaseHacknet() {
if (!Player.canAfford(cost)) { return -1; } if (!Player.canAfford(cost)) { return -1; }
Player.loseMoney(cost); Player.loseMoney(cost);
const server = Player.createHacknetServer(); const server = Player.createHacknetServer();
Player.hashManager.updateCapacity(Player); updateHashManagerCapacity();
return numOwned; return numOwned;
} else { } else {
@@ -79,8 +95,7 @@ export function purchaseHacknet() {
// Auto generate a name for the Node // Auto generate a name for the Node
const name = "hacknet-node-" + numOwned; const name = "hacknet-node-" + numOwned;
const node = new HacknetNode(name); const node = new HacknetNode(name, Player.hacknet_node_money_mult);
node.updateMoneyGainRate(Player);
Player.loseMoney(cost); Player.loseMoney(cost);
Player.hacknetNodes.push(node); Player.hacknetNodes.push(node);
@@ -110,32 +125,32 @@ export function getCostOfNextHacknetServer() {
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult; return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
} }
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
} }
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player))) { if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player.hacknet_node_level_cost_mult))) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
let levelsToMax = maxLevel - nodeObj.level; let levelsToMax = maxLevel - nodeObj.level;
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player))) { if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.hacknet_node_level_cost_mult))) {
return levelsToMax; return levelsToMax;
} }
while (min <= max) { while (min <= max) {
var curr = (min + max) / 2 | 0; var curr = (min + max) / 2 | 0;
if (curr !== maxLevel && if (curr !== maxLevel &&
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player)) && Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult)) &&
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player))) { Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player.hacknet_node_level_cost_mult))) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player))) { } else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
max = curr - 1; max = curr - 1;
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player))) { } else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@@ -144,12 +159,13 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
} }
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player))) { if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player.hacknet_node_ram_cost_mult))) {
return 0; return 0;
} }
@@ -159,45 +175,46 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
} else { } else {
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram)); levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
} }
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player))) { if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player.hacknet_node_ram_cost_mult))) {
return levelsToMax; return levelsToMax;
} }
//We'll just loop until we find the max //We'll just loop until we find the max
for (let i = levelsToMax-1; i >= 0; --i) { for (let i = levelsToMax-1; i >= 0; --i) {
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player))) { if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player.hacknet_node_ram_cost_mult))) {
return i; return i;
} }
} }
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
} }
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player))) { if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player.hacknet_node_core_cost_mult))) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.cores; const levelsToMax = maxLevel - nodeObj.cores;
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player))) { if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.hacknet_node_core_cost_mult))) {
return levelsToMax; return levelsToMax;
} }
//Use a binary search to find the max possible number of upgrades // Use a binary search to find the max possible number of upgrades
while (min <= max) { while (min <= max) {
let curr = (min + max) / 2 | 0; let curr = (min + max) / 2 | 0;
if (curr != maxLevel && if (curr != maxLevel &&
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player)) && Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult)) &&
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player))) { Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player.hacknet_node_core_cost_mult))) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player))) { } else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
max = curr - 1; max = curr - 1;
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player))) { } else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@@ -207,6 +224,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
@@ -242,7 +260,136 @@ export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Initial construction of Hacknet Nodes UI export function purchaseLevelUpgrade(node, levels=1) {
const sanitizedLevels = Math.round(levels);
const cost = node.calculateLevelUpgradeCost(sanitizedLevels, Player.hacknet_node_level_cost_mult);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false;
}
const isServer = (node instanceof HacknetServer);
// If we're at max level, return false
if (node.level >= (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
return false;
}
// If the number of specified upgrades would exceed the max level, calculate
// the maximum number of upgrades and use that
if (node.level + sanitizedLevels > (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel) - node.level);
return purchaseLevelUpgrade(node, diff);
}
if (!Player.canAfford(cost)) {
return false;
}
Player.loseMoney(cost);
node.upgradeLevel(sanitizedLevels, Player.hacknet_node_money_mult);
return true;
}
export function purchaseRamUpgrade(node, levels=1) {
const sanitizedLevels = Math.round(levels);
const cost = node.calculateRamUpgradeCost(sanitizedLevels, Player.hacknet_node_ram_cost_mult);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false;
}
const isServer = (node instanceof HacknetServer);
// Fail if we're already at max
if (node.ram >= (isServer ? HacknetServerMaxRam : HacknetNodeMaxRam)) {
return false;
}
// If the number of specified upgrades would exceed the max RAM, calculate the
// max possible number of upgrades and use that
if (isServer) {
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / node.maxRam)));
return purchaseRamUpgrade(node, diff);
}
} else {
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / node.ram)));
return purchaseRamUpgrade(node, diff);
}
}
if (!Player.canAfford(cost)) {
return false;
}
Player.loseMoney(cost);
node.upgradeRam(sanitizedLevels, Player.hacknet_node_money_mult);
return true;
}
export function purchaseCoreUpgrade(node, levels=1) {
const sanitizedLevels = Math.round(levels);
const cost = node.calculateCoreUpgradeCost(sanitizedLevels, Player.hacknet_node_core_cost_mult);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false;
}
const isServer = (node instanceof HacknetServer);
// Fail if we're already at max
if (node.cores >= (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) {
return false;
}
// If the specified number of upgrades would exceed the max Cores, calculate
// the max possible number of upgrades and use that
if (node.cores + sanitizedLevels > (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores) - node.cores);
return purchaseCoreUpgrade(node, diff);
}
if (!Player.canAfford(cost)) {
return false;
}
Player.loseMoney(cost);
node.upgradeCore(sanitizedLevels, Player.hacknet_node_money_mult);
return true;
}
export function purchaseCacheUpgrade(node, levels=1) {
const sanitizedLevels = Math.round(levels);
const cost = node.calculateCacheUpgradeCost(sanitizedLevels);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false;
}
if (!(node instanceof HacknetServer)) {
console.warn(`purchaseCacheUpgrade() called for a non-HacknetNode`);
return false;
}
// Fail if we're already at max
if (node.cache + sanitizedLevels > HacknetServerMaxCache) {
const diff = Math.max(0, HacknetServerMaxCache - node.cache);
return purchaseCacheUpgrade(node, diff);
}
if (!Player.canAfford(cost)) {
return false;
}
Player.loseMoney(cost);
node.upgradeCache(sanitizedLevels);
return true;
}
// Create/Refresh Hacknet Nodes UI
export function renderHacknetNodesUI() { export function renderHacknetNodesUI() {
if (!routing.isOn(Page.HacknetNodes)) { return; } if (!routing.isOn(Page.HacknetNodes)) { return; }
@@ -294,7 +441,10 @@ function processAllHacknetServerEarnings(numCycles) {
let hashes = 0; let hashes = 0;
for (let i = 0; i < Player.hacknetNodes.length; ++i) { for (let i = 0; i < Player.hacknetNodes.length; ++i) {
const hserver = AllServers[Player.hacknetNodes[i]]; // hacknetNodes array only contains the IP addresses // hacknetNodes array only contains the IP addresses of the servers.
// Also, update the hash rate before processing
const hserver = AllServers[Player.hacknetNodes[i]];
hserver.updateHashRate(Player.hacknet_node_money_mult);
hashes += hserver.process(numCycles); hashes += hserver.process(numCycles);
} }
@@ -303,6 +453,37 @@ function processAllHacknetServerEarnings(numCycles) {
return hashes; return hashes;
} }
export function updateHashManagerCapacity() {
if (!(Player.hashManager instanceof HashManager)) {
console.error(`Player does not have a HashManager`);
return;
}
const nodes = Player.hacknetNodes;
if (nodes.length === 0) {
Player.hashManager.updateCapacity(0);
return;
}
let total = 0;
for (let i = 0; i < nodes.length; ++i) {
if (typeof nodes[i] !== "string") {
Player.hashManager.updateCapacity(0);
return;
}
const h = AllServers[nodes[i]];
if (!(h instanceof HacknetServer)) {
Player.hashManager.updateCapacity(0);
return;
}
total += h.hashCapacity;
}
Player.hashManager.updateCapacity(total);
}
export function purchaseHashUpgrade(upgName, upgTarget) { export function purchaseHashUpgrade(upgName, upgTarget) {
if (!(Player.hashManager instanceof HashManager)) { if (!(Player.hashManager instanceof HashManager)) {
console.error(`Player does not have a HashManager`); console.error(`Player does not have a HashManager`);

View File

@@ -9,7 +9,6 @@ import { IHacknetNode } from "./IHacknetNode";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { Generic_fromJSON, import { Generic_fromJSON,
@@ -62,12 +61,14 @@ export class HacknetNode implements IHacknetNode {
// Total money earned by this Node // Total money earned by this Node
totalMoneyGenerated: number = 0; totalMoneyGenerated: number = 0;
constructor(name: string="") { constructor(name: string="", prodMult: number=1) {
this.name = name; this.name = name;
this.updateMoneyGainRate(prodMult);
} }
// Get the cost to upgrade this Node's number of cores // Get the cost to upgrade this Node's number of cores
calculateCoreUpgradeCost(levels: number=1, p: IPlayer): number { calculateCoreUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -86,13 +87,13 @@ export class HacknetNode implements IHacknetNode {
++currentCores; ++currentCores;
} }
totalCost *= p.hacknet_node_core_cost_mult; totalCost *= costMult;
return totalCost; return totalCost;
} }
// Get the cost to upgrade this Node's level // Get the cost to upgrade this Node's level
calculateLevelUpgradeCost(levels: number=1, p: IPlayer): number { calculateLevelUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -110,11 +111,11 @@ export class HacknetNode implements IHacknetNode {
++currLevel; ++currLevel;
} }
return BaseCostForHacknetNode / 2 * totalMultiplier * p.hacknet_node_level_cost_mult; return BaseCostForHacknetNode / 2 * totalMultiplier * costMult;
} }
// Get the cost to upgrade this Node's RAM // Get the cost to upgrade this Node's RAM
calculateRamUpgradeCost(levels: number=1, p: IPlayer): number { calculateRamUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -138,7 +139,7 @@ export class HacknetNode implements IHacknetNode {
++numUpgrades; ++numUpgrades;
} }
totalCost *= p.hacknet_node_ram_cost_mult; totalCost *= costMult;
return totalCost; return totalCost;
} }
@@ -161,112 +162,37 @@ export class HacknetNode implements IHacknetNode {
// Upgrade this Node's number of cores, if possible // Upgrade this Node's number of cores, if possible
// Returns a boolean indicating whether new cores were successfully bought // Returns a boolean indicating whether new cores were successfully bought
purchaseCoreUpgrade(levels: number=1, p: IPlayer): boolean { upgradeCore(levels: number=1, prodMult: number): void {
const sanitizedLevels = Math.round(levels); this.cores = Math.min(HacknetNodeMaxCores, Math.round(this.cores + levels));
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p); this.updateMoneyGainRate(prodMult);
if (isNaN(cost) || sanitizedLevels < 0) {
return false;
}
// Fail if we're already at max
if (this.cores >= HacknetNodeMaxCores) {
return false;
}
// If the specified number of upgrades would exceed the max Cores, calculate
// the max possible number of upgrades and use that
if (this.cores + sanitizedLevels > HacknetNodeMaxCores) {
const diff = Math.max(0, HacknetNodeMaxCores - this.cores);
return this.purchaseCoreUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
this.cores = Math.round(this.cores + sanitizedLevels); // Just in case of floating point imprecision
this.updateMoneyGainRate(p);
return true;
} }
// Upgrade this Node's level, if possible // Upgrade this Node's level, if possible
// Returns a boolean indicating whether the level was successfully updated // Returns a boolean indicating whether the level was successfully updated
purchaseLevelUpgrade(levels: number=1, p: IPlayer): boolean { upgradeLevel(levels: number=1, prodMult: number): void {
const sanitizedLevels = Math.round(levels); this.level = Math.min(HacknetNodeMaxLevel, Math.round(this.level + levels));
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p); this.updateMoneyGainRate(prodMult);
if (isNaN(cost) || sanitizedLevels < 0) {
return false;
}
// If we're at max level, return false
if (this.level >= HacknetNodeMaxLevel) {
return false;
}
// If the number of specified upgrades would exceed the max level, calculate
// the maximum number of upgrades and use that
if (this.level + sanitizedLevels > HacknetNodeMaxLevel) {
var diff = Math.max(0, HacknetNodeMaxLevel - this.level);
return this.purchaseLevelUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
this.level = Math.round(this.level + sanitizedLevels); // Just in case of floating point imprecision
this.updateMoneyGainRate(p);
return true;
} }
// Upgrade this Node's RAM, if possible // Upgrade this Node's RAM, if possible
// Returns a boolean indicating whether the RAM was successfully upgraded // Returns a boolean indicating whether the RAM was successfully upgraded
purchaseRamUpgrade(levels: number=1, p: IPlayer): boolean { upgradeRam(levels: number=1, prodMult: number): void {
const sanitizedLevels = Math.round(levels); for (let i = 0; i < levels; ++i) {
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
if (isNaN(cost) || sanitizedLevels < 0) {
return false;
}
// Fail if we're already at max
if (this.ram >= HacknetNodeMaxRam) {
return false;
}
// If the number of specified upgrades would exceed the max RAM, calculate the
// max possible number of upgrades and use that
if (this.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
var diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / this.ram)));
return this.purchaseRamUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
for (let i = 0; i < sanitizedLevels; ++i) {
this.ram *= 2; // Ram is always doubled this.ram *= 2; // Ram is always doubled
} }
this.ram = Math.round(this.ram); // Handle any floating point precision issues this.ram = Math.round(this.ram); // Handle any floating point precision issues
this.updateMoneyGainRate(p); this.updateMoneyGainRate(prodMult);
return true;
} }
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop // Re-calculate this Node's production and update the moneyGainRatePerSecond prop
updateMoneyGainRate(p: IPlayer): void { updateMoneyGainRate(prodMult: number): void {
//How much extra $/s is gained per level //How much extra $/s is gained per level
var gainPerLevel = HacknetNodeMoneyGainPerLevel; var gainPerLevel = HacknetNodeMoneyGainPerLevel;
this.moneyGainRatePerSecond = (this.level * gainPerLevel) * this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
Math.pow(1.035, this.ram - 1) * Math.pow(1.035, this.ram - 1) *
((this.cores + 5) / 6) * ((this.cores + 5) / 6) *
p.hacknet_node_money_mult * prodMult *
BitNodeMultipliers.HacknetNodeMoney; BitNodeMultipliers.HacknetNodeMoney;
if (isNaN(this.moneyGainRatePerSecond)) { if (isNaN(this.moneyGainRatePerSecond)) {
this.moneyGainRatePerSecond = 0; this.moneyGainRatePerSecond = 0;

View File

@@ -6,15 +6,16 @@ import { CONSTANTS } from "../Constants";
import { IHacknetNode } from "./IHacknetNode"; import { IHacknetNode } from "./IHacknetNode";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer";
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { createRandomIp } from "../../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
import { Generic_fromJSON, import {
Generic_toJSON, Generic_fromJSON,
Reviver } from "../../utils/JSONReviver"; Generic_toJSON,
Reviver
} from "../../utils/JSONReviver";
// Constants for Hacknet Server stats/production // Constants for Hacknet Server stats/production
export const HacknetServerHashesPerLevel: number = 0.001; export const HacknetServerHashesPerLevel: number = 0.001;
@@ -31,7 +32,7 @@ export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for co
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
export const MaxNumberHacknetServers: number = 25; // Max number of Hacknet Servers you can own export const MaxNumberHacknetServers: number = 20; // Max number of Hacknet Servers you can own
// Constants for max upgrade levels for Hacknet Server // Constants for max upgrade levels for Hacknet Server
export const HacknetServerMaxLevel: number = 300; export const HacknetServerMaxLevel: number = 300;
@@ -46,7 +47,6 @@ interface IConstructorParams {
isConnectedTo?: boolean; isConnectedTo?: boolean;
maxRam?: number; maxRam?: number;
organizationName?: string; organizationName?: string;
player?: IPlayer;
} }
export class HacknetServer extends BaseServer implements IHacknetNode { export class HacknetServer extends BaseServer implements IHacknetNode {
@@ -81,10 +81,6 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
this.maxRam = 1; this.maxRam = 1;
this.updateHashCapacity(); this.updateHashCapacity();
if (params.player) {
this.updateHashRate(params.player);
}
} }
calculateCacheUpgradeCost(levels: number): number { calculateCacheUpgradeCost(levels: number): number {
@@ -109,7 +105,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
return totalCost; return totalCost;
} }
calculateCoreUpgradeCost(levels: number, p: IPlayer): number { calculateCoreUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -127,12 +123,12 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
++currentCores; ++currentCores;
} }
totalCost *= BaseCostForHacknetServerCore; totalCost *= BaseCostForHacknetServerCore;
totalCost *= p.hacknet_node_core_cost_mult; totalCost *= costMult;
return totalCost; return totalCost;
} }
calculateLevelUpgradeCost(levels: number, p: IPlayer): number { calculateLevelUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -150,10 +146,10 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
++currLevel; ++currLevel;
} }
return 10 * BaseCostForHacknetServer * totalMultiplier * p.hacknet_node_level_cost_mult; return 10 * BaseCostForHacknetServer * totalMultiplier * costMult;
} }
calculateRamUpgradeCost(levels: number, p: IPlayer): number { calculateRamUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@@ -175,149 +171,48 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
currentRam *= 2; currentRam *= 2;
++numUpgrades; ++numUpgrades;
} }
totalCost *= p.hacknet_node_ram_cost_mult; totalCost *= costMult;
return totalCost; return totalCost;
} }
// Process this Hacknet Server in the game loop. // Process this Hacknet Server in the game loop. Returns the number of hashes generated
// Returns the number of hashes generated
process(numCycles: number=1): number { process(numCycles: number=1): number {
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000; const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
return this.hashRate * seconds; return this.hashRate * seconds;
} }
// Returns a boolean indicating whether the cache was successfully upgraded upgradeCache(levels: number): void {
purchaseCacheUpgrade(levels: number, p: IPlayer): boolean { this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels));
const sanitizedLevels = Math.round(levels);
const cost = this.calculateCacheUpgradeCost(levels);
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
return false;
}
if (this.cache >= HacknetServerMaxCache) {
return false;
}
// If the specified number of upgrades would exceed the max, try to purchase
// the maximum possible
if (this.cache + levels > HacknetServerMaxCache) {
const diff = Math.max(0, HacknetServerMaxCache - this.cache);
return this.purchaseCacheUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
this.cache = Math.round(this.cache + sanitizedLevels);
this.updateHashCapacity(); this.updateHashCapacity();
return true;
} }
// Returns a boolean indicating whether the number of cores was successfully upgraded upgradeCore(levels: number, prodMult: number): void {
purchaseCoreUpgrade(levels: number, p: IPlayer): boolean { this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels));
const sanitizedLevels = Math.round(levels); this.updateHashRate(prodMult);
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
return false;
}
if (this.cores >= HacknetServerMaxCores) {
return false;
}
// If the specified number of upgrades would exceed the max, try to purchase
// the maximum possible
if (this.cores + sanitizedLevels > HacknetServerMaxCores) {
const diff = Math.max(0, HacknetServerMaxCores - this.cores);
return this.purchaseCoreUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
this.cores = Math.round(this.cores + sanitizedLevels);
this.updateHashRate(p);
return true;
} }
// Returns a boolean indicating whether the level was successfully upgraded upgradeLevel(levels: number, prodMult: number): void {
purchaseLevelUpgrade(levels: number, p: IPlayer): boolean { this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels));
const sanitizedLevels = Math.round(levels); this.updateHashRate(prodMult);
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
return false;
}
if (this.level >= HacknetServerMaxLevel) {
return false;
}
// If the specified number of upgrades would exceed the max, try to purchase the
// maximum possible
if (this.level + sanitizedLevels > HacknetServerMaxLevel) {
const diff = Math.max(0, HacknetServerMaxLevel - this.level);
return this.purchaseLevelUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
this.level = Math.round(this.level + sanitizedLevels);
this.updateHashRate(p);
return true;
} }
// Returns a boolean indicating whether the RAM was successfully upgraded upgradeRam(levels: number, prodMult: number): boolean {
purchaseRamUpgrade(levels: number, p: IPlayer): boolean { for (let i = 0; i < levels; ++i) {
const sanitizedLevels = Math.round(levels);
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
if(isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
return false;
}
if (this.maxRam >= HacknetServerMaxRam) {
return false;
}
// If the specified number of upgrades would exceed the max, try to purchase
// just the maximum possible
if (this.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / this.maxRam)));
return this.purchaseRamUpgrade(diff, p);
}
if (!p.canAfford(cost)) {
return false;
}
p.loseMoney(cost);
for (let i = 0; i < sanitizedLevels; ++i) {
this.maxRam *= 2; this.maxRam *= 2;
} }
this.maxRam = Math.round(this.maxRam); this.maxRam = Math.min(HacknetServerMaxRam, Math.round(this.maxRam));
this.updateHashRate(p); this.updateHashRate(prodMult);
return true; return true;
} }
/** // Whenever a script is run, we must update this server's hash rate
* Whenever a script is run, we must update this server's hash rate runScript(script: RunningScript, prodMult?: number): void {
*/
runScript(script: RunningScript, p?: IPlayer): void {
super.runScript(script); super.runScript(script);
if (p) { if (prodMult != null && typeof prodMult === "number") {
this.updateHashRate(p); this.updateHashRate(prodMult);
} }
} }
@@ -325,7 +220,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
this.hashCapacity = 32 * Math.pow(2, this.cache); this.hashCapacity = 32 * Math.pow(2, this.cache);
} }
updateHashRate(p: IPlayer): void { updateHashRate(prodMult: number): void {
const baseGain = HacknetServerHashesPerLevel * this.level; const baseGain = HacknetServerHashesPerLevel * this.level;
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam)); const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
const coreMultiplier = 1 + (this.cores - 1) / 5; const coreMultiplier = 1 + (this.cores - 1) / 5;
@@ -333,7 +228,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio; const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney; this.hashRate = hashRate * prodMult * BitNodeMultipliers.HacknetNodeMoney;
if (isNaN(this.hashRate)) { if (isNaN(this.hashRate)) {
this.hashRate = 0; this.hashRate = 0;

View File

@@ -6,12 +6,9 @@
* his hashes, and contains method for grabbing the data/multipliers from * his hashes, and contains method for grabbing the data/multipliers from
* those upgrades * those upgrades
*/ */
import { HacknetServer } from "./HacknetServer";
import { HashUpgrades } from "./HashUpgrades"; import { HashUpgrades } from "./HashUpgrades";
import { IMap } from "../types"; import { IMap } from "../types";
import { IPlayer } from "../PersonObjects/IPlayer";
import { AllServers } from "../Server/AllServers";
import { Generic_fromJSON, import { Generic_fromJSON,
Generic_toJSON, Generic_toJSON,
Reviver } from "../../utils/JSONReviver"; Reviver } from "../../utils/JSONReviver";
@@ -84,14 +81,14 @@ export class HashManager {
return upg.getCost(currLevel); return upg.getCost(currLevel);
} }
prestige(p: IPlayer): void { prestige(): void {
for (const name in HashUpgrades) { for (const name in HashUpgrades) {
this.upgrades[name] = 0; this.upgrades[name] = 0;
} }
this.hashes = 0; this.hashes = 0;
if (p != null) {
this.updateCapacity(p); // When prestiging, player's hacknet nodes are always reset. So capacity = 0
} this.updateCapacity(0);
} }
/** /**
@@ -99,14 +96,16 @@ export class HashManager {
*/ */
refundUpgrade(upgName: string): void { refundUpgrade(upgName: string): void {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
// Reduce the level first, so we get the right cost
--this.upgrades[upgName];
const currLevel = this.upgrades[upgName]; const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null || currLevel === 0) { if (upg == null || currLevel == null || currLevel < 0) {
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`); console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
return; return;
} }
// Reduce the level first, so we get the right cost
--this.upgrades[upgName];
const cost = upg.getCost(currLevel); const cost = upg.getCost(currLevel);
this.hashes += cost; this.hashes += cost;
} }
@@ -116,33 +115,11 @@ export class HashManager {
this.hashes = Math.min(this.hashes, this.capacity); this.hashes = Math.min(this.hashes, this.capacity);
} }
updateCapacity(p: IPlayer): void { updateCapacity(newCap: number): void {
if (p.hacknetNodes.length <= 0) { if (newCap < 0) {
this.capacity = 0; this.capacity = 0;
return;
} }
this.capacity = Math.max(newCap, 0);
// Make sure the Player's `hacknetNodes` property actually holds Hacknet Servers
const ip: string = <string>p.hacknetNodes[0];
if (typeof ip !== "string") {
this.capacity = 0;
return;
}
const hserver = <HacknetServer>AllServers[ip];
if (!(hserver instanceof HacknetServer)) {
this.capacity = 0;
return;
}
let total: number = 0;
for (let i = 0; i < p.hacknetNodes.length; ++i) {
const h = <HacknetServer>AllServers[<string>p.hacknetNodes[i]];
total += h.hashCapacity;
}
this.capacity = total;
} }
/** /**

View File

@@ -1,16 +1,14 @@
// Interface for a Hacknet Node. Implemented by both a basic Hacknet Node, // Interface for a Hacknet Node. Implemented by both a basic Hacknet Node,
// and the upgraded Hacknet Server in BitNode-9 // and the upgraded Hacknet Server in BitNode-9
import { IPlayer } from "../PersonObjects/IPlayer";
export interface IHacknetNode { export interface IHacknetNode {
cores: number; cores: number;
level: number; level: number;
onlineTimeSeconds: number; onlineTimeSeconds: number;
calculateCoreUpgradeCost: (levels: number, p: IPlayer) => number; calculateCoreUpgradeCost: (levels: number, costMult: number) => number;
calculateLevelUpgradeCost: (levels: number, p: IPlayer) => number; calculateLevelUpgradeCost: (levels: number, costMult: number) => number;
calculateRamUpgradeCost: (levels: number, p: IPlayer) => number; calculateRamUpgradeCost: (levels: number, costMult: number) => number;
purchaseCoreUpgrade: (levels: number, p: IPlayer) => boolean; upgradeCore: (levels: number, prodMult: number) => void;
purchaseLevelUpgrade: (levels: number, p: IPlayer) => boolean; upgradeLevel: (levels: number, prodMult: number) => void;
purchaseRamUpgrade: (levels: number, p: IPlayer) => boolean; upgradeRam: (levels: number, prodMult: number) => void;
} }

View File

@@ -18,14 +18,17 @@ export const HashUpgradesMetadata: IConstructorParams[] = [
{ {
costPerLevel: 50, costPerLevel: 50,
desc: "Use hashes to decrease the minimum security of a single server by 2%. " + desc: "Use hashes to decrease the minimum security of a single server by 2%. " +
"Note that a server's minimum security cannot go below 1.", "Note that a server's minimum security cannot go below 1. This effect persists " +
"until you install Augmentations (since servers are reset at that time).",
hasTargetServer: true, hasTargetServer: true,
name: "Reduce Minimum Security", name: "Reduce Minimum Security",
value: 0.98, value: 0.98,
}, },
{ {
costPerLevel: 50, costPerLevel: 50,
desc: "Use hashes to increase the maximum amount of money on a single server by 2%", desc: "Use hashes to increase the maximum amount of money on a single server by 2%. " +
"This effect persists until you install Augmentations (since servers " +
"are reset at that time).",
hasTargetServer: true, hasTargetServer: true,
name: "Increase Maximum Money", name: "Increase Maximum Money",
value: 1.02, value: 1.02,

View File

@@ -4,12 +4,19 @@
*/ */
import React from "react"; import React from "react";
import { HacknetNodeMaxLevel, import {
HacknetNodeMaxRam, HacknetNodeMaxLevel,
HacknetNodeMaxCores } from "../HacknetNode"; HacknetNodeMaxRam,
import { getMaxNumberLevelUpgrades, HacknetNodeMaxCores
getMaxNumberRamUpgrades, } from "../HacknetNode";
getMaxNumberCoreUpgrades } from "../HacknetHelpers"; import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
} from "../HacknetHelpers";
import { Player } from "../../Player"; import { Player } from "../../Player";
@@ -35,7 +42,7 @@ export class HacknetNode extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
if (Player.money.lt(upgradeLevelCost)) { if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
@@ -48,7 +55,7 @@ export class HacknetNode extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel); numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
} }
node.purchaseLevelUpgrade(numUpgrades, Player); purchaseLevelUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
@@ -66,7 +73,7 @@ export class HacknetNode extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
if (Player.money.lt(upgradeRamCost)) { if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
@@ -79,7 +86,7 @@ export class HacknetNode extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam); numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
} }
node.purchaseRamUpgrade(numUpgrades, Player); purchaseRamUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
@@ -97,7 +104,7 @@ export class HacknetNode extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
if (Player.money.lt(upgradeCoreCost)) { if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
@@ -110,7 +117,7 @@ export class HacknetNode extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores); numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
} }
node.purchaseCoreUpgrade(numUpgrades, Player); purchaseCoreUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }

View File

@@ -4,14 +4,23 @@
*/ */
import React from "react"; import React from "react";
import { HacknetServerMaxLevel, import {
HacknetServerMaxRam, HacknetServerMaxLevel,
HacknetServerMaxCores, HacknetServerMaxRam,
HacknetServerMaxCache } from "../HacknetServer"; HacknetServerMaxCores,
import { getMaxNumberLevelUpgrades, HacknetServerMaxCache
getMaxNumberRamUpgrades, } from "../HacknetServer";
getMaxNumberCoreUpgrades, import {
getMaxNumberCacheUpgrades } from "../HacknetHelpers"; getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
getMaxNumberCacheUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
purchaseCacheUpgrade,
updateHashManagerCapacity,
} from "../HacknetHelpers";
import { Player } from "../../Player"; import { Player } from "../../Player";
@@ -37,7 +46,7 @@ export class HacknetServer extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
if (Player.money.lt(upgradeLevelCost)) { if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
@@ -50,7 +59,7 @@ export class HacknetServer extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel); numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
} }
node.purchaseLevelUpgrade(numUpgrades, Player); purchaseLevelUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
@@ -69,7 +78,7 @@ export class HacknetServer extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
if (Player.money.lt(upgradeRamCost)) { if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
@@ -82,7 +91,7 @@ export class HacknetServer extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam); numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
} }
node.purchaseRamUpgrade(numUpgrades, Player); purchaseRamUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
@@ -101,7 +110,7 @@ export class HacknetServer extends React.Component {
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
if (Player.money.lt(upgradeCoreCost)) { if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
@@ -114,7 +123,7 @@ export class HacknetServer extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores); numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
} }
node.purchaseCoreUpgrade(numUpgrades, Player); purchaseCoreUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
@@ -146,9 +155,9 @@ export class HacknetServer extends React.Component {
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache); numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
} }
node.purchaseCacheUpgrade(numUpgrades, Player); purchaseCacheUpgrade(node, numUpgrades);
recalculate(); recalculate();
Player.hashManager.updateCapacity(Player); updateHashManagerCapacity();
return false; return false;
} }

View File

@@ -39,6 +39,8 @@ export class HacknetRoot extends React.Component {
} }
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this); this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
this.handlePurchaseButtonClick = this.handlePurchaseButtonClick.bind(this);
this.recalculateTotalProduction = this.recalculateTotalProduction.bind(this);
} }
componentDidMount() { componentDidMount() {
@@ -50,6 +52,12 @@ export class HacknetRoot extends React.Component {
createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup }); createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup });
} }
handlePurchaseButtonClick() {
if (purchaseHacknet() >= 0) {
this.recalculateTotalProduction();
}
}
recalculateTotalProduction() { recalculateTotalProduction() {
let total = 0; let total = 0;
for (let i = 0; i < Player.hacknetNodes.length; ++i) { for (let i = 0; i < Player.hacknetNodes.length; ++i) {
@@ -85,13 +93,6 @@ export class HacknetRoot extends React.Component {
purchaseCost = getCostOfNextHacknetNode(); purchaseCost = getCostOfNextHacknetNode();
} }
// onClick event handler for purchase button
const purchaseOnClick = () => {
if (purchaseHacknet() >= 0) {
this.recalculateTotalProduction();
}
}
// onClick event handlers for purchase multiplier buttons // onClick event handlers for purchase multiplier buttons
const purchaseMultiplierOnClicks = [ const purchaseMultiplierOnClicks = [
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1), this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
@@ -112,7 +113,7 @@ export class HacknetRoot extends React.Component {
key={hserver.hostname} key={hserver.hostname}
node={hserver} node={hserver}
purchaseMultiplier={this.state.purchaseMultiplier} purchaseMultiplier={this.state.purchaseMultiplier}
recalculate={this.recalculateTotalProduction.bind(this)} recalculate={this.recalculateTotalProduction}
/> />
) )
} else { } else {
@@ -121,7 +122,7 @@ export class HacknetRoot extends React.Component {
key={node.name} key={node.name}
node={node} node={node}
purchaseMultiplier={this.state.purchaseMultiplier} purchaseMultiplier={this.state.purchaseMultiplier}
recalculate={this.recalculateTotalProduction.bind(this)} recalculate={this.recalculateTotalProduction}
/> />
) )
} }
@@ -132,7 +133,7 @@ export class HacknetRoot extends React.Component {
<h1>Hacknet Nodes</h1> <h1>Hacknet Nodes</h1>
<GeneralInfo /> <GeneralInfo />
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={purchaseOnClick} /> <PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={this.handlePurchaseButtonClick} />
<br /> <br />
<div id={"hacknet-nodes-money-multipliers-div"}> <div id={"hacknet-nodes-money-multipliers-div"}>

View File

@@ -129,7 +129,7 @@ function endInfiltration(inst, success) {
clearEventListeners("infiltration-bribe"); clearEventListeners("infiltration-bribe");
clearEventListeners("infiltration-escape"); clearEventListeners("infiltration-escape");
Engine.loadLocationContent(); Engine.loadLocationContent(false);
} }
function nextInfiltrationLevel(inst) { function nextInfiltrationLevel(inst) {

View File

@@ -321,7 +321,7 @@ function iTutorialEvaluateStep() {
Engine.loadActiveScriptsContent(); Engine.loadActiveScriptsContent();
iTutorialSetText("This page displays stats/information about all of your scripts that are " + iTutorialSetText("This page displays stats/information about all of your scripts that are " +
"running across every existing server. You can use this to gauge how well " + "running across every existing server. You can use this to gauge how well " +
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal'" + "your scripts are doing. Let's go back to the Terminal now using the 'Terminal' " +
"link."); "link.");
nextBtn.style.display = "none"; nextBtn.style.display = "none";

View File

@@ -1,4 +1,4 @@
import * as acorn from "../utils/acorn"; import * as acorn from "acorn";
/** /**
* @license * @license
* JavaScript Interpreter * JavaScript Interpreter

View File

@@ -2,46 +2,53 @@
* Location and traveling-related helper functions. * Location and traveling-related helper functions.
* Mostly used for UI * Mostly used for UI
*/ */
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { CityName } from "./data/CityNames"; import { CityName } from "./data/CityNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { AllServers, import {
AddToAllServers } from "../Server/AllServers"; AddToAllServers,
import { Server } from "../Server/Server"; createUniqueRandomIp,
import { getPurchaseServerCost, } from "../Server/AllServers";
purchaseRamForHomeComputer, import { safetlyCreateUniqueServer } from "../Server/ServerHelpers";
purchaseServer } from "../Server/ServerPurchases"; import {
import { SpecialServerIps } from "../Server/SpecialServerIps"; getPurchaseServerCost,
import { Settings } from "../Settings/Settings"; purchaseRamForHomeComputer,
purchaseServer
} from "../Server/ServerPurchases";
import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { createRandomIp } from "../../utils/IPAddress"; import {
import { yesNoBoxGetYesButton, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxGetNoButton,
yesNoBoxClose, yesNoBoxClose,
yesNoBoxCreate, yesNoBoxCreate,
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetYesButton,
yesNoTxtInpBoxGetNoButton, yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxClose, yesNoTxtInpBoxClose,
yesNoTxtInpBoxCreate } from "../../utils/YesNoBox"; yesNoTxtInpBoxCreate
} from "../../utils/YesNoBox";
import { createElement } from "../../utils/uiHelpers/createElement"; import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup"; import { createPopup } from "../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton"; import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../utils/uiHelpers/removeElementById";
/** /**
* Create a pop-up box that lets the player confirm traveling to a different city * Create a pop-up box that lets the player confirm traveling to a different city.
* If settings are configured to suppress this popup, just instantly travel * If settings are configured to suppress this popup, just instantly travel.
* The actual "Travel" implementation is implemented in the UI, and is passed in * The actual "Travel" implementation is implemented in the UI, and is passed in
* as an argument * as an argument.
* @param {CityName} destination - City that the player is traveling to
* @param {Function} travelFn - Function that changes the player's state for traveling
*/ */
type TravelFunction = (to: CityName) => void; type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction) { export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost = CONSTANTS.TravelCost; const cost = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
@@ -75,10 +82,10 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
/** /**
* Create a pop-up box that lets the player purchase a server. * Create a pop-up box that lets the player purchase a server.
* @param ram - Amount of RAM (GB) on server * @param {number} ram - Amount of RAM (GB) on server
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createPurchaseServerPopup(ram: number, p: IPlayer) { export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
const cost = getPurchaseServerCost(ram); const cost = getPurchaseServerCost(ram);
if (cost === Infinity) { if (cost === Infinity) {
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer"); dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
@@ -106,6 +113,7 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) {
/** /**
* Create a popup that lets the player start a Corporation * Create a popup that lets the player start a Corporation
* @param {IPlayer} p - Player object
*/ */
export function createStartCorporationPopup(p: IPlayer) { export function createStartCorporationPopup(p: IPlayer) {
if (!p.canAccessCorporation() || p.hasCorporation()) { return; } if (!p.canAccessCorporation() || p.hasCorporation()) { return; }
@@ -167,8 +175,10 @@ export function createStartCorporationPopup(p: IPlayer) {
if (worldHeader instanceof HTMLElement) { if (worldHeader instanceof HTMLElement) {
worldHeader.click(); worldHeader.click(); worldHeader.click(); worldHeader.click();
} }
dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " + dialogBoxCreate(
"You can visit and manage your company in the City"); "Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City"
);
removeElementById(popupId); removeElementById(popupId);
return false; return false;
} }
@@ -182,21 +192,23 @@ export function createStartCorporationPopup(p: IPlayer) {
/** /**
* Create a popup that lets the player upgrade the cores on his/her home computer * Create a popup that lets the player upgrade the cores on his/her home computer
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createUpgradeHomeCoresPopup(p: IPlayer) { export function createUpgradeHomeCoresPopup(p: IPlayer) {
const currentCores = p.getHomeComputer().cpuCores; const currentCores = p.getHomeComputer().cpuCores;
if (currentCores >= 8) { return; } // Max of 8 cores if (currentCores >= 8) { return; } // Max of 8 cores
//Cost of purchasing another cost is found by indexing this array with number of current cores // Cost of purchasing another cost is found by indexing this array with number of current cores
const allCosts = [0, const allCosts = [
10e9, // 1->2 Cores - 10 bn 0,
250e9, // 2->3 Cores - 250 bn 10e9,
5e12, // 3->4 Cores - 5 trillion 250e9,
100e12, // 4->5 Cores - 100 trillion 5e12,
1e15, // 5->6 Cores - 1 quadrillion 100e12,
20e15, // 6->7 Cores - 20 quadrillion 1e15,
200e15]; // 7->8 Cores - 200 quadrillion 20e15,
200e15
];
const cost: number = allCosts[currentCores]; const cost: number = allCosts[currentCores];
const yesBtn = yesNoBoxGetYesButton(); const yesBtn = yesNoBoxGetYesButton();
@@ -210,8 +222,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
} else { } else {
p.loseMoney(cost); p.loseMoney(cost);
p.getHomeComputer().cpuCores++; p.getHomeComputer().cpuCores++;
dialogBoxCreate("You purchased an additional CPU Core for your home computer! It now has " + dialogBoxCreate(
p.getHomeComputer().cpuCores + " cores."); "You purchased an additional CPU Core for your home computer! It now has " +
p.getHomeComputer().cpuCores + " cores."
);
} }
yesNoBoxClose(); yesNoBoxClose();
}); });
@@ -221,15 +235,17 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate("Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + yesNoBoxCreate(
"lets you start with an additional Core Node in Hacking Missions.<br><br>" + "Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " +
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + "lets you start with an additional Core Node in Hacking Missions.<br><br>" +
"cost " + numeralWrapper.formatMoney(cost)); "Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " +
"cost " + numeralWrapper.formatMoney(cost)
);
} }
/** /**
* Create a popup that lets the player upgrade the RAM on his/her home computer * Create a popup that lets the player upgrade the RAM on his/her home computer
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createUpgradeHomeRamPopup(p: IPlayer) { export function createUpgradeHomeRamPopup(p: IPlayer) {
const cost: number = p.getUpgradeHomeRamCost(); const cost: number = p.getUpgradeHomeRamCost();
@@ -250,15 +266,17 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate("Would you like to purchase additional RAM for your home computer? <br><br>" + yesNoBoxCreate(
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" + "Would you like to purchase additional RAM for your home computer? <br><br>" +
"This will cost " + numeralWrapper.format(cost, '$0.000a')); "This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" +
"This will cost " + numeralWrapper.format(cost, '$0.000a')
);
} }
/** /**
* Attempt to purchase a TOR router * Attempt to purchase a TOR router
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function purchaseTorRouter(p: IPlayer) { export function purchaseTorRouter(p: IPlayer) {
if (p.hasTorRouter()) { if (p.hasTorRouter()) {
@@ -271,8 +289,8 @@ export function purchaseTorRouter(p: IPlayer) {
} }
p.loseMoney(CONSTANTS.TorRouterCost); p.loseMoney(CONSTANTS.TorRouterCost);
const darkweb = new Server({ const darkweb = safetlyCreateUniqueServer({
ip: createRandomIp(), hostname:"darkweb", organizationName:"", ip: createUniqueRandomIp(), hostname:"darkweb", organizationName:"",
isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1 isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1
}); });
AddToAllServers(darkweb); AddToAllServers(darkweb);
@@ -280,7 +298,9 @@ export function purchaseTorRouter(p: IPlayer) {
p.getHomeComputer().serversOnNetwork.push(darkweb.ip); p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
darkweb.serversOnNetwork.push(p.getHomeComputer().ip); darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
dialogBoxCreate("You have purchased a Tor router!<br>" + dialogBoxCreate(
"You now have access to the dark web from your home computer<br>" + "You have purchased a Tor router!<br>" +
"Use the scan/scan-analyze commands to search for the dark web connection."); "You now have access to the dark web from your home computer<br>" +
"Use the scan/scan-analyze commands to search for the dark web connection."
);
} }

View File

@@ -76,37 +76,46 @@ export class UniversityLocation extends React.Component<IProps, any> {
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult; const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult; const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
const earnHackingExpTooltip = `Gain hacking experience!`
const earnCharismaExpTooltip = `Gain charisma experience!`;
return ( return (
<div> <div>
<StdButton <StdButton
onClick={this.study} onClick={this.study}
style={this.btnStyle} style={this.btnStyle}
text={`Study Computer Science (free)`} text={`Study Computer Science (free)`}
tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.dataStructures} onClick={this.dataStructures}
style={this.btnStyle} style={this.btnStyle}
text={`Take Data Structures course (${numeralWrapper.formatMoney(dataStructuresCost)} / sec)`} text={`Take Data Structures course (${numeralWrapper.formatMoney(dataStructuresCost)} / sec)`}
tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.networks} onClick={this.networks}
style={this.btnStyle} style={this.btnStyle}
text={`Take Networks course (${numeralWrapper.formatMoney(networksCost)} / sec)`} text={`Take Networks course (${numeralWrapper.formatMoney(networksCost)} / sec)`}
tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.algorithms} onClick={this.algorithms}
style={this.btnStyle} style={this.btnStyle}
text={`Take Algorithms course (${numeralWrapper.formatMoney(algorithmsCost)} / sec)`} text={`Take Algorithms course (${numeralWrapper.formatMoney(algorithmsCost)} / sec)`}
tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.management} onClick={this.management}
style={this.btnStyle} style={this.btnStyle}
text={`Take Management course (${numeralWrapper.formatMoney(managementCost)} / sec)`} text={`Take Management course (${numeralWrapper.formatMoney(managementCost)} / sec)`}
tooltip={earnCharismaExpTooltip}
/> />
<StdButton <StdButton
onClick={this.leadership} onClick={this.leadership}
style={this.btnStyle} style={this.btnStyle}
text={`Take Leadership course (${numeralWrapper.formatMoney(leadershipCost)} / sec)`} text={`Take Leadership course (${numeralWrapper.formatMoney(leadershipCost)} / sec)`}
tooltip={earnCharismaExpTooltip}
/> />
</div> </div>
) )

View File

@@ -1,15 +1,15 @@
import { Message } from "./Message"; import { Message } from "./Message";
import { Augmentatation } from "../Augmentation/Augmentation"; import { Augmentatation } from "../Augmentation/Augmentation";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { inMission } from "../Missions"; import { inMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { redPillFlag } from "../RedPill"; import { redPillFlag } from "../RedPill";
import { GetServerByHostname } from "../Server/ServerHelpers"; import { GetServerByHostname } from "../Server/ServerHelpers";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { dialogBoxCreate, import { dialogBoxCreate, dialogBoxOpened} from "../../utils/DialogBox";
dialogBoxOpened} from "../../utils/DialogBox"; import { Reviver } from "../../utils/JSONReviver";
//Sends message to player, including a pop up //Sends message to player, including a pop up
function sendMessage(msg, forced=false) { function sendMessage(msg, forced=false) {
@@ -31,7 +31,7 @@ function showMessage(msg) {
function addMessageToServer(msg, serverHostname) { function addMessageToServer(msg, serverHostname) {
var server = GetServerByHostname(serverHostname); var server = GetServerByHostname(serverHostname);
if (server == null) { if (server == null) {
console.log("WARNING: Did not locate " + serverHostname); console.warn(`Could not find server ${serverHostname}`);
return; return;
} }
for (var i = 0; i < server.messages.length; ++i) { for (var i = 0; i < server.messages.length; ++i) {

View File

@@ -0,0 +1,72 @@
/**
* The environment in which a script runs. The environment holds
* Netscript functions and arguments for that script.
*/
import { IMap } from "../types";
export class Environment {
/**
* Parent environment. Used to implement "scope"
*/
parent: Environment | null = null;
/**
* Whether or not the script that uses this Environment should stop running
*/
stopFlag: boolean = false;
/**
* Environment variables (currently only Netscript functions)
*/
vars: IMap<any> = {};
constructor(parent: Environment | null) {
if (parent instanceof Environment) {
this.vars = Object.assign({}, parent.vars);
}
this.parent = parent;
}
/**
* Finds the scope where the variable with the given name is defined
*/
lookup(name: string): Environment | null {
let scope: Environment | null = this;
while (scope) {
if (Object.prototype.hasOwnProperty.call(scope.vars, name)) {
return scope;
}
scope = scope.parent;
}
return null;
}
//Get the current value of a variable
get(name: string): any {
if (name in this.vars) {
return this.vars[name];
}
throw new Error(`Undefined variable ${name}`);
}
//Sets the value of a variable in any scope
set(name: string, value: any) {
var scope = this.lookup(name);
//If scope has a value, then this variable is already set in a higher scope, so
//set is there. Otherwise, create a new variable in the local scope
if (scope !== null) {
return scope.vars[name] = value;
} else {
return this.vars[name] = value;
}
}
//Creates (or overwrites) a variable in the current scope
def(name: string, value: any) {
return this.vars[name] = value;
}
}

View File

@@ -0,0 +1,332 @@
import { IMap } from "../types";
// TODO remember to update RamCalculations.js and WorkerScript.js
// RAM costs for Netscript functions
export const RamCostConstants: IMap<number> = {
ScriptBaseRamCost: 1.6,
ScriptDomRamCost: 25,
ScriptHackRamCost: 0.1,
ScriptHackAnalyzeRamCost: 1,
ScriptGrowRamCost: 0.15,
ScriptGrowthAnalyzeRamCost: 1,
ScriptWeakenRamCost: 0.15,
ScriptScanRamCost: 0.2,
ScriptPortProgramRamCost: 0.05,
ScriptRunRamCost: 1.0,
ScriptExecRamCost: 1.3,
ScriptSpawnRamCost: 2.0,
ScriptScpRamCost: 0.6,
ScriptKillRamCost: 0.5,
ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05,
ScriptGetHackingLevelRamCost: 0.05,
ScriptGetMultipliersRamCost: 4.0,
ScriptGetServerRamCost: 0.1,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptHacknetNodesRamCost: 4.0,
ScriptHNUpgLevelRamCost: 0.4,
ScriptHNUpgRamRamCost: 0.6,
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
ScriptGetPurchaseServerRamCost: 0.25,
ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05,
ScriptGetPurchasedServerMaxRam: 0.05,
ScriptRoundRamCost: 0.05,
ScriptReadWriteRamCost: 1.0,
ScriptArbScriptRamCost: 1.0,
ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4,
ScriptSingularityFn1RamCost: 2,
ScriptSingularityFn2RamCost: 3,
ScriptSingularityFn3RamCost: 5,
ScriptGangApiBaseRamCost: 4,
ScriptBladeburnerApiBaseRamCost: 4,
}
export const RamCosts: IMap<any> = {
hacknet: {
numNodes: () => 0,
purchaseNode: () => 0,
getPurchaseNodeCost: () => 0,
getNodeStats: () => 0,
upgradeLevel: () => 0,
upgradeRam: () => 0,
upgradeCore: () => 0,
upgradeCache: () => 0,
getLevelUpgradeCost: () => 0,
getRamUpgradeCost: () => 0,
getCoreUpgradeCost: () => 0,
getCacheUpgradeCost: () => 0,
numHashes: () => 0,
hashCost: () => 0,
spendHashes: () => 0,
},
sprintf: () => 0,
vsprintf: () => 0,
scan: () => RamCostConstants.ScriptScanRamCost,
hack: () => RamCostConstants.ScriptHackRamCost,
hackAnalyzeThreads: () => RamCostConstants.ScriptHackAnalyzeRamCost,
hackAnalyzePercent: () => RamCostConstants.ScriptHackAnalyzeRamCost,
hackChance: () => RamCostConstants.ScriptHackAnalyzeRamCost,
sleep: () => 0,
grow: () => RamCostConstants.ScriptGrowRamCost,
growthAnalyze: () => RamCostConstants.ScriptGrowthAnalyzeRamCost,
weaken: () => RamCostConstants.ScriptWeakenRamCost,
print: () => 0,
tprint: () => 0,
clearLog: () => 0,
disableLog: () => 0,
enableLog: () => 0,
isLogEnabled: () => 0,
getScriptLogs: () => 0,
nuke: () => RamCostConstants.ScriptPortProgramRamCost,
brutessh: () => RamCostConstants.ScriptPortProgramRamCost,
ftpcrack: () => RamCostConstants.ScriptPortProgramRamCost,
relaysmtp: () => RamCostConstants.ScriptPortProgramRamCost,
httpworm: () => RamCostConstants.ScriptPortProgramRamCost,
sqlinject: () => RamCostConstants.ScriptPortProgramRamCost,
run: () => RamCostConstants.ScriptRunRamCost,
exec: () => RamCostConstants.ScriptExecRamCost,
spawn: () => RamCostConstants.ScriptSpawnRamCost,
kill: () => RamCostConstants.ScriptKillRamCost,
killall: () => RamCostConstants.ScriptKillRamCost,
exit: () => 0,
scp: () => RamCostConstants.ScriptScpRamCost,
ls: () => RamCostConstants.ScriptScanRamCost,
ps: () => RamCostConstants.ScriptScanRamCost,
hasRootAccess: () => RamCostConstants.ScriptHasRootAccessRamCost,
getIp: () => RamCostConstants.ScriptGetHostnameRamCost,
getHostname: () => RamCostConstants.ScriptGetHostnameRamCost,
getHackingLevel: () => RamCostConstants.ScriptGetHackingLevelRamCost,
getHackingMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getHacknetMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getBitNodeMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getServerMoneyAvailable: () => RamCostConstants.ScriptGetServerRamCost,
getServerSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
getServerBaseSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
getServerMinSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
getServerRequiredHackingLevel: () => RamCostConstants.ScriptGetServerRamCost,
getServerMaxMoney: () => RamCostConstants.ScriptGetServerRamCost,
getServerGrowth: () => RamCostConstants.ScriptGetServerRamCost,
getServerNumPortsRequired: () => RamCostConstants.ScriptGetServerRamCost,
getServerRam: () => RamCostConstants.ScriptGetServerRamCost,
serverExists: () => RamCostConstants.ScriptGetServerRamCost,
fileExists: () => RamCostConstants.ScriptFileExistsRamCost,
isRunning: () => RamCostConstants.ScriptIsRunningRamCost,
getStockSymbols: () => RamCostConstants.ScriptGetStockRamCost,
getStockPrice: () => RamCostConstants.ScriptGetStockRamCost,
getStockAskPrice: () => RamCostConstants.ScriptGetStockRamCost,
getStockBidPrice: () => RamCostConstants.ScriptGetStockRamCost,
getStockPosition: () => RamCostConstants.ScriptGetStockRamCost,
getStockMaxShares: () => RamCostConstants.ScriptGetStockRamCost,
getStockPurchaseCost: () => RamCostConstants.ScriptGetStockRamCost,
getStockSaleGain: () => RamCostConstants.ScriptGetStockRamCost,
buyStock: () => RamCostConstants.ScriptBuySellStockRamCost,
sellStock: () => RamCostConstants.ScriptBuySellStockRamCost,
shortStock: () => RamCostConstants.ScriptBuySellStockRamCost,
sellShort: () => RamCostConstants.ScriptBuySellStockRamCost,
placeOrder: () => RamCostConstants.ScriptBuySellStockRamCost,
cancelOrder: () => RamCostConstants.ScriptBuySellStockRamCost,
getOrders: () => RamCostConstants.ScriptBuySellStockRamCost,
getStockVolatility: () => RamCostConstants.ScriptBuySellStockRamCost,
getStockForecast: () => RamCostConstants.ScriptBuySellStockRamCost,
purchase4SMarketData: () => RamCostConstants.ScriptBuySellStockRamCost,
purchase4SMarketDataTixApi: () => RamCostConstants.ScriptBuySellStockRamCost,
getPurchasedServerLimit: () => RamCostConstants.ScriptGetPurchasedServerLimit,
getPurchasedServerMaxRam: () => RamCostConstants.ScriptGetPurchasedServerMaxRam,
getPurchasedServerCost: () => RamCostConstants.ScriptGetPurchaseServerRamCost,
purchaseServer: () => RamCostConstants.ScriptPurchaseServerRamCost,
deleteServer: () => RamCostConstants.ScriptPurchaseServerRamCost,
getPurchasedServers: () => RamCostConstants.ScriptPurchaseServerRamCost,
write: () => RamCostConstants.ScriptReadWriteRamCost,
tryWrite: () => RamCostConstants.ScriptReadWriteRamCost,
read: () => RamCostConstants.ScriptReadWriteRamCost,
peek: () => RamCostConstants.ScriptReadWriteRamCost,
clear: () => RamCostConstants.ScriptReadWriteRamCost,
getPortHandle: () => RamCostConstants.ScriptReadWriteRamCost * 10,
rm: () => RamCostConstants.ScriptReadWriteRamCost,
scriptRunning: () => RamCostConstants.ScriptArbScriptRamCost,
scriptKill: () => RamCostConstants.ScriptArbScriptRamCost,
getScriptName: () => 0,
getScriptRam: () => RamCostConstants.ScriptGetScriptRamCost,
getHackTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
getGrowTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
getWeakenTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
getScriptIncome: () => RamCostConstants.ScriptGetScriptRamCost,
getScriptExpGain: () => RamCostConstants.ScriptGetScriptRamCost,
nFormat: () => 0,
getTimeSinceLastAug: () => RamCostConstants.ScriptGetHackTimeRamCost,
prompt: () => 0,
wget: () => 0,
getFavorToDonate: () => RamCostConstants.ScriptGetFavorToDonate,
// Singularity Functions
universityCourse: () => RamCostConstants.ScriptSingularityFn1RamCost,
gymWorkout: () => RamCostConstants.ScriptSingularityFn1RamCost,
travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost,
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2,
upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost,
getUpgradeHomeRamCost: () => RamCostConstants.ScriptSingularityFn2RamCost / 2,
workForCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,
applyToCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,
getCompanyRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
getCompanyFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
getCompanyFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,
checkFactionInvitations: () => RamCostConstants.ScriptSingularityFn2RamCost,
joinFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,
workForFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,
getFactionRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
getFactionFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,
getFactionFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,
donateToFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,
createProgram: () => RamCostConstants.ScriptSingularityFn3RamCost,
commitCrime: () => RamCostConstants.ScriptSingularityFn3RamCost,
getCrimeChance: () => RamCostConstants.ScriptSingularityFn3RamCost,
getOwnedAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,
getOwnedSourceFiles: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationsFromFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationPrereq: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationCost: () => RamCostConstants.ScriptSingularityFn3RamCost,
purchaseAugmentation: () => RamCostConstants.ScriptSingularityFn3RamCost,
installAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,
// Gang API
gang : {
getMemberNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
getGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getOtherGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getMemberInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
canRecruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
recruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getTaskNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
setMemberTask: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getEquipmentNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
getEquipmentCost: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getEquipmentType: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
purchaseEquipment: () => RamCostConstants.ScriptGangApiBaseRamCost,
ascendMember: () => RamCostConstants.ScriptGangApiBaseRamCost,
setTerritoryWarfare: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
getChanceToWinClash: () => RamCostConstants.ScriptGangApiBaseRamCost,
getBonusTime: () => 0,
},
// Bladeburner API
bladeburner : {
getContractNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
getOperationNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
getBlackOpNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
getBlackOpRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
getGeneralActionNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
getSkillNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
startAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
stopBladeburnerAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
getCurrentAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4,
getActionTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionEstimatedSuccessChance: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionRepGain: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionCountRemaining: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionMaxLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionCurrentLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
setActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
setActionLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getSkillPoints: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getSkillLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getSkillUpgradeCost: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
upgradeSkill: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
setTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getCityEstimatedPopulation: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getCityEstimatedCommunities: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getCityChaos: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
switchCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getStamina: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
joinBladeburnerFaction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
joinBladeburnerDivision: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,
getBonusTime: () => 0,
},
// Coding Contract API
codingcontract : {
attempt: () => RamCostConstants.ScriptCodingContractBaseRamCost,
getContractType: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
getData: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
getDescription: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,
getNumTriesRemaining: () => RamCostConstants.ScriptCodingContractBaseRamCost / 5,
},
// Duplicate Sleeve API
sleeve : {
getNumSleeves: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToShockRecovery: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToSynchronize: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToCommitCrime: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToUniversityCourse: () => RamCostConstants.ScriptSleeveBaseRamCost,
travel: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToCompanyWork: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToFactionWork: () => RamCostConstants.ScriptSleeveBaseRamCost,
setToGymWorkout: () => RamCostConstants.ScriptSleeveBaseRamCost,
getSleeveStats: () => RamCostConstants.ScriptSleeveBaseRamCost,
getTask: () => RamCostConstants.ScriptSleeveBaseRamCost,
getInformation: () => RamCostConstants.ScriptSleeveBaseRamCost,
getSleeveAugmentations: () => RamCostConstants.ScriptSleeveBaseRamCost,
getSleevePurchasableAugs: () => RamCostConstants.ScriptSleeveBaseRamCost,
purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,
},
heart: {
// Easter egg function
break : () => 0,
}
}
export function getRamCost(...args: string[]): number {
if (args.length === 0) {
console.warn(`No arguments passed to getRamCost()`);
return 0;
}
let curr = RamCosts[args[0]];
for (let i = 1; i < args.length; ++i) {
if (curr == null) {
console.warn(`Invalid function passed to getRamCost: ${args}`);
return 0;
}
const currType = typeof curr;
if (currType === "function" || currType === "number") {
break;
}
curr = curr[args[i]];
}
const currType = typeof curr;
if (currType === "function") {
return curr();
}
if (currType === "number") {
return curr;
}
console.warn(`Unexpected type (${currType}) for value [${args}]`);
return 0;
}

View File

@@ -0,0 +1,194 @@
/**
* The worker agent for running a script instance. Each running script instance
* has its own underlying WorkerScript object.
*
* Note that these objects are not saved and re-loaded when the game is refreshed.
* Instead, whenever the game is opened, WorkerScripts are re-created from
* RunningScript objects
*/
import { Environment } from "./Environment";
import { RamCostConstants } from "./RamCostGenerator";
import { RunningScript } from "../Script/RunningScript";
import { Script } from "../Script/Script";
import { AllServers } from "../Server/AllServers";
import { BaseServer } from "../Server/BaseServer";
import { IMap } from "../types";
export class WorkerScript {
/**
* Script's arguments
*/
args: any[];
/**
* Copy of the script's code
*/
code: string = "";
/**
* Holds the timeoutID (numeric value) for whenever this script is blocked by a
* timed Netscript function. i.e. Holds the return value of setTimeout()
*/
delay: number | null = null;
/**
* Holds the Promise resolve() function for when the script is "blocked" by an async op
*/
delayResolve?: () => void;
/**
* Stores names of all functions that have logging disabled
*/
disableLogs: IMap<string> = {};
/**
* Used for dynamic RAM calculation. Stores names of all functions that have
* already been checked by this script.
* TODO: Could probably just combine this with loadedFns?
*/
dynamicLoadedFns: IMap<string> = {};
/**
* Tracks dynamic RAM usage
*/
dynamicRamUsage: number = RamCostConstants.ScriptBaseRamCost;
/**
* Netscript Environment for this script
*/
env: Environment;
/**
* Status message in case of script error. Currently unused I think
*/
errorMessage: string = "";
/**
* Used for static RAM calculation. Stores names of all functions that have
* already been checked by this script
*/
loadedFns: IMap<string> = {};
/**
* Filename of script
*/
name: string;
/**
* Script's output/return value. Currently not used or implemented
*/
output: string = "";
/**
* Process ID. Must be an integer. Used for efficient script
* killing and removal.
*/
pid: number;
/**
* Script's Static RAM usage. Equivalent to underlying script's RAM usage
*/
ramUsage: number = 0;
/**
* Whether or not this workerScript is currently running
*/
running: boolean = false;
/**
* Reference to underlying RunningScript object
*/
scriptRef: RunningScript;
/**
* IP Address on which this script is running
*/
serverIp: string;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => object) {
this.name = runningScriptObj.filename;
this.serverIp = runningScriptObj.server;
const sanitizedPid = Math.round(pid);
if (typeof sanitizedPid !== "number" || isNaN(sanitizedPid)) {
throw new Error(`Invalid PID when constructing WorkerScript: ${pid}`);
}
this.pid = sanitizedPid;
runningScriptObj.pid = sanitizedPid;
// Get the underlying script's code
const server = AllServers[this.serverIp];
if (server == null) {
throw new Error(`WorkerScript constructed with invalid server ip: ${this.serverIp}`);
}
let found = false;
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === this.name) {
found = true;
this.code = server.scripts[i].code;
}
}
if (!found) {
throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`);
}
this.env = new Environment(null);
if (typeof nsFuncsGenerator === "function") {
this.env.vars = nsFuncsGenerator(this);
}
this.env.set("args", runningScriptObj.args.slice());
this.scriptRef = runningScriptObj;
this.args = runningScriptObj.args.slice();
}
/**
* Returns the Server on which this script is running
*/
getServer() {
return AllServers[this.serverIp];
}
/**
* Returns the Script object for the underlying script.
* Returns null if it cannot be found (which would be a bug)
*/
getScript(): Script | null {
let server = this.getServer();
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === this.name) {
return server.scripts[i];
}
}
console.error("Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong");
return null;
}
/**
* Returns the script with the specified filename on the specified server,
* or null if it cannot be found
*/
getScriptOnServer(fn: string, server: BaseServer): Script | null {
if (server == null) {
server = this.getServer();
}
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === fn) {
return server.scripts[i];
}
}
return null;
}
shouldLog(fn: string): boolean {
return (this.disableLogs.ALL == null && this.disableLogs[fn] == null);
}
log(txt: string): void {
this.scriptRef.log(txt);
}
}

View File

@@ -0,0 +1,6 @@
/**
* Event emitter that triggers when scripts are started/stopped
*/
import { EventEmitter } from "../utils/EventEmitter";
export const WorkerScriptStartStopEventEmitter = new EventEmitter();

View File

@@ -0,0 +1,6 @@
/**
* Global pool of all active scripts (scripts that are currently running)
*/
import { WorkerScript } from "./WorkerScript";
export const workerScripts: Map<number, WorkerScript> = new Map();

View File

@@ -0,0 +1,136 @@
/**
* Stops an actively-running script (represented by a WorkerScript object)
* and removes it from the global pool of active scripts.
*/
import { WorkerScript } from "./WorkerScript";
import { workerScripts } from "./WorkerScripts";
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
import { RunningScript } from "../Script/RunningScript";
import { AllServers } from "../Server/AllServers";
import { compareArrays } from "../../utils/helpers/compareArrays";
import { roundToTwo } from "../../utils/helpers/roundToTwo";
export function killWorkerScript(runningScriptObj: RunningScript, serverIp: string, rerenderUi: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean;
export function killWorkerScript(pid: number): boolean;
export function killWorkerScript(script: RunningScript | WorkerScript | number, serverIp?: string, rerenderUi?: boolean): boolean {
if (rerenderUi == null || typeof rerenderUi !== "boolean") {
rerenderUi = true;
}
if (script instanceof WorkerScript) {
stopAndCleanUpWorkerScript(script);
return true;
} else if (script instanceof RunningScript && typeof serverIp === "string") {
// Try to kill by PID
const res = killWorkerScriptByPid(script.pid, rerenderUi);
if (res) { return res; }
// If for some reason that doesn't work, we'll try the old way
for (const ws of workerScripts.values()) {
if (ws.name == script.filename && ws.serverIp == serverIp &&
compareArrays(ws.args, script.args)) {
stopAndCleanUpWorkerScript(ws, rerenderUi);
return true;
}
}
return false;
} else if (typeof script === "number") {
return killWorkerScriptByPid(script, rerenderUi);
} else {
console.error(`killWorkerScript() called with invalid argument:`);
console.error(script);
return false;
}
}
function killWorkerScriptByPid(pid: number, rerenderUi: boolean=true): boolean {
const ws = workerScripts.get(pid);
if (ws instanceof WorkerScript) {
stopAndCleanUpWorkerScript(ws, rerenderUi);
return true;
}
return false;
}
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi: boolean=true): void {
workerScript.env.stopFlag = true;
killNetscriptDelay(workerScript);
removeWorkerScript(workerScript, rerenderUi);
}
/**
* Helper function that removes the script being killed from the global pool.
* Also handles other cleanup-time operations
*
* @param {WorkerScript | number} - Identifier for WorkerScript. Either the object itself, or
* its index in the global workerScripts array
*/
function removeWorkerScript(workerScript: WorkerScript, rerenderUi: boolean=true): void {
if (workerScript instanceof WorkerScript) {
const ip = workerScript.serverIp;
const name = workerScript.name;
// Get the server on which the script runs
const server = AllServers[ip];
if (server == null) {
console.error(`Could not find server on which this script is running: ${ip}`);
return;
}
// Recalculate ram used on that server
server.ramUsed = roundToTwo(server.ramUsed - workerScript.ramUsage);
if (server.ramUsed < 0) {
console.warn(`Server (${server.hostname}) RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`);
server.ramUsed = 0;
}
// Delete the RunningScript object from that server
for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScript = server.runningScripts[i];
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
server.runningScripts.splice(i, 1);
break;
}
}
// Delete script from global pool (workerScripts)
const res = workerScripts.delete(workerScript.pid);
if (!res) {
console.warn(`removeWorkerScript() called with WorkerScript that wasn't in the global map:`);
console.warn(workerScript);
}
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emitEvent();
}
} else {
console.error(`Invalid argument passed into removeWorkerScript():`);
console.error(workerScript);
return;
}
}
/**
* Helper function that interrupts a script's delay if it is in the middle of a
* timed, blocked operation (like hack(), sleep(), etc.). This allows scripts to
* be killed immediately even if they're in the middle of one of those long operations
*/
function killNetscriptDelay(workerScript: WorkerScript) {
if (workerScript instanceof WorkerScript) {
if (workerScript.delay) {
clearTimeout(workerScript.delay);
if (workerScript.delayResolve) {
workerScript.delayResolve();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More