mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 14:28:36 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e9b028174 | ||
|
|
c56645c794 | ||
|
|
3ce2e83dd8 | ||
|
|
3241945452 | ||
|
|
fb857642e8 | ||
|
|
cc0e6548ff | ||
|
|
6c3c569a44 | ||
|
|
b5ebbba43d | ||
|
|
bf9b837e31 | ||
|
|
7f88ade30e | ||
|
|
36499ae9f2 | ||
|
|
4b95ba9ed1 | ||
|
|
804e4c23e3 | ||
|
|
b6b6d8e9fa | ||
|
|
c566c838be | ||
|
|
51d9274626 | ||
|
|
c8b478c208 | ||
|
|
18a3f061b4 | ||
|
|
3f8b9e4a32 | ||
|
|
e63ad76701 | ||
|
|
cb66ad9628 | ||
|
|
971bfbada4 | ||
|
|
b646c15521 | ||
|
|
92f7d12c0e | ||
|
|
7172f4e527 | ||
|
|
5592a8bc96 | ||
|
|
c4cb7daac5 | ||
|
|
75bc34208c | ||
|
|
83fc4d81b2 | ||
|
|
3cf18f100a | ||
|
|
8fbb072596 | ||
|
|
ea7f0752cb | ||
|
|
0f8f572519 | ||
|
|
52b6defebd | ||
|
|
34d749809a | ||
|
|
2ce4af2498 | ||
|
|
227bcf146e | ||
|
|
139a5add20 | ||
|
|
3a61a5cfa1 | ||
|
|
96db360a36 |
@@ -38,6 +38,7 @@ button {
|
||||
}
|
||||
|
||||
.a-link-button-inactive,
|
||||
.std-button-disabled,
|
||||
.std-button:disabled {
|
||||
text-decoration: none;
|
||||
background-color: #333;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Character Overview Panel (top-right)
|
||||
* Styling for the Character Overview Panel (top-right panel)
|
||||
*/
|
||||
|
||||
#character-overview-wrapper {
|
||||
|
||||
75
css/hacknetnodes.scss
Normal file
75
css/hacknetnodes.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
@import "mixins";
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Hacknet Nodes UI Page
|
||||
*/
|
||||
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.hacknet-general-info {
|
||||
margin: 10px;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
||||
@@ -138,81 +138,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Hacknet Nodes */
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-text,
|
||||
#hacknet-nodes-container li {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hacknet-node {
|
||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table;
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
height: 30px;
|
||||
|
||||
p {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: $defaultFontSize * 4;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-page-text {
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
/* Pop-up boxes */
|
||||
.popup-box-container {
|
||||
display: none; /* Hidden by default */
|
||||
display: none; /* Initially hidden */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 10; /* Sit on top */
|
||||
left: 0;
|
||||
|
||||
@@ -68,13 +68,14 @@
|
||||
@include boxShadow($boxShadowArgs);
|
||||
|
||||
background-color: #555;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
resize: none;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#script-editor-status {
|
||||
|
||||
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
132
dist/engine.css
vendored
132
dist/engine.css
vendored
@@ -486,6 +486,7 @@ button {
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
|
||||
|
||||
.a-link-button-inactive,
|
||||
.std-button-disabled,
|
||||
.std-button:disabled {
|
||||
text-decoration: none;
|
||||
background-color: #333;
|
||||
@@ -501,11 +502,15 @@ button {
|
||||
.a-link-button-inactive:hover .tooltiptext,
|
||||
.a-link-button-inactive:hover .tooltiptexthigh,
|
||||
.a-link-button-inactive:hover .tooltiptextleft,
|
||||
.std-button-disabled:hover .tooltiptext,
|
||||
.std-button-disabled:hover .tooltiptexthigh,
|
||||
.std-button-disabled:hover .tooltiptextleft,
|
||||
.std-button:disabled:hover .tooltiptext,
|
||||
.std-button:disabled:hover .tooltiptexthigh,
|
||||
.std-button:disabled:hover .tooltiptextleft {
|
||||
visibility: visible; }
|
||||
.a-link-button-inactive:active,
|
||||
.std-button-disabled:active,
|
||||
.std-button:disabled:active {
|
||||
pointer-events: none; }
|
||||
|
||||
@@ -671,7 +676,7 @@ button {
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/**
|
||||
* Styling for the Character Overview Panel (top-right)
|
||||
* Styling for the Character Overview Panel (top-right panel)
|
||||
*/
|
||||
#character-overview-wrapper {
|
||||
position: relative; }
|
||||
@@ -881,13 +886,14 @@ button {
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
background-color: #555;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
resize: none;
|
||||
width: 50%; }
|
||||
|
||||
#script-editor-status {
|
||||
float: left;
|
||||
@@ -927,6 +933,64 @@ button {
|
||||
|
||||
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/**
|
||||
* Styling for the Hacknet Nodes UI Page
|
||||
*/
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px; }
|
||||
|
||||
.hacknet-general-info {
|
||||
margin: 10px;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; }
|
||||
#hacknet-nodes-container li.hacknet-node {
|
||||
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw; }
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left; }
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right; }
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block; }
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table; }
|
||||
.hacknet-node-container .row {
|
||||
display: table-row;
|
||||
height: 30px; }
|
||||
.hacknet-node-container .row p {
|
||||
display: table-cell; }
|
||||
.hacknet-node-container .upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
/* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: 64px; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
||||
@@ -1044,64 +1108,6 @@ button {
|
||||
color: #fff;
|
||||
margin-left: 5%; }
|
||||
|
||||
/* Hacknet Nodes */
|
||||
#hacknet-nodes-container {
|
||||
position: fixed;
|
||||
padding: 10px; }
|
||||
|
||||
#hacknet-nodes-text,
|
||||
#hacknet-nodes-container li {
|
||||
margin: 10px;
|
||||
padding: 10px; }
|
||||
|
||||
#hacknet-nodes-container li {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; }
|
||||
#hacknet-nodes-container li.hacknet-node {
|
||||
-webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
||||
margin: 6px;
|
||||
padding: 7px;
|
||||
width: 35vw;
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
|
||||
#hacknet-nodes-list {
|
||||
list-style: none;
|
||||
width: 82vw; }
|
||||
|
||||
#hacknet-nodes-money {
|
||||
margin: 10px;
|
||||
float: left; }
|
||||
|
||||
#hacknet-nodes-money-multipliers-div {
|
||||
display: inline-block;
|
||||
width: 70vw; }
|
||||
|
||||
#hacknet-nodes-multipliers {
|
||||
float: right; }
|
||||
|
||||
#hacknet-nodes-purchase-button {
|
||||
display: inline-block; }
|
||||
|
||||
.hacknet-node-container {
|
||||
display: inline-table; }
|
||||
.hacknet-node-container .row {
|
||||
display: table-row;
|
||||
height: 30px; }
|
||||
.hacknet-node-container .row p {
|
||||
display: table-cell; }
|
||||
.hacknet-node-container .upgradable-info {
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
/* Don't want the vertical margin/padding, just left & right */
|
||||
padding: 0 4px;
|
||||
width: 64px; }
|
||||
|
||||
.menu-page-text {
|
||||
width: 70vw; }
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
@@ -1402,7 +1408,7 @@ button {
|
||||
/* Pop-up boxes */
|
||||
.popup-box-container {
|
||||
display: none;
|
||||
/* Hidden by default */
|
||||
/* Initially hidden */
|
||||
position: fixed;
|
||||
/* Stay in place */
|
||||
z-index: 10;
|
||||
|
||||
130
dist/vendor.bundle.js
vendored
130
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@@ -80,6 +80,16 @@ when you normally install Augmentations.
|
||||
The cost of purchasing an Augmentation for a Duplicate Sleeve is **not** affected
|
||||
by how many Augmentations you have purchased for yourself, and vice versa.
|
||||
|
||||
Memory
|
||||
~~~~~~
|
||||
Sleeve memory dictates what a sleeve's synchronization will be when its reset by
|
||||
switching BitNodes. For example, if a sleeve has a memory of 10, then when you
|
||||
switch BitNodes its synchronization will initially be set to 10, rather than 1.
|
||||
|
||||
Memory can only be increased by purchasing upgrades from The Covenant.
|
||||
It is a persistent stat, meaning it never gets reset back to 1.
|
||||
The maximum possible value for a sleeve's memory is 100.
|
||||
|
||||
Re-sleeving
|
||||
^^^^^^^^^^^
|
||||
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
||||
|
||||
@@ -16,6 +16,85 @@ the terminal and enter::
|
||||
|
||||
nano .fconf
|
||||
|
||||
|
||||
.. _terminal_filesystem:
|
||||
|
||||
Filesystem (Directories)
|
||||
------------------------
|
||||
The Terminal contains a **very** basic filesystem that allows you to store and
|
||||
organize your files into different directories. Note that this is **not** a true
|
||||
filesystem implementation. Instead, it is done almost entirely using string manipulation.
|
||||
For this reason, many of the nice & useful features you'd find in a real
|
||||
filesystem do not exist.
|
||||
|
||||
Here are the Terminal commands you'll commonly use when dealing with the filesystem.
|
||||
|
||||
* :ref:`ls_terminal_command`
|
||||
* :ref:`cd_terminal_command`
|
||||
* :ref:`mv_terminal_command`
|
||||
|
||||
Directories
|
||||
^^^^^^^^^^^
|
||||
In order to create a directory, simply name a file using a full absolute Linux-style path::
|
||||
|
||||
/scripts/myScript.js
|
||||
|
||||
This will automatically create a "directory" called :code:`scripts`. This will also work
|
||||
for subdirectories::
|
||||
|
||||
/scripts/hacking/helpers/myHelperScripts.script
|
||||
|
||||
Files in the root directory do not need to begin with a forward slash::
|
||||
|
||||
thisIsAFileInTheRootDirectory.txt
|
||||
|
||||
Note that there is no way to manually create or remove directories. The creation and
|
||||
deletion of directories is automatically handled as you name/rename/delete
|
||||
files.
|
||||
|
||||
Absolute vs Relative Paths
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Many Terminal commands accept absolute both absolute and relative paths for specifying a
|
||||
file.
|
||||
|
||||
An absolute path specifies the location of the file from the root directory (/).
|
||||
Any path that begins with the forward slash is an absolute path::
|
||||
|
||||
$ nano /scripts/myScript.js
|
||||
$ cat /serverList.txt
|
||||
|
||||
A relative path specifies the location of the file relative to the current working directory.
|
||||
Any path that does **not** begin with a forward slash is a relative path. Note that the
|
||||
Linux-style dot symbols will work for relative paths::
|
||||
|
||||
. (a single dot) - represents the current directory
|
||||
.. (two dots) - represents the parent directory
|
||||
|
||||
$ cd ..
|
||||
$ nano ../scripts/myScript.js
|
||||
$ nano ../../helper.js
|
||||
|
||||
Netscript
|
||||
^^^^^^^^^
|
||||
Note that in order to reference a file, :ref:`netscript` functions require the
|
||||
**full** absolute file path. For example
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
run("/scripts/hacking/helpers.myHelperScripts.script");
|
||||
rm("/logs/myHackingLogs.txt");
|
||||
rm("thisIsAFileInTheRootDirectory.txt");
|
||||
|
||||
.. note:: A full file path **must** begin with a forward slash (/) if that file
|
||||
is not in the root directory.
|
||||
|
||||
Missing Features
|
||||
^^^^^^^^^^^^^^^^
|
||||
These features that are typically in Linux filesystems have not yet been added to the game:
|
||||
|
||||
* Tab autocompletion does not work with relative paths
|
||||
* :code:`mv` only accepts full filepaths for the destination argument. It does not accept directories
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
@@ -98,6 +177,25 @@ Display a message (.msg), literature (.lit), or text (.txt) file::
|
||||
$ cat foo.lit
|
||||
$ cat servers.txt
|
||||
|
||||
.. _cd_terminal_command:
|
||||
|
||||
cd
|
||||
^^
|
||||
|
||||
$ cd [dir]
|
||||
|
||||
Change to the specified directory.
|
||||
|
||||
See :ref:`terminal_filesystem` for details on directories.
|
||||
|
||||
Note that this command works even for directories that don't exist. If you change
|
||||
to a directory that doesn't exist, it will not be created. A directory is only created
|
||||
once there is a file in it::
|
||||
|
||||
$ cd scripts/hacking
|
||||
$ cd /logs
|
||||
$ cd ..
|
||||
|
||||
check
|
||||
^^^^^
|
||||
|
||||
@@ -234,27 +332,35 @@ killall
|
||||
|
||||
Kills all scripts on the current server.
|
||||
|
||||
.. _ls_terminal_command:
|
||||
|
||||
ls
|
||||
^^
|
||||
|
||||
$ ls [| grep pattern]
|
||||
$ ls [dir] [| grep pattern]
|
||||
|
||||
Prints files on the current server to the Terminal screen.
|
||||
Prints files and directories on the current server to the Terminal screen.
|
||||
|
||||
If this command is run with no arguments, then it prints all files on the current
|
||||
server to the Terminal screen. The files will be displayed in alphabetical
|
||||
order.
|
||||
If this command is run with no arguments, then it prints all files and directories on the current
|
||||
server to the Terminal screen. Directories will be printed first in alphabetical order,
|
||||
followed by the files (also in alphabetical order).
|
||||
|
||||
The '| grep pattern' is an optional parameter that can be used to only display files
|
||||
whose filenames match the specified pattern. For example, if you wanted to only display
|
||||
files with the .script extension, you could use::
|
||||
The :code:`dir` optional parameter allows you to specify the directory for which to display
|
||||
files.
|
||||
|
||||
The :code:`| grep pattern` optional parameter allows you to only display files and directories
|
||||
with a certain pattern in their names.
|
||||
|
||||
Examples::
|
||||
|
||||
// List files/directories with the '.script' extension in the current directory
|
||||
$ ls | grep .script
|
||||
|
||||
Alternatively, if you wanted to display all files with the word *purchase* in the filename,
|
||||
you could use::
|
||||
// List files/directories with the '.js' extension in the root directory
|
||||
$ ls / | grep .js
|
||||
|
||||
$ ls | grep purchase
|
||||
// List files/directories with the word 'purchase' in the name, in the :code:`scripts` directory
|
||||
$ ls scripts | grep purchase
|
||||
|
||||
|
||||
lscpu
|
||||
@@ -282,6 +388,28 @@ The first example above will print the amount of RAM needed to run 'foo.script'
|
||||
with a single thread. The second example above will print the amount of RAM needed
|
||||
to run 'foo.script' with 50 threads.
|
||||
|
||||
.. _mv_terminal_command:
|
||||
|
||||
mv
|
||||
^^
|
||||
|
||||
$ mv [source] [destination]
|
||||
|
||||
Move the source file to the specified destination in the filesystem.
|
||||
See :ref:`terminal_filesystem` for more details about the Terminal's filesystem.
|
||||
This command only works for scripts and text files (.txt). It cannot, however, be used
|
||||
to convert from script to text file, or vice versa.
|
||||
|
||||
This function can also be used to rename files.
|
||||
|
||||
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
|
||||
full filepath. It cannot be a directory.
|
||||
|
||||
Examples::
|
||||
|
||||
$ mv hacking.script scripts/hacking.script
|
||||
$ mv myScript.js myOldScript.js
|
||||
|
||||
nano
|
||||
^^^^
|
||||
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.46.0 - 4/3/2019
|
||||
------------------
|
||||
* Added BitNode-9: Hacktocracy
|
||||
* Changed BitNode-11's multipliers to make it slightly harder overall
|
||||
* Source-File 11 is now slightly stronger
|
||||
* Added several functions to Netscript Sleeve API for buying Sleeve augmentations (by hydroflame)
|
||||
* Added a new stat for Duplicate Sleeves: Memory
|
||||
* Increase baseline experience earned from Infiltration, but it now gives diminishing returns (on exp) as you get to higher difficulties/levels
|
||||
* In Bladeburner, stamina gained from Hyperbolic Regeneration Chamber is now a percentage of your max stamina
|
||||
|
||||
* Corporation Changes:
|
||||
* 'Demand' value of products decreases more slowly
|
||||
* Bug Fix: Fixed a Corporation issue that broke the Market-TA2 Research
|
||||
* Bug Fix: Issuing New Shares now works properly
|
||||
|
||||
* Bug Fix: Money Statistics tracker was incorrectly recording profits when selling stocks manually
|
||||
* Bug Fix: Fixed an issue with the job requirement tooltip for security jobs
|
||||
|
||||
v0.45.1 - 3/23/2019
|
||||
-------------------
|
||||
* Added two new Corporation Researches
|
||||
|
||||
@@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.45'
|
||||
version = '0.46'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.45.0'
|
||||
release = '0.46.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
getScriptRam() Netscript Function
|
||||
===========================
|
||||
=================================
|
||||
|
||||
.. js:function:: getScriptRam(scriptname[, hostname/ip])
|
||||
|
||||
|
||||
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
17
doc/source/netscript/hacknetnodeapi/getCacheUpgradeCost.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
getCacheUpgradeCost() Netscript Function
|
||||
========================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: getCacheUpgradeCost(i, n)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
:param number n: Number of times to upgrade cache. Must be positive. Rounded to nearest integer
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||
a Hacknet Node).
|
||||
|
||||
Returns the cost of upgrading the cache level of the specified Hacknet Server by *n*.
|
||||
|
||||
If an invalid value for *n* is provided, then this function returns 0. If the
|
||||
specified Hacknet Server is already at the max cache level, then Infinity is returned.
|
||||
@@ -1,6 +1,8 @@
|
||||
getNodeStats() Netscript Function
|
||||
=================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: getNodeStats(i)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
@@ -12,7 +14,12 @@ getNodeStats() Netscript Function
|
||||
level: Node's level,
|
||||
ram: Node's RAM,
|
||||
cores: Node's number of cores,
|
||||
production: Node's money earned per second,
|
||||
cache: Cache level. Only applicable for Hacknet Servers
|
||||
production: Node's production per second
|
||||
timeOnline: Number of seconds since Node has been purchased,
|
||||
totalProduction: Total number of money Node has produced
|
||||
totalProduction: Total amount that the Node has produced
|
||||
}
|
||||
|
||||
.. note:: Note that for Hacknet Nodes, production refers to the amount of money the node generates.
|
||||
For Hacknet Servers (the upgraded version of Hacknet Nodes), production refers to the amount
|
||||
of hashes the node generates.
|
||||
|
||||
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
hashCost() Netscript Function
|
||||
=============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: hashCost(upgName, upgTarget)
|
||||
|
||||
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes required for the specified upgrade. The name of the
|
||||
upgrade must be an exact match.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
var upgradeName = "Sell for Corporation Funds";
|
||||
if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||
hacknet.spendHashes(upgName);
|
||||
}
|
||||
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
numHashes() Netscript Function
|
||||
==============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: numHashes()
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes you have
|
||||
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
spendHashes() Netscript Function
|
||||
================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: spendHashes(upgName, upgTarget)
|
||||
|
||||
:param string upgName: Name of upgrade to spend hashes on. Must be an exact match
|
||||
:param string upgTarget: Object to which upgrade applies. Required for certain upgrades
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Spend the hashes generated by your Hacknet Servers on an upgrade. Returns a boolean value -
|
||||
true if the upgrade is successfully purchased, and false otherwise.
|
||||
|
||||
The name of the upgrade must be an exact match. The :code:`upgTarget` argument is used
|
||||
for upgrades such as :code:`Reduce Minimum Security`, which applies to a specific server.
|
||||
In this case, the :code:`upgTarget` argument must be the hostname of the server.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
hacknet.spendHashes("Sell for Corporation Funds");
|
||||
hacknet.spendHashes("Increase Maximum Money", "foodnstuff");
|
||||
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
19
doc/source/netscript/hacknetnodeapi/upgradeCache.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
upgradeCache() Netscript Function
|
||||
=================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: upgradeCache(i, n)
|
||||
|
||||
:param number i: Index/Identifier of Hacknet Node. :ref:`See here for details <netscript_hacknetnodeapi_referencingahacknetnode>`
|
||||
:param number n: Number of cache levels to purchase. Must be positive. Rounded to nearest integer
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
|
||||
a Hacknet Node).
|
||||
|
||||
Tries to upgrade the specified Hacknet Server's cache *n* times.
|
||||
|
||||
Returns true if it successfully upgrades the Server's cache *n* times, or if
|
||||
it purchases some positive amount and the Server reaches its max cache level.
|
||||
|
||||
Returns false otherwise.
|
||||
@@ -3,6 +3,10 @@
|
||||
Netscript Hacknet Node API
|
||||
==========================
|
||||
|
||||
.. warning:: Not all functions in the Hacknet Node API are immediately available.
|
||||
For this reason, the documentation for this API may contains spoilers
|
||||
for the game.
|
||||
|
||||
Netscript provides the following API for accessing and upgrading your Hacknet Nodes
|
||||
through scripts.
|
||||
|
||||
@@ -31,9 +35,14 @@ In :ref:`netscriptjs`::
|
||||
upgradeLevel() <hacknetnodeapi/upgradeLevel>
|
||||
upgradeRam() <hacknetnodeapi/upgradeRam>
|
||||
upgradeCore() <hacknetnodeapi/upgradeCore>
|
||||
upgradeCache() <hacknetnodeapi/upgradeCache>
|
||||
getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost>
|
||||
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
|
||||
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
|
||||
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
|
||||
numHashes() <hacknetnodeapi/numHashes>
|
||||
hashCost() <hacknetnodeapi/hashCost>
|
||||
spendHashes() <hacknetnodeapi/spendHashes>
|
||||
|
||||
.. _netscript_hacknetnodeapi_referencingahacknetnode:
|
||||
|
||||
@@ -68,23 +77,25 @@ The following is an example of one way a script can be used to automate the
|
||||
purchasing and upgrading of Hacknet Nodes.
|
||||
|
||||
This script attempts to purchase Hacknet Nodes until the player has a total of 8. Then
|
||||
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores::
|
||||
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
function myMoney() {
|
||||
return getServerMoneyAvailable("home");() <hacknetnodeapi/ return getServerMoneyAvailable("home");>
|
||||
}
|
||||
}() <hacknetnodeapi/>
|
||||
return getServerMoneyAvailable("home");
|
||||
}
|
||||
|
||||
disableLog("getServerMoneyAvailable");
|
||||
disableLog("sleep");
|
||||
|
||||
cnt = 8;
|
||||
var cnt = 8;
|
||||
|
||||
while(hacknet.numNodes() < cnt) {
|
||||
res = hacknet.purchaseNode();
|
||||
print("Purchased hacknet Node with index " + res);
|
||||
};
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
for (var i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).level <= 80) {
|
||||
var cost = hacknet.getLevelUpgradeCost(i, 10);
|
||||
while (myMoney() < cost) {
|
||||
@@ -95,9 +106,9 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to level 80");
|
||||
print("All nodes upgraded to level 80");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
for (var i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).ram < 16) {
|
||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
||||
while (myMoney() < cost) {
|
||||
@@ -108,43 +119,4 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 16GB RAM");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).level <= 140) {
|
||||
var cost = hacknet.getLevelUpgradeCost(i, 5);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeLevel(i, 5);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to level 140");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknet.getNodeStats(i).ram < 64) {
|
||||
var cost = hacknet.getRamUpgradeCost(i, 2);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeRam(i, 2);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 64GB RAM (MAX)");
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
while (hacknetnodes.getNodeStatsi(i).cores < 8) {
|
||||
var cost = hacknet.getCoreUpgradeCost(7);
|
||||
while (myMoney() < cost) {
|
||||
print("Need $" + cost + " . Have $" + myMoney());
|
||||
sleep(3000);
|
||||
}
|
||||
res = hacknet.upgradeCore(i, 7);
|
||||
};
|
||||
};
|
||||
|
||||
print("All nodes upgrade to 8 cores");
|
||||
print("All nodes upgraded to 16GB RAM");
|
||||
|
||||
@@ -41,6 +41,9 @@ In :ref:`netscriptjs`::
|
||||
setToUniversityCourse() <sleeveapi/setToUniversityCourse>
|
||||
setToGymWorkout() <sleeveapi/setToGymWorkout>
|
||||
travel() <sleeveapi/travel>
|
||||
getSleeveAugmentations() <sleeveapi/getSleeveAugmentations>
|
||||
getSleevePurchasableAugs() <sleeveapi/getSleevePurchasableAugs>
|
||||
purchaseSleeveAug() <sleeveapi/purchaseSleeveAug>
|
||||
|
||||
.. _netscript_sleeveapi_referencingaduplicatesleeve:
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
getSleeveAugmentations() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: getSleeveAugmentations(sleeveNumber)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to retrieve augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
|
||||
Return a list of augmentation names that this sleeve has installed.
|
||||
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
17
doc/source/netscript/sleeveapi/getSleevePurchasableAugs.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
getSleevePurchasableAugs() Netscript Function
|
||||
=============================================
|
||||
|
||||
.. js:function:: getSleevePurchasableAugs(sleeveNumber)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to retrieve purchasable augmentations from. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
|
||||
Return a list of augmentations that the player can buy for this sleeve.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
[
|
||||
{
|
||||
name: string, // augmentation name
|
||||
cost: number, // augmentation cost
|
||||
}
|
||||
]
|
||||
@@ -10,8 +10,8 @@ getSleeveStats() Netscript Function
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
shock: current shock of the sleeve [0-1],
|
||||
sync: current sync of the sleeve [0-1],
|
||||
shock: current shock of the sleeve [0-100],
|
||||
sync: current sync of the sleeve [0-100],
|
||||
hacking_skill: current hacking skill of the sleeve,
|
||||
strength: current strength of the sleeve,
|
||||
defense: current defense of the sleeve,
|
||||
|
||||
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
9
doc/source/netscript/sleeveapi/purchaseSleeveAug.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
purchaseSleeveAug() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: purchaseSleeveAug(sleeveNumber, augName)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to buy an aug for. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
:param string augName: Name of the aug to buy. Must be an exact match
|
||||
|
||||
Return true if the aug was purchased and installed on the sleeve.
|
||||
391
index.html
391
index.html
@@ -201,257 +201,9 @@
|
||||
|
||||
<!-- Hacknet Nodes -->
|
||||
<div id="hacknet-nodes-container" class="generic-menupage-container">
|
||||
<h1 id="hacknet-nodes-title"> Hacknet Nodes </h1>
|
||||
<p id="hacknet-nodes-text" class="menu-page-text">
|
||||
The Hacknet is a global, decentralized network of machines. It is used by hackers all around
|
||||
the world to anonymously share computing power and perform distributed cyberattacks without the
|
||||
fear of being traced.
|
||||
<br/><br/>
|
||||
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
|
||||
resources to the Hacknet network. This allows you to take a small percentage of profits
|
||||
from hacks performed on the network. Essentially, you are renting out your Node's computing power.
|
||||
<br/><br/>
|
||||
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
|
||||
in order to increase its computing power and thereby increase the profit you earn from it.
|
||||
</p>
|
||||
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
|
||||
<br/>
|
||||
<div id="hacknet-nodes-money-multipliers-div">
|
||||
<p id="hacknet-nodes-money">
|
||||
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br/>
|
||||
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
|
||||
</p>
|
||||
<span id="hacknet-nodes-multipliers">
|
||||
<a id="hacknet-nodes-1x-multiplier" class="a-link-button-inactive"> x1 </a>
|
||||
<a id="hacknet-nodes-5x-multiplier" class="a-link-button"> x5 </a>
|
||||
<a id="hacknet-nodes-10x-multiplier" class="a-link-button"> x10 </a>
|
||||
<a id="hacknet-nodes-max-multiplier" class="a-link-button"> MAX </a>
|
||||
</span>
|
||||
</div>
|
||||
<ul id="hacknet-nodes-list">
|
||||
</ul>
|
||||
<!-- React Component -->
|
||||
</div>
|
||||
|
||||
<!-- World -->
|
||||
<div id="world-container" class="generic-menupage-container">
|
||||
<h2 id="world-city-name"> </h2>
|
||||
<p id="world-city-desc"> </p>
|
||||
<ul id="aevum-locations-list">
|
||||
<li id="aevum-travelagency-li">
|
||||
<a id="aevum-travelagency" class="a-link-button"> Travel Agency </a>
|
||||
</li>
|
||||
<li id="aevum-hospital-li">
|
||||
<a id="aevum-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="aevum-summituniversity-li">
|
||||
<a id="aevum-summituniversity" class="a-link-button"> Summit University </a>
|
||||
</li>
|
||||
<li id="aevum-ecorp-li">
|
||||
<a id="aevum-ecorp" class="a-link-button"> ECorp </a>
|
||||
</li>
|
||||
<li id="aevum-bachmanandassociates-li">
|
||||
<a id="aevum-bachmanandassociates" class="a-link-button"> Bachman & Associates</a>
|
||||
</li>
|
||||
<li id="aevum-clarkeincorporated-li">
|
||||
<a id="aevum-clarkeincorporated" class="a-link-button"> Clarke Incorporated </a>
|
||||
</li>
|
||||
<li id="aevum-fulcrumtechnologies-li">
|
||||
<a id="aevum-fulcrumtechnologies" class="a-link-button"> Fulcrum Technologies </a>
|
||||
</li>
|
||||
<li id="aevum-aerocorp-li">
|
||||
<a id="aevum-aerocorp" class="a-link-button"> AeroCorp </a>
|
||||
</li>
|
||||
<li id="aevum-galacticcybersystems-li">
|
||||
<a id="aevum-galacticcybersystems" class="a-link-button"> Galactic Cybersystems </a>
|
||||
</li>
|
||||
<li id="aevum-watchdogsecurity-li">
|
||||
<a id="aevum-watchdogsecurity" class="a-link-button">Watchdog Security </a>
|
||||
</li>
|
||||
<li id="aevum-rhoconstruction-li">
|
||||
<a id="aevum-rhoconstruction" class="a-link-button">Rho Construction </a>
|
||||
</li>
|
||||
<li id="aevum-aevumpolice-li">
|
||||
<a id="aevum-aevumpolice" class="a-link-button">Aevum Police</a>
|
||||
</li>
|
||||
<li id="aevum-netlinktechnologies-li">
|
||||
<a id="aevum-netlinktechnologies" class="a-link-button">NetLink Technologies</a>
|
||||
</li>
|
||||
<li id="aevum-crushfitnessgym-li">
|
||||
<a id="aevum-crushfitnessgym" class="a-link-button">Crush Fitness Gym </a>
|
||||
</li>
|
||||
<li id="aevum-snapfitnessgym-li">
|
||||
<a id="aevum-snapfitnessgym" class="a-link-button">Snap Fitness Gym</a>
|
||||
</li>
|
||||
<li id="aevum-slums-li">
|
||||
<a id="aevum-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="chongqing-locations-list">
|
||||
<li id="chongqing-travelagency-li">
|
||||
<a id="chongqing-travelagency" class="a-link-button"> Travel Agency </a>
|
||||
</li>
|
||||
<li id="chongqing-hospital-li">
|
||||
<a id="chongqing-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="chonqging-kuaigonginternational-li">
|
||||
<a id="chongqing-kuaigonginternational" class="a-link-button">KuaiGong International </a>
|
||||
</li>
|
||||
<li id="chongqing-solarisspacesystems-li">
|
||||
<a id="chongqing-solarisspacesystems" class="a-link-button">Solaris Space Systems</a>
|
||||
</li>
|
||||
<li id="chongqing-slums-li">
|
||||
<a id="chongqing-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="sector12-locations-list">
|
||||
<li id="sector12-travelagency-li">
|
||||
<a id="sector12-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="sector12-hospital-li">
|
||||
<a id="sector12-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="sector12-rothmanuniversity-li">
|
||||
<a id="sector12-rothmanuniversity" class="a-link-button"> Rothman University</a>
|
||||
</li>
|
||||
<li id="sector12-megacorp-li">
|
||||
<a id="sector12-megacorp" class="a-link-button">MegaCorp</a>
|
||||
</li>
|
||||
<li id="sector12-bladeindustries-li">
|
||||
<a id="sector12-bladeindustries" class="a-link-button"> Blade Industries</a>
|
||||
</li>
|
||||
<li id="sector12-foursigma-li">
|
||||
<a id="sector12-foursigma" class="a-link-button">Four Sigma</a>
|
||||
</li>
|
||||
<li id="sector12-icarusmicrosystems-li">
|
||||
<a id="sector12-icarusmicrosystems" class="a-link-button"> Icarus Microsystems</a>
|
||||
</li>
|
||||
<li id="sector12-universalenergy-li">
|
||||
<a id="sector12-universalenergy" class="a-link-button">Universal Energy </a>
|
||||
</li>
|
||||
<li id="sector12-deltaone-li">
|
||||
<a id="sector12-deltaone" class="a-link-button">DeltaOne </a>
|
||||
</li>
|
||||
<li id="sector12-cia-li">
|
||||
<a id="sector12-cia" class="a-link-button">Central Intelligence Agency </a>
|
||||
</li>
|
||||
<li id="sector12-nsa-li">
|
||||
<a id="sector12-nsa" class="a-link-button">National Security Agency </a>
|
||||
</li>
|
||||
<li id="sector12-alphaenterprises-li">
|
||||
<a id="sector12-alphaenterprises" class="a-link-button">Alpha Enterprises</a>
|
||||
</li>
|
||||
<li id="sector12-carmichaelsecurity-li">
|
||||
<a id="sector12-carmichaelsecurity" class="a-link-button"> Carmichael Security</a>
|
||||
</li>
|
||||
<li id="sector12-foodnstuff-li">
|
||||
<a id="sector12-foodnstuff" class="a-link-button">FoodNStuff</a>
|
||||
</li>
|
||||
<li id="sector12-joesguns-li">
|
||||
<a id="sector12-joesguns" class="a-link-button"> Joe's Guns</a>
|
||||
</li>
|
||||
<li id="sector12-irongym-li">
|
||||
<a id="sector12-irongym" class="a-link-button">Iron Gym </a>
|
||||
</li>
|
||||
<li id="sector12-powerhousegym-li">
|
||||
<a id="sector12-powerhousegym" class="a-link-button">Powerhouse Gym</a>
|
||||
</li>
|
||||
<li id="sector12-slums-li">
|
||||
<a id="sector12-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
<li id="sector12-cityhall-li">
|
||||
<a id="sector12-cityhall" class="a-link-button">City Hall</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="newtokyo-locations-list">
|
||||
<li id="newtokyo-travelagency-li">
|
||||
<a id="newtokyo-travelagency" class="a-link-button"> Travel Agency</a>
|
||||
</li>
|
||||
<li id="newtokyo-hospital-li">
|
||||
<a id="newtokyo-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="newtokyo-defcomm-li">
|
||||
<a id="newtokyo-defcomm" class="a-link-button"> DefComm</a>
|
||||
</li>
|
||||
<li id="newtokyo-vitalife-li">
|
||||
<a id="newtokyo-vitalife" class="a-link-button">VitaLife </a>
|
||||
</li>
|
||||
<li id="newtokyo-globalpharmaceuticals-li">
|
||||
<a id="newtokyo-globalpharmaceuticals" class="a-link-button">Global Pharmaceuticals</a>
|
||||
</li>
|
||||
<li id="newtokyo-noodlebar-li">
|
||||
<a id="newtokyo-noodlebar" class="a-link-button">Noodle Bar </a>
|
||||
</li>
|
||||
<li id="newtokyo-slums-li">
|
||||
<a id="newtokyo-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="ishima-locations-list">
|
||||
<li id="ishima-travelagency-li">
|
||||
<a id="ishima-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="ishima-hospital-li">
|
||||
<a id="ishima-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="ishima-stormtechnologies-li">
|
||||
<a id="ishima-stormtechnologies" class="a-link-button">Storm Technologies</a>
|
||||
</li>
|
||||
<li id="ishima-novamedical-li">
|
||||
<a id="ishima-novamedical" class="a-link-button">Nova Medical</a>
|
||||
</li>
|
||||
<li id="ishima-omegasoftware-li">
|
||||
<a id="ishima-omegasoftware" class="a-link-button">Omega Software </a>
|
||||
</li>
|
||||
<li id="ishima-slums-li">
|
||||
<a id="ishima-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="volhaven-locations-list">
|
||||
<li id="volhaven-travelagency-li">
|
||||
<a id="volhaven-travelagency" class="a-link-button">Travel Agency </a>
|
||||
</li>
|
||||
<li id="volhaven-hospital-li">
|
||||
<a id="volhaven-hospital" class="a-link-button">Hospital</a>
|
||||
</li>
|
||||
<li id="volhaven-zbinstituteoftechnology-li">
|
||||
<a id="volhaven-zbinstituteoftechnology" class="a-link-button">ZB Insitute of Technology</a>
|
||||
</li>
|
||||
<li id="volhaven-omnitekincorporated-li">
|
||||
<a id="volhaven-omnitekincorporated" class="a-link-button">OmniTek Incorporated </a>
|
||||
</li>
|
||||
<li id="volhaven-nwo-li">
|
||||
<a id="volhaven-nwo" class="a-link-button">NWO</a>
|
||||
</li>
|
||||
<li id="volhaven-helislabs-li">
|
||||
<a id="volhaven-helioslabs" class="a-link-button">Helios Labs</a>
|
||||
</li>
|
||||
<li id="volhaven-omniacybersystems-li">
|
||||
<a id="volhaven-omniacybersystems" class="a-link-button">Omnia Cybersystems</a>
|
||||
</li>
|
||||
<li id="volhaven-lexocorp-li">
|
||||
<a id="volhaven-lexocorp" class="a-link-button">LexoCorp</a>
|
||||
</li>
|
||||
<li id="volhaven-syscoresecurities-li">
|
||||
<a id="volhaven-syscoresecurities" class="a-link-button">SysCore Securities</a>
|
||||
</li>
|
||||
<li id="volhaven-computek-li">
|
||||
<a id="volhaven-computek" class="a-link-button">CompuTek</a>
|
||||
</li>
|
||||
<li id="volhaven-milleniumfitnessgym-li">
|
||||
<a id="volhaven-milleniumfitnessgym" class="a-link-button">Millenium Fitness Gym</a>
|
||||
</li>
|
||||
<li id="volhaven-slums-li">
|
||||
<a id="volhaven-slums" class="a-link-button">The Slums</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="generic-locations-list"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Create a program(executable) -->
|
||||
<div id="create-program-container" class="generic-menupage-container">
|
||||
<p id="create-program-page-text">
|
||||
@@ -478,150 +230,31 @@
|
||||
<div id="tutorial-container" class="generic-menupage-container">
|
||||
<h1> Tutorial (AKA Links to Documentation) </h1>
|
||||
<a id="tutorial-getting-started-link" class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html">
|
||||
Getting Started
|
||||
</a><br><br>
|
||||
Getting Started</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html">
|
||||
Servers & Networking
|
||||
</a><br><br>
|
||||
Servers & Networking</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html">
|
||||
Hacking
|
||||
</a><br><br>
|
||||
Hacking</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html">
|
||||
Scripts
|
||||
</a><br><br>
|
||||
Scripts</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
|
||||
Netscript Programming Language
|
||||
</a><br><br>
|
||||
Netscript Programming Language</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html">
|
||||
Traveling
|
||||
</a><br><br>
|
||||
Traveling</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html">
|
||||
Companies
|
||||
</a><br><br>
|
||||
Companies</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html">
|
||||
Infiltration
|
||||
</a><br><br>
|
||||
Infiltration</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html">
|
||||
Factions
|
||||
</a><br><br>
|
||||
Factions</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html">
|
||||
Augmentations
|
||||
</a><br><br>
|
||||
Augmentations</a><br><br>
|
||||
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
|
||||
Keyboard Shortcuts
|
||||
</a>
|
||||
Keyboard Shortcuts</a>
|
||||
</div>
|
||||
|
||||
<!-- Location (visiting a location in World) -->
|
||||
<div id="location-container" class="generic-menupage-container">
|
||||
<a id="location-return-to-world-button" class="a-link-button"> Return to World </a>
|
||||
<h1 id="location-name"></h1>
|
||||
<p id="location-info"> </p>
|
||||
|
||||
<p id="location-job-title"> </p>
|
||||
<p id="location-text-divider-1"> --------------- </p>
|
||||
<p id="location-job-reputation" class="tooltip"> </p>
|
||||
<p id="location-text-divider-2"> --------------- </p>
|
||||
<p id="location-company-favor" class="tooltip"> </p>
|
||||
<p id="location-text-divider-3"> --------------- </p>
|
||||
|
||||
<!-- Jobs/Work at a company -->
|
||||
<a id="location-software-job" class="a-link-button tooltip"> Apply for Software Job</a>
|
||||
<a id="location-software-consultant-job" class="a-link-button tooltip"> Apply for Software Consultant Job</a>
|
||||
<a id="location-it-job" class="a-link-button tooltip"> Apply for IT Job </a>
|
||||
<a id="location-security-engineer-job" class="a-link-button tooltip"> Apply for Security Engineer Job</a>
|
||||
<a id="location-network-engineer-job" class="a-link-button tooltip"> Apply for Network Engineer Job</a>
|
||||
<a id="location-business-job" class="a-link-button tooltip"> Apply for Business Job</a>
|
||||
<a id="location-business-consultant-job" class="a-link-button tooltip"> Apply for Business Consultant Job </a>
|
||||
<a id="location-security-job" class="a-link-button tooltip"> Apply for Security Job</a>
|
||||
<a id="location-agent-job" class="a-link-button tooltip"> Apply to be an Agent</a>
|
||||
<a id="location-employee-job" class="a-link-button tooltip"> Apply to be an Employee </a>
|
||||
<a id="location-parttime-employee-job" class="a-link-button tooltip"> Apply to be a Part-time Employee </a>
|
||||
<a id="location-waiter-job" class="a-link-button tooltip"> Apply to be a Waiter</a>
|
||||
<a id="location-parttime-waiter-job" class="a-link-button tooltip"> Apply to be a Part-time Waiter</a>
|
||||
|
||||
<a id="location-work" class="a-link-button"> Work </a>
|
||||
|
||||
<!-- Gym -->
|
||||
<a id="location-gym-train-str" class="a-link-button">Train Strength</a>
|
||||
<a id="location-gym-train-def" class="a-link-button">Train Defense </a>
|
||||
<a id="location-gym-train-dex" class="a-link-button">Train Dexterity</a>
|
||||
<a id="location-gym-train-agi" class="a-link-button">Train Agility</a>
|
||||
|
||||
<!-- Study/Take classes at a university -->
|
||||
<a id="location-study-computer-science" class="a-link-button">Study Computer Science (free)</a>
|
||||
<a id="location-data-structures-class" class="a-link-button">Take Data Structures course</a>
|
||||
<a id="location-networks-class" class="a-link-button">Take Networks course</a>
|
||||
<a id="location-algorithms-class" class="a-link-button">Take Algorithms course</a>
|
||||
<a id="location-management-class" class="a-link-button">Take Management course</a>
|
||||
<a id="location-leadership-class" class="a-link-button">Take Leadership course</a>
|
||||
|
||||
<!-- Purchase servers -->
|
||||
<a id="location-purchase-2gb" class="a-link-button"> Purchase 2GB Server - $150,000</a>
|
||||
<a id="location-purchase-4gb" class="a-link-button"> Purchase 4GB Server - $300,000</a>
|
||||
<a id="location-purchase-8gb" class="a-link-button"> Purchase 8GB Server - $600,000</a>
|
||||
<a id="location-purchase-16gb" class="a-link-button"> Purchase 16GB Server - $1,200,000</a>
|
||||
<a id="location-purchase-32gb" class="a-link-button"> Purchase 32GB Server - $2,400,000</a>
|
||||
<a id="location-purchase-64gb" class="a-link-button"> Purchase 64GB Server - $4,800,000</a>
|
||||
<a id="location-purchase-128gb" class="a-link-button"> Purchase 128GB Server - $9,600,000</a>
|
||||
<a id="location-purchase-256gb" class="a-link-button"> Purchase 256GB Server - $19,200,000</a>
|
||||
<a id="location-purchase-512gb" class="a-link-button"> Purchase 512GB Server - $38,400,000</a>
|
||||
<a id="location-purchase-1tb" class="a-link-button"> Purchase 1TB Server - $75,000,000</a>
|
||||
<a id="location-purchase-tor" class="a-link-button"> Purchase TOR Router - $100,000</a>
|
||||
<a id="location-purchase-home-ram" class="a-link-button"> Purchase additional RAM for Home computer </a>
|
||||
<a id="location-purchase-home-cores" class="a-link-button"> Purchase additional Core for Home computer </a>
|
||||
|
||||
<!-- Infiltrate -->
|
||||
<a id="location-infiltrate" class="a-link-button tooltip"> Infiltrate Company
|
||||
<span class="tooltiptext">
|
||||
Infiltrate this company's facility to try and steal their classified secrets!
|
||||
Warning: You may end up hospitalized if you are unsuccessful!
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Hospital -->
|
||||
<a id="location-hospital-treatment" class="a-link-button"> Get Treatment for Wounds </a>
|
||||
|
||||
<!-- Travel agency -->
|
||||
<p id="location-travel-agency-text">
|
||||
From here, you can travel to any other city! A ticket costs $200,000.
|
||||
</p>
|
||||
<a id="location-travel-to-aevum" class="a-link-button"> Travel to Aevum </a>
|
||||
<a id="location-travel-to-chongqing" class="a-link-button"> Travel to Chongqing</a>
|
||||
<a id="location-travel-to-sector12" class="a-link-button"> Travel to Sector-12</a>
|
||||
<a id="location-travel-to-newtokyo" class="a-link-button"> Travel to New Tokyo</a>
|
||||
<a id="location-travel-to-ishima" class="a-link-button"> Travel to Ishima</a>
|
||||
<a id="location-travel-to-volhaven" class="a-link-button"> Travel to Volhaven</a>
|
||||
|
||||
<!-- Slums -->
|
||||
<p id="location-slums-description">
|
||||
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
|
||||
other shadowy entities. The city's government and police have neglected this area for years...
|
||||
<br/><br/><br/>
|
||||
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
|
||||
successful. Your chance at successfully committing a crime is determined by your stats.
|
||||
</p>
|
||||
<a class="a-link-button tooltip" id="location-slums-shoplift"> Shoplift </a>
|
||||
<a id="location-slums-rob-store" class="a-link-button tooltip"> Rob a store </a>
|
||||
<a id="location-slums-mug" class="a-link-button tooltip"> Mug someone </a>
|
||||
<a id="location-slums-larceny" class="a-link-button tooltip"> Commit Larceny </a>
|
||||
<a id="location-slums-deal-drugs" class="a-link-button tooltip"> Deal Drugs </a>
|
||||
<a id="location-slums-bond-forgery" class="a-link-button tooltip">Bond Forgery</a>
|
||||
<a id="location-slums-traffic-arms" class="a-link-button tooltip">Traffick Illegal Arms</a>
|
||||
<a id="location-slums-homicide" class="a-link-button tooltip">Homicide</a>
|
||||
<a id="location-slums-gta" class="a-link-button tooltip"> Grand Theft Auto </a>
|
||||
<a id="location-slums-kidnap" class="a-link-button tooltip"> Kidnap and Ransom </a>
|
||||
<a id="location-slums-assassinate" class="a-link-button tooltip"> Assassinate </a>
|
||||
<a id="location-slums-heist" class="a-link-button tooltip"> Heist </a>
|
||||
|
||||
<!-- City Hall -->
|
||||
<a id="location-cityhall-create-corporation" class="a-link-button">Create a Corporation</a>
|
||||
|
||||
<!-- Bladeburner @ NSA -->
|
||||
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
|
||||
|
||||
<!-- Re-sleeving @ VitaLife -->
|
||||
<a id="location-vitalife-resleeve" class="a-link-button">Re-Sleeve</a>
|
||||
</div>
|
||||
|
||||
<div id="infiltration-container" class="generic-menupage-container">
|
||||
|
||||
1090
package-lock.json
generated
1090
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@
|
||||
"istanbul": "^0.4.5",
|
||||
"js-beautify": "^1.5.10",
|
||||
"json5": "^1.0.1",
|
||||
"less": "^3.0.4",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^4.1.0",
|
||||
"lodash": "^4.17.10",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
@@ -79,7 +79,7 @@
|
||||
"stylelint": "^9.2.1",
|
||||
"stylelint-declaration-use-variable": "^1.6.1",
|
||||
"stylelint-order": "^0.8.1",
|
||||
"ts-loader": "^4.4.1",
|
||||
"ts-loader": "^4.5.0",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.2",
|
||||
"uglify-es": "^3.3.9",
|
||||
@@ -89,7 +89,7 @@
|
||||
"webpack": "^4.12.0",
|
||||
"webpack-cli": "^3.0.4",
|
||||
"webpack-dev-middleware": "^3.1.3",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import { IMap } from "./types";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
|
||||
let Aliases = {};
|
||||
let GlobalAliases = {};
|
||||
export let Aliases: IMap<string> = {};
|
||||
export let GlobalAliases: IMap<string> = {};
|
||||
|
||||
function loadAliases(saveString) {
|
||||
export function loadAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
Aliases = {};
|
||||
} else {
|
||||
@@ -11,7 +12,7 @@ function loadAliases(saveString) {
|
||||
}
|
||||
}
|
||||
|
||||
function loadGlobalAliases(saveString) {
|
||||
export function loadGlobalAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
GlobalAliases = {};
|
||||
} else {
|
||||
@@ -20,7 +21,7 @@ function loadGlobalAliases(saveString) {
|
||||
}
|
||||
|
||||
//Print all aliases to terminal
|
||||
function printAliases() {
|
||||
export function printAliases(): void {
|
||||
for (var name in Aliases) {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
post("alias " + name + "=" + Aliases[name]);
|
||||
@@ -34,7 +35,7 @@ function printAliases() {
|
||||
}
|
||||
|
||||
//True if successful, false otherwise
|
||||
function parseAliasDeclaration(dec,global=false) {
|
||||
export function parseAliasDeclaration(dec: string, global: boolean=false) {
|
||||
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
||||
var matches = dec.match(re);
|
||||
if (matches == null || matches.length != 3) {return false;}
|
||||
@@ -46,50 +47,53 @@ function parseAliasDeclaration(dec,global=false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function addAlias(name, value) {
|
||||
if (name in GlobalAliases){
|
||||
function addAlias(name: string, value: string): void {
|
||||
if (name in GlobalAliases) {
|
||||
delete GlobalAliases[name];
|
||||
}
|
||||
Aliases[name] = value;
|
||||
}
|
||||
|
||||
function addGlobalAlias(name, value) {
|
||||
function addGlobalAlias(name: string, value: string): void {
|
||||
if (name in Aliases){
|
||||
delete Aliases[name];
|
||||
}
|
||||
GlobalAliases[name] = value;
|
||||
}
|
||||
|
||||
function getAlias(name) {
|
||||
function getAlias(name: string): string | null {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
return Aliases[name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGlobalAlias(name) {
|
||||
function getGlobalAlias(name: string): string | null {
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
return GlobalAliases[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeAlias(name) {
|
||||
export function removeAlias(name: string): boolean {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
delete Aliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
delete GlobalAliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Returns the original string with any aliases substituted in
|
||||
//Aliases only applied to "whole words", one level deep
|
||||
function substituteAliases(origCommand) {
|
||||
var commandArray = origCommand.split(" ");
|
||||
export function substituteAliases(origCommand: string): string {
|
||||
const commandArray = origCommand.split(" ");
|
||||
if (commandArray.length > 0){
|
||||
// For the unalias command, dont substite
|
||||
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
||||
@@ -112,6 +116,3 @@ function substituteAliases(origCommand) {
|
||||
}
|
||||
return commandArray.join(" ");
|
||||
}
|
||||
|
||||
export {Aliases, GlobalAliases, printAliases, parseAliasDeclaration,
|
||||
removeAlias, substituteAliases, loadAliases, loadGlobalAliases};
|
||||
@@ -2069,7 +2069,7 @@ function installAugmentations(cbScript=null) {
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, []); //No args
|
||||
runningScriptObj.threads = 1; //Only 1 thread
|
||||
home.runningScripts.push(runningScriptObj);
|
||||
home.runScript(runningScriptObj, Player);
|
||||
addWorkerScript(runningScriptObj, home);
|
||||
}
|
||||
}
|
||||
@@ -2097,12 +2097,6 @@ function displayAugmentationsContent(contentEl) {
|
||||
innerText:"Purchased Augmentations",
|
||||
}));
|
||||
|
||||
//Bladeburner text, once mechanic is unlocked
|
||||
var bladeburnerText = "\n";
|
||||
if (Player.bitNodeN === 6 || hasBladeburnerSF) {
|
||||
bladeburnerText = "Bladeburner Progress\n\n";
|
||||
}
|
||||
|
||||
contentEl.appendChild(createElement("pre", {
|
||||
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" +
|
||||
@@ -2114,7 +2108,6 @@ function displayAugmentationsContent(contentEl) {
|
||||
"Hacknet Nodes\n" +
|
||||
"Faction/Company reputation\n" +
|
||||
"Stocks\n" +
|
||||
bladeburnerText +
|
||||
"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)."
|
||||
|
||||
@@ -173,7 +173,24 @@ export function initBitNodes() {
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Do Androids Dream?", "COMING SOON");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"You cannnot purchase additional servers<br>" +
|
||||
"Hacking is significantly less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||
@@ -183,7 +200,7 @@ export function initBitNodes() {
|
||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased.<br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||
@@ -198,8 +215,9 @@ export function initBitNodes() {
|
||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking stat and experience gain are halved<br>" +
|
||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||
"The growth rate of servers is halved<br>" +
|
||||
"The growth rate of servers is significantly reduced<br>" +
|
||||
"Weakening a server is twice as effective<br>" +
|
||||
"Company wages are decreased by 50%<br>" +
|
||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||
@@ -210,9 +228,9 @@ export function initBitNodes() {
|
||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%");
|
||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||
"To iterate is human, to recurse divine.<br><br>" +
|
||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||
@@ -245,9 +263,9 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
}
|
||||
|
||||
switch (p.bitNodeN) {
|
||||
case 1: //Source Genesis (every multiplier is 1)
|
||||
case 1: // Source Genesis (every multiplier is 1)
|
||||
break;
|
||||
case 2: //Rise of the Underworld
|
||||
case 2: // Rise of the Underworld
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.8;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.2;
|
||||
@@ -257,7 +275,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
break;
|
||||
case 3: //Corporatocracy
|
||||
case 3: // Corporatocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
BitNodeMultipliers.RepToDonateToFaction = 0.5;
|
||||
BitNodeMultipliers.AugmentationRepCost = 3;
|
||||
@@ -272,7 +290,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||
break;
|
||||
case 4: //The Singularity
|
||||
case 4: // The Singularity
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
||||
@@ -286,7 +304,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||
break;
|
||||
case 5: //Artificial intelligence
|
||||
case 5: // Artificial intelligence
|
||||
BitNodeMultipliers.ServerMaxMoney = 2;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@@ -299,7 +317,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
break;
|
||||
case 6: //Bladeburner
|
||||
case 6: // Bladeburner
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.4;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.5;
|
||||
@@ -314,7 +332,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.HackExpGain = 0.25;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 7: //Bladeburner 2079
|
||||
case 7: // Bladeburner 2079
|
||||
BitNodeMultipliers.BladeburnerRank = 0.6;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 3;
|
||||
@@ -334,7 +352,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
break;
|
||||
case 8: //Ghost of Wall Street
|
||||
case 8: // Ghost of Wall Street
|
||||
BitNodeMultipliers.ScriptHackMoney = 0;
|
||||
BitNodeMultipliers.ManualHackMoney = 0;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0;
|
||||
@@ -345,6 +363,27 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 5;
|
||||
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.1;
|
||||
BitNodeMultipliers.HackExpGain = 0.05;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 5;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
||||
@@ -369,9 +408,11 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.5;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.2;
|
||||
BitNodeMultipliers.ServerWeakenRate = 2;
|
||||
BitNodeMultipliers.CrimeMoney = 3;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
||||
@@ -379,8 +420,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||
BitNodeMultipliers.InfiltrationMoney = 2.5;
|
||||
BitNodeMultipliers.InfiltrationRep = 2.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.01;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.1;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.25;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
break;
|
||||
|
||||
@@ -120,7 +120,8 @@ interface IBitNodeMultipliers {
|
||||
HackingLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences how much money each Hacknet node can generate.
|
||||
* Influences how much money is produced by Hacknet Nodes.
|
||||
* Influeces the hash rate of Hacknet Servers (unlocked in BitNode-9)
|
||||
*/
|
||||
HacknetNodeMoney: number;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Engine } from "./engine";
|
||||
import { Faction } from "./Faction/Faction";
|
||||
import { Factions, factionExists } from "./Faction/Factions";
|
||||
import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers";
|
||||
import { Locations } from "./Locations";
|
||||
import { Player } from "./Player";
|
||||
import { hackWorldDaemon, redPillFlag } from "./RedPill";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
@@ -78,8 +77,8 @@ const RanksPerSkillPoint = 3; //How many ranks needed to get 1 Skill
|
||||
|
||||
const ContractBaseMoneyGain = 250e3; //Base Money Gained per contract
|
||||
|
||||
const HrcHpGain = 2; // HP gained from Hyperbolic Regeneration Chamber
|
||||
const HrcStaminaGain = 0.1; // Stamina gained from Hyperbolic Regeneration Chamber
|
||||
const HrcHpGain = 2; // HP Gained from Hyperbolic Regeneration chamber
|
||||
const HrcStaminaGain = 1; // Percentage Stamina gained from Hyperbolic Regeneration Chamber
|
||||
|
||||
//DOM related variables
|
||||
var ActiveActionCssClass = "bladeburner-active-action";
|
||||
@@ -218,7 +217,7 @@ $(document).keydown(function(event) {
|
||||
});
|
||||
|
||||
function City(params={}) {
|
||||
this.name = params.name ? params.name : Locations.Sector12;
|
||||
this.name = params.name ? params.name : CityNames[2]; // Sector-12
|
||||
|
||||
//Synthoid population and estimate
|
||||
this.pop = params.pop ? params.pop : getRandomInt(PopulationThreshold, 1.5 * PopulationThreshold);
|
||||
@@ -690,7 +689,7 @@ function Bladeburner(params={}) {
|
||||
for (var i = 0; i < CityNames.length; ++i) {
|
||||
this.cities[CityNames[i]] = new City({name:CityNames[i]});
|
||||
}
|
||||
this.city = Locations.Sector12;
|
||||
this.city = CityNames[2]; // Sector-12
|
||||
|
||||
//Map of SkillNames -> level
|
||||
this.skills = {};
|
||||
@@ -1417,14 +1416,17 @@ Bladeburner.prototype.completeAction = function() {
|
||||
}
|
||||
this.startAction(this.action); // Repeat Action
|
||||
break;
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]:
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]: {
|
||||
Player.regenerateHp(HrcHpGain);
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + HrcStaminaGain);
|
||||
|
||||
const staminaGain = this.maxStamina * (HrcStaminaGain / 100);
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
|
||||
this.startAction(this.action);
|
||||
if (this.logging.general) {
|
||||
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${HrcHpGain} HP and gained ${HrcStaminaGain} stamina`);
|
||||
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${HrcHpGain} HP and gained ${numeralWrapper.format(staminaGain, "0.0")} stamina`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
|
||||
break;
|
||||
@@ -2513,7 +2515,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
|
||||
display:"inline-block",
|
||||
innerHTML:action.desc + "\n\n" +
|
||||
`Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
|
||||
|
||||
|
||||
"Time Required (s): " + formatNumber(actionTime, 0) + "\n" +
|
||||
"Contracts remaining: " + Math.floor(action.count) + "\n" +
|
||||
"Successes: " + action.successes + "\n" +
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { CompanyPosition } from "./CompanyPosition";
|
||||
import * as posNames from "./data/companypositionnames";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export interface IConstructorParams {
|
||||
name: string;
|
||||
info: string;
|
||||
@@ -93,6 +96,43 @@ export class Company {
|
||||
}
|
||||
}
|
||||
|
||||
hasAgentPositions(): boolean {
|
||||
return (this.companyPositions[posNames.AgentCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasBusinessConsultantPositions(): boolean {
|
||||
return (this.companyPositions[posNames.BusinessConsultantCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasBusinessPositions(): boolean {
|
||||
return (this.companyPositions[posNames.BusinessCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasEmployeePositions(): boolean {
|
||||
return (this.companyPositions[posNames.MiscCompanyPositions[1]] != null);
|
||||
}
|
||||
|
||||
hasITPositions(): boolean {
|
||||
return (this.companyPositions[posNames.ITCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasSecurityPositions(): boolean {
|
||||
return (this.companyPositions[posNames.SecurityCompanyPositions[2]] != null);
|
||||
}
|
||||
|
||||
hasSoftwareConsultantPositions(): boolean {
|
||||
return (this.companyPositions[posNames.SoftwareConsultantCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasSoftwarePositions(): boolean {
|
||||
return (this.companyPositions[posNames.SoftwareCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
hasWaiterPositions(): boolean {
|
||||
return (this.companyPositions[posNames.MiscCompanyPositions[0]] != null);
|
||||
}
|
||||
|
||||
|
||||
gainFavor(): void {
|
||||
if (this.favor == null) { this.favor = 0; }
|
||||
if (this.rolloverRep == null) { this.rolloverRep = 0; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import * as names from "./data/CompanyPositionNames";
|
||||
import * as names from "./data/companypositionnames";
|
||||
|
||||
/* tslint:disable:completed-docs */
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { CompanyPosition } from "./CompanyPosition";
|
||||
import { CompanyPositions } from "./CompanyPositions";
|
||||
|
||||
export function getNextCompanyPosition(currPos: CompanyPosition | null): CompanyPosition | null {
|
||||
export function getNextCompanyPositionHelper(currPos: CompanyPosition | null): CompanyPosition | null {
|
||||
if (currPos == null) { return null; }
|
||||
|
||||
const nextPosName: string | null = currPos.nextPosition;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IConstructorParams } from "../Company";
|
||||
import { Locations } from "../../Locations";
|
||||
import * as posNames from "./CompanyPositionNames";
|
||||
import { IMap } from "../../types";
|
||||
import * as posNames from "./companypositionnames";
|
||||
import { IConstructorParams } from "../Company";
|
||||
|
||||
import { IMap } from "../../types";
|
||||
import { LocationName } from "../../Locations/data/LocationNames";
|
||||
|
||||
// Create Objects containing Company Positions by category
|
||||
// Will help in metadata construction later
|
||||
@@ -89,7 +90,7 @@ CEOOnly[posNames.BusinessCompanyPositions[5]] = true;
|
||||
// Metadata
|
||||
export const companiesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
name: Locations.AevumECorp,
|
||||
name: LocationName.AevumECorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -101,7 +102,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12MegaCorp,
|
||||
name: LocationName.Sector12MegaCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -113,7 +114,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumBachmanAndAssociates,
|
||||
name: LocationName.AevumBachmanAndAssociates,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -125,7 +126,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12BladeIndustries,
|
||||
name: LocationName.Sector12BladeIndustries,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -137,7 +138,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenNWO,
|
||||
name: LocationName.VolhavenNWO,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -149,7 +150,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 249,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumClarkeIncorporated,
|
||||
name: LocationName.AevumClarkeIncorporated,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -161,7 +162,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenOmniTekIncorporated,
|
||||
name: LocationName.VolhavenOmniTekIncorporated,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -173,7 +174,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12FourSigma,
|
||||
name: LocationName.Sector12FourSigma,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -185,7 +186,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.ChongqingKuaiGongInternational,
|
||||
name: LocationName.ChongqingKuaiGongInternational,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -197,7 +198,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumFulcrumTechnologies,
|
||||
name: LocationName.AevumFulcrumTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -208,7 +209,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaStormTechnologies,
|
||||
name: LocationName.IshimaStormTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -220,7 +221,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoDefComm,
|
||||
name: LocationName.NewTokyoDefComm,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -232,7 +233,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenHeliosLabs,
|
||||
name: LocationName.VolhavenHeliosLabs,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -244,7 +245,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoVitaLife,
|
||||
name: LocationName.NewTokyoVitaLife,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -256,7 +257,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12IcarusMicrosystems,
|
||||
name: LocationName.Sector12IcarusMicrosystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -268,7 +269,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12UniversalEnergy,
|
||||
name: LocationName.Sector12UniversalEnergy,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -280,7 +281,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumGalacticCybersystems,
|
||||
name: LocationName.AevumGalacticCybersystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -292,7 +293,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumAeroCorp,
|
||||
name: LocationName.AevumAeroCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -305,7 +306,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenOmniaCybersystems,
|
||||
name: LocationName.VolhavenOmniaCybersystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -318,7 +319,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.ChongqingSolarisSpaceSystems,
|
||||
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -331,7 +332,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12DeltaOne,
|
||||
name: LocationName.Sector12DeltaOne,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
CEOOnly,
|
||||
@@ -344,7 +345,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoGlobalPharmaceuticals,
|
||||
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -357,7 +358,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 224,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaNovaMedical,
|
||||
name: LocationName.IshimaNovaMedical,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -370,7 +371,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 199,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12CIA,
|
||||
name: LocationName.Sector12CIA,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -385,7 +386,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 149,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12NSA,
|
||||
name: LocationName.Sector12NSA,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -400,7 +401,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 149,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumWatchdogSecurity,
|
||||
name: LocationName.AevumWatchdogSecurity,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
@@ -415,7 +416,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 124,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenLexoCorp,
|
||||
name: LocationName.VolhavenLexoCorp,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -428,7 +429,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumRhoConstruction,
|
||||
name: LocationName.AevumRhoConstruction,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToLeadDeveloper,
|
||||
@@ -439,7 +440,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 49,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12AlphaEnterprises,
|
||||
name: LocationName.Sector12AlphaEnterprises,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToLeadDeveloper,
|
||||
@@ -451,7 +452,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumPolice,
|
||||
name: LocationName.AevumPolice,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllSecurityPositions,
|
||||
@@ -462,7 +463,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenSysCoreSecurities,
|
||||
name: LocationName.VolhavenSysCoreSecurities,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -472,7 +473,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 124,
|
||||
},
|
||||
{
|
||||
name: Locations.VolhavenCompuTek,
|
||||
name: LocationName.VolhavenCompuTek,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -482,7 +483,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 74,
|
||||
},
|
||||
{
|
||||
name: Locations.AevumNetLinkTechnologies,
|
||||
name: LocationName.AevumNetLinkTechnologies,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions
|
||||
@@ -492,7 +493,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 99,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12CarmichaelSecurity,
|
||||
name: LocationName.Sector12CarmichaelSecurity,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
@@ -505,7 +506,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 74,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12FoodNStuff,
|
||||
name: LocationName.Sector12FoodNStuff,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
EmployeeOnly, PartTimeEmployeeOnly
|
||||
@@ -515,7 +516,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 0,
|
||||
},
|
||||
{
|
||||
name: Locations.Sector12JoesGuns,
|
||||
name: LocationName.Sector12JoesGuns,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
EmployeeOnly, PartTimeEmployeeOnly
|
||||
@@ -525,7 +526,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 0,
|
||||
},
|
||||
{
|
||||
name: Locations.IshimaOmegaSoftware,
|
||||
name: LocationName.IshimaOmegaSoftware,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllSoftwarePositions,
|
||||
@@ -537,7 +538,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
jobStatReqOffset: 49,
|
||||
},
|
||||
{
|
||||
name: Locations.NewTokyoNoodleBar,
|
||||
name: LocationName.NewTokyoNoodleBar,
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
WaiterOnly, PartTimeWaiterOnly
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Metadata used for constructing Company Positions
|
||||
import { IConstructorParams } from "../CompanyPosition";
|
||||
import * as posNames from "./CompanyPositionNames";
|
||||
import * as posNames from "./companypositionnames";
|
||||
|
||||
export const companyPositionMetadata: IConstructorParams[] = [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
/**
|
||||
* Generic Game Constants
|
||||
*
|
||||
* Constants for specific mechanics or features will NOT be here.
|
||||
*/
|
||||
import {IMap} from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.45.1",
|
||||
Version: "0.46.1",
|
||||
|
||||
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||
@@ -17,24 +22,9 @@ export let CONSTANTS: IMap<any> = {
|
||||
/* Base costs */
|
||||
BaseCostFor1GBOfRamHome: 32000,
|
||||
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
||||
BaseCostFor1GBOfRamHacknetNode: 30000,
|
||||
|
||||
TravelCost: 200e3,
|
||||
|
||||
BaseCostForHacknetNode: 1000,
|
||||
BaseCostForHacknetNodeCore: 500000,
|
||||
|
||||
/* Hacknet Node constants */
|
||||
HacknetNodeMoneyGainPerLevel: 1.6,
|
||||
HacknetNodePurchaseNextMult: 1.85, //Multiplier when purchasing an additional hacknet node
|
||||
HacknetNodeUpgradeLevelMult: 1.04, //Multiplier for cost when upgrading level
|
||||
HacknetNodeUpgradeRamMult: 1.28, //Multiplier for cost when upgrading RAM
|
||||
HacknetNodeUpgradeCoreMult: 1.48, //Multiplier for cost when buying another core
|
||||
|
||||
HacknetNodeMaxLevel: 200,
|
||||
HacknetNodeMaxRam: 64,
|
||||
HacknetNodeMaxCores: 16,
|
||||
|
||||
/* Faction and Company favor */
|
||||
BaseFavorToDonate: 150,
|
||||
DonateMoneyToRepDivisor: 1e6,
|
||||
@@ -126,6 +116,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
|
||||
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
|
||||
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
|
||||
InfiltrationExpPow: 0.8,
|
||||
|
||||
//Stock market constants
|
||||
WSEAccountCost: 200e6,
|
||||
@@ -282,37 +273,16 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.45.1
|
||||
* Added two new Corporation Researches
|
||||
* General UI improvements (by hydroflame and koriar)
|
||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
||||
v0.46.1
|
||||
* Added a very rudimentary directory system to the Terminal
|
||||
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
|
||||
|
||||
v0.45.0
|
||||
* Corporation changes:
|
||||
** Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
||||
** This means that each Corporation 'state' will now only take 2 seconds, rather than 3
|
||||
** Increased initial salaries for newly-hired employees
|
||||
** Increased the cost multiplier for upgrading office size (the cost will increase faster)
|
||||
** The stats of your employees now has a slightly larger effect on production & sales
|
||||
** Added several new Research upgrades
|
||||
** Market-TA research now allows you to automatically set sale price at optimal values
|
||||
** Market-TA research now works for Products (not just Materials)
|
||||
** Reduced the amount of Scientific Research needed to unlock the Hi-Tech R&D Laboratory from 10k to 5k
|
||||
** Energy Material requirement of the Software industry reduced from 1 to 0.5
|
||||
** It is now slightly easier to increase the Software industry's production multiplier
|
||||
** Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
|
||||
** You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
|
||||
** Significantly changed the effects of the different employee positions. See updated descriptions
|
||||
** Reduced the amount of money you gain from private investors
|
||||
** Training employees is now 3x more effective
|
||||
** Bug Fix: An industry's products are now properly separated between different cities
|
||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
||||
* Modified the multipliers of BitNode-3 and BitNode-8 to make them slightly harder
|
||||
* After installing Augmentations, Duplicate Sleeves will now default to Synchronize if their Shock is 0
|
||||
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
|
||||
* Bug Fix: growthAnalyze() function now properly accounts for BitNode multipliers
|
||||
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
|
||||
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
|
||||
* 'Generate Coding Contract' hash upgrade is now more expensive
|
||||
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
|
||||
* The cost of selling hashes for money no longer increases each time
|
||||
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
|
||||
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
|
||||
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
|
||||
`
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import { BitNodeMultipliers } from "../BitNode/BitNode
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { showLiterature } from "../Literature";
|
||||
import { Locations } from "../Locations";
|
||||
import { createCityMap } from "../Locations/Cities";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { Player } from "../Player";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
@@ -113,15 +113,15 @@ $(document).mousedown(function(event) {
|
||||
var empManualAssignmentModeActive = false;
|
||||
function Industry(params={}) {
|
||||
this.offices = { //Maps locations to offices. 0 if no office at that location
|
||||
[Locations.Aevum]: 0,
|
||||
[Locations.Chongqing]: 0,
|
||||
[Locations.Sector12]: new OfficeSpace({
|
||||
loc:Locations.Sector12,
|
||||
[CityName.Aevum]: 0,
|
||||
[CityName.Chongqing]: 0,
|
||||
[CityName.Sector12]: new OfficeSpace({
|
||||
loc:CityName.Sector12,
|
||||
size:OfficeInitialSize,
|
||||
}),
|
||||
[Locations.NewTokyo]: 0,
|
||||
[Locations.Ishima]: 0,
|
||||
[Locations.Volhaven]: 0
|
||||
[CityName.NewTokyo]: 0,
|
||||
[CityName.Ishima]: 0,
|
||||
[CityName.Volhaven]: 0
|
||||
};
|
||||
|
||||
this.name = params.name ? params.name : 0;
|
||||
@@ -172,17 +172,17 @@ function Industry(params={}) {
|
||||
this.newInd = true;
|
||||
|
||||
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
|
||||
[Locations.Aevum]: 0,
|
||||
[Locations.Chonqing]: 0,
|
||||
[Locations.Sector12]: new Warehouse({
|
||||
[CityName.Aevum]: 0,
|
||||
[CityName.Chonqing]: 0,
|
||||
[CityName.Sector12]: new Warehouse({
|
||||
corp: params.corp,
|
||||
industry: this,
|
||||
loc: Locations.Sector12,
|
||||
loc: CityName.Sector12,
|
||||
size: WarehouseInitialSize,
|
||||
}),
|
||||
[Locations.NewTokyo]: 0,
|
||||
[Locations.Ishima]: 0,
|
||||
[Locations.Volhaven]: 0
|
||||
[CityName.NewTokyo]: 0,
|
||||
[CityName.Ishima]: 0,
|
||||
[CityName.Volhaven]: 0
|
||||
};
|
||||
|
||||
this.init();
|
||||
@@ -563,13 +563,15 @@ Industry.prototype.processMaterialMarket = function(marketCycles=1) {
|
||||
}
|
||||
}
|
||||
|
||||
//Process change in demand and competition for this industry's products
|
||||
// Process change in demand and competition for this industry's products
|
||||
Industry.prototype.processProductMarket = function(marketCycles=1) {
|
||||
//Demand gradually decreases, and competition gradually increases
|
||||
for (var name in this.products) {
|
||||
// Demand gradually decreases, and competition gradually increases
|
||||
for (const name in this.products) {
|
||||
if (this.products.hasOwnProperty(name)) {
|
||||
var product = this.products[name];
|
||||
var change = getRandomInt(1, 3) * 0.0004;
|
||||
const product = this.products[name];
|
||||
let change = getRandomInt(0, 3) * 0.0004;
|
||||
if (change === 0) { continue; }
|
||||
|
||||
if (this.type === Industries.Pharmaceutical || this.type === Industries.Software ||
|
||||
this.type === Industries.Robotics) {
|
||||
change *= 3;
|
||||
@@ -770,7 +772,17 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
||||
* advertisingFactor
|
||||
* this.getSalesMultiplier());
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
const optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = mat.bCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
}
|
||||
|
||||
// We'll store this "Optimal Price" in a property so that we don't have
|
||||
// to re-calculate it for the UI
|
||||
@@ -1089,7 +1101,12 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
optimalPrice = 0;
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = product.pCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + product.pCost;
|
||||
}
|
||||
@@ -1251,7 +1268,7 @@ Industry.prototype.getAdvertisingFactors = function() {
|
||||
|
||||
//Returns a multiplier based on a materials demand and competition that affects sales
|
||||
Industry.prototype.getMarketFactor = function(mat) {
|
||||
return mat.dmd * (100 - mat.cmp)/100;
|
||||
return Math.max(0.1, mat.dmd * (100 - mat.cmp) / 100);
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether this Industry has the specified Research
|
||||
|
||||
@@ -86,7 +86,7 @@ export class Material {
|
||||
this.mku = 6;
|
||||
break;
|
||||
case "Energy":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 80; this.cmpR = [65, 95];
|
||||
this.bCost = 2000; this.mv = 0.2;
|
||||
this.mku = 6;
|
||||
@@ -122,26 +122,26 @@ export class Material {
|
||||
this.mku = 2;
|
||||
break;
|
||||
case "Real Estate":
|
||||
this.dmd = 50; this.dmdR = [5, 100];
|
||||
this.dmd = 50; this.dmdR = [5, 99];
|
||||
this.cmp = 50; this.cmpR = [25, 75];
|
||||
this.bCost = 80e3; this.mv = 1.5; //Less mv bc its processed twice
|
||||
this.mku = 1.5;
|
||||
break;
|
||||
case "Drugs":
|
||||
this.dmd = 60; this.dmdR = [45, 75];
|
||||
this.cmp = 70; this.cmpR = [40, 100];
|
||||
this.cmp = 70; this.cmpR = [40, 99];
|
||||
this.bCost = 40e3; this.mv = 1.6;
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "Robots":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 9];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 75e3; this.mv = 0.5; //Less mv bc its processed twice
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "AI Cores":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 15e3; this.mv = 0.8; //Less mv bc its processed twice
|
||||
this.mku = 0.5;
|
||||
break;
|
||||
|
||||
@@ -3,11 +3,9 @@ import { MaterialSizes } from "./MaterialSizes";
|
||||
import { ProductRatingWeights,
|
||||
IProductRatingWeight } from "./ProductRatingWeights";
|
||||
|
||||
import { Cities } from "../Locations/Cities";
|
||||
import { createCityMap } from "../Locations/createCityMap";
|
||||
import { IMap } from "../types";
|
||||
|
||||
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
@@ -31,6 +31,7 @@ import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
@@ -780,7 +781,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
@@ -1052,7 +1058,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
@@ -1406,7 +1417,7 @@ export class CorporationEventHandler {
|
||||
}
|
||||
|
||||
// Array of all cities. Used later
|
||||
const cities = Object.values(Cities);
|
||||
const cities = Object.keys(Cities);
|
||||
|
||||
// Parse quantity
|
||||
if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
|
||||
|
||||
@@ -300,8 +300,8 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
<br />
|
||||
|
||||
<p>Avg Employee Morale: {numeralWrapper.format(avgMorale, "0.000")}</p>
|
||||
<p>Avg Happiness Morale: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Energy Morale: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Avg Employee Happiness: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Employee Energy: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
||||
{
|
||||
vechain &&
|
||||
|
||||
@@ -218,7 +218,7 @@ function MaterialComponent(props) {
|
||||
mat.buy === 0 && mat.imp === 0;
|
||||
|
||||
// Purchase material button
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
|
||||
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
||||
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
||||
|
||||
@@ -229,9 +229,9 @@ function MaterialComponent(props) {
|
||||
let sellButtonText;
|
||||
if (mat.sllman[0]) {
|
||||
if (isString(mat.sllman[1])) {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${mat.sllman[1]})`
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${mat.sllman[1]})`
|
||||
} else {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${numeralWrapper.format(mat.sllman[1], nfB)})`;
|
||||
}
|
||||
|
||||
if (mat.marketTa2) {
|
||||
@@ -469,7 +469,7 @@ export class IndustryWarehouse extends BaseReactComponent {
|
||||
return (
|
||||
<div className={"cmpy-mgmt-warehouse-panel"}>
|
||||
<p className={"tooltip"} style={sizeUsageStyle}>
|
||||
Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")}
|
||||
Storage: {numeralWrapper.formatBigNumber(warehouse.sizeUsed)} / {numeralWrapper.formatBigNumber(warehouse.size)}
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { overviewPage } from "./Routing";
|
||||
|
||||
import { OfficeSpace } from "../Corporation";
|
||||
|
||||
import { Cities } from "../../Locations/Cities";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
|
||||
export class MainPanel extends BaseReactComponent {
|
||||
constructor(props) {
|
||||
@@ -19,13 +19,13 @@ export class MainPanel extends BaseReactComponent {
|
||||
|
||||
this.state = {
|
||||
division: "",
|
||||
city: Cities.Sector12,
|
||||
city: CityName.Sector12,
|
||||
}
|
||||
}
|
||||
|
||||
// We can pass this setter to child components
|
||||
changeCityState(newCity) {
|
||||
if (Object.values(Cities).includes(newCity)) {
|
||||
if (Object.values(CityName).includes(newCity)) {
|
||||
this.state.city = newCity;
|
||||
} else {
|
||||
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
|
||||
@@ -45,7 +45,7 @@ export class MainPanel extends BaseReactComponent {
|
||||
const currentDivision = this.routing().current();
|
||||
if (currentDivision !== this.state.division) {
|
||||
this.state.division = currentDivision;
|
||||
this.state.city = Cities.Sector12;
|
||||
this.state.city = CityName.Sector12;
|
||||
}
|
||||
|
||||
return this.renderDivisionPage();
|
||||
|
||||
@@ -54,7 +54,7 @@ export class Overview extends BaseReactComponent {
|
||||
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
||||
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
||||
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br>`;
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
|
||||
}
|
||||
|
||||
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
|
||||
|
||||
@@ -7,14 +7,13 @@ import { Engine } from "../engine";
|
||||
import { Faction } from "./Faction";
|
||||
import { Factions } from "./Factions";
|
||||
import { FactionInfos } from "./FactionInfo";
|
||||
import { Locations} from "../Location";
|
||||
import { HackingMission, setInMission } from "../Missions";
|
||||
import { Player } from "../Player";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
|
||||
import { createPurchaseSleevesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
import { createSleevePurchasesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
|
||||
import {Page, routing} from "../ui/navigationTracking";
|
||||
import {numeralWrapper} from "../ui/numeralFormat";
|
||||
@@ -348,7 +347,7 @@ function displayFactionContent(factionName) {
|
||||
class: "std-button",
|
||||
innerText: "Purchase Duplicate Sleeves",
|
||||
clickListener: () => {
|
||||
createPurchaseSleevesFromCovenantPopup(Player);
|
||||
createSleevePurchasesFromCovenantPopup(Player);
|
||||
}
|
||||
}));
|
||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
|
||||
|
||||
1
src/Hacking/README.md
Normal file
1
src/Hacking/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Implementation of underlying Hacking mechanics
|
||||
53
src/Hacking/netscriptCanHack.ts
Normal file
53
src/Hacking/netscriptCanHack.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Functions used to determine whether the target can be hacked (or grown/weakened).
|
||||
* Meant to be used for Netscript implementation
|
||||
*
|
||||
* The returned status object's message should be used for logging in Netscript
|
||||
*/
|
||||
import { IReturnStatus } from "../types";
|
||||
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Server } from "../Server/Server";
|
||||
|
||||
function baseCheck(server: Server | HacknetServer, fnName: string): IReturnStatus {
|
||||
if (server instanceof HacknetServer) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot ${fnName} ${server.hostname} server because it is a Hacknet Node`
|
||||
}
|
||||
}
|
||||
|
||||
if (server.hasAdminRights === false) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot ${fnName} ${server.hostname} server because you do not have root access`,
|
||||
}
|
||||
}
|
||||
|
||||
return { res: true }
|
||||
}
|
||||
|
||||
export function netscriptCanHack(server: Server | HacknetServer, p: IPlayer): IReturnStatus {
|
||||
const initialCheck = baseCheck(server, "hack");
|
||||
if (!initialCheck.res) { return initialCheck; }
|
||||
|
||||
let s = <Server>server;
|
||||
|
||||
if (s.requiredHackingSkill > p.hacking_skill) {
|
||||
return {
|
||||
res: false,
|
||||
msg: `Cannot hack ${server.hostname} server because your hacking skill is not high enough`,
|
||||
}
|
||||
}
|
||||
|
||||
return { res: true }
|
||||
}
|
||||
|
||||
export function netscriptCanGrow(server: Server | HacknetServer): IReturnStatus {
|
||||
return baseCheck(server, "grow");
|
||||
}
|
||||
|
||||
export function netscriptCanWeaken(server: Server | HacknetServer): IReturnStatus {
|
||||
return baseCheck(server, "weaken");
|
||||
}
|
||||
418
src/Hacknet/HacknetHelpers.jsx
Normal file
418
src/Hacknet/HacknetHelpers.jsx
Normal file
@@ -0,0 +1,418 @@
|
||||
import { HacknetNode,
|
||||
BaseCostForHacknetNode,
|
||||
HacknetNodePurchaseNextMult,
|
||||
HacknetNodeMaxLevel,
|
||||
HacknetNodeMaxRam,
|
||||
HacknetNodeMaxCores } from "./HacknetNode";
|
||||
import { HacknetServer,
|
||||
BaseCostForHacknetServer,
|
||||
HacknetServerPurchaseMult,
|
||||
HacknetServerMaxLevel,
|
||||
HacknetServerMaxRam,
|
||||
HacknetServerMaxCores,
|
||||
HacknetServerMaxCache,
|
||||
MaxNumberHacknetServers } from "./HacknetServer";
|
||||
import { HashManager } from "./HashManager";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { generateRandomContract } from "../CodingContractGenerator";
|
||||
import { iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "../InteractiveTutorial";
|
||||
import { Player } from "../Player";
|
||||
import { AddToAllServers,
|
||||
AllServers } from "../Server/AllServers";
|
||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
|
||||
import {getElementById} from "../../utils/uiHelpers/getElementById";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { HacknetRoot } from "./ui/Root";
|
||||
|
||||
let hacknetNodesDiv;
|
||||
function hacknetNodesInit() {
|
||||
hacknetNodesDiv = document.getElementById("hacknet-nodes-container");
|
||||
document.removeEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
|
||||
// Returns a boolean indicating whether the player has Hacknet Servers
|
||||
// (the upgraded form of Hacknet Nodes)
|
||||
export function hasHacknetServers() {
|
||||
return (Player.bitNodeN === 9 || SourceFileFlags[9] > 0);
|
||||
}
|
||||
|
||||
export function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* END INTERACTIVE TUTORIAL */
|
||||
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
if (hasHacknetServers()) {
|
||||
const cost = getCostOfNextHacknetServer();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetServer is NaN`)
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
Player.loseMoney(cost);
|
||||
const server = Player.createHacknetServer();
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
|
||||
return numOwned;
|
||||
} else {
|
||||
const cost = getCostOfNextHacknetNode();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error(`Calculated cost of purchasing HacknetNode is NaN`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
|
||||
// Auto generate a name for the Node
|
||||
const name = "hacknet-node-" + numOwned;
|
||||
const node = new HacknetNode(name);
|
||||
node.updateMoneyGainRate(Player);
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(node);
|
||||
|
||||
return numOwned;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasMaxNumberHacknetServers() {
|
||||
return hasHacknetServers() && Player.hacknetNodes.length >= MaxNumberHacknetServers;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetNode() {
|
||||
// Cost increases exponentially based on how many you own
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetNodePurchaseNextMult;
|
||||
|
||||
return BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
export function getCostOfNextHacknetServer() {
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const mult = HacknetServerPurchaseMult;
|
||||
|
||||
if (numOwned > MaxNumberHacknetServers) { return Infinity; }
|
||||
|
||||
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node
|
||||
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
let levelsToMax = maxLevel - nodeObj.level;
|
||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
while (min <= max) {
|
||||
var curr = (min + max) / 2 | 0;
|
||||
if (curr !== maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player)) &&
|
||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let levelsToMax;
|
||||
if (nodeObj instanceof HacknetServer) {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.maxRam));
|
||||
} else {
|
||||
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
|
||||
}
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//We'll just loop until we find the max
|
||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cores;
|
||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player)) &&
|
||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
|
||||
if (maxLevel == null) {
|
||||
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
|
||||
}
|
||||
|
||||
if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let min = 1;
|
||||
let max = maxLevel - 1;
|
||||
const levelsToMax = maxLevel - nodeObj.cache;
|
||||
if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
// Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != maxLevel &&
|
||||
Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) &&
|
||||
!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
max = curr -1 ;
|
||||
} else if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initial construction of Hacknet Nodes UI
|
||||
export function renderHacknetNodesUI() {
|
||||
if (!routing.isOn(Page.HacknetNodes)) { return; }
|
||||
|
||||
ReactDOM.render(<HacknetRoot />, hacknetNodesDiv);
|
||||
}
|
||||
|
||||
export function clearHacknetNodesUI() {
|
||||
if (hacknetNodesDiv instanceof HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(hacknetNodesDiv);
|
||||
}
|
||||
|
||||
hacknetNodesDiv.style.display = "none";
|
||||
}
|
||||
|
||||
export function processHacknetEarnings(numCycles) {
|
||||
// Determine if player has Hacknet Nodes or Hacknet Servers, then
|
||||
// call the appropriate function
|
||||
if (Player.hacknetNodes.length === 0) { return 0; }
|
||||
if (hasHacknetServers()) {
|
||||
return processAllHacknetServerEarnings(numCycles);
|
||||
} else if (Player.hacknetNodes[0] instanceof HacknetNode) {
|
||||
return processAllHacknetNodeEarnings(numCycles);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function processAllHacknetNodeEarnings(numCycles) {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function processSingleHacknetNodeEarnings(numCycles, nodeObj) {
|
||||
const totalEarnings = nodeObj.process(numCycles);
|
||||
Player.gainMoney(totalEarnings);
|
||||
Player.recordMoneySource(totalEarnings, "hacknetnode");
|
||||
|
||||
return totalEarnings;
|
||||
}
|
||||
|
||||
function processAllHacknetServerEarnings(numCycles) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`)
|
||||
}
|
||||
|
||||
let hashes = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
const hserver = AllServers[Player.hacknetNodes[i]]; // hacknetNodes array only contains the IP addresses
|
||||
hashes += hserver.process(numCycles);
|
||||
}
|
||||
|
||||
Player.hashManager.storeHashes(hashes);
|
||||
|
||||
return hashes;
|
||||
}
|
||||
|
||||
export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
console.error(`Player does not have a HashManager`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// HashManager handles the transaction. This just needs to actually implement
|
||||
// the effects of the upgrade
|
||||
if (Player.hashManager.upgrade(upgName)) {
|
||||
const upg = HashUpgrades[upgName];
|
||||
|
||||
switch (upgName) {
|
||||
case "Sell for Money": {
|
||||
Player.gainMoney(upg.value);
|
||||
break;
|
||||
}
|
||||
case "Sell for Corporation Funds": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
Player.corporation.funds = Player.corporation.funds.plus(upg.value);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Reduce Minimum Security": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMinimumSecurity(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Increase Maximum Money": {
|
||||
try {
|
||||
const target = GetServerByHostname(upgTarget);
|
||||
if (target == null) {
|
||||
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
target.changeMaximumMoney(upg.value, true);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Improve Studying": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Improve Gym Training": {
|
||||
// Multiplier handled by HashManager
|
||||
break;
|
||||
}
|
||||
case "Exchange for Corporation Research": {
|
||||
// This will throw if player doesn't have a corporation
|
||||
try {
|
||||
for (const division of Player.corporation.divisions) {
|
||||
division.sciResearch.qty += upg.value;
|
||||
}
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Exchange for Bladeburner Rank": {
|
||||
// This will throw if player isnt in Bladeburner
|
||||
try {
|
||||
Player.bladeburner.changeRank(upg.value);
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Exchange for Bladeburner SP": {
|
||||
// This will throw if player isn't in Bladeburner
|
||||
try {
|
||||
// As long as we don't change `Bladeburner.totalSkillPoints`, this
|
||||
// shouldn't affect anything else
|
||||
Player.bladeburner.skillPoints += upg.value;
|
||||
} catch(e) {
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Generate Coding Contract": {
|
||||
generateRandomContract();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`)
|
||||
Player.hashManager.refundUpgrade(upgName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
285
src/Hacknet/HacknetNode.ts
Normal file
285
src/Hacknet/HacknetNode.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Hacknet Node Class
|
||||
*
|
||||
* Hacknet Nodes are specialized machines that passively earn the player money over time.
|
||||
* They can be upgraded to increase their production
|
||||
*/
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Node production
|
||||
export const HacknetNodeMoneyGainPerLevel: number = 1.6; // Base production per level
|
||||
|
||||
// Constants for Hacknet Node purchase/upgrade costs
|
||||
export const BaseCostForHacknetNode: number = 1000;
|
||||
export const BaseCostFor1GBOfRamHacknetNode: number = 30e3;
|
||||
export const BaseCostForHacknetNodeCore: number = 500e3;
|
||||
export const HacknetNodePurchaseNextMult: number = 1.85; // Multiplier when purchasing an additional hacknet node
|
||||
export const HacknetNodeUpgradeLevelMult: number = 1.04; // Multiplier for cost when upgrading level
|
||||
export const HacknetNodeUpgradeRamMult: number = 1.28; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetNodeUpgradeCoreMult: number = 1.48; // Multiplier for cost when buying another core
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Nodes
|
||||
export const HacknetNodeMaxLevel: number = 200;
|
||||
export const HacknetNodeMaxRam: number = 64;
|
||||
export const HacknetNodeMaxCores: number = 16;
|
||||
|
||||
export class HacknetNode implements IHacknetNode {
|
||||
/**
|
||||
* Initiatizes a HacknetNode object from a JSON save state.
|
||||
*/
|
||||
static fromJSON(value: any): HacknetNode {
|
||||
return Generic_fromJSON(HacknetNode, value.data);
|
||||
}
|
||||
|
||||
// Node's number of cores
|
||||
cores: number = 1;
|
||||
|
||||
// Node's Level
|
||||
level: number = 1;
|
||||
|
||||
// Node's production per second
|
||||
moneyGainRatePerSecond: number = 0;
|
||||
|
||||
// Identifier for Node. Includes the full "name" (hacknet-node-N)
|
||||
name: string;
|
||||
|
||||
// How long this Node has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Node's RAM (GB)
|
||||
ram: number = 1;
|
||||
|
||||
// Total money earned by this Node
|
||||
totalMoneyGenerated: number = 0;
|
||||
|
||||
constructor(name: string="") {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's number of cores
|
||||
calculateCoreUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetNodeMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const coreBaseCost = BaseCostForHacknetNodeCore;
|
||||
const mult = HacknetNodeUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
|
||||
++currentCores;
|
||||
}
|
||||
|
||||
totalCost *= p.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's level
|
||||
calculateLevelUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetNodeMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetNodeUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return BaseCostForHacknetNode / 2 * totalMultiplier * p.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
// Get the cost to upgrade this Node's RAM
|
||||
calculateRamUpgradeCost(levels: number=1, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.ram >= HacknetNodeMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.ram));
|
||||
let currentRam = this.ram;
|
||||
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBOfRamHacknetNode;
|
||||
let mult = Math.pow(HacknetNodeUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
|
||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Node in the game loop.
|
||||
// Returns the amount of money generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
let gain = this.moneyGainRatePerSecond * seconds;
|
||||
if (isNaN(gain)) {
|
||||
console.error(`Hacknet Node ${this.name} calculated earnings of NaN`);
|
||||
gain = 0;
|
||||
}
|
||||
|
||||
this.totalMoneyGenerated += gain;
|
||||
this.onlineTimeSeconds += seconds;
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
||||
// Upgrade this Node's number of cores, if possible
|
||||
// Returns a boolean indicating whether new cores were successfully bought
|
||||
purchaseCoreUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.cores >= HacknetNodeMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max Cores, calculate
|
||||
// the max possible number of upgrades and use that
|
||||
if (this.cores + sanitizedLevels > HacknetNodeMaxCores) {
|
||||
const diff = Math.max(0, HacknetNodeMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + sanitizedLevels); // Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Upgrade this Node's level, if possible
|
||||
// Returns a boolean indicating whether the level was successfully updated
|
||||
purchaseLevelUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're at max level, return false
|
||||
if (this.level >= HacknetNodeMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number of specified upgrades would exceed the max level, calculate
|
||||
// the maximum number of upgrades and use that
|
||||
if (this.level + sanitizedLevels > HacknetNodeMaxLevel) {
|
||||
var diff = Math.max(0, HacknetNodeMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.level = Math.round(this.level + sanitizedLevels); // Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Upgrade this Node's RAM, if possible
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
purchaseRamUpgrade(levels: number=1, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || sanitizedLevels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.ram >= HacknetNodeMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number of specified upgrades would exceed the max RAM, calculate the
|
||||
// max possible number of upgrades and use that
|
||||
if (this.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
|
||||
var diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / this.ram)));
|
||||
return this.purchaseRamUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
this.ram *= 2; // Ram is always doubled
|
||||
}
|
||||
this.ram = Math.round(this.ram); // Handle any floating point precision issues
|
||||
this.updateMoneyGainRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
|
||||
updateMoneyGainRate(p: IPlayer): void {
|
||||
//How much extra $/s is gained per level
|
||||
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
|
||||
|
||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||
Math.pow(1.035, this.ram - 1) *
|
||||
((this.cores + 5) / 6) *
|
||||
p.hacknet_node_money_mult *
|
||||
BitNodeMultipliers.HacknetNodeMoney;
|
||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetNode", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetNode = HacknetNode;
|
||||
350
src/Hacknet/HacknetServer.ts
Normal file
350
src/Hacknet/HacknetServer.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
/**
|
||||
* Hacknet Servers - Reworked Hacknet Node mechanic for BitNode-9
|
||||
*/
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Constants for Hacknet Server stats/production
|
||||
export const HacknetServerHashesPerLevel: number = 0.001;
|
||||
|
||||
// Constants for Hacknet Server purchase/upgrade costs
|
||||
export const BaseCostForHacknetServer: number = 50e3;
|
||||
export const BaseCostFor1GBHacknetServerRam: number = 200e3;
|
||||
export const BaseCostForHacknetServerCore: number = 1e6;
|
||||
export const BaseCostForHacknetServerCache: number = 10e6;
|
||||
|
||||
export const HacknetServerPurchaseMult: number = 3.2; // Multiplier for puchasing an additional Hacknet Server
|
||||
export const HacknetServerUpgradeLevelMult: number = 1.1; // Multiplier for cost when upgrading level
|
||||
export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for cost when upgrading RAM
|
||||
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
|
||||
export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
|
||||
|
||||
export const MaxNumberHacknetServers: number = 25; // Max number of Hacknet Servers you can own
|
||||
|
||||
// Constants for max upgrade levels for Hacknet Server
|
||||
export const HacknetServerMaxLevel: number = 300;
|
||||
export const HacknetServerMaxRam: number = 8192;
|
||||
export const HacknetServerMaxCores: number = 128;
|
||||
export const HacknetServerMaxCache: number = 15;
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
organizationName?: string;
|
||||
player?: IPlayer;
|
||||
}
|
||||
|
||||
export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
// Initializes a HacknetServer Object from a JSON save state
|
||||
static fromJSON(value: any): HacknetServer {
|
||||
return Generic_fromJSON(HacknetServer, value.data);
|
||||
}
|
||||
|
||||
// Cache level. Affects hash Capacity
|
||||
cache: number = 1;
|
||||
|
||||
// Number of cores. Improves hash production
|
||||
cores: number = 1;
|
||||
|
||||
// Number of hashes that can be stored by this Hacknet Server
|
||||
hashCapacity: number = 0;
|
||||
|
||||
// Hashes produced per second
|
||||
hashRate: number = 0;
|
||||
|
||||
// Similar to Node level. Improves hash production
|
||||
level: number = 1;
|
||||
|
||||
// How long this HacknetServer has existed, in seconds
|
||||
onlineTimeSeconds: number = 0;
|
||||
|
||||
// Total number of hashes earned by this
|
||||
totalHashesGenerated: number = 0;
|
||||
|
||||
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
|
||||
super(params);
|
||||
|
||||
this.maxRam = 1;
|
||||
this.updateHashCapacity();
|
||||
|
||||
if (params.player) {
|
||||
this.updateHashRate(params.player);
|
||||
}
|
||||
}
|
||||
|
||||
calculateCacheUpgradeCost(levels: number): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cache >= HacknetServerMaxCache) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCacheMult;
|
||||
let totalCost = 0;
|
||||
let currentCache = this.cache;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCache - 1);
|
||||
++currentCache;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCache;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateCoreUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetServerMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalCost += Math.pow(mult, currentCores-1);
|
||||
++currentCores;
|
||||
}
|
||||
totalCost *= BaseCostForHacknetServerCore;
|
||||
totalCost *= p.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
calculateLevelUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetServerMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const mult = HacknetServerUpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = this.level;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return 10 * BaseCostForHacknetServer * totalMultiplier * p.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
calculateRamUpgradeCost(levels: number, p: IPlayer): number {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.maxRam >= HacknetServerMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.maxRam));
|
||||
let currentRam = this.maxRam;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
let baseCost = currentRam * BaseCostFor1GBHacknetServerRam;
|
||||
let mult = Math.pow(HacknetServerUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
totalCost *= p.hacknet_node_ram_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Process this Hacknet Server in the game loop.
|
||||
// Returns the number of hashes generated
|
||||
process(numCycles: number=1): number {
|
||||
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
|
||||
|
||||
return this.hashRate * seconds;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the cache was successfully upgraded
|
||||
purchaseCacheUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCacheUpgradeCost(levels);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cache >= HacknetServerMaxCache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// the maximum possible
|
||||
if (this.cache + levels > HacknetServerMaxCache) {
|
||||
const diff = Math.max(0, HacknetServerMaxCache - this.cache);
|
||||
return this.purchaseCacheUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cache = Math.round(this.cache + sanitizedLevels);
|
||||
this.updateHashCapacity();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the number of cores was successfully upgraded
|
||||
purchaseCoreUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateCoreUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cores >= HacknetServerMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// the maximum possible
|
||||
if (this.cores + sanitizedLevels > HacknetServerMaxCores) {
|
||||
const diff = Math.max(0, HacknetServerMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + sanitizedLevels);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the level was successfully upgraded
|
||||
purchaseLevelUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateLevelUpgradeCost(sanitizedLevels, p);
|
||||
if (isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.level >= HacknetServerMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase the
|
||||
// maximum possible
|
||||
if (this.level + sanitizedLevels > HacknetServerMaxLevel) {
|
||||
const diff = Math.max(0, HacknetServerMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
this.level = Math.round(this.level + sanitizedLevels);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether the RAM was successfully upgraded
|
||||
purchaseRamUpgrade(levels: number, p: IPlayer): boolean {
|
||||
const sanitizedLevels = Math.round(levels);
|
||||
const cost = this.calculateRamUpgradeCost(sanitizedLevels, p);
|
||||
if(isNaN(cost) || cost <= 0 || sanitizedLevels <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.maxRam >= HacknetServerMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the specified number of upgrades would exceed the max, try to purchase
|
||||
// just the maximum possible
|
||||
if (this.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
|
||||
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / this.maxRam)));
|
||||
return this.purchaseRamUpgrade(diff, p);
|
||||
}
|
||||
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
this.maxRam *= 2;
|
||||
}
|
||||
this.maxRam = Math.round(this.maxRam);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever a script is run, we must update this server's hash rate
|
||||
*/
|
||||
runScript(script: RunningScript, p?: IPlayer): void {
|
||||
super.runScript(script);
|
||||
if (p) {
|
||||
this.updateHashRate(p);
|
||||
}
|
||||
}
|
||||
|
||||
updateHashCapacity(): void {
|
||||
this.hashCapacity = 32 * Math.pow(2, this.cache);
|
||||
}
|
||||
|
||||
updateHashRate(p: IPlayer): void {
|
||||
const baseGain = HacknetServerHashesPerLevel * this.level;
|
||||
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
|
||||
const coreMultiplier = 1 + (this.cores - 1) / 5;
|
||||
const ramRatio = (1 - this.ramUsed / this.maxRam);
|
||||
|
||||
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
|
||||
|
||||
this.hashRate = hashRate * p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney;
|
||||
|
||||
if (isNaN(this.hashRate)) {
|
||||
this.hashRate = 0;
|
||||
console.error(`Error calculating Hacknet Server hash production. This is a bug. Please report to game dev`, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HacknetServer", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetServer = HacknetServer;
|
||||
177
src/Hacknet/HashManager.ts
Normal file
177
src/Hacknet/HashManager.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* This is a central class for storing and managing the player's hashes,
|
||||
* which are generated by Hacknet Servers
|
||||
*
|
||||
* It is also used to keep track of what upgrades the player has bought with
|
||||
* his hashes, and contains method for grabbing the data/multipliers from
|
||||
* those upgrades
|
||||
*/
|
||||
import { HacknetServer } from "./HacknetServer";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { IMap } from "../types";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { AllServers } from "../Server/AllServers";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export class HashManager {
|
||||
// Initiatizes a HashManager object from a JSON save state.
|
||||
static fromJSON(value: any): HashManager {
|
||||
return Generic_fromJSON(HashManager, value.data);
|
||||
}
|
||||
|
||||
// Max number of hashes this can hold. Equal to the sum of capacities of
|
||||
// all Hacknet Servers
|
||||
capacity: number = 0;
|
||||
|
||||
// Number of hashes currently in storage
|
||||
hashes: number = 0;
|
||||
|
||||
// Map of Hash Upgrade Name -> levels in that upgrade
|
||||
upgrades: IMap<number> = {};
|
||||
|
||||
constructor() {
|
||||
for (const name in HashUpgrades) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic helper function for getting a multiplier from a HashUpgrade
|
||||
*/
|
||||
getMult(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Could not find Hash Study upgrade`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1 + ((upg.value * currLevel) / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves studying. This returns that multiplier
|
||||
*/
|
||||
getStudyMult(): number {
|
||||
const upgName = "Improve Studying";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the Hash upgrades improves gym training. This returns that multiplier
|
||||
*/
|
||||
getTrainingMult(): number {
|
||||
const upgName = "Improve Gym Training";
|
||||
|
||||
return this.getMult(upgName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cost (in hashes) of an upgrade
|
||||
*/
|
||||
getUpgradeCost(upgName: string): number {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.getUpgradeCost(): ${upgName}`);
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return upg.getCost(currLevel);
|
||||
}
|
||||
|
||||
prestige(p: IPlayer): void {
|
||||
for (const name in HashUpgrades) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
this.hashes = 0;
|
||||
if (p != null) {
|
||||
this.updateCapacity(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts an upgrade and refunds the hashes used to buy it
|
||||
*/
|
||||
refundUpgrade(upgName: string): void {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null || currLevel === 0) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce the level first, so we get the right cost
|
||||
--this.upgrades[upgName];
|
||||
const cost = upg.getCost(currLevel);
|
||||
this.hashes += cost;
|
||||
}
|
||||
|
||||
storeHashes(numHashes: number): void {
|
||||
this.hashes += numHashes;
|
||||
this.hashes = Math.min(this.hashes, this.capacity);
|
||||
}
|
||||
|
||||
updateCapacity(p: IPlayer): void {
|
||||
if (p.hacknetNodes.length <= 0) {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the Player's `hacknetNodes` property actually holds Hacknet Servers
|
||||
const ip: string = <string>p.hacknetNodes[0];
|
||||
if (typeof ip !== "string") {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const hserver = <HacknetServer>AllServers[ip];
|
||||
if (!(hserver instanceof HacknetServer)) {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let total: number = 0;
|
||||
for (let i = 0; i < p.hacknetNodes.length; ++i) {
|
||||
const h = <HacknetServer>AllServers[<string>p.hacknetNodes[i]];
|
||||
total += h.hashCapacity;
|
||||
}
|
||||
|
||||
this.capacity = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean indicating whether or not the upgrade was successfully purchased
|
||||
* Note that this does NOT actually implement the effect
|
||||
*/
|
||||
upgrade(upgName: string): boolean {
|
||||
const upg = HashUpgrades[upgName];
|
||||
if (upg == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = this.getUpgradeCost(upgName);
|
||||
|
||||
if (this.hashes < cost) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hashes -= cost;
|
||||
++this.upgrades[upgName];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Serialize the current object to a JSON save state.
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("HashManager", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.HashManager = HashManager;
|
||||
61
src/Hacknet/HashUpgrade.ts
Normal file
61
src/Hacknet/HashUpgrade.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Object representing an upgrade that can be purchased with hashes
|
||||
*/
|
||||
export interface IConstructorParams {
|
||||
cost?: number;
|
||||
costPerLevel: number;
|
||||
desc: string;
|
||||
hasTargetServer?: boolean;
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export class HashUpgrade {
|
||||
/**
|
||||
* If the upgrade has a flat cost (never increases), it goes here
|
||||
* Otherwise, this property should be undefined
|
||||
*
|
||||
* This property overrides the 'costPerLevel' property
|
||||
*/
|
||||
cost?: number;
|
||||
|
||||
/**
|
||||
* Base cost for this upgrade. Every time the upgrade is purchased,
|
||||
* its cost increases by this same amount (so its 1x, 2x, 3x, 4x, etc.)
|
||||
*/
|
||||
costPerLevel: number = 0;
|
||||
|
||||
/**
|
||||
* Description of what the upgrade does
|
||||
*/
|
||||
desc: string = "";
|
||||
|
||||
/**
|
||||
* Boolean indicating that this upgrade's effect affects a single server,
|
||||
* the "target" server
|
||||
*/
|
||||
hasTargetServer: boolean = false;
|
||||
|
||||
// Name of upgrade
|
||||
name: string = "";
|
||||
|
||||
// Generic value used to indicate the potency/amount of this upgrade's effect
|
||||
// The meaning varies between different upgrades
|
||||
value: number = 0;
|
||||
|
||||
constructor(p: IConstructorParams) {
|
||||
if (p.cost != null) { this.cost = p.cost; }
|
||||
|
||||
this.costPerLevel = p.costPerLevel;
|
||||
this.desc = p.desc;
|
||||
this.hasTargetServer = p.hasTargetServer ? p.hasTargetServer : false;
|
||||
this.name = p.name;
|
||||
this.value = p.value;
|
||||
}
|
||||
|
||||
getCost(levels: number): number {
|
||||
if (typeof this.cost === "number") { return this.cost; }
|
||||
|
||||
return Math.round((levels + 1) * this.costPerLevel);
|
||||
}
|
||||
}
|
||||
18
src/Hacknet/HashUpgrades.ts
Normal file
18
src/Hacknet/HashUpgrades.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Map of all Hash Upgrades
|
||||
* Key = Hash name, Value = HashUpgrade object
|
||||
*/
|
||||
import { HashUpgrade,
|
||||
IConstructorParams } from "./HashUpgrade";
|
||||
import { HashUpgradesMetadata } from "./data/HashUpgradesMetadata";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const HashUpgrades: IMap<HashUpgrade> = {};
|
||||
|
||||
function createHashUpgrade(p: IConstructorParams) {
|
||||
HashUpgrades[p.name] = new HashUpgrade(p);
|
||||
}
|
||||
|
||||
for (const metadata of HashUpgradesMetadata) {
|
||||
createHashUpgrade(metadata);
|
||||
}
|
||||
16
src/Hacknet/IHacknetNode.ts
Normal file
16
src/Hacknet/IHacknetNode.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// Interface for a Hacknet Node. Implemented by both a basic Hacknet Node,
|
||||
// and the upgraded Hacknet Server in BitNode-9
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export interface IHacknetNode {
|
||||
cores: number;
|
||||
level: number;
|
||||
onlineTimeSeconds: number;
|
||||
|
||||
calculateCoreUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
calculateLevelUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
calculateRamUpgradeCost: (levels: number, p: IPlayer) => number;
|
||||
purchaseCoreUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
purchaseLevelUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
purchaseRamUpgrade: (levels: number, p: IPlayer) => boolean;
|
||||
}
|
||||
71
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
71
src/Hacknet/data/HashUpgradesMetadata.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// Metadata used to construct all Hash Upgrades
|
||||
import { IConstructorParams } from "../HashUpgrade";
|
||||
|
||||
export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
cost: 4,
|
||||
costPerLevel: 4,
|
||||
desc: "Sell hashes for $1m",
|
||||
name: "Sell for Money",
|
||||
value: 1e6,
|
||||
},
|
||||
{
|
||||
costPerLevel: 100,
|
||||
desc: "Sell hashes for $1b in Corporation funds",
|
||||
name: "Sell for Corporation Funds",
|
||||
value: 1e9,
|
||||
},
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to decrease the minimum security of a single server by 2%. " +
|
||||
"Note that a server's minimum security cannot go below 1.",
|
||||
hasTargetServer: true,
|
||||
name: "Reduce Minimum Security",
|
||||
value: 0.98,
|
||||
},
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to increase the maximum amount of money on a single server by 2%",
|
||||
hasTargetServer: true,
|
||||
name: "Increase Maximum Money",
|
||||
value: 1.02,
|
||||
},
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to improve the experience earned when studying at a university by 20%. " +
|
||||
"This effect persists until you install Augmentations",
|
||||
name: "Improve Studying",
|
||||
value: 20, // Improves studying by value%
|
||||
},
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to improve the experience earned when training at the gym by 20%. This effect " +
|
||||
"persists until you install Augmentations",
|
||||
name: "Improve Gym Training",
|
||||
value: 20, // Improves training by value%
|
||||
},
|
||||
{
|
||||
costPerLevel: 200,
|
||||
desc: "Exchange hashes for 1k Scientific Research in all of your Corporation's Industries",
|
||||
name: "Exchange for Corporation Research",
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
costPerLevel: 250,
|
||||
desc: "Exchange hashes for 100 Bladeburner Rank",
|
||||
name: "Exchange for Bladeburner Rank",
|
||||
value: 100,
|
||||
},
|
||||
{
|
||||
costPerLevel: 250,
|
||||
desc: "Exchanges hashes for 10 Bladeburner Skill Points",
|
||||
name: "Exchange for Bladeburner SP",
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
costPerLevel: 200,
|
||||
desc: "Generate a random Coding Contract somewhere on the network",
|
||||
name: "Generate Coding Contract",
|
||||
value: 1,
|
||||
},
|
||||
]
|
||||
56
src/Hacknet/ui/GeneralInfo.jsx
Normal file
56
src/Hacknet/ui/GeneralInfo.jsx
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI
|
||||
*
|
||||
* Displays general information about Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers } from "../HacknetHelpers";
|
||||
|
||||
export class GeneralInfo extends React.Component {
|
||||
getSecondParagraph() {
|
||||
if (hasHacknetServers()) {
|
||||
return `Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` +
|
||||
`Hacknet Servers will perform computations and operations on the network, earning ` +
|
||||
`you hashes. Hashes can be spent on a variety of different upgrades.`;
|
||||
} else {
|
||||
return `Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
|
||||
`and contribute its resources to the Hacknet networ. This allows you to take ` +
|
||||
`a small percentage of profits from hacks performed on the network. Essentially, ` +
|
||||
`you are renting out your Node's computing power.`;
|
||||
}
|
||||
}
|
||||
|
||||
getThirdParagraph() {
|
||||
if (hasHacknetServers()) {
|
||||
return `Hacknet Servers can also be used as servers to run scripts. However, running scripts ` +
|
||||
`on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` +
|
||||
`rate will be reduced by the percentage of RAM that is being used by that Server to run ` +
|
||||
`scripts.`
|
||||
} else {
|
||||
return `Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` +
|
||||
`can be upgraded in order to increase its computing power and thereby increase ` +
|
||||
`the profit you earn from it.`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p className={"hacknet-general-info"}>
|
||||
The Hacknet is a global, decentralized network of machines. It is used by
|
||||
hackers all around the world to anonymously share computing power and
|
||||
perform distributed cyberattacks without the fear of being traced.
|
||||
</p>
|
||||
<br />
|
||||
<p className={"hacknet-general-info"}>
|
||||
{this.getSecondParagraph()}
|
||||
</p>
|
||||
<br />
|
||||
<p className={"hacknet-general-info"}>
|
||||
{this.getThirdParagraph()}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
153
src/Hacknet/ui/HacknetNode.jsx
Normal file
153
src/Hacknet/ui/HacknetNode.jsx
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI.
|
||||
* This Component displays the panel for a single Hacknet Node
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { HacknetNodeMaxLevel,
|
||||
HacknetNodeMaxRam,
|
||||
HacknetNodeMaxCores } from "../HacknetNode";
|
||||
import { getMaxNumberLevelUpgrades,
|
||||
getMaxNumberRamUpgrades,
|
||||
getMaxNumberCoreUpgrades } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export class HacknetNode extends React.Component {
|
||||
render() {
|
||||
const node = this.props.node;
|
||||
const purchaseMult = this.props.purchaseMultiplier;
|
||||
const recalculate = this.props.recalculate;
|
||||
|
||||
// Upgrade Level Button
|
||||
let upgradeLevelText, upgradeLevelClass;
|
||||
if (node.level >= HacknetNodeMaxLevel) {
|
||||
upgradeLevelText = "MAX LEVEL";
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
||||
} else {
|
||||
const levelsToMax = HacknetNodeMaxLevel - node.level;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeLevelClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeLevelOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
|
||||
}
|
||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
let upgradeRamText, upgradeRamClass;
|
||||
if (node.ram >= HacknetNodeMaxRam) {
|
||||
upgradeRamText = "MAX RAM";
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
||||
} else {
|
||||
const levelsToMax = Math.round(Math.log2(HacknetNodeMaxRam / node.ram));
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeRamClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeRamOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
|
||||
}
|
||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
let upgradeCoresText, upgradeCoresClass;
|
||||
if (node.cores >= HacknetNodeMaxCores) {
|
||||
upgradeCoresText = "MAX CORES";
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
||||
} else {
|
||||
const levelsToMax = HacknetNodeMaxCores - node.cores;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCoresClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCoresOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
|
||||
}
|
||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={"hacknet-node"}>
|
||||
<div className={"hacknet-node-container"}>
|
||||
<div className={"row"}>
|
||||
<p>Node name:</p>
|
||||
<span className={"text"}>{node.name}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Production:</p>
|
||||
<span className={"text money-gold"}>
|
||||
{numeralWrapper.formatMoney(node.totalMoneyGenerated)} ({numeralWrapper.formatMoney(node.moneyGainRatePerSecond)} / sec)
|
||||
</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span>
|
||||
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
|
||||
{upgradeLevelText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>RAM:</p><span className={"text upgradable-info"}>{node.ram}GB</span>
|
||||
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
|
||||
{upgradeRamText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span>
|
||||
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
|
||||
{upgradeCoresText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
200
src/Hacknet/ui/HacknetServer.jsx
Normal file
200
src/Hacknet/ui/HacknetServer.jsx
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* React Component for the Hacknet Node UI.
|
||||
* This Component displays the panel for a single Hacknet Node
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { HacknetServerMaxLevel,
|
||||
HacknetServerMaxRam,
|
||||
HacknetServerMaxCores,
|
||||
HacknetServerMaxCache } from "../HacknetServer";
|
||||
import { getMaxNumberLevelUpgrades,
|
||||
getMaxNumberRamUpgrades,
|
||||
getMaxNumberCoreUpgrades,
|
||||
getMaxNumberCacheUpgrades } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export class HacknetServer extends React.Component {
|
||||
render() {
|
||||
const node = this.props.node;
|
||||
const purchaseMult = this.props.purchaseMultiplier;
|
||||
const recalculate = this.props.recalculate;
|
||||
|
||||
// Upgrade Level Button
|
||||
let upgradeLevelText, upgradeLevelClass;
|
||||
if (node.level >= HacknetServerMaxLevel) {
|
||||
upgradeLevelText = "MAX LEVEL";
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxLevel - node.level;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player);
|
||||
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`;
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeLevelClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeLevelOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel);
|
||||
}
|
||||
node.purchaseLevelUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade RAM Button
|
||||
let upgradeRamText, upgradeRamClass;
|
||||
if (node.maxRam >= HacknetServerMaxRam) {
|
||||
upgradeRamText = "MAX RAM";
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
||||
} else {
|
||||
const levelsToMax = Math.round(Math.log2(HacknetServerMaxRam / node.maxRam));
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player);
|
||||
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`;
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeRamClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeRamOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam);
|
||||
}
|
||||
node.purchaseRamUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade Cores Button
|
||||
let upgradeCoresText, upgradeCoresClass;
|
||||
if (node.cores >= HacknetServerMaxCores) {
|
||||
upgradeCoresText = "MAX CORES";
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxCores - node.cores;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player);
|
||||
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`;
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoresClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCoresClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCoresOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores);
|
||||
}
|
||||
node.purchaseCoreUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade Cache button
|
||||
let upgradeCacheText, upgradeCacheClass;
|
||||
if (node.cache >= HacknetServerMaxCache) {
|
||||
upgradeCacheText = "MAX CACHE";
|
||||
upgradeCacheClass = "std-button-disabled";
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (purchaseMult === "MAX") {
|
||||
multiplier = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
||||
} else {
|
||||
const levelsToMax = HacknetServerMaxCache - node.cache;
|
||||
multiplier = Math.min(levelsToMax, purchaseMult);
|
||||
}
|
||||
|
||||
const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier);
|
||||
upgradeCacheText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCacheCost)}`;
|
||||
if (Player.money.lt(upgradeCacheCost)) {
|
||||
upgradeCacheClass = "std-button-disabled";
|
||||
} else {
|
||||
upgradeCacheClass = "std-button";
|
||||
}
|
||||
}
|
||||
const upgradeCacheOnClick = () => {
|
||||
let numUpgrades = purchaseMult;
|
||||
if (purchaseMult === "MAX") {
|
||||
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache);
|
||||
}
|
||||
node.purchaseCacheUpgrade(numUpgrades, Player);
|
||||
recalculate();
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={"hacknet-node"}>
|
||||
<div className={"hacknet-node-container"}>
|
||||
<div className={"row"}>
|
||||
<p>Node name:</p>
|
||||
<span className={"text"}>{node.hostname}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Production:</p>
|
||||
<span className={"text money-gold"}>
|
||||
{numeralWrapper.formatBigNumber(node.totalHashesGenerated)} ({numeralWrapper.formatBigNumber(node.hashRate)} / sec)
|
||||
</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Hash Capacity:</p>
|
||||
<span className={"text"}>{node.hashCapacity}</span>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span>
|
||||
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
|
||||
{upgradeLevelText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>RAM:</p><span className={"text upgradable-info"}>{node.maxRam}GB</span>
|
||||
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
|
||||
{upgradeRamText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span>
|
||||
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
|
||||
{upgradeCoresText}
|
||||
</button>
|
||||
</div>
|
||||
<div className={"row"}>
|
||||
<p>Cache Level:</p><span className={"text upgradable-info"}>{node.cache}</span>
|
||||
<button className={upgradeCacheClass} onClick={upgradeCacheOnClick}>
|
||||
{upgradeCacheText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
130
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
130
src/Hacknet/ui/HashUpgradePopup.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Create the pop-up for purchasing upgrades with hashes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { purchaseHashUpgrade } from "../HacknetHelpers";
|
||||
import { HashManager } from "../HashManager";
|
||||
import { HashUpgrades } from "../HashUpgrades";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { PopupCloseButton } from "../../ui/React/PopupCloseButton";
|
||||
import { ServerDropdown,
|
||||
ServerType } from "../../ui/React/ServerDropdown"
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
class HashUpgrade extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedServer: "foodnstuff",
|
||||
}
|
||||
|
||||
this.changeTargetServer = this.changeTargetServer.bind(this);
|
||||
this.purchase = this.purchase.bind(this, this.props.hashManager, this.props.upg);
|
||||
}
|
||||
|
||||
changeTargetServer(e) {
|
||||
this.setState({
|
||||
selectedServer: e.target.value
|
||||
});
|
||||
}
|
||||
|
||||
purchase(hashManager, upg) {
|
||||
const canPurchase = hashManager.hashes >= hashManager.getUpgradeCost(upg.name);
|
||||
if (canPurchase) {
|
||||
const res = purchaseHashUpgrade(upg.name, this.state.selectedServer);
|
||||
if (res) {
|
||||
this.props.rerender();
|
||||
} else {
|
||||
dialogBoxCreate("Failed to purchase upgrade. This may be because you do not have enough hashes, " +
|
||||
"or because you do not have access to the feature this upgrade affects.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const hashManager = this.props.hashManager;
|
||||
const upg = this.props.upg;
|
||||
const cost = hashManager.getUpgradeCost(upg.name);
|
||||
|
||||
// Purchase button
|
||||
const canPurchase = hashManager.hashes >= cost;
|
||||
const btnClass = canPurchase ? "std-button" : "std-button-disabled";
|
||||
|
||||
// We'll reuse a Bladeburner css class
|
||||
return (
|
||||
<div className={"bladeburner-action"}>
|
||||
<h2>{upg.name}</h2>
|
||||
<p>Cost: {numeralWrapper.format(cost, "0.000a")}</p>
|
||||
<p>{upg.desc}</p>
|
||||
<button className={btnClass} onClick={this.purchase}>
|
||||
Purchase
|
||||
</button>
|
||||
{
|
||||
upg.hasTargetServer &&
|
||||
<ServerDropdown
|
||||
serverType={ServerType.Foreign}
|
||||
onChange={this.changeTargetServer}
|
||||
style={{margin: "5px"}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HashUpgradePopup extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
totalHashes: Player.hashManager.hashes,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.interval = setInterval(() => this.tick(), 1e3);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.setState({
|
||||
totalHashes: Player.hashManager.hashes,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const rerender = this.props.rerender;
|
||||
|
||||
const hashManager = Player.hashManager;
|
||||
if (!(hashManager instanceof HashManager)) {
|
||||
throw new Error(`Player does not have a HashManager)`);
|
||||
}
|
||||
|
||||
const upgradeElems = Object.keys(HashUpgrades).map((upgName) => {
|
||||
const upg = HashUpgrades[upgName];
|
||||
return <HashUpgrade upg={upg} hashManager={hashManager} key={upg.name} rerender={rerender} />
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PopupCloseButton popup={this.props.popupId} text={"Close"} />
|
||||
<p>Spend your hashes on a variety of different upgrades</p>
|
||||
<p>Hashes: {numeralWrapper.formatBigNumber(this.state.totalHashes)}</p>
|
||||
{upgradeElems}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
41
src/Hacknet/ui/MultiplierButtons.jsx
Normal file
41
src/Hacknet/ui/MultiplierButtons.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* React Component for the Multiplier buttons on the Hacknet page.
|
||||
* These buttons let the player control how many Nodes/Upgrades they're
|
||||
* purchasing when using the UI (x1, x5, x10, MAX)
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { PurchaseMultipliers } from "./Root";
|
||||
|
||||
function MultiplierButton(props) {
|
||||
return (
|
||||
<button className={props.className} onClick={props.onClick}>{props.text}</button>
|
||||
)
|
||||
}
|
||||
|
||||
export function MultiplierButtons(props) {
|
||||
if (props.purchaseMultiplier == null) {
|
||||
throw new Error(`MultiplierButtons constructed without required props`);
|
||||
}
|
||||
|
||||
const mults = ["x1", "x5", "x10", "MAX"];
|
||||
const onClicks = props.onClicks;
|
||||
const buttons = [];
|
||||
for (let i = 0; i < mults.length; ++i) {
|
||||
const mult = mults[i];
|
||||
const btnProps = {
|
||||
className: props.purchaseMultiplier === PurchaseMultipliers[mult] ? "std-button-disabled" : "std-button",
|
||||
key: mult,
|
||||
onClick: onClicks[i],
|
||||
text: mult,
|
||||
}
|
||||
|
||||
buttons.push(<MultiplierButton {...btnProps} />)
|
||||
}
|
||||
|
||||
return (
|
||||
<span id={"hacknet-nodes-multipliers"}>
|
||||
{buttons}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
51
src/Hacknet/ui/PlayerInfo.jsx
Normal file
51
src/Hacknet/ui/PlayerInfo.jsx
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* React Component for displaying Player info and stats on the Hacknet Node UI.
|
||||
* This includes:
|
||||
* - Player's money
|
||||
* - Player's production from Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers } from "../HacknetHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export function PlayerInfo(props) {
|
||||
const hasServers = hasHacknetServers();
|
||||
|
||||
let prod;
|
||||
if (hasServers) {
|
||||
prod = numeralWrapper.format(props.totalProduction, "0.000a") + " hashes / sec";
|
||||
} else {
|
||||
prod = numeralWrapper.formatMoney(props.totalProduction) + " / sec";
|
||||
}
|
||||
|
||||
let hashInfo;
|
||||
if (hasServers) {
|
||||
hashInfo = numeralWrapper.format(Player.hashManager.hashes, "0.000a") + " / " +
|
||||
numeralWrapper.format(Player.hashManager.capacity, "0.000a");
|
||||
}
|
||||
|
||||
return (
|
||||
<p id={"hacknet-nodes-money"}>
|
||||
<span>Money:</span>
|
||||
<span className={"money-gold"}>{numeralWrapper.formatMoney(Player.money.toNumber())}</span><br />
|
||||
|
||||
{
|
||||
hasServers &&
|
||||
<span>Hashes:</span>
|
||||
}
|
||||
{
|
||||
hasServers &&
|
||||
<span className={"money-gold"}>{hashInfo}</span>
|
||||
}
|
||||
{
|
||||
hasServers &&
|
||||
<br />
|
||||
}
|
||||
|
||||
<span>Total Hacknet Node Production:</span>
|
||||
<span className={"money-gold"}>{prod}</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
40
src/Hacknet/ui/PurchaseButton.jsx
Normal file
40
src/Hacknet/ui/PurchaseButton.jsx
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* React Component for the button that is used to purchase new Hacknet Nodes
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { hasHacknetServers,
|
||||
hasMaxNumberHacknetServers } from "../HacknetHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export function PurchaseButton(props) {
|
||||
if (props.multiplier == null || props.onClick == null) {
|
||||
throw new Error(`PurchaseButton constructed without required props`);
|
||||
}
|
||||
|
||||
const cost = props.cost;
|
||||
let className = Player.canAfford(cost) ? "std-button" : "std-button-disabled";
|
||||
let text;
|
||||
let style = null;
|
||||
if (hasHacknetServers()) {
|
||||
if (hasMaxNumberHacknetServers()) {
|
||||
className = "std-button-disabled";
|
||||
text = "Hacknet Server limit reached";
|
||||
style = {color: "red"};
|
||||
} else {
|
||||
text = `Purchase Hacknet Server - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
} else {
|
||||
text = `Purchase Hacknet Node - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className}
|
||||
onClick={props.onClick}
|
||||
style={style}>
|
||||
{text}
|
||||
</button>
|
||||
|
||||
)
|
||||
}
|
||||
154
src/Hacknet/ui/Root.jsx
Normal file
154
src/Hacknet/ui/Root.jsx
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Root React Component for the Hacknet Node UI
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { GeneralInfo } from "./GeneralInfo";
|
||||
import { HacknetNode } from "./HacknetNode";
|
||||
import { HacknetServer } from "./HacknetServer";
|
||||
import { HashUpgradePopup } from "./HashUpgradePopup";
|
||||
import { MultiplierButtons } from "./MultiplierButtons";
|
||||
import { PlayerInfo } from "./PlayerInfo";
|
||||
import { PurchaseButton } from "./PurchaseButton";
|
||||
|
||||
import { getCostOfNextHacknetNode,
|
||||
getCostOfNextHacknetServer,
|
||||
hasHacknetServers,
|
||||
purchaseHacknet } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { PopupCloseButton } from "../../ui/React/PopupCloseButton";
|
||||
|
||||
export const PurchaseMultipliers = Object.freeze({
|
||||
"x1": 1,
|
||||
"x5": 5,
|
||||
"x10": 10,
|
||||
"MAX": "MAX",
|
||||
});
|
||||
|
||||
export class HacknetRoot extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
purchaseMultiplier: PurchaseMultipliers.x1,
|
||||
totalProduction: 0, // Total production ($ / s) of Hacknet Nodes
|
||||
}
|
||||
|
||||
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.recalculateTotalProduction();
|
||||
}
|
||||
|
||||
createHashUpgradesPopup() {
|
||||
const id = "hacknet-server-hash-upgrades-popup";
|
||||
createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup });
|
||||
}
|
||||
|
||||
recalculateTotalProduction() {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (hasHacknetServers()) {
|
||||
const hserver = AllServers[Player.hacknetNodes[i]];
|
||||
if (hserver) {
|
||||
total += hserver.hashRate;
|
||||
} else {
|
||||
console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`)
|
||||
}
|
||||
} else {
|
||||
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
totalProduction: total,
|
||||
});
|
||||
}
|
||||
|
||||
setPurchaseMultiplier(mult) {
|
||||
this.setState({
|
||||
purchaseMultiplier: mult,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// Cost to purchase a new Hacknet Node
|
||||
let purchaseCost;
|
||||
if (hasHacknetServers()) {
|
||||
purchaseCost = getCostOfNextHacknetServer();
|
||||
} else {
|
||||
purchaseCost = getCostOfNextHacknetNode();
|
||||
}
|
||||
|
||||
// onClick event handler for purchase button
|
||||
const purchaseOnClick = () => {
|
||||
if (purchaseHacknet() >= 0) {
|
||||
this.recalculateTotalProduction();
|
||||
}
|
||||
}
|
||||
|
||||
// onClick event handlers for purchase multiplier buttons
|
||||
const purchaseMultiplierOnClicks = [
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x5),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x10),
|
||||
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.MAX),
|
||||
];
|
||||
|
||||
// HacknetNode components
|
||||
const nodes = Player.hacknetNodes.map((node) => {
|
||||
if (hasHacknetServers()) {
|
||||
const hserver = AllServers[node];
|
||||
if (hserver == null) {
|
||||
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
|
||||
}
|
||||
return (
|
||||
<HacknetServer
|
||||
key={hserver.hostname}
|
||||
node={hserver}
|
||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<HacknetNode
|
||||
key={node.name}
|
||||
node={node}
|
||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Hacknet Nodes</h1>
|
||||
<GeneralInfo />
|
||||
|
||||
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={purchaseOnClick} />
|
||||
|
||||
<br />
|
||||
<div id={"hacknet-nodes-money-multipliers-div"}>
|
||||
<PlayerInfo totalProduction={this.state.totalProduction} />
|
||||
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={this.state.purchaseMultiplier} />
|
||||
</div>
|
||||
|
||||
{
|
||||
hasHacknetServers() &&
|
||||
<button className={"std-button"} onClick={this.createHashUpgradesPopup} style={{display: "block"}}>
|
||||
{"Spend Hashes on Upgrades"}
|
||||
</button>
|
||||
}
|
||||
|
||||
<ul id={"hacknet-nodes-list"}>{nodes}</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,694 +0,0 @@
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import {iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "./InteractiveTutorial";
|
||||
import {Player} from "./Player";
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {Reviver, Generic_toJSON,
|
||||
Generic_fromJSON} from "../utils/JSONReviver";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {getElementById} from "../utils/uiHelpers/getElementById";
|
||||
|
||||
// Stores total money gain rate from all of the player's Hacknet Nodes
|
||||
let TotalHacknetNodeProduction = 0;
|
||||
|
||||
/**
|
||||
* Overwrites the inner text of the specified HTML element if it is different from what currently exists.
|
||||
* @param {string} elementId The HTML ID to find the first instance of.
|
||||
* @param {string} text The inner text that should be set.
|
||||
*/
|
||||
function updateText(elementId, text) {
|
||||
var el = getElementById(elementId);
|
||||
if (el.innerText != text) {
|
||||
el.innerText = text;
|
||||
}
|
||||
};
|
||||
|
||||
/* HacknetNode.js */
|
||||
function hacknetNodesInit() {
|
||||
var performMapping = function(x) {
|
||||
getElementById("hacknet-nodes-" + x.id + "-multiplier")
|
||||
.addEventListener("click", function() {
|
||||
hacknetNodePurchaseMultiplier = x.multiplier;
|
||||
updateHacknetNodesMultiplierButtons();
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
var mappings = [
|
||||
{ id: "1x", multiplier: 1 },
|
||||
{ id: "5x", multiplier: 5 },
|
||||
{ id: "10x", multiplier: 10 },
|
||||
{ id: "max", multiplier: 0 }
|
||||
];
|
||||
for (var elem of mappings) {
|
||||
// Encapsulate in a function so that the appropriate scope is kept in the click handler.
|
||||
performMapping(elem);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit, false);
|
||||
|
||||
function HacknetNode(name) {
|
||||
this.level = 1;
|
||||
this.ram = 1; //GB
|
||||
this.cores = 1;
|
||||
|
||||
this.name = name;
|
||||
|
||||
this.totalMoneyGenerated = 0;
|
||||
this.onlineTimeSeconds = 0;
|
||||
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.updateMoneyGainRate = function() {
|
||||
//How much extra $/s is gained per level
|
||||
var gainPerLevel = CONSTANTS.HacknetNodeMoneyGainPerLevel;
|
||||
|
||||
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
|
||||
Math.pow(1.035, this.ram-1) *
|
||||
((this.cores + 5) / 6) *
|
||||
Player.hacknet_node_money_mult *
|
||||
BitNodeMultipliers.HacknetNodeMoney;
|
||||
if (isNaN(this.moneyGainRatePerSecond)) {
|
||||
this.moneyGainRatePerSecond = 0;
|
||||
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer");
|
||||
}
|
||||
|
||||
updateTotalHacknetProduction();
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateLevelUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
var mult = CONSTANTS.HacknetNodeUpgradeLevelMult;
|
||||
var totalMultiplier = 0; //Summed
|
||||
var currLevel = this.level;
|
||||
for (var i = 0; i < levels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
}
|
||||
|
||||
return CONSTANTS.BaseCostForHacknetNode / 2 * totalMultiplier * Player.hacknet_node_level_cost_mult;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseLevelUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateLevelUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If we're at max level, return false
|
||||
if (this.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the number of specified upgrades would exceed the max level, calculate
|
||||
//the maximum number of upgrades and use that
|
||||
if (this.level + levels > CONSTANTS.HacknetNodeMaxLevel) {
|
||||
var diff = Math.max(0, CONSTANTS.HacknetNodeMaxLevel - this.level);
|
||||
return this.purchaseLevelUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
this.level = Math.round(this.level + levels); //Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateRamUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
let totalCost = 0;
|
||||
let numUpgrades = Math.round(Math.log2(this.ram));
|
||||
let currentRam = this.ram;
|
||||
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
let baseCost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHacknetNode;
|
||||
let mult = Math.pow(CONSTANTS.HacknetNodeUpgradeRamMult, numUpgrades);
|
||||
|
||||
totalCost += (baseCost * mult);
|
||||
|
||||
currentRam *= 2;
|
||||
++numUpgrades;
|
||||
}
|
||||
|
||||
totalCost *= Player.hacknet_node_ram_cost_mult
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseRamUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateRamUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if we're already at max
|
||||
if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the number of specified upgrades would exceed the max RAM, calculate the
|
||||
//max possible number of upgrades and use that
|
||||
if (this.ram * Math.pow(2, levels) > CONSTANTS.HacknetNodeMaxRam) {
|
||||
var diff = Math.max(0, Math.log2(Math.round(CONSTANTS.HacknetNodeMaxRam / this.ram)));
|
||||
return this.purchaseRamUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
this.ram *= 2; //Ram is always doubled
|
||||
}
|
||||
this.ram = Math.round(this.ram); //Handle any floating point precision issues
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.calculateCoreUpgradeCost = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
if (isNaN(levels) || levels < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const coreBaseCost = CONSTANTS.BaseCostForHacknetNodeCore;
|
||||
const mult = CONSTANTS.HacknetNodeUpgradeCoreMult;
|
||||
let totalCost = 0;
|
||||
let currentCores = this.cores;
|
||||
for (let i = 0; i < levels; ++i) {
|
||||
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
|
||||
++currentCores;
|
||||
}
|
||||
|
||||
totalCost *= Player.hacknet_node_core_cost_mult;
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
HacknetNode.prototype.purchaseCoreUpgrade = function(levels=1) {
|
||||
levels = Math.round(levels);
|
||||
var cost = this.calculateCoreUpgradeCost(levels);
|
||||
if (isNaN(cost) || levels < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Fail if we're already at max
|
||||
if (this.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the specified number of upgrades would exceed the max Cores, calculate
|
||||
//the max possible number of upgrades and use that
|
||||
if (this.cores + levels > CONSTANTS.HacknetNodeMaxCores) {
|
||||
var diff = Math.max(0, CONSTANTS.HacknetNodeMaxCores - this.cores);
|
||||
return this.purchaseCoreUpgrade(diff);
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player.loseMoney(cost);
|
||||
this.cores = Math.round(this.cores + levels); //Just in case of floating point imprecision
|
||||
this.updateMoneyGainRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Saving and loading HackNets */
|
||||
HacknetNode.prototype.toJSON = function() {
|
||||
return Generic_toJSON("HacknetNode", this);
|
||||
}
|
||||
|
||||
HacknetNode.fromJSON = function(value) {
|
||||
return Generic_fromJSON(HacknetNode, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.HacknetNode = HacknetNode;
|
||||
|
||||
function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* END INTERACTIVE TUTORIAL */
|
||||
|
||||
var cost = getCostOfNextHacknetNode();
|
||||
if (isNaN(cost)) {
|
||||
throw new Error("Cost is NaN");
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
//dialogBoxCreate("You cannot afford to purchase a Hacknet Node!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Auto generate a name for the node for now...TODO
|
||||
var numOwned = Player.hacknetNodes.length;
|
||||
var name = "hacknet-node-" + numOwned;
|
||||
var node = new HacknetNode(name);
|
||||
node.updateMoneyGainRate();
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(node);
|
||||
|
||||
if (routing.isOn(Page.HacknetNodes)) {
|
||||
displayHacknetNodesContent();
|
||||
}
|
||||
updateTotalHacknetProduction();
|
||||
return numOwned;
|
||||
}
|
||||
|
||||
//Calculates the total production from all HacknetNodes
|
||||
function updateTotalHacknetProduction() {
|
||||
var total = 0;
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
|
||||
}
|
||||
TotalHacknetNodeProduction = total;
|
||||
}
|
||||
|
||||
function getCostOfNextHacknetNode() {
|
||||
//Cost increases exponentially based on how many you own
|
||||
var numOwned = Player.hacknetNodes.length;
|
||||
var mult = CONSTANTS.HacknetNodePurchaseNextMult;
|
||||
return CONSTANTS.BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
|
||||
}
|
||||
|
||||
var hacknetNodePurchaseMultiplier = 1;
|
||||
function updateHacknetNodesMultiplierButtons() {
|
||||
var mult1x = document.getElementById("hacknet-nodes-1x-multiplier");
|
||||
var mult5x = document.getElementById("hacknet-nodes-5x-multiplier");
|
||||
var mult10x = document.getElementById("hacknet-nodes-10x-multiplier");
|
||||
var multMax = document.getElementById("hacknet-nodes-max-multiplier");
|
||||
mult1x.setAttribute("class", "a-link-button");
|
||||
mult5x.setAttribute("class", "a-link-button");
|
||||
mult10x.setAttribute("class", "a-link-button");
|
||||
multMax.setAttribute("class", "a-link-button");
|
||||
if (Player.hacknetNodes.length == 0) {
|
||||
mult1x.setAttribute("class", "a-link-button-inactive");
|
||||
mult5x.setAttribute("class", "a-link-button-inactive");
|
||||
mult10x.setAttribute("class", "a-link-button-inactive");
|
||||
multMax.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 1) {
|
||||
mult1x.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 5) {
|
||||
mult5x.setAttribute("class", "a-link-button-inactive");
|
||||
} else if (hacknetNodePurchaseMultiplier == 10) {
|
||||
mult10x.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
multMax.setAttribute("class", "a-link-button-inactive");
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node
|
||||
function getMaxNumberLevelUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var min = 1;
|
||||
var max = CONSTANTS.HacknetNodeMaxLevel - 1;
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxLevel - nodeObj.level;
|
||||
if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
while (min <= max) {
|
||||
var curr = (min + max) / 2 | 0;
|
||||
if (curr != CONSTANTS.HacknetNodeMaxLevel &&
|
||||
Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr)) &&
|
||||
Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getMaxNumberRamUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const levelsToMax = Math.round(Math.log2(CONSTANTS.HacknetNodeMaxRam / nodeObj.ram));
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//We'll just loop until we find the max
|
||||
for (let i = levelsToMax-1; i >= 0; --i) {
|
||||
if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getMaxNumberCoreUpgrades(nodeObj) {
|
||||
if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var min = 1;
|
||||
var max = CONSTANTS.HacknetNodeMaxCores - 1;
|
||||
const levelsToMax = CONSTANTS.HacknetNodeMaxCores - nodeObj.cores;
|
||||
if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax))) {
|
||||
return levelsToMax;
|
||||
}
|
||||
|
||||
//Use a binary search to find the max possible number of upgrades
|
||||
while (min <= max) {
|
||||
let curr = (min + max) / 2 | 0;
|
||||
if (curr != CONSTANTS.HacknetNodeMaxCores &&
|
||||
Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr)) &&
|
||||
Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1))) {
|
||||
return Math.min(levelsToMax, curr);
|
||||
} else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr))) {
|
||||
max = curr - 1;
|
||||
} else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr))) {
|
||||
min = curr + 1;
|
||||
} else {
|
||||
return Math.min(levelsToMax, curr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Creates Hacknet Node DOM elements when the page is opened
|
||||
function displayHacknetNodesContent() {
|
||||
//Update Hacknet Nodes button
|
||||
var newPurchaseButton = clearEventListeners("hacknet-nodes-purchase-button");
|
||||
|
||||
newPurchaseButton.addEventListener("click", function() {
|
||||
purchaseHacknet();
|
||||
return false;
|
||||
});
|
||||
|
||||
//Handle Purchase multiplier buttons
|
||||
updateHacknetNodesMultiplierButtons();
|
||||
|
||||
//Remove all old hacknet Node DOM elements
|
||||
var hacknetNodesList = document.getElementById("hacknet-nodes-list");
|
||||
while (hacknetNodesList.firstChild) {
|
||||
hacknetNodesList.removeChild(hacknetNodesList.firstChild);
|
||||
}
|
||||
|
||||
//Then re-create them
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
createHacknetNodeDomElement(Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
updateTotalHacknetProduction();
|
||||
updateHacknetNodesContent();
|
||||
}
|
||||
|
||||
//Update information on all Hacknet Node DOM elements
|
||||
function updateHacknetNodesContent() {
|
||||
//Set purchase button to inactive if not enough money, and update its price display
|
||||
var cost = getCostOfNextHacknetNode();
|
||||
var purchaseButton = getElementById("hacknet-nodes-purchase-button");
|
||||
var formattedCost = numeralWrapper.formatMoney(cost);
|
||||
|
||||
updateText("hacknet-nodes-purchase-button", `Purchase Hacknet Node - ${formattedCost}`);
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
purchaseButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
purchaseButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
|
||||
//Update player's money
|
||||
updateText("hacknet-nodes-player-money", numeralWrapper.formatMoney(Player.money.toNumber()));
|
||||
updateText("hacknet-nodes-total-production", numeralWrapper.formatMoney(TotalHacknetNodeProduction) + " / sec");
|
||||
|
||||
//Update information in each owned hacknet node
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
updateHacknetNodeDomElement(Player.hacknetNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Creates a single Hacknet Node DOM element
|
||||
function createHacknetNodeDomElement(nodeObj) {
|
||||
var nodeName = nodeObj.name;
|
||||
|
||||
var nodeLevelContainer = createElement("div", {
|
||||
class: "hacknet-node-level-container row",
|
||||
innerHTML: "<p>Level:</p><span class=\"text upgradable-info\" id=\"hacknet-node-level-" + nodeName + "\"></span>"
|
||||
});
|
||||
|
||||
var nodeRamContainer = createElement("div", {
|
||||
class: "hacknet-node-ram-container row",
|
||||
innerHTML: "<p>RAM:</p><span class=\"text upgradable-info\" id=\"hacknet-node-ram-" + nodeName + "\"></span>"
|
||||
});
|
||||
|
||||
var nodeCoresContainer = createElement("div", {
|
||||
class: "hacknet-node-cores-container row",
|
||||
innerHTML: "<p>Cores:</p><span class=\"text upgradable-info\" id=\"hacknet-node-cores-" + nodeName + "\"><span>"
|
||||
})
|
||||
var containingDiv = createElement("div", {
|
||||
class: "hacknet-node-container",
|
||||
innerHTML: "<div class=\"hacknet-node-name-container row\">" +
|
||||
"<p>Node name:</p>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-name-" + nodeName + "\"></span>" +
|
||||
"</div>" +
|
||||
"<div class=\"hacknet-node-production-container row\">" +
|
||||
"<p>Production:</p>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-total-production-" + nodeName + "\"></span>" +
|
||||
"<span class=\"text\" id=\"hacknet-node-production-rate-" + nodeName + "\"></span>" +
|
||||
"</div>"
|
||||
});
|
||||
containingDiv.appendChild(nodeLevelContainer);
|
||||
containingDiv.appendChild(nodeRamContainer);
|
||||
containingDiv.appendChild(nodeCoresContainer);
|
||||
|
||||
var listItem = createElement("li", {
|
||||
class: "hacknet-node"
|
||||
});
|
||||
listItem.appendChild(containingDiv);
|
||||
|
||||
//Upgrade buttons
|
||||
nodeLevelContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-level-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberLevelUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseLevelUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
nodeRamContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-ram-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberRamUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseRamUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
nodeCoresContainer.appendChild(createElement("a", {
|
||||
id: "hacknet-node-upgrade-core-" + nodeName,
|
||||
class: "a-link-button-inactive",
|
||||
clickListener: function() {
|
||||
let numUpgrades = hacknetNodePurchaseMultiplier;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
numUpgrades = getMaxNumberCoreUpgrades(nodeObj);
|
||||
}
|
||||
nodeObj.purchaseCoreUpgrade(numUpgrades);
|
||||
updateHacknetNodesContent();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
document.getElementById("hacknet-nodes-list").appendChild(listItem);
|
||||
|
||||
//Set the text and stuff inside the DOM element
|
||||
updateHacknetNodeDomElement(nodeObj);
|
||||
}
|
||||
|
||||
//Updates information on a single hacknet node DOM element
|
||||
function updateHacknetNodeDomElement(nodeObj) {
|
||||
var nodeName = nodeObj.name;
|
||||
|
||||
updateText("hacknet-node-name-" + nodeName, nodeName);
|
||||
updateText("hacknet-node-total-production-" + nodeName, numeralWrapper.formatMoney(nodeObj.totalMoneyGenerated));
|
||||
updateText("hacknet-node-production-rate-" + nodeName, "(" + numeralWrapper.formatMoney(nodeObj.moneyGainRatePerSecond) + " / sec)");
|
||||
updateText("hacknet-node-level-" + nodeName, nodeObj.level);
|
||||
updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB");
|
||||
updateText("hacknet-node-cores-" + nodeName, nodeObj.cores);
|
||||
|
||||
//Upgrade level
|
||||
var upgradeLevelButton = getElementById("hacknet-node-upgrade-level-" + nodeName);
|
||||
|
||||
if (nodeObj.level >= CONSTANTS.HacknetNodeMaxLevel) {
|
||||
updateText("hacknet-node-upgrade-level-" + nodeName, "MAX LEVEL");
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
//Max
|
||||
multiplier = getMaxNumberLevelUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxLevel - nodeObj.level;
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
|
||||
var upgradeLevelCost = nodeObj.calculateLevelUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-level-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeLevelCost))
|
||||
if (Player.money.lt(upgradeLevelCost)) {
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeLevelButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
|
||||
//Upgrade RAM
|
||||
var upgradeRamButton = getElementById("hacknet-node-upgrade-ram-" + nodeName);
|
||||
|
||||
if (nodeObj.ram >= CONSTANTS.HacknetNodeMaxRam) {
|
||||
updateText("hacknet-node-upgrade-ram-" + nodeName, "MAX RAM");
|
||||
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
multiplier = getMaxNumberRamUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = Math.round(Math.log2(CONSTANTS.HacknetNodeMaxRam / nodeObj.ram));
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
|
||||
var upgradeRamCost = nodeObj.calculateRamUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-ram-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeRamCost));
|
||||
if (Player.money.lt(upgradeRamCost)) {
|
||||
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeRamButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
|
||||
//Upgrade Cores
|
||||
var upgradeCoreButton = getElementById("hacknet-node-upgrade-core-" + nodeName);
|
||||
|
||||
if (nodeObj.cores >= CONSTANTS.HacknetNodeMaxCores) {
|
||||
updateText("hacknet-node-upgrade-core-" + nodeName, "MAX CORES");
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
let multiplier = 0;
|
||||
if (hacknetNodePurchaseMultiplier == 0) {
|
||||
multiplier = getMaxNumberCoreUpgrades(nodeObj);
|
||||
} else {
|
||||
var levelsToMax = CONSTANTS.HacknetNodeMaxCores - nodeObj.cores;
|
||||
multiplier = Math.min(levelsToMax, hacknetNodePurchaseMultiplier);
|
||||
}
|
||||
var upgradeCoreCost = nodeObj.calculateCoreUpgradeCost(multiplier);
|
||||
updateText("hacknet-node-upgrade-core-" + nodeName, "Upgrade x" + multiplier + " - " + numeralWrapper.formatMoney(upgradeCoreCost));
|
||||
if (Player.money.lt(upgradeCoreCost)) {
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
|
||||
} else {
|
||||
upgradeCoreButton.setAttribute("class", "a-link-button");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function processAllHacknetNodeEarnings(numCycles) {
|
||||
var total = 0;
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function processSingleHacknetNodeEarnings(numCycles, nodeObj) {
|
||||
var cyclesPerSecond = 1000 / Engine._idleSpeed;
|
||||
var earningPerCycle = nodeObj.moneyGainRatePerSecond / cyclesPerSecond;
|
||||
if (isNaN(earningPerCycle)) {
|
||||
console.error("Hacknet Node '" + nodeObj.name + "' Calculated earnings is NaN");
|
||||
earningPerCycle = 0;
|
||||
}
|
||||
|
||||
var totalEarnings = numCycles * earningPerCycle;
|
||||
nodeObj.totalMoneyGenerated += totalEarnings;
|
||||
nodeObj.onlineTimeSeconds += (numCycles * (Engine._idleSpeed / 1000));
|
||||
Player.gainMoney(totalEarnings);
|
||||
Player.recordMoneySource(totalEarnings, "hacknetnode");
|
||||
return totalEarnings;
|
||||
}
|
||||
|
||||
function getHacknetNode(name) {
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (Player.hacknetNodes[i].name == name) {
|
||||
return Player.hacknetNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export {
|
||||
HacknetNode,
|
||||
displayHacknetNodesContent,
|
||||
getCostOfNextHacknetNode,
|
||||
getHacknetNode,
|
||||
getMaxNumberLevelUpgrades,
|
||||
hacknetNodesInit,
|
||||
processAllHacknetNodeEarnings,
|
||||
purchaseHacknet,
|
||||
updateHacknetNodesContent,
|
||||
updateHacknetNodesMultiplierButtons,
|
||||
updateTotalHacknetProduction
|
||||
};
|
||||
10
src/IEngine.ts
Normal file
10
src/IEngine.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* TypeScript interface for the game engine (engine.js), which can't be converted
|
||||
* to TypeScript at the moment
|
||||
*/
|
||||
export interface IEngine {
|
||||
loadBladeburnerContent: () => void;
|
||||
loadInfiltrationContent: () => void;
|
||||
loadResleevingContent: () => void;
|
||||
loadStockMarketContent: () => void;
|
||||
}
|
||||
7
src/Infiltration.d.ts
vendored
Normal file
7
src/Infiltration.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { LocationName } from "./Locations/data/LocationNames";
|
||||
|
||||
export declare function beginInfiltration(companyName: LocationName,
|
||||
startLevel: number,
|
||||
rewardVal: number,
|
||||
maxClearance: number,
|
||||
diff: number): void;
|
||||
@@ -52,41 +52,81 @@ function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff)
|
||||
this.intExpGained = 0;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||
if (!this.clearanceLevel || isNaN(this.clearanceLevel) || !this.maxClearanceLevel ||isNaN(this.maxClearanceLevel)) return 1;
|
||||
return 2.5 * this.clearanceLevel / this.maxClearanceLevel;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.hackingExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedHackingExp = function() {
|
||||
if(!this.hackingExpGained || isNaN(this.hackingExpGained)) return 0;
|
||||
return Math.pow(this.hackingExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.strExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedStrengthExp = function() {
|
||||
if (!this.strExpGained || isNaN(this.strExpGained)) return 0;
|
||||
return Math.pow(this.strExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.defExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDefenseExp = function() {
|
||||
if (!this.defExpGained || isNaN(this.defExpGained)) return 0;
|
||||
return Math.pow(this.defExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.dexExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDexterityExp = function() {
|
||||
if (!this.dexExpGained || isNaN(this.dexExpGained)) return 0;
|
||||
return Math.pow(this.dexExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.agiExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedAgilityExp = function() {
|
||||
if (!this.agiExpGained || isNaN(this.agiExpGained)) return 0;
|
||||
return Math.pow(this.agiExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.chaExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedCharismaExp = function() {
|
||||
if (!this.chaExpGained || isNaN(this.chaExpGained)) return 0;
|
||||
return Math.pow(this.chaExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.intExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
|
||||
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
|
||||
return Math.pow(this.intExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
||||
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
|
||||
clearInfiltrationStatusText();
|
||||
@@ -477,12 +517,12 @@ function updateInfiltrationLevelText(inst) {
|
||||
"Total value of stolen secrets<br>" +
|
||||
"Reputation: <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
|
||||
"Money: <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
|
||||
"Hack exp gained: " + formatNumber(inst.hackingExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Str exp gained: " + formatNumber(inst.strExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Def exp gained: " + formatNumber(inst.defExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Dex exp gained: " + formatNumber(inst.dexExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Agi exp gained: " + formatNumber(inst.agiExpGained * expMultiplier, 3) + "<br>" +
|
||||
"Cha exp gained: " + formatNumber(inst.chaExpGained * expMultiplier, 3);
|
||||
"Hack exp gained: " + formatNumber(inst.calcGainedHackingExp(), 3) + "<br>" +
|
||||
"Str exp gained: " + formatNumber(inst.calcGainedStrengthExp(), 3) + "<br>" +
|
||||
"Def exp gained: " + formatNumber(inst.calcGainedDefenseExp(), 3) + "<br>" +
|
||||
"Dex exp gained: " + formatNumber(inst.calcGainedDexterityExp(), 3) + "<br>" +
|
||||
"Agi exp gained: " + formatNumber(inst.calcGainedAgilityExp(), 3) + "<br>" +
|
||||
"Cha exp gained: " + formatNumber(inst.calcGainedCharismaExp(), 3);
|
||||
/* eslint-enable no-irregular-whitespace */
|
||||
}
|
||||
|
||||
|
||||
@@ -382,13 +382,13 @@ function iTutorialEvaluateStep() {
|
||||
//Flash 'City' menu and set its tutorial click handler
|
||||
cityMainMenu.setAttribute("class", "flashing-button");
|
||||
cityMainMenu.addEventListener("click", function() {
|
||||
Engine.loadWorldContent();
|
||||
Engine.loadLocationContent();
|
||||
iTutorialNextStep();
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
case iTutorialSteps.WorldDescription:
|
||||
Engine.loadWorldContent();
|
||||
Engine.loadLocationContent();
|
||||
iTutorialSetText("This page lists all of the different locations you can currently " +
|
||||
"travel to. Each location has something that you can do. " +
|
||||
"There's a lot of content out in the world, make sure " +
|
||||
|
||||
2295
src/Location.js
2295
src/Location.js
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
import { IMap } from "./types";
|
||||
|
||||
/**
|
||||
* Display Location Content when visiting somewhere in the World
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const Locations: IMap<string> = {
|
||||
// Cities
|
||||
Aevum: "Aevum",
|
||||
Chongqing: "Chongqing",
|
||||
Ishima: "Ishima",
|
||||
NewTokyo: "New Tokyo",
|
||||
Sector12: "Sector-12",
|
||||
Volhaven: "Volhaven",
|
||||
|
||||
// Aevum Locations
|
||||
AevumAeroCorp: "AeroCorp",
|
||||
AevumBachmanAndAssociates: "Bachman & Associates",
|
||||
AevumClarkeIncorporated: "Clarke Incorporated",
|
||||
AevumCrushFitnessGym: "Crush Fitness Gym",
|
||||
AevumECorp: "ECorp",
|
||||
AevumFulcrumTechnologies: "Fulcrum Technologies",
|
||||
AevumGalacticCybersystems: "Galactic Cybersystems",
|
||||
AevumNetLinkTechnologies: "NetLink Technologies",
|
||||
AevumPolice: "Aevum Police Headquarters",
|
||||
AevumRhoConstruction: "Rho Construction",
|
||||
AevumSlums: "Aevum Slums",
|
||||
AevumSnapFitnessGym: "Snap Fitness Gym",
|
||||
AevumSummitUniversity: "Summit University",
|
||||
AevumTravelAgency: "Aevum Travel Agency",
|
||||
AevumWatchdogSecurity: "Watchdog Security",
|
||||
|
||||
// Chongqing locations
|
||||
ChongqingKuaiGongInternational: "KuaiGong International",
|
||||
ChongqingSlums: "Chongqing Slums",
|
||||
ChongqingSolarisSpaceSystems: "Solaris Space Systems",
|
||||
ChongqingTravelAgency: "Chongqing Travel Agency",
|
||||
|
||||
// Sector 12
|
||||
Sector12AlphaEnterprises: "Alpha Enterprises",
|
||||
Sector12BladeIndustries: "Blade Industries",
|
||||
Sector12CIA: "Central Intelligence Agency",
|
||||
Sector12CarmichaelSecurity: "Carmichael Security",
|
||||
Sector12CityHall: "Sector-12 City Hall",
|
||||
Sector12DeltaOne: "DeltaOne",
|
||||
Sector12FoodNStuff: "FoodNStuff",
|
||||
Sector12FourSigma: "Four Sigma",
|
||||
Sector12IcarusMicrosystems: "Icarus Microsystems",
|
||||
Sector12IronGym: "Iron Gym",
|
||||
Sector12JoesGuns: "Joe's Guns",
|
||||
Sector12MegaCorp: "MegaCorp",
|
||||
Sector12NSA: "National Security Agency",
|
||||
Sector12PowerhouseGym: "Powerhouse Gym",
|
||||
Sector12RothmanUniversity: "Rothman University",
|
||||
Sector12Slums: "Sector-12 Slums",
|
||||
Sector12TravelAgency: "Sector-12 Travel Agency",
|
||||
Sector12UniversalEnergy: "Universal Energy",
|
||||
|
||||
// New Tokyo
|
||||
NewTokyoDefComm: "DefComm",
|
||||
NewTokyoGlobalPharmaceuticals: "Global Pharmaceuticals",
|
||||
NewTokyoNoodleBar: "Noodle Bar",
|
||||
NewTokyoSlums: "New Tokyo Slums",
|
||||
NewTokyoTravelAgency: "New Tokyo Travel Agency",
|
||||
NewTokyoVitaLife: "VitaLife",
|
||||
|
||||
// Ishima
|
||||
IshimaNovaMedical: "Nova Medical",
|
||||
IshimaOmegaSoftware: "Omega Software",
|
||||
IshimaSlums: "Ishima Slums",
|
||||
IshimaStormTechnologies: "Storm Technologies",
|
||||
IshimaTravelAgency: "Ishima Travel Agency",
|
||||
|
||||
// Volhaven
|
||||
VolhavenCompuTek: "CompuTek",
|
||||
VolhavenHeliosLabs: "Helios Labs",
|
||||
VolhavenLexoCorp: "LexoCorp",
|
||||
VolhavenMilleniumFitnessGym: "Millenium Fitness Gym",
|
||||
VolhavenNWO: "NWO",
|
||||
VolhavenOmniTekIncorporated: "OmniTek Incorporated",
|
||||
VolhavenOmniaCybersystems: "Omnia Cybersystems",
|
||||
VolhavenSlums: "Volhaven Slums",
|
||||
VolhavenSysCoreSecurities: "SysCore Securities",
|
||||
VolhavenTravelAgency: "Volhaven Travel Agency",
|
||||
VolhavenZBInstituteOfTechnology: "ZB Institute of Technology",
|
||||
|
||||
// Generic locations
|
||||
Hospital: "Hospital",
|
||||
WorldStockExchange: "World Stock Exchange",
|
||||
};
|
||||
@@ -1,14 +1,8 @@
|
||||
/**
|
||||
* Map of all Cities in the game
|
||||
* Key = City Name, Value = City object
|
||||
*/
|
||||
import { City } from "./City";
|
||||
import { IMap } from "../types";
|
||||
|
||||
/**
|
||||
* Display Location Content when visiting somewhere in the World
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const Cities: IMap<string> = {
|
||||
Aevum: "Aevum",
|
||||
Chongqing: "Chongqing",
|
||||
Ishima: "Ishima",
|
||||
NewTokyo: "New Tokyo",
|
||||
Sector12: "Sector-12",
|
||||
Volhaven: "Volhaven",
|
||||
};
|
||||
export const Cities: IMap<City> = {};
|
||||
|
||||
26
src/Locations/City.ts
Normal file
26
src/Locations/City.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Class representing a City in the game
|
||||
*/
|
||||
import { CityName } from "./data/CityNames";
|
||||
import { LocationName } from "./data/LocationNames";
|
||||
|
||||
export class City {
|
||||
/**
|
||||
* List of all locations in this city
|
||||
*/
|
||||
locations: LocationName[];
|
||||
|
||||
/**
|
||||
* Name of this city
|
||||
*/
|
||||
name: CityName;
|
||||
|
||||
constructor(name: CityName, locations: LocationName[]=[]) {
|
||||
this.name = name;
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
addLocation(loc: LocationName): void {
|
||||
this.locations.push(loc);
|
||||
}
|
||||
}
|
||||
82
src/Locations/Location.ts
Normal file
82
src/Locations/Location.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Class representing a visitable location in the world
|
||||
*/
|
||||
import { CityName } from "./data/CityNames";
|
||||
import { LocationName } from "./data/LocationNames";
|
||||
import { LocationType } from "./LocationTypeEnum";
|
||||
|
||||
interface IInfiltrationMetadata {
|
||||
baseRewardValue: number;
|
||||
difficulty: number;
|
||||
maxClearanceLevel: number;
|
||||
startingSecurityLevel: number;
|
||||
}
|
||||
|
||||
export interface IConstructorParams {
|
||||
city?: CityName | null;
|
||||
costMult?: number;
|
||||
expMult?: number;
|
||||
infiltrationData?: IInfiltrationMetadata;
|
||||
name?: LocationName;
|
||||
types?: LocationType[];
|
||||
techVendorMaxRam?: number;
|
||||
techVendorMinRam?: number;
|
||||
}
|
||||
|
||||
export class Location {
|
||||
/**
|
||||
* Name of city this location is in. If this property is null, it means this i
|
||||
* is a generic location that is available in all cities
|
||||
*/
|
||||
city: CityName | null = null;
|
||||
|
||||
/**
|
||||
* Cost multiplier that influences how expensive a gym/university is
|
||||
*/
|
||||
costMult: number = 0;
|
||||
|
||||
/**
|
||||
* Exp multiplier that influences how effective a gym/university is
|
||||
*/
|
||||
expMult: number = 0;
|
||||
|
||||
/**
|
||||
* Companies can be infiltrated. This contains the data required for that
|
||||
* infiltration event
|
||||
*/
|
||||
infiltrationData?: IInfiltrationMetadata;
|
||||
|
||||
/**
|
||||
* Identifier for location
|
||||
*/
|
||||
name: LocationName = LocationName.Void;
|
||||
|
||||
/**
|
||||
* List of what type(s) this location is. A location can be multiple types
|
||||
* (e.g. company and tech vendor)
|
||||
*/
|
||||
types: LocationType[] = [];
|
||||
|
||||
/**
|
||||
* Tech vendors allow you to purchase servers.
|
||||
* This property defines the max RAM server you can purchase from this vendor
|
||||
*/
|
||||
techVendorMaxRam: number = 0;
|
||||
|
||||
/**
|
||||
* Tech vendors allow you to purchase servers.
|
||||
* This property defines the max RAM server you can purchase from this vendor
|
||||
*/
|
||||
techVendorMinRam: number = 0;
|
||||
|
||||
constructor(p: IConstructorParams) {
|
||||
if (p.city) { this.city = p.city; }
|
||||
if (p.costMult) { this.costMult = p.costMult; }
|
||||
if (p.expMult) { this.expMult = p.expMult; }
|
||||
if (p.infiltrationData) { this.infiltrationData = p.infiltrationData; }
|
||||
if (p.name) { this.name = p.name; }
|
||||
if (p.types) { this.types = p.types; }
|
||||
if (p.techVendorMaxRam) { this.techVendorMaxRam = p.techVendorMaxRam; }
|
||||
if (p.techVendorMinRam) { this.techVendorMinRam = p.techVendorMinRam; }
|
||||
}
|
||||
}
|
||||
14
src/Locations/LocationTypeEnum.ts
Normal file
14
src/Locations/LocationTypeEnum.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Enum defining the different types of possible locations
|
||||
*/
|
||||
export enum LocationType {
|
||||
Company,
|
||||
Gym,
|
||||
Hospital,
|
||||
Slums,
|
||||
Special, // This location has special options/activities (e.g. Bladeburner, Re-sleeving)
|
||||
StockMarket,
|
||||
TechVendor,
|
||||
TravelAgency,
|
||||
University,
|
||||
}
|
||||
56
src/Locations/Locations.ts
Normal file
56
src/Locations/Locations.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Map of all Locations in the game
|
||||
* Key = Location name, value = Location object
|
||||
*/
|
||||
import { City } from "./City";
|
||||
import { Cities } from "./Cities";
|
||||
import { Location,
|
||||
IConstructorParams } from "./Location";
|
||||
import { CityName } from "./data/CityNames";
|
||||
import { LocationsMetadata } from "./data/LocationsMetadata";
|
||||
|
||||
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const Locations: IMap<Location> = {};
|
||||
|
||||
/**
|
||||
* Here, we'll initialize both Locations and Cities data. These can both
|
||||
* be initialized from the `LocationsMetadata`
|
||||
*/
|
||||
function constructLocation(p: IConstructorParams): Location {
|
||||
if (!p.name) {
|
||||
throw new Error(`Invalid constructor parameters for Location. No 'name' property`);
|
||||
}
|
||||
|
||||
if (Locations[p.name] instanceof Location) {
|
||||
console.warn(`Property with name ${p.name} already exists and is being overwritten`);
|
||||
}
|
||||
|
||||
Locations[p.name] = new Location(p);
|
||||
|
||||
return Locations[p.name];
|
||||
}
|
||||
|
||||
// First construct all cities
|
||||
Cities[CityName.Aevum] = new City(CityName.Aevum);
|
||||
Cities[CityName.Chongqing] = new City(CityName.Chongqing);
|
||||
Cities[CityName.Ishima] = new City(CityName.Ishima);
|
||||
Cities[CityName.NewTokyo] = new City(CityName.NewTokyo);
|
||||
Cities[CityName.Sector12] = new City(CityName.Sector12);
|
||||
Cities[CityName.Volhaven] = new City(CityName.Volhaven);
|
||||
|
||||
// Then construct all locations, and add them to the cities as we go.
|
||||
for (const metadata of LocationsMetadata) {
|
||||
const loc = constructLocation(metadata);
|
||||
|
||||
const cityName = loc.city;
|
||||
if (cityName === null) {
|
||||
// Generic location, add to all cities
|
||||
for (const city in Cities) {
|
||||
Cities[city].addLocation(loc.name);
|
||||
}
|
||||
} else {
|
||||
Cities[cityName].addLocation(loc.name);
|
||||
}
|
||||
}
|
||||
282
src/Locations/LocationsHelpers.ts
Normal file
282
src/Locations/LocationsHelpers.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* Location and traveling-related helper functions.
|
||||
* Mostly used for UI
|
||||
*/
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { CityName } from "./data/CityNames";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { AllServers,
|
||||
AddToAllServers } from "../Server/AllServers";
|
||||
import { Server } from "../Server/Server";
|
||||
import { getPurchaseServerCost,
|
||||
purchaseRamForHomeComputer,
|
||||
purchaseServer } from "../Server/ServerPurchases";
|
||||
import { SpecialServerIps } from "../Server/SpecialServerIps";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
import { yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton,
|
||||
yesNoBoxClose,
|
||||
yesNoBoxCreate,
|
||||
yesNoTxtInpBoxGetYesButton,
|
||||
yesNoTxtInpBoxGetNoButton,
|
||||
yesNoTxtInpBoxClose,
|
||||
yesNoTxtInpBoxCreate } from "../../utils/YesNoBox";
|
||||
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../../utils/uiHelpers/createPopup";
|
||||
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
|
||||
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* The actual "Travel" implementation is implemented in the UI, and is passed in
|
||||
* as an argument
|
||||
*/
|
||||
type TravelFunction = (to: CityName) => void;
|
||||
export function createTravelPopup(destination: CityName, travelFn: TravelFunction) {
|
||||
const cost = CONSTANTS.TravelCost;
|
||||
|
||||
if (Settings.SuppressTravelConfirmation) {
|
||||
travelFn(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
if (yesBtn == null || noBtn == null) {
|
||||
console.warn(`Could nto find YesNo pop-up box buttons`);
|
||||
return;
|
||||
}
|
||||
|
||||
yesBtn.innerHTML = "Yes";
|
||||
yesBtn.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
travelFn(destination);
|
||||
return false;
|
||||
});
|
||||
|
||||
noBtn.innerHTML = "No";
|
||||
noBtn.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
return false;
|
||||
});
|
||||
|
||||
yesNoBoxCreate(`Would you like to travel to ${destination}? The trip will ` +
|
||||
`cost ${numeralWrapper.formatMoney(cost)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a pop-up box that lets the player purchase a server.
|
||||
* @param ram - Amount of RAM (GB) on server
|
||||
* @param p - Player object
|
||||
*/
|
||||
export function createPurchaseServerPopup(ram: number, p: IPlayer) {
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
|
||||
return;
|
||||
}
|
||||
|
||||
var yesBtn = yesNoTxtInpBoxGetYesButton();
|
||||
var noBtn = yesNoTxtInpBoxGetNoButton();
|
||||
if (yesBtn == null || noBtn == null) { return; }
|
||||
yesBtn.innerHTML = "Purchase Server";
|
||||
noBtn.innerHTML = "Cancel";
|
||||
yesBtn.addEventListener("click", function() {
|
||||
purchaseServer(ram, p);
|
||||
yesNoTxtInpBoxClose();
|
||||
});
|
||||
noBtn.addEventListener("click", function() {
|
||||
yesNoTxtInpBoxClose();
|
||||
});
|
||||
|
||||
yesNoTxtInpBoxCreate(
|
||||
`Would you like to purchase a new server with ${ram} GB of RAM for ` +
|
||||
`${numeralWrapper.formatMoney(cost)}?<br><br>Please enter the server hostname below:<br>`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a popup that lets the player start a Corporation
|
||||
*/
|
||||
export function createStartCorporationPopup(p: IPlayer) {
|
||||
if (!p.canAccessCorporation() || p.hasCorporation) { return; }
|
||||
|
||||
const popupId = "create-corporation-popup";
|
||||
const txt = createElement("p", {
|
||||
innerHTML: "Would you like to start a corporation? This will require $150b for registration " +
|
||||
"and initial funding. This $150b can either be self-funded, or you can obtain " +
|
||||
"the seed money from the government in exchange for 500 million shares<br><br>" +
|
||||
"If you would like to start one, please enter a name for your corporation below:",
|
||||
});
|
||||
|
||||
const nameInput = createElement("input", {
|
||||
placeholder: "Corporation Name",
|
||||
}) as HTMLInputElement;
|
||||
|
||||
const selfFundedButton = createElement("button", {
|
||||
class: "popup-box-button",
|
||||
innerText: "Self-Fund",
|
||||
clickListener: () => {
|
||||
if (!p.canAfford(150e9)) {
|
||||
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b");
|
||||
return false;
|
||||
}
|
||||
p.loseMoney(150e9);
|
||||
|
||||
const companyName = nameInput.value;
|
||||
if (companyName == null || companyName == "") {
|
||||
dialogBoxCreate("Invalid company name!");
|
||||
return false;
|
||||
}
|
||||
|
||||
p.startCorporation(companyName);
|
||||
|
||||
const worldHeader = document.getElementById("world-menu-header");
|
||||
if (worldHeader instanceof HTMLElement) {
|
||||
worldHeader.click(); worldHeader.click();
|
||||
}
|
||||
dialogBoxCreate("Congratulations! You just self-funded your own corporation. You can visit " +
|
||||
"and manage your company in the City");
|
||||
removeElementById(popupId);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const seedMoneyButton = createElement("button", {
|
||||
class: "popup-box-button",
|
||||
innerText: "Use Seed Money",
|
||||
clickListener: () => {
|
||||
const companyName = nameInput.value;
|
||||
if (companyName == null || companyName == "") {
|
||||
dialogBoxCreate("Invalid company name!");
|
||||
return false;
|
||||
}
|
||||
|
||||
p.startCorporation(companyName, 500e6);
|
||||
|
||||
const worldHeader = document.getElementById("world-menu-header");
|
||||
if (worldHeader instanceof HTMLElement) {
|
||||
worldHeader.click(); worldHeader.click();
|
||||
}
|
||||
dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " +
|
||||
"You can visit and manage your company in the City");
|
||||
removeElementById(popupId);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
const cancelBtn = createPopupCloseButton(popupId, { class: "popup-box-button" });
|
||||
|
||||
createPopup(popupId, [txt, nameInput, cancelBtn, selfFundedButton, seedMoneyButton]);
|
||||
nameInput.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a popup that lets the player upgrade the cores on his/her home computer
|
||||
* @param p - Player object
|
||||
*/
|
||||
export function createUpgradeHomeCoresPopup(p: IPlayer) {
|
||||
const currentCores = p.getHomeComputer().cpuCores;
|
||||
if (currentCores >= 8) { return; } // Max of 8 cores
|
||||
|
||||
//Cost of purchasing another cost is found by indexing this array with number of current cores
|
||||
const allCosts = [0,
|
||||
10e9, // 1->2 Cores - 10 bn
|
||||
250e9, // 2->3 Cores - 250 bn
|
||||
5e12, // 3->4 Cores - 5 trillion
|
||||
100e12, // 4->5 Cores - 100 trillion
|
||||
1e15, // 5->6 Cores - 1 quadrillion
|
||||
20e15, // 6->7 Cores - 20 quadrillion
|
||||
200e15]; // 7->8 Cores - 200 quadrillion
|
||||
const cost: number = allCosts[currentCores];
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
if (yesBtn == null || noBtn == null) { return; }
|
||||
|
||||
yesBtn.innerHTML = "Purchase";
|
||||
yesBtn.addEventListener("click", ()=>{
|
||||
if (!p.canAfford(cost)) {
|
||||
dialogBoxCreate("You do not have enough money to purchase an additional CPU Core for your home computer!");
|
||||
} else {
|
||||
p.loseMoney(cost);
|
||||
p.getHomeComputer().cpuCores++;
|
||||
dialogBoxCreate("You purchased an additional CPU Core for your home computer! It now has " +
|
||||
p.getHomeComputer().cpuCores + " cores.");
|
||||
}
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
noBtn.innerHTML = "Cancel";
|
||||
noBtn.addEventListener("click", ()=>{
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
yesNoBoxCreate("Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " +
|
||||
"lets you start with an additional Core Node in Hacking Missions.<br><br>" +
|
||||
"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
|
||||
* @param p - Player object
|
||||
*/
|
||||
export function createUpgradeHomeRamPopup(p: IPlayer) {
|
||||
const cost: number = p.getUpgradeHomeRamCost();
|
||||
const ram: number = p.getHomeComputer().maxRam;
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
if (yesBtn == null || noBtn == null) { return; }
|
||||
|
||||
yesBtn.innerText = "Purchase";
|
||||
yesBtn.addEventListener("click", ()=>{
|
||||
purchaseRamForHomeComputer(cost, p);
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
noBtn.innerText = "Cancel";
|
||||
noBtn.addEventListener("click", ()=>{
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
yesNoBoxCreate("Would you like to purchase additional RAM for your home computer? <br><br>" +
|
||||
"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
|
||||
* @param p - Player object
|
||||
*/
|
||||
export function purchaseTorRouter(p: IPlayer) {
|
||||
if (!p.canAfford(CONSTANTS.TorRouterCost)) {
|
||||
dialogBoxCreate("You cannot afford to purchase the Tor router");
|
||||
return;
|
||||
}
|
||||
p.loseMoney(CONSTANTS.TorRouterCost);
|
||||
|
||||
const darkweb = new Server({
|
||||
ip: createRandomIp(), hostname:"darkweb", organizationName:"",
|
||||
isConnectedTo:false, adminRights:false, purchasedByPlayer:false, maxRam:1
|
||||
});
|
||||
AddToAllServers(darkweb);
|
||||
SpecialServerIps.addIp("Darkweb Server", darkweb.ip);
|
||||
|
||||
p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
|
||||
darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
|
||||
dialogBoxCreate("You have purchased a Tor router!<br>" +
|
||||
"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.");
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { IMap } from "../types";
|
||||
|
||||
export function createCityMap<T>(initValue: T): IMap<T> {
|
||||
const map: IMap<any> = {};
|
||||
const cities = Object.values(Cities);
|
||||
const cities = Object.keys(Cities);
|
||||
for (let i = 0; i < cities.length; ++i) {
|
||||
map[cities[i]] = initValue;
|
||||
}
|
||||
|
||||
12
src/Locations/data/CityNames.ts
Normal file
12
src/Locations/data/CityNames.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* All possible Cities in the game. Names only, not actual "City" object
|
||||
* Implemented as an enum for typing purposes
|
||||
*/
|
||||
export enum CityName {
|
||||
Aevum = "Aevum",
|
||||
Chongqing = "Chongqing",
|
||||
Ishima = "Ishima",
|
||||
NewTokyo = "New Tokyo",
|
||||
Sector12 = "Sector-12",
|
||||
Volhaven = "Volhaven",
|
||||
};
|
||||
80
src/Locations/data/LocationNames.ts
Normal file
80
src/Locations/data/LocationNames.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Names of all locations
|
||||
*/
|
||||
export enum LocationName {
|
||||
// Cities
|
||||
Aevum = "Aevum",
|
||||
Chongqing = "Chongqing",
|
||||
Ishima = "Ishima",
|
||||
NewTokyo = "New Tokyo",
|
||||
Sector12 = "Sector-12",
|
||||
Volhaven = "Volhaven",
|
||||
|
||||
// Aevum Locations
|
||||
AevumAeroCorp = "AeroCorp",
|
||||
AevumBachmanAndAssociates = "Bachman & Associates",
|
||||
AevumClarkeIncorporated = "Clarke Incorporated",
|
||||
AevumCrushFitnessGym = "Crush Fitness Gym",
|
||||
AevumECorp = "ECorp",
|
||||
AevumFulcrumTechnologies = "Fulcrum Technologies",
|
||||
AevumGalacticCybersystems = "Galactic Cybersystems",
|
||||
AevumNetLinkTechnologies = "NetLink Technologies",
|
||||
AevumPolice = "Aevum Police Headquarters",
|
||||
AevumRhoConstruction = "Rho Construction",
|
||||
AevumSnapFitnessGym = "Snap Fitness Gym",
|
||||
AevumSummitUniversity = "Summit University",
|
||||
AevumWatchdogSecurity = "Watchdog Security",
|
||||
|
||||
// Chongqing locations
|
||||
ChongqingKuaiGongInternational = "KuaiGong International",
|
||||
ChongqingSolarisSpaceSystems = "Solaris Space Systems",
|
||||
|
||||
// Sector 12
|
||||
Sector12AlphaEnterprises = "Alpha Enterprises",
|
||||
Sector12BladeIndustries = "Blade Industries",
|
||||
Sector12CIA = "Central Intelligence Agency",
|
||||
Sector12CarmichaelSecurity = "Carmichael Security",
|
||||
Sector12CityHall = "Sector-12 City Hall",
|
||||
Sector12DeltaOne = "DeltaOne",
|
||||
Sector12FoodNStuff = "FoodNStuff",
|
||||
Sector12FourSigma = "Four Sigma",
|
||||
Sector12IcarusMicrosystems = "Icarus Microsystems",
|
||||
Sector12IronGym = "Iron Gym",
|
||||
Sector12JoesGuns = "Joe's Guns",
|
||||
Sector12MegaCorp = "MegaCorp",
|
||||
Sector12NSA = "National Security Agency",
|
||||
Sector12PowerhouseGym = "Powerhouse Gym",
|
||||
Sector12RothmanUniversity = "Rothman University",
|
||||
Sector12UniversalEnergy = "Universal Energy",
|
||||
|
||||
// New Tokyo
|
||||
NewTokyoDefComm = "DefComm",
|
||||
NewTokyoGlobalPharmaceuticals = "Global Pharmaceuticals",
|
||||
NewTokyoNoodleBar = "Noodle Bar",
|
||||
NewTokyoVitaLife = "VitaLife",
|
||||
|
||||
// Ishima
|
||||
IshimaNovaMedical = "Nova Medical",
|
||||
IshimaOmegaSoftware = "Omega Software",
|
||||
IshimaStormTechnologies = "Storm Technologies",
|
||||
|
||||
// Volhaven
|
||||
VolhavenCompuTek = "CompuTek",
|
||||
VolhavenHeliosLabs = "Helios Labs",
|
||||
VolhavenLexoCorp = "LexoCorp",
|
||||
VolhavenMilleniumFitnessGym = "Millenium Fitness Gym",
|
||||
VolhavenNWO = "NWO",
|
||||
VolhavenOmniTekIncorporated = "OmniTek Incorporated",
|
||||
VolhavenOmniaCybersystems = "Omnia Cybersystems",
|
||||
VolhavenSysCoreSecurities = "SysCore Securities",
|
||||
VolhavenZBInstituteOfTechnology = "ZB Institute of Technology",
|
||||
|
||||
// Generic locations
|
||||
Hospital = "Hospital",
|
||||
Slums = "The Slums",
|
||||
TravelAgency = "Travel Agency",
|
||||
WorldStockExchange = "World Stock Exchange",
|
||||
|
||||
// Default name for Location objects
|
||||
Void = "The Void",
|
||||
};
|
||||
502
src/Locations/data/LocationsMetadata.ts
Normal file
502
src/Locations/data/LocationsMetadata.ts
Normal file
@@ -0,0 +1,502 @@
|
||||
/**
|
||||
* Metadata for constructing Location objects for all Locations
|
||||
* in the game
|
||||
*/
|
||||
import { CityName } from "./CityNames";
|
||||
import { LocationName } from "./LocationNames";
|
||||
import { IConstructorParams } from "../Location";
|
||||
import { LocationType } from "../LocationTypeEnum";
|
||||
|
||||
export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 32,
|
||||
difficulty: 4.4,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 1350,
|
||||
},
|
||||
name: LocationName.AevumAeroCorp,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 42,
|
||||
difficulty: 4.1,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 1350,
|
||||
},
|
||||
name: LocationName.AevumBachmanAndAssociates,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 34,
|
||||
difficulty: 3.6,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 1800,
|
||||
},
|
||||
name: LocationName.AevumClarkeIncorporated,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
costMult: 3,
|
||||
expMult: 2,
|
||||
name: LocationName.AevumCrushFitnessGym,
|
||||
types: [LocationType.Gym],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 116,
|
||||
difficulty: 6,
|
||||
maxClearanceLevel: 150,
|
||||
startingSecurityLevel: 4800,
|
||||
},
|
||||
name: LocationName.AevumECorp,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 512,
|
||||
techVendorMinRam: 128,
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 96,
|
||||
difficulty: 6.2,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 4140,
|
||||
},
|
||||
name: LocationName.AevumFulcrumTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 1024,
|
||||
techVendorMinRam: 256,
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 30,
|
||||
difficulty: 3.95,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 1260,
|
||||
},
|
||||
name: LocationName.AevumGalacticCybersystems,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 10,
|
||||
difficulty: 1.4,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 144,
|
||||
},
|
||||
name: LocationName.AevumNetLinkTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 64,
|
||||
techVendorMinRam: 8,
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.2,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 565,
|
||||
},
|
||||
name: LocationName.AevumPolice,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 16,
|
||||
difficulty: 1.9,
|
||||
maxClearanceLevel: 20,
|
||||
startingSecurityLevel: 485,
|
||||
},
|
||||
name: LocationName.AevumRhoConstruction,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
costMult: 10,
|
||||
expMult: 5,
|
||||
name: LocationName.AevumSnapFitnessGym,
|
||||
types: [LocationType.Gym],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
costMult: 4,
|
||||
expMult: 3,
|
||||
name: LocationName.AevumSummitUniversity,
|
||||
types: [LocationType.University],
|
||||
},
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 20,
|
||||
difficulty: 3,
|
||||
maxClearanceLevel: 30,
|
||||
startingSecurityLevel: 690,
|
||||
},
|
||||
name: LocationName.AevumWatchdogSecurity,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Chongqing,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 100,
|
||||
difficulty: 6.1,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 4450,
|
||||
},
|
||||
name: LocationName.ChongqingKuaiGongInternational,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Chongqing,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 52,
|
||||
difficulty: 6,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 2915,
|
||||
},
|
||||
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 20,
|
||||
difficulty: 3.2,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 485,
|
||||
},
|
||||
name: LocationName.IshimaNovaMedical,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 10,
|
||||
difficulty: 1.6,
|
||||
maxClearanceLevel: 40,
|
||||
startingSecurityLevel: 130,
|
||||
},
|
||||
name: LocationName.IshimaOmegaSoftware,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 128,
|
||||
techVendorMinRam: 4,
|
||||
},
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.1,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 570,
|
||||
},
|
||||
name: LocationName.IshimaStormTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 512,
|
||||
techVendorMinRam: 32,
|
||||
},
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 4,
|
||||
maxClearanceLevel: 70,
|
||||
startingSecurityLevel: 1050,
|
||||
},
|
||||
name: LocationName.NewTokyoDefComm,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 3.8,
|
||||
maxClearanceLevel: 80,
|
||||
startingSecurityLevel: 700,
|
||||
},
|
||||
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
name: LocationName.NewTokyoNoodleBar,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 22,
|
||||
difficulty: 3.5,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 605,
|
||||
},
|
||||
name: LocationName.NewTokyoVitaLife,
|
||||
types: [LocationType.Company, LocationType.Special],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 14,
|
||||
difficulty: 2.25,
|
||||
maxClearanceLevel: 40,
|
||||
startingSecurityLevel: 200,
|
||||
},
|
||||
name: LocationName.Sector12AlphaEnterprises,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 8,
|
||||
techVendorMinRam: 2,
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 46,
|
||||
difficulty: 4.2,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 2160,
|
||||
},
|
||||
name: LocationName.Sector12BladeIndustries,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
name: LocationName.Sector12CIA,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.5,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 405,
|
||||
},
|
||||
name: LocationName.Sector12CarmichaelSecurity,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
name: LocationName.Sector12CityHall,
|
||||
types: [LocationType.Special],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.3,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 700,
|
||||
},
|
||||
name: LocationName.Sector12DeltaOne,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
name: LocationName.Sector12FoodNStuff,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 58,
|
||||
difficulty: 7,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 1350,
|
||||
},
|
||||
name: LocationName.Sector12FourSigma,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 32,
|
||||
difficulty: 5.4,
|
||||
maxClearanceLevel: 70,
|
||||
startingSecurityLevel: 730,
|
||||
},
|
||||
name: LocationName.Sector12IcarusMicrosystems,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
expMult: 1,
|
||||
costMult: 1,
|
||||
name: LocationName.Sector12IronGym,
|
||||
types: [LocationType.Gym],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 8,
|
||||
difficulty: 1.8,
|
||||
maxClearanceLevel: 20,
|
||||
startingSecurityLevel: 120,
|
||||
},
|
||||
name: LocationName.Sector12JoesGuns,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 114,
|
||||
difficulty: 6.75,
|
||||
maxClearanceLevel: 125,
|
||||
startingSecurityLevel: 4500,
|
||||
},
|
||||
name: LocationName.Sector12MegaCorp,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
name: LocationName.Sector12NSA,
|
||||
types: [LocationType.Company, LocationType.Special],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
costMult: 20,
|
||||
expMult: 10,
|
||||
name: LocationName.Sector12PowerhouseGym,
|
||||
types: [LocationType.Gym],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
costMult: 3,
|
||||
expMult: 2,
|
||||
name: LocationName.Sector12RothmanUniversity,
|
||||
types: [LocationType.University],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.3,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 700,
|
||||
},
|
||||
name: LocationName.Sector12UniversalEnergy,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 12,
|
||||
difficulty: 2.1,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 195,
|
||||
},
|
||||
name: LocationName.VolhavenCompuTek,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 256,
|
||||
techVendorMinRam: 8,
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 3,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 1080,
|
||||
},
|
||||
name: LocationName.VolhavenHeliosLabs,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 14,
|
||||
difficulty: 2,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 340,
|
||||
},
|
||||
name: LocationName.VolhavenLexoCorp,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
costMult: 7,
|
||||
expMult: 4,
|
||||
name: LocationName.VolhavenMilleniumFitnessGym,
|
||||
types: [LocationType.Gym],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 56,
|
||||
difficulty: 6.8,
|
||||
maxClearanceLevel: 200,
|
||||
startingSecurityLevel: 1460,
|
||||
},
|
||||
name: LocationName.VolhavenNWO,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 44,
|
||||
difficulty: 4.4,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 1215,
|
||||
},
|
||||
name: LocationName.VolhavenOmniTekIncorporated,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
techVendorMaxRam: 1024,
|
||||
techVendorMinRam: 128,
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 4.9,
|
||||
maxClearanceLevel: 90,
|
||||
startingSecurityLevel: 725,
|
||||
},
|
||||
name: LocationName.VolhavenOmniaCybersystems,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.4,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 430,
|
||||
},
|
||||
name: LocationName.VolhavenSysCoreSecurities,
|
||||
types: [LocationType.Company],
|
||||
},
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
costMult: 5,
|
||||
expMult: 4,
|
||||
name: LocationName.VolhavenZBInstituteOfTechnology,
|
||||
types: [LocationType.University],
|
||||
},
|
||||
{
|
||||
city: null,
|
||||
name: LocationName.Hospital,
|
||||
types: [LocationType.Hospital],
|
||||
},
|
||||
{
|
||||
city: null,
|
||||
name: LocationName.Slums,
|
||||
types: [LocationType.Slums],
|
||||
},
|
||||
{
|
||||
city: null,
|
||||
name: LocationName.TravelAgency,
|
||||
types: [LocationType.TravelAgency],
|
||||
},
|
||||
{
|
||||
city: null,
|
||||
name: LocationName.WorldStockExchange,
|
||||
types: [LocationType.StockMarket],
|
||||
},
|
||||
];
|
||||
48
src/Locations/ui/ApplyToJobButton.tsx
Normal file
48
src/Locations/ui/ApplyToJobButton.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* React Component for a button that's used to apply for a job
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Company } from "../../Company/Company";
|
||||
import { CompanyPosition } from "../../Company/CompanyPosition";
|
||||
import { getJobRequirementText } from "../../Company/GetJobRequirementText";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
company: Company;
|
||||
entryPosType: CompanyPosition;
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
p: IPlayer;
|
||||
style?: object;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class ApplyToJobButton extends React.Component<IProps, any> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.getJobRequirementTooltip = this.getJobRequirementTooltip.bind(this);
|
||||
}
|
||||
|
||||
getJobRequirementTooltip(): string {
|
||||
const pos = this.props.p.getNextCompanyPosition(this.props.company, this.props.entryPosType);
|
||||
if (pos == null) { return "" };
|
||||
|
||||
if (!this.props.company.hasPosition(pos)) { return ""; }
|
||||
|
||||
return getJobRequirementText(this.props.company, pos, true);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StdButton
|
||||
onClick={this.props.onClick}
|
||||
style={this.props.style}
|
||||
text={this.props.text}
|
||||
tooltip={this.getJobRequirementTooltip()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
34
src/Locations/ui/City.tsx
Normal file
34
src/Locations/ui/City.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* React Component for displaying a City's UI.
|
||||
* This UI shows all of the available locations in the city, and lets the player
|
||||
* visit those locations
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { City } from "../City";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
city: City;
|
||||
enterLocation: (to: LocationName) => void;
|
||||
}
|
||||
|
||||
export class LocationCity extends React.Component<IProps, any> {
|
||||
render() {
|
||||
const locationButtons = this.props.city.locations.map((locName) => {
|
||||
return (
|
||||
<li key={locName}>
|
||||
<StdButton onClick={this.props.enterLocation.bind(this, locName)} text={locName} />
|
||||
</li>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{locationButtons}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
}
|
||||
371
src/Locations/ui/CompanyLocation.tsx
Normal file
371
src/Locations/ui/CompanyLocation.tsx
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a company
|
||||
*
|
||||
* This subcomponent renders all of the buttons for applying to jobs at a company
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { ApplyToJobButton } from "./ApplyToJobButton";
|
||||
|
||||
import { Location } from "../Location";
|
||||
import { Locations } from "../Locations";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { beginInfiltration } from "../../Infiltration";
|
||||
|
||||
import { Companies } from "../../Company/Companies";
|
||||
import { Company } from "../../Company/Company";
|
||||
import { CompanyPosition } from "../../Company/CompanyPosition";
|
||||
import { CompanyPositions } from "../../Company/CompanyPositions";
|
||||
import * as posNames from "../../Company/data/companypositionnames";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
engine: IEngine;
|
||||
locName: LocationName;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
employedHere: boolean;
|
||||
}
|
||||
|
||||
export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
/**
|
||||
* We'll keep a reference to the Company that this component is being rendered for,
|
||||
* so we don't have to look it up every time
|
||||
*/
|
||||
company: Company;
|
||||
|
||||
/**
|
||||
* CompanyPosition object for the job that the player holds at this company
|
||||
* (if he has one)
|
||||
*/
|
||||
companyPosition: CompanyPosition | null = null;
|
||||
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
/**
|
||||
* Reference to the Location that this component is being rendered for
|
||||
*/
|
||||
location: Location;
|
||||
|
||||
/**
|
||||
* Name of company position that player holds, if applicable
|
||||
*/
|
||||
jobTitle: string | null = null;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.applyForAgentJob = this.applyForAgentJob.bind(this);
|
||||
this.applyForBusinessConsultantJob = this.applyForBusinessConsultantJob.bind(this);
|
||||
this.applyForBusinessJob = this.applyForBusinessJob.bind(this);
|
||||
this.applyForEmployeeJob = this.applyForEmployeeJob.bind(this);
|
||||
this.applyForItJob = this.applyForItJob.bind(this);
|
||||
this.applyForPartTimeEmployeeJob = this.applyForPartTimeEmployeeJob.bind(this);
|
||||
this.applyForPartTimeWaiterJob = this.applyForPartTimeWaiterJob.bind(this);
|
||||
this.applyForSecurityJob = this.applyForSecurityJob.bind(this);
|
||||
this.applyForSoftwareConsultantJob = this.applyForSoftwareConsultantJob.bind(this);
|
||||
this.applyForSoftwareJob = this.applyForSoftwareJob.bind(this);
|
||||
this.applyForWaiterJob = this.applyForWaiterJob.bind(this);
|
||||
this.startInfiltration = this.startInfiltration.bind(this);
|
||||
this.work = this.work.bind(this);
|
||||
|
||||
this.location = Locations[props.locName];
|
||||
if (this.location == null) {
|
||||
throw new Error(`CompanyLocation component constructed with invalid location: ${props.locName}`);
|
||||
}
|
||||
|
||||
this.company = Companies[props.locName];
|
||||
if (this.company == null) {
|
||||
throw new Error(`CompanyLocation component constructed with invalid company: ${props.locName}`);
|
||||
}
|
||||
|
||||
this.state = {
|
||||
employedHere: false,
|
||||
}
|
||||
|
||||
this.checkIfEmployedHere(false);
|
||||
}
|
||||
|
||||
applyForAgentJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForAgentJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForBusinessConsultantJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForBusinessJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForBusinessJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForEmployeeJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForEmployeeJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForItJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForItJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForPartTimeEmployeeJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForPartTimeWaiterJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSecurityJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSecurityJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSoftwareConsultantJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSoftwareJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSoftwareJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForWaiterJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForWaiterJob();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
checkIfEmployedHere(updateState=false) {
|
||||
this.jobTitle = this.props.p.jobs[this.props.locName];
|
||||
if (this.jobTitle != null) {
|
||||
this.companyPosition = CompanyPositions[this.jobTitle];
|
||||
}
|
||||
|
||||
if (updateState) {
|
||||
this.setState({
|
||||
employedHere: this.jobTitle != null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startInfiltration(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
const loc = this.location;
|
||||
|
||||
this.props.engine.loadInfiltrationContent();
|
||||
|
||||
const data = loc.infiltrationData;
|
||||
if (data == null) { return false; }
|
||||
beginInfiltration(this.props.locName, data.startingSecurityLevel, data.baseRewardValue, data.maxClearanceLevel, data.difficulty);
|
||||
}
|
||||
|
||||
work(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
|
||||
const pos = this.companyPosition;
|
||||
if (pos instanceof CompanyPosition) {
|
||||
if (pos.isPartTimeJob() || pos.isSoftwareConsultantJob() || pos.isBusinessConsultantJob()) {
|
||||
this.props.p.startWorkPartTime(this.props.locName);
|
||||
} else {
|
||||
this.props.p.startWork(this.props.locName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const isEmployedHere = this.jobTitle != null;
|
||||
const favorGain = this.company.getFavorGain();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
isEmployedHere &&
|
||||
<div>
|
||||
<p>Job Title: {this.jobTitle}</p>
|
||||
<p>--------------------</p>
|
||||
<p className={"tooltip"}>
|
||||
Company reputation: {numeralWrapper.format(this.company.playerReputation, "0,0.000")}
|
||||
<span className={"tooltiptext"}>
|
||||
You will earn {numeralWrapper.format(favorGain[0], "0,0")} company
|
||||
favor upon resetting after installing Augmentations
|
||||
</span>
|
||||
</p>
|
||||
<p>--------------------</p>
|
||||
<p className={"tooltip"}>
|
||||
Company Favor: {numeralWrapper.format(this.company.favor, "0,0")}
|
||||
<span className={"tooltiptext"}>
|
||||
Company favor increases the rate at which you earn reputation for this company by
|
||||
1% per favor. Company favor is gained whenever you reset after installing Augmentations. The amount
|
||||
of favor you gain depends on how much reputation you have with the comapny.
|
||||
</span>
|
||||
</p>
|
||||
<StdButton
|
||||
id={"foo-work-button-id"}
|
||||
onClick={this.work}
|
||||
style={this.btnStyle}
|
||||
text={"Work"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
this.company.hasAgentPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.AgentCompanyPositions[0]]}
|
||||
onClick={this.applyForAgentJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Agent Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasBusinessConsultantPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]]}
|
||||
onClick={this.applyForBusinessConsultantJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Business Consultant Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasBusinessPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.BusinessCompanyPositions[0]]}
|
||||
onClick={this.applyForBusinessJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Business Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasEmployeePositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[1]]}
|
||||
onClick={this.applyForEmployeeJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply to be an Employee"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasEmployeePositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[1]]}
|
||||
onClick={this.applyForPartTimeEmployeeJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply to be a part-time Employee"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasITPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.ITCompanyPositions[0]]}
|
||||
onClick={this.applyForItJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for IT Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasSecurityPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.SecurityCompanyPositions[2]]}
|
||||
onClick={this.applyForSecurityJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Security Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasSoftwareConsultantPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]]}
|
||||
onClick={this.applyForSoftwareConsultantJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Software Consultant Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasSoftwarePositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.SoftwareCompanyPositions[0]]}
|
||||
onClick={this.applyForSoftwareJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply for Software Job"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasWaiterPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[0]]}
|
||||
onClick={this.applyForWaiterJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply to be a Waiter"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.company.hasWaiterPositions() &&
|
||||
<ApplyToJobButton
|
||||
company={this.company}
|
||||
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[0]]}
|
||||
onClick={this.applyForPartTimeWaiterJob}
|
||||
p={this.props.p}
|
||||
style={this.btnStyle}
|
||||
text={"Apply to be a part-time Waiter"}
|
||||
/>
|
||||
}
|
||||
{
|
||||
(this.location.infiltrationData != null) &&
|
||||
<StdButton
|
||||
onClick={this.startInfiltration}
|
||||
style={this.btnStyle}
|
||||
text={"Infiltration Company"}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
148
src/Locations/ui/GenericLocation.tsx
Normal file
148
src/Locations/ui/GenericLocation.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* React Component for displaying a location's UI
|
||||
*
|
||||
* This is a "router" component of sorts, meaning it deduces the type of
|
||||
* location that is being rendered and then creates the proper component(s) for that.
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CompanyLocation } from "./CompanyLocation";
|
||||
import { GymLocation } from "./GymLocation";
|
||||
import { HospitalLocation } from "./HospitalLocation";
|
||||
import { SlumsLocation } from "./SlumsLocation";
|
||||
import { SpecialLocation } from "./SpecialLocation";
|
||||
import { TechVendorLocation } from "./TechVendorLocation";
|
||||
import { TravelAgencyLocation } from "./TravelAgencyLocation";
|
||||
import { UniversityLocation } from "./UniversityLocation";
|
||||
|
||||
import { Location } from "../Location";
|
||||
import { LocationType } from "../LocationTypeEnum";
|
||||
import { CityName } from "../data/CityNames";
|
||||
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
engine: IEngine;
|
||||
loc: Location;
|
||||
p: IPlayer;
|
||||
returnToCity: () => void;
|
||||
travel: (to: CityName) => void;
|
||||
}
|
||||
|
||||
export class GenericLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what needs to be rendered for this location based on the locations
|
||||
* type. Returns an array of React components that should be rendered
|
||||
*/
|
||||
getLocationSpecificContent(): React.ReactNode[] {
|
||||
const content: React.ReactNode[] = [];
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.Company)) {
|
||||
content.push(
|
||||
<CompanyLocation
|
||||
engine={this.props.engine}
|
||||
key={"companylocation"}
|
||||
locName={this.props.loc.name}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.Gym)) {
|
||||
content.push(
|
||||
<GymLocation
|
||||
key={"gymlocation"}
|
||||
loc={this.props.loc}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.Hospital)) {
|
||||
content.push(
|
||||
<HospitalLocation
|
||||
key={"hospitallocation"}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.Slums)) {
|
||||
content.push(
|
||||
<SlumsLocation
|
||||
key={"slumslocation"}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.Special)) {
|
||||
content.push(
|
||||
<SpecialLocation
|
||||
engine={this.props.engine}
|
||||
key={"speciallocation"}
|
||||
loc={this.props.loc}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.TechVendor)) {
|
||||
content.push(
|
||||
<TechVendorLocation
|
||||
key={"techvendorlocation"}
|
||||
loc={this.props.loc}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.TravelAgency)) {
|
||||
content.push(
|
||||
<TravelAgencyLocation
|
||||
key={"travelagencylocation"}
|
||||
p={this.props.p}
|
||||
travel={this.props.travel}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.loc.types.includes(LocationType.University)) {
|
||||
content.push(
|
||||
<UniversityLocation
|
||||
key={"universitylocation"}
|
||||
loc={this.props.loc}
|
||||
p={this.props.p}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
render() {
|
||||
const locContent: React.ReactNode[] = this.getLocationSpecificContent();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton onClick={this.props.returnToCity} style={this.btnStyle} text={"Return to World"} />
|
||||
<h1>{this.props.loc.name}</h1>
|
||||
{locContent}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
89
src/Locations/ui/GymLocation.tsx
Normal file
89
src/Locations/ui/GymLocation.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a gym
|
||||
*
|
||||
* This subcomponent renders all of the buttons for training at the gym
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Location } from "../Location";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
export class GymLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.trainStrength = this.trainStrength.bind(this);
|
||||
this.trainDefense = this.trainDefense.bind(this);
|
||||
this.trainDexterity = this.trainDexterity.bind(this);
|
||||
this.trainAgility = this.trainAgility.bind(this);
|
||||
}
|
||||
|
||||
train(stat: string) {
|
||||
const loc = this.props.loc;
|
||||
this.props.p.startClass(loc.costMult, loc.expMult, stat);
|
||||
}
|
||||
|
||||
trainStrength() {
|
||||
return this.train(CONSTANTS.ClassGymStrength);
|
||||
}
|
||||
|
||||
trainDefense() {
|
||||
return this.train(CONSTANTS.ClassGymDefense);
|
||||
}
|
||||
|
||||
trainDexterity() {
|
||||
return this.train(CONSTANTS.ClassGymDexterity);
|
||||
}
|
||||
|
||||
trainAgility() {
|
||||
return this.train(CONSTANTS.ClassGymAgility);
|
||||
}
|
||||
|
||||
render() {
|
||||
const costMult: number = this.props.loc.costMult;
|
||||
|
||||
const cost = CONSTANTS.ClassGymBaseCost * costMult;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton
|
||||
onClick={this.trainStrength}
|
||||
style={this.btnStyle}
|
||||
text={`Train Strength (${numeralWrapper.formatMoney(cost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.trainDefense}
|
||||
style={this.btnStyle}
|
||||
text={`Train Defense (${numeralWrapper.formatMoney(cost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.trainDexterity}
|
||||
style={this.btnStyle}
|
||||
text={`Train Dexterity (${numeralWrapper.formatMoney(cost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.trainAgility}
|
||||
style={this.btnStyle}
|
||||
text={`Train Agility (${numeralWrapper.formatMoney(cost)} / sec)`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
76
src/Locations/ui/HospitalLocation.tsx
Normal file
76
src/Locations/ui/HospitalLocation.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a hospital
|
||||
*
|
||||
* This subcomponent renders all of the buttons for hospital options
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
currHp: number;
|
||||
}
|
||||
|
||||
export class HospitalLocation extends React.Component<IProps, IState> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.getCost = this.getCost.bind(this);
|
||||
this.getHealed = this.getHealed.bind(this);
|
||||
|
||||
this.state = {
|
||||
currHp: this.props.p.hp,
|
||||
}
|
||||
}
|
||||
|
||||
getCost(): number {
|
||||
return (this.props.p.max_hp - this.props.p.hp) * CONSTANTS.HospitalCostPerHp;
|
||||
}
|
||||
|
||||
getHealed(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
|
||||
if (this.props.p.hp < 0) { this.props.p.hp = 0; }
|
||||
if (this.props.p.hp >= this.props.p.max_hp) { return; }
|
||||
|
||||
const cost = this.getCost();
|
||||
this.props.p.loseMoney(cost);
|
||||
this.props.p.hp = this.props.p.max_hp;
|
||||
|
||||
// This just forces a re-render to update the cost
|
||||
this.setState({
|
||||
currHp: this.props.p.hp,
|
||||
});
|
||||
|
||||
dialogBoxCreate(`You were healed to full health! The hospital billed you for ${numeralWrapper.formatMoney(cost)}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const cost = this.getCost();
|
||||
|
||||
return (
|
||||
<AutoupdatingStdButton
|
||||
onClick={this.getHealed}
|
||||
style={this.btnStyle}
|
||||
text={`Get treatment for wounds - ${numeralWrapper.formatMoney(cost)}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
151
src/Locations/ui/Root.tsx
Normal file
151
src/Locations/ui/Root.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Root React Component for displaying overall Location UI
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { LocationCity } from "./City";
|
||||
import { GenericLocation } from "./GenericLocation";
|
||||
|
||||
import { Cities } from "../Cities";
|
||||
import { Locations } from "../Locations";
|
||||
import { LocationType } from "../LocationTypeEnum";
|
||||
|
||||
import { CityName } from "../data/CityNames";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
type IProps = {
|
||||
initiallyInCity?: boolean;
|
||||
engine: IEngine;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
city: CityName;
|
||||
inCity: boolean;
|
||||
location: LocationName;
|
||||
}
|
||||
|
||||
export class LocationRoot extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
city: props.p.city,
|
||||
inCity: props.initiallyInCity == null ? true : props.initiallyInCity,
|
||||
location: props.p.location,
|
||||
}
|
||||
|
||||
this.enterLocation = this.enterLocation.bind(this);
|
||||
this.returnToCity = this.returnToCity.bind(this);
|
||||
this.travel = this.travel.bind(this);
|
||||
}
|
||||
|
||||
enterLocation(to: LocationName): void {
|
||||
this.props.p.gotoLocation(to);
|
||||
this.setState({
|
||||
inCity: false,
|
||||
location: to,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click listener for a button that lets the player go from a specific location
|
||||
* back to the city
|
||||
*/
|
||||
returnToCity(): void {
|
||||
this.setState({
|
||||
inCity: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render UI for a city
|
||||
*/
|
||||
renderCity(): React.ReactNode {
|
||||
const city = Cities[this.state.city];
|
||||
if (city == null) {
|
||||
throw new Error(`Invalid city when rendering UI: ${this.state.city}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{this.state.city}</h2>
|
||||
<LocationCity city={city} enterLocation={this.enterLocation} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render UI for a specific location
|
||||
*/
|
||||
renderLocation(): React.ReactNode {
|
||||
const loc = Locations[this.state.location];
|
||||
|
||||
if (loc == null) {
|
||||
throw new Error(`Invalid location when rendering UI: ${this.state.location}`);
|
||||
}
|
||||
|
||||
if (loc.types.includes(LocationType.StockMarket)) {
|
||||
this.props.engine.loadStockMarketContent();
|
||||
}
|
||||
|
||||
return (
|
||||
<GenericLocation
|
||||
engine={this.props.engine}
|
||||
loc={loc}
|
||||
p={this.props.p}
|
||||
returnToCity={this.returnToCity}
|
||||
travel={this.travel}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Travel to a different city
|
||||
* @param {CityName} to - Destination city
|
||||
*/
|
||||
travel(to: CityName): void {
|
||||
const p = this.props.p;
|
||||
const cost = CONSTANTS.TravelCost;
|
||||
if (!p.canAfford(cost)) {
|
||||
dialogBoxCreate(`You cannot afford to travel to ${to}`);
|
||||
return;
|
||||
}
|
||||
|
||||
p.loseMoney(cost);
|
||||
p.travel(to);
|
||||
dialogBoxCreate(`You are now in ${to}!`);
|
||||
|
||||
// Dynamically update main menu
|
||||
if (p.firstTimeTraveled === false) {
|
||||
p.firstTimeTraveled = true;
|
||||
const travelTab = document.getElementById("travel-tab");
|
||||
const worldHeader = document.getElementById("world-menu-header");
|
||||
if (travelTab != null && worldHeader !== null) {
|
||||
travelTab.style.display = "list-item";
|
||||
worldHeader.click(); worldHeader.click();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.p.travel(to)) {
|
||||
this.setState({
|
||||
inCity: true,
|
||||
city: to
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.inCity) {
|
||||
return this.renderCity();
|
||||
} else {
|
||||
return this.renderLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
206
src/Locations/ui/SlumsLocation.tsx
Normal file
206
src/Locations/ui/SlumsLocation.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a slum
|
||||
*
|
||||
* This subcomponent renders all of the buttons for committing crimes
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Crimes } from "../../Crime/Crimes";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
export class SlumsLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.shoplift = this.shoplift.bind(this);
|
||||
this.robStore = this.robStore.bind(this);
|
||||
this.mug = this.mug.bind(this);
|
||||
this.larceny = this.larceny.bind(this);
|
||||
this.dealDrugs = this.dealDrugs.bind(this);
|
||||
this.bondForgery = this.bondForgery.bind(this);
|
||||
this.traffickArms = this.traffickArms.bind(this);
|
||||
this.homicide = this.homicide.bind(this);
|
||||
this.grandTheftAuto = this.grandTheftAuto.bind(this);
|
||||
this.kidnap = this.kidnap.bind(this);
|
||||
this.assassinate = this.assassinate.bind(this);
|
||||
this.heist = this.heist.bind(this);
|
||||
}
|
||||
|
||||
shoplift(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Shoplift.commit(this.props.p);
|
||||
}
|
||||
|
||||
robStore(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.RobStore.commit(this.props.p);
|
||||
}
|
||||
|
||||
mug(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Mug.commit(this.props.p);
|
||||
}
|
||||
|
||||
larceny(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Larceny.commit(this.props.p);
|
||||
}
|
||||
|
||||
dealDrugs(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.DealDrugs.commit(this.props.p);
|
||||
}
|
||||
|
||||
bondForgery(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.BondForgery.commit(this.props.p);
|
||||
}
|
||||
|
||||
traffickArms(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.TraffickArms.commit(this.props.p);
|
||||
}
|
||||
|
||||
homicide(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Homicide.commit(this.props.p);
|
||||
}
|
||||
|
||||
grandTheftAuto(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.GrandTheftAuto.commit(this.props.p);
|
||||
}
|
||||
|
||||
kidnap(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Kidnap.commit(this.props.p);
|
||||
}
|
||||
|
||||
assassinate(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Assassination.commit(this.props.p);
|
||||
}
|
||||
|
||||
heist(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
Crimes.Heist.commit(this.props.p);
|
||||
}
|
||||
|
||||
render() {
|
||||
const shopliftChance = Crimes.Shoplift.successRate(this.props.p);
|
||||
const robStoreChance = Crimes.RobStore.successRate(this.props.p);
|
||||
const mugChance = Crimes.Mug.successRate(this.props.p);
|
||||
const larcenyChance = Crimes.Larceny.successRate(this.props.p);
|
||||
const drugsChance = Crimes.DealDrugs.successRate(this.props.p);
|
||||
const bondChance = Crimes.BondForgery.successRate(this.props.p);
|
||||
const armsChance = Crimes.TraffickArms.successRate(this.props.p);
|
||||
const homicideChance = Crimes.Homicide.successRate(this.props.p);
|
||||
const gtaChance = Crimes.GrandTheftAuto.successRate(this.props.p);
|
||||
const kidnapChance = Crimes.Kidnap.successRate(this.props.p);
|
||||
const assassinateChance = Crimes.Assassination.successRate(this.props.p);
|
||||
const heistChance = Crimes.Heist.successRate(this.props.p);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.shoplift}
|
||||
style={this.btnStyle}
|
||||
text={`Shoplift (${numeralWrapper.formatPercentage(shopliftChance)} chance of success)`}
|
||||
tooltip={"Attempt to shoplift from a low-end retailer"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.robStore}
|
||||
style={this.btnStyle}
|
||||
text={`Rob store (${numeralWrapper.formatPercentage(robStoreChance)} chance of success)`}
|
||||
tooltip={"Attempt to commit armed robbery on a high-end store"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.mug}
|
||||
style={this.btnStyle}
|
||||
text={`Mug someone (${numeralWrapper.formatPercentage(mugChance)} chance of success)`}
|
||||
tooltip={"Attempt to mug a random person on the street"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.larceny}
|
||||
style={this.btnStyle}
|
||||
text={`Larceny (${numeralWrapper.formatPercentage(larcenyChance)} chance of success)`}
|
||||
tooltip={"Attempt to rob property from someone's house"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.dealDrugs}
|
||||
style={this.btnStyle}
|
||||
text={`Deal Drugs (${numeralWrapper.formatPercentage(drugsChance)} chance of success)`}
|
||||
tooltip={"Attempt to deal drugs"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.bondForgery}
|
||||
style={this.btnStyle}
|
||||
text={`Bond Forgery (${numeralWrapper.formatPercentage(bondChance)} chance of success)`}
|
||||
tooltip={"Attempt to forge corporate bonds"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.traffickArms}
|
||||
style={this.btnStyle}
|
||||
text={`Traffick illegal Arms (${numeralWrapper.formatPercentage(armsChance)} chance of success)`}
|
||||
tooltip={"Attempt to smuggle illegal arms into the city"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.homicide}
|
||||
style={this.btnStyle}
|
||||
text={`Homicide (${numeralWrapper.formatPercentage(homicideChance)} chance of success)`}
|
||||
tooltip={"Attempt to murder a random person on the street"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.grandTheftAuto}
|
||||
style={this.btnStyle}
|
||||
text={`Grand theft Auto (${numeralWrapper.formatPercentage(gtaChance)} chance of success)`}
|
||||
tooltip={"Attempt to commit grand theft auto"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.kidnap}
|
||||
style={this.btnStyle}
|
||||
text={`Kidnap and Ransom (${numeralWrapper.formatPercentage(kidnapChance)} chance of success)`}
|
||||
tooltip={"Attempt to kidnap and ransom a high-profile-target"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.assassinate}
|
||||
style={this.btnStyle}
|
||||
text={`Assassinate (${numeralWrapper.formatPercentage(assassinateChance)} chance of success)`}
|
||||
tooltip={"Attempt to assassinate a high-profile target"}
|
||||
/>
|
||||
<AutoupdatingStdButton
|
||||
intervalTime={5e3}
|
||||
onClick={this.heist}
|
||||
style={this.btnStyle}
|
||||
text={`Heist (${numeralWrapper.formatPercentage(heistChance)} chance of success)`}
|
||||
tooltip={"Attempt to pull off the ultimate heist"}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
149
src/Locations/ui/SpecialLocation.tsx
Normal file
149
src/Locations/ui/SpecialLocation.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location has special
|
||||
* actions/options/properties
|
||||
*
|
||||
* Examples:
|
||||
* - Bladeburner @ NSA
|
||||
* - Re-sleeving @ VitaLife
|
||||
* - Create Corporation @ City Hall
|
||||
*
|
||||
* This subcomponent creates all of the buttons for interacting with those special
|
||||
* properties
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Location } from "../Location";
|
||||
import { createStartCorporationPopup } from "../LocationsHelpers";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
type IProps = {
|
||||
engine: IEngine;
|
||||
loc: Location;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
inBladeburner: boolean;
|
||||
}
|
||||
|
||||
export class SpecialLocation extends React.Component<IProps, IState> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.createCorporationPopup = this.createCorporationPopup.bind(this);
|
||||
this.handleBladeburner = this.handleBladeburner.bind(this);
|
||||
this.handleResleeving = this.handleResleeving.bind(this);
|
||||
|
||||
this.state = {
|
||||
inBladeburner: this.props.p.inBladeburner(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for "Create Corporation" button at Sector-12 City Hall
|
||||
*/
|
||||
createCorporationPopup() {
|
||||
createStartCorporationPopup(this.props.p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for Bladeburner button at Sector-12 NSA
|
||||
*/
|
||||
handleBladeburner() {
|
||||
const p = this.props.p;
|
||||
if (p.inBladeburner()) {
|
||||
// Enter Bladeburner division
|
||||
this.props.engine.loadBladeburnerContent();
|
||||
} else {
|
||||
// Apply for Bladeburner division
|
||||
if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) {
|
||||
p.startBladeburner({ new: true });
|
||||
dialogBoxCreate("You have been accepted into the Bladeburner division!");
|
||||
this.setState({
|
||||
inBladeburner: true,
|
||||
});
|
||||
|
||||
const worldHeader = document.getElementById("world-menu-header");
|
||||
if (worldHeader instanceof HTMLElement) {
|
||||
worldHeader.click(); worldHeader.click();
|
||||
}
|
||||
} else {
|
||||
dialogBoxCreate("Rejected! Please apply again when you have 100 of each combat stat (str, def, dex, agi)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for Resleeving button at New Tokyo VitaLife
|
||||
*/
|
||||
handleResleeving() {
|
||||
this.props.engine.loadResleevingContent();
|
||||
}
|
||||
|
||||
renderBladeburner(): React.ReactNode {
|
||||
if (!this.props.p.canAccessBladeburner()) { return null; }
|
||||
const text = this.state.inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
||||
return (
|
||||
<StdButton
|
||||
onClick={this.handleBladeburner}
|
||||
style={this.btnStyle}
|
||||
text={text}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderCreateCorporation(): React.ReactNode {
|
||||
if (!this.props.p.canAccessCorporation()) { return null; }
|
||||
return (
|
||||
<AutoupdatingStdButton
|
||||
disabled={!this.props.p.canAccessCorporation() || this.props.p.hasCorporation()}
|
||||
onClick={this.createCorporationPopup}
|
||||
style={this.btnStyle}
|
||||
text={"Create a Corporation"}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderResleeving(): React.ReactNode {
|
||||
if (!this.props.p.canAccessResleeving()) { return null; }
|
||||
return (
|
||||
<StdButton
|
||||
onClick={this.handleResleeving}
|
||||
style={this.btnStyle}
|
||||
text={"Re-Sleeve"}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
switch (this.props.loc.name) {
|
||||
case LocationName.NewTokyoVitaLife: {
|
||||
return this.renderResleeving();
|
||||
}
|
||||
case LocationName.Sector12CityHall: {
|
||||
return this.renderCreateCorporation();
|
||||
}
|
||||
case LocationName.Sector12NSA: {
|
||||
return this.renderBladeburner();
|
||||
}
|
||||
default:
|
||||
console.error(`Location ${this.props.loc.name} doesn't have any special properties`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/Locations/ui/TechVendorLocation.tsx
Normal file
106
src/Locations/ui/TechVendorLocation.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a tech vendor
|
||||
*
|
||||
* This subcomponent renders all of the buttons for purchasing things from tech vendors
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Location } from "../Location";
|
||||
import { createPurchaseServerPopup,
|
||||
createUpgradeHomeCoresPopup,
|
||||
createUpgradeHomeRamPopup,
|
||||
purchaseTorRouter } from "../LocationsHelpers";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { getPurchaseServerCost } from "../../Server/ServerPurchases";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
export class TechVendorLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.state = {
|
||||
torPurchased: props.p.hasTorRouter(),
|
||||
}
|
||||
|
||||
this.createUpgradeHomeCoresPopup = this.createUpgradeHomeCoresPopup.bind(this);
|
||||
this.createUpgradeHomeRamPopup = this.createUpgradeHomeRamPopup.bind(this);
|
||||
this.purchaseTorRouter = this.purchaseTorRouter.bind(this);
|
||||
}
|
||||
|
||||
createUpgradeHomeCoresPopup() {
|
||||
createUpgradeHomeCoresPopup(this.props.p);
|
||||
}
|
||||
|
||||
createUpgradeHomeRamPopup() {
|
||||
createUpgradeHomeRamPopup(this.props.p);
|
||||
}
|
||||
|
||||
purchaseTorRouter() {
|
||||
purchaseTorRouter(this.props.p);
|
||||
}
|
||||
|
||||
render() {
|
||||
const loc: Location = this.props.loc;
|
||||
|
||||
const purchaseServerButtons: React.ReactNode[] = [];
|
||||
for (let i = loc.techVendorMinRam; i <= loc.techVendorMaxRam; i *= 2) {
|
||||
const cost = getPurchaseServerCost(i);
|
||||
purchaseServerButtons.push(
|
||||
<StdButton
|
||||
key={i}
|
||||
onClick={() => createPurchaseServerPopup(i, this.props.p)}
|
||||
style={this.btnStyle}
|
||||
text={`Purchase ${i}GB Server - ${numeralWrapper.formatMoney(cost)}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{purchaseServerButtons}
|
||||
{
|
||||
this.state.torPurchased ? (
|
||||
<StdButtonPurchased
|
||||
style={this.btnStyle}
|
||||
text={"TOR Router - Purchased"}
|
||||
/>
|
||||
) : (
|
||||
<StdButton
|
||||
onClick={this.purchaseTorRouter}
|
||||
style={this.btnStyle}
|
||||
text={`Purchase TOR Router - ${numeralWrapper.formatMoney(CONSTANTS.TorRouterCost)}`}
|
||||
/>
|
||||
)
|
||||
|
||||
}
|
||||
<StdButton
|
||||
onClick={this.createUpgradeHomeRamPopup}
|
||||
style={this.btnStyle}
|
||||
text={`Purchase additional RAM for Home computer`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.createUpgradeHomeCoresPopup}
|
||||
style={this.btnStyle}
|
||||
text={`Purchase additional Core for Home computer`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
62
src/Locations/ui/TravelAgencyLocation.tsx
Normal file
62
src/Locations/ui/TravelAgencyLocation.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a Travel Agency
|
||||
*
|
||||
* This subcomponent renders all of the buttons for traveling to different cities
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CityName } from "../data/CityNames";
|
||||
import { createTravelPopup } from "../LocationsHelpers";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
travel: (to: CityName) => void;
|
||||
}
|
||||
|
||||
export class TravelAgencyLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
}
|
||||
|
||||
render() {
|
||||
const travelBtns: React.ReactNode[] = [];
|
||||
for (const key in CityName) {
|
||||
const city = CityName[key];
|
||||
|
||||
// Skip current city
|
||||
if (city === this.props.p.city) { continue; }
|
||||
|
||||
travelBtns.push(
|
||||
<StdButton
|
||||
key={city}
|
||||
onClick={createTravelPopup.bind(null, city, this.props.travel)}
|
||||
style={this.btnStyle}
|
||||
text={`Travel to ${city}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
From here, you can travel to any other city! A ticket
|
||||
costs {numeralWrapper.formatMoney(CONSTANTS.TravelCost)}
|
||||
</p>
|
||||
{travelBtns}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
114
src/Locations/ui/UniversityLocation.tsx
Normal file
114
src/Locations/ui/UniversityLocation.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* React Subcomponent for displaying a location's UI, when that location is a university
|
||||
*
|
||||
* This subcomponent renders all of the buttons for studying/taking courses
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Location } from "../Location";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
export class UniversityLocation extends React.Component<IProps, any> {
|
||||
/**
|
||||
* Stores button styling that sets them all to block display
|
||||
*/
|
||||
btnStyle: object;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.take = this.take.bind(this);
|
||||
this.study = this.study.bind(this);
|
||||
this.dataStructures = this.dataStructures.bind(this);
|
||||
this.networks = this.networks.bind(this);
|
||||
this.algorithms = this.algorithms.bind(this);
|
||||
this.management = this.management.bind(this);
|
||||
this.leadership = this.leadership.bind(this);
|
||||
}
|
||||
|
||||
take(stat: string) {
|
||||
const loc = this.props.loc;
|
||||
this.props.p.startClass(loc.costMult, loc.expMult, stat);
|
||||
}
|
||||
|
||||
study() {
|
||||
return this.take(CONSTANTS.ClassStudyComputerScience);
|
||||
}
|
||||
|
||||
dataStructures() {
|
||||
return this.take(CONSTANTS.ClassDataStructures);
|
||||
}
|
||||
|
||||
networks() {
|
||||
return this.take(CONSTANTS.ClassNetworks);
|
||||
}
|
||||
|
||||
algorithms() {
|
||||
return this.take(CONSTANTS.ClassAlgorithms);
|
||||
}
|
||||
|
||||
management() {
|
||||
return this.take(CONSTANTS.ClassManagement);
|
||||
}
|
||||
|
||||
leadership() {
|
||||
return this.take(CONSTANTS.ClassLeadership);
|
||||
}
|
||||
|
||||
render() {
|
||||
const costMult: number = this.props.loc.costMult;
|
||||
|
||||
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
|
||||
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
|
||||
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
|
||||
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
|
||||
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton
|
||||
onClick={this.study}
|
||||
style={this.btnStyle}
|
||||
text={`Study Computer Science (free)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.dataStructures}
|
||||
style={this.btnStyle}
|
||||
text={`Take Data Structures course (${numeralWrapper.formatMoney(dataStructuresCost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.networks}
|
||||
style={this.btnStyle}
|
||||
text={`Take Networks course (${numeralWrapper.formatMoney(networksCost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.algorithms}
|
||||
style={this.btnStyle}
|
||||
text={`Take Algorithms course (${numeralWrapper.formatMoney(algorithmsCost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.management}
|
||||
style={this.btnStyle}
|
||||
text={`Take Management course (${numeralWrapper.formatMoney(managementCost)} / sec)`}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.leadership}
|
||||
style={this.btnStyle}
|
||||
text={`Take Leadership course (${numeralWrapper.formatMoney(leadershipCost)} / sec)`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,11 @@ function checkForMessagesToSend() {
|
||||
}
|
||||
} else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) {
|
||||
sendMessage(jumper0);
|
||||
Player.getHomeComputer().programs.push(Programs.Flight.name);
|
||||
const flightName = Programs.Flight.name;
|
||||
const homeComp = Player.getHomeComputer();
|
||||
if (!homeComp.programs.includes(flightName)) {
|
||||
homeComp.programs.push(flightName);
|
||||
}
|
||||
} else if (jumper1 && !jumper1.recvd && Player.hacking_skill >= 40) {
|
||||
sendMessage(jumper1);
|
||||
} else if (cybersecTest && !cybersecTest.recvd && Player.hacking_skill >= 50) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {HacknetNode} from "./HacknetNode";
|
||||
import {NetscriptFunctions} from "./NetscriptFunctions";
|
||||
import {NetscriptPort} from "./NetscriptPort";
|
||||
|
||||
@@ -56,37 +55,6 @@ Environment.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setArrayElement: function(name, idx, value) {
|
||||
if (!(idx instanceof Array)) {
|
||||
throw new Error("idx parameter is not an Array");
|
||||
}
|
||||
var scope = this.lookup(name);
|
||||
if (!scope && this.parent) {
|
||||
throw new Error("Undefined variable " + name);
|
||||
}
|
||||
var arr = (scope || this).vars[name];
|
||||
if (!(arr.constructor === Array || arr instanceof Array)) {
|
||||
throw new Error("Variable is not an array: " + name);
|
||||
}
|
||||
var res = arr;
|
||||
for (var iterator = 0; iterator < idx.length-1; ++iterator) {
|
||||
var i = idx[iterator];
|
||||
if (!(res instanceof Array) || i >= res.length) {
|
||||
throw new Error("Out-of-bounds array access");
|
||||
}
|
||||
res = res[i];
|
||||
}
|
||||
|
||||
//Cant assign to ports or HacknetNodes
|
||||
if (res[idx[idx.length-1]] instanceof HacknetNode) {
|
||||
throw new Error("Cannot assign a Hacknet Node handle to a new value");
|
||||
}
|
||||
if (res[idx[idx.length-1]] instanceof NetscriptPort) {
|
||||
throw new Error("Cannot assign a Netscript Port handle to a new value");
|
||||
}
|
||||
return res[idx[idx.length-1]] = value;
|
||||
},
|
||||
|
||||
//Creates (or overwrites) a variable in the current scope
|
||||
def: function(name, value) {
|
||||
return this.vars[name] = value;
|
||||
|
||||
@@ -196,8 +196,11 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = threads;
|
||||
server.runningScripts.push(runningScriptObj); //Push onto runningScripts
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
|
||||
// Push onto runningScripts.
|
||||
// This has to come after addWorkerScript() because that fn updates RAM usage
|
||||
server.runScript(runningScriptObj, Player);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,19 @@ import { Factions,
|
||||
import { joinFaction,
|
||||
purchaseAugmentation } from "./Faction/FactionHelpers";
|
||||
import { FactionWorkType } from "./Faction/FactionWorkTypeEnum";
|
||||
import { netscriptCanGrow,
|
||||
netscriptCanHack,
|
||||
netscriptCanWeaken } from "./Hacking/netscriptCanHack";
|
||||
|
||||
import { getCostOfNextHacknetNode,
|
||||
purchaseHacknet } from "./HacknetNode";
|
||||
import {Locations} from "./Locations";
|
||||
getCostOfNextHacknetServer,
|
||||
purchaseHacknet,
|
||||
hasHacknetServers,
|
||||
purchaseHashUpgrade } from "./Hacknet/HacknetHelpers";
|
||||
import { CityName } from "./Locations/data/CityNames";
|
||||
import { LocationName } from "./Locations/data/LocationNames";
|
||||
|
||||
import { HacknetServer } from "./Hacknet/HacknetServer";
|
||||
import { Message } from "./Message/Message";
|
||||
import { Messages } from "./Message/MessageHelpers";
|
||||
import {inMission} from "./Missions";
|
||||
@@ -73,7 +83,8 @@ import {WorkerScript, workerScripts,
|
||||
import {makeRuntimeRejectMsg, netscriptDelay,
|
||||
runScriptFromScript} from "./NetscriptEvaluator";
|
||||
import {NetscriptPort} from "./NetscriptPort";
|
||||
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum"
|
||||
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum";
|
||||
import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/Sleeve";
|
||||
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
@@ -193,11 +204,11 @@ function initSingularitySFFlags() {
|
||||
}
|
||||
|
||||
function NetscriptFunctions(workerScript) {
|
||||
var updateDynamicRam = function(fnName, ramCost) {
|
||||
const updateDynamicRam = function(fnName, ramCost) {
|
||||
if (workerScript.dynamicLoadedFns[fnName]) {return;}
|
||||
workerScript.dynamicLoadedFns[fnName] = true;
|
||||
|
||||
const threads = workerScript.scriptRef.threads;
|
||||
let threads = workerScript.scriptRef.threads;
|
||||
if (typeof threads !== 'number') {
|
||||
console.warn(`WorkerScript detected NaN for threadcount for ${workerScript.name} on ${workerScript.serverIp}`);
|
||||
threads = 1;
|
||||
@@ -214,7 +225,7 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
};
|
||||
|
||||
var updateStaticRam = function(fnName, ramCost) {
|
||||
const updateStaticRam = function(fnName, ramCost) {
|
||||
if (workerScript.loadedFns[fnName]) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -227,28 +238,56 @@ function NetscriptFunctions(workerScript) {
|
||||
* Gets the Server for a specific hostname/ip, throwing an error
|
||||
* if the server doesn't exist.
|
||||
* @param {string} Hostname or IP of the server
|
||||
* @param {string} callingFnName - Name of calling function. For logging purposes
|
||||
* @returns {Server} The specified Server
|
||||
*/
|
||||
var safeGetServer = function(ip, callingFnName="") {
|
||||
const safeGetServer = function(ip, callingFnName="") {
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.log(`ERROR: Invalid IP or hostname passed into ${callingFnName}()`);
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid IP or hostname passed into ${callingFnName}() function`);
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to fail a function if the function's target is a Hacknet Server.
|
||||
* This is used for functions that should run on normal Servers, but not Hacknet Servers
|
||||
* @param {Server} server - Target server
|
||||
* @param {string} callingFn - Name of calling function. For logging purposes
|
||||
* @returns {boolean} True if the server is a Hacknet Server, false otherwise
|
||||
*/
|
||||
const failOnHacknetServer = function(server, callingFn="") {
|
||||
if (server instanceof HacknetServer) {
|
||||
workerScript.log(`ERROR: ${callingFn}() failed because it does not work on Hacknet Servers`);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to get Hacknet Node object
|
||||
var getHacknetNode = function(i) {
|
||||
const getHacknetNode = function(i) {
|
||||
if (isNaN(i)) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid index specified for Hacknet Node: " + i);
|
||||
}
|
||||
if (i < 0 || i >= Player.hacknetNodes.length) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Index specified for Hacknet Node is out-of-bounds: " + i);
|
||||
}
|
||||
return Player.hacknetNodes[i];
|
||||
|
||||
if (hasHacknetServers()) {
|
||||
const hserver = AllServers[Player.hacknetNodes[i]];
|
||||
if (hserver == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`);
|
||||
}
|
||||
|
||||
return hserver;
|
||||
} else {
|
||||
return Player.hacknetNodes[i];
|
||||
}
|
||||
};
|
||||
|
||||
var getCodingContract = function(fn, ip) {
|
||||
const getCodingContract = function(fn, ip) {
|
||||
var server = safeGetServer(ip, "getCodingContract");
|
||||
return server.getContract(fn);
|
||||
}
|
||||
@@ -262,43 +301,81 @@ function NetscriptFunctions(workerScript) {
|
||||
return purchaseHacknet();
|
||||
},
|
||||
getPurchaseNodeCost : function() {
|
||||
return getCostOfNextHacknetNode();
|
||||
if (hasHacknetServers()) {
|
||||
return getCostOfNextHacknetServer();
|
||||
} else {
|
||||
return getCostOfNextHacknetNode();
|
||||
}
|
||||
},
|
||||
getNodeStats : function(i) {
|
||||
var node = getHacknetNode(i);
|
||||
return {
|
||||
const node = getHacknetNode(i);
|
||||
const hasUpgraded = hasHacknetServers();
|
||||
const res = {
|
||||
name: node.name,
|
||||
level: node.level,
|
||||
ram: node.ram,
|
||||
ram: hasUpgraded ? node.maxRam : node.ram,
|
||||
cores: node.cores,
|
||||
production: node.moneyGainRatePerSecond,
|
||||
production: hasUpgraded ? node.hashRate : node.moneyGainRatePerSecond,
|
||||
timeOnline: node.onlineTimeSeconds,
|
||||
totalProduction: node.totalMoneyGenerated,
|
||||
totalProduction: hasUpgraded ? node.totalHashesGenerated : node.totalMoneyGenerated,
|
||||
};
|
||||
|
||||
if (hasUpgraded) {
|
||||
res.cache = node.cache;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
upgradeLevel : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseLevelUpgrade(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.purchaseLevelUpgrade(n, Player);
|
||||
},
|
||||
upgradeRam : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseRamUpgrade(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.purchaseRamUpgrade(n, Player);
|
||||
},
|
||||
upgradeCore : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.purchaseCoreUpgrade(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.purchaseCoreUpgrade(n, Player);
|
||||
},
|
||||
upgradeCache : function(i, n) {
|
||||
if (!hasHacknetServers()) { return false; }
|
||||
const node = getHacknetNode(i);
|
||||
const res = node.purchaseCacheUpgrade(n, Player);
|
||||
if (res) {
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
getLevelUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateLevelUpgradeCost(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.calculateLevelUpgradeCost(n, Player);
|
||||
},
|
||||
getRamUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateRamUpgradeCost(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.calculateRamUpgradeCost(n, Player);
|
||||
},
|
||||
getCoreUpgradeCost : function(i, n) {
|
||||
var node = getHacknetNode(i);
|
||||
return node.calculateCoreUpgradeCost(n);
|
||||
const node = getHacknetNode(i);
|
||||
return node.calculateCoreUpgradeCost(n, Player);
|
||||
},
|
||||
getCacheUpgradeCost : function(i, n) {
|
||||
if (!hasHacknetServers()) { return Infinity; }
|
||||
const node = getHacknetNode(i);
|
||||
return node.calculateCacheUpgradeCost(n);
|
||||
},
|
||||
numHashes : function() {
|
||||
if (!hasHacknetServers()) { return 0; }
|
||||
return Player.hashManager.hashes;
|
||||
},
|
||||
hashCost : function(upgName) {
|
||||
if (!hasHacknetServers()) { return Infinity; }
|
||||
|
||||
return Player.hashManager.getUpgradeCost(upgName);
|
||||
},
|
||||
spendHashes : function(upgName, upgTarget) {
|
||||
if (!hasHacknetServers()) { return false; }
|
||||
return purchaseHashUpgrade(upgName, upgTarget);
|
||||
}
|
||||
},
|
||||
sprintf : sprintf,
|
||||
@@ -350,19 +427,16 @@ function NetscriptFunctions(workerScript) {
|
||||
var hackingTime = calculateHackingTime(server); //This is in seconds
|
||||
|
||||
//No root access or skill level too low
|
||||
if (server.hasAdminRights == false) {
|
||||
workerScript.scriptRef.log("Cannot hack this server (" + server.hostname + ") because user does not have root access");
|
||||
throw makeRuntimeRejectMsg(workerScript, "Cannot hack this server (" + server.hostname + ") because user does not have root access");
|
||||
}
|
||||
|
||||
if (server.requiredHackingSkill > Player.hacking_skill) {
|
||||
workerScript.scriptRef.log("Cannot hack this server (" + server.hostname + ") because user's hacking skill is not high enough");
|
||||
throw makeRuntimeRejectMsg(workerScript, "Cannot hack this server (" + server.hostname + ") because user's hacking skill is not high enough");
|
||||
const canHack = netscriptCanHack(server, Player);
|
||||
if (!canHack.res) {
|
||||
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
|
||||
throw makeRuntimeRejectMsg(workerScript, canHack.msg);
|
||||
}
|
||||
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.hack == null) {
|
||||
workerScript.scriptRef.log("Attempting to hack " + ip + " in " + hackingTime.toFixed(3) + " seconds (t=" + threads + ")");
|
||||
}
|
||||
|
||||
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
|
||||
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
|
||||
var hackChance = calculateHackingChance(server);
|
||||
@@ -481,9 +555,10 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
//No root access or skill level too low
|
||||
if (server.hasAdminRights == false) {
|
||||
workerScript.scriptRef.log("Cannot grow this server (" + server.hostname + ") because user does not have root access");
|
||||
throw makeRuntimeRejectMsg(workerScript, "Cannot grow this server (" + server.hostname + ") because user does not have root access");
|
||||
const canHack = netscriptCanGrow(server);
|
||||
if (!canHack.res) {
|
||||
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
|
||||
throw makeRuntimeRejectMsg(workerScript, canHack.msg);
|
||||
}
|
||||
|
||||
var growTime = calculateGrowTime(server);
|
||||
@@ -542,9 +617,10 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
//No root access or skill level too low
|
||||
if (server.hasAdminRights == false) {
|
||||
workerScript.scriptRef.log("Cannot weaken this server (" + server.hostname + ") because user does not have root access");
|
||||
throw makeRuntimeRejectMsg(workerScript, "Cannot weaken this server (" + server.hostname + ") because user does not have root access");
|
||||
const canHack = netscriptCanWeaken(server);
|
||||
if (!canHack.res) {
|
||||
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
|
||||
throw makeRuntimeRejectMsg(workerScript, canHack.msg);
|
||||
}
|
||||
|
||||
var weakenTime = calculateWeakenTime(server);
|
||||
@@ -1300,25 +1376,22 @@ function NetscriptFunctions(workerScript) {
|
||||
let copy = Object.assign({}, BitNodeMultipliers);
|
||||
return copy;
|
||||
},
|
||||
getServerMoneyAvailable : function(ip){
|
||||
getServerMoneyAvailable : function(ip) {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("getServerMoneyAvailable", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerMoneyAvailable", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerMoneyAvailable() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerMoneyAvailable() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
const server = safeGetServer(ip, "getServerMoneyAvailable");
|
||||
if (failOnHacknetServer(server, "getServerMoneyAvailable")) { return 0; }
|
||||
if (server.hostname == "home") {
|
||||
//Return player's money
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerMoneyAvailable == null) {
|
||||
workerScript.scriptRef.log("getServerMoneyAvailable('home') returned player's money: $" + formatNumber(Player.money.toNumber(), 2));
|
||||
// Return player's money
|
||||
if (workerScript.shouldLog("getServerMoneyAvailable")) {
|
||||
workerScript.log("getServerMoneyAvailable('home') returned player's money: $" + formatNumber(Player.money.toNumber(), 2));
|
||||
}
|
||||
return Player.money.toNumber();
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerMoneyAvailable == null) {
|
||||
workerScript.scriptRef.log("getServerMoneyAvailable() returned " + formatNumber(server.moneyAvailable, 2) + " for " + server.hostname);
|
||||
if (workerScript.shouldLog("getServerMoneyAvailable")) {
|
||||
workerScript.log("getServerMoneyAvailable() returned " + formatNumber(server.moneyAvailable, 2) + " for " + server.hostname);
|
||||
}
|
||||
return server.moneyAvailable;
|
||||
},
|
||||
@@ -1327,13 +1400,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerSecurityLevel == null) {
|
||||
workerScript.scriptRef.log("getServerSecurityLevel() returned " + formatNumber(server.hackDifficulty, 3) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerSecurityLevel");
|
||||
if (failOnHacknetServer(server, "getServerSecurityLevel")) { return 1; }
|
||||
if (workerScript.shouldLog("getServerSecurityLevel")) {
|
||||
workerScript.log("getServerSecurityLevel() returned " + formatNumber(server.hackDifficulty, 3) + " for " + server.hostname);
|
||||
}
|
||||
return server.hackDifficulty;
|
||||
},
|
||||
@@ -1342,13 +1412,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerBaseSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerBaseSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerBaseSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerBaseSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerBaseSecurityLevel == null) {
|
||||
workerScript.scriptRef.log("getServerBaseSecurityLevel() returned " + formatNumber(server.baseDifficulty, 3) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerBaseSecurityLevel");
|
||||
if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { return 1; }
|
||||
if (workerScript.shouldLog("getServerBaseSecurityLevel")) {
|
||||
workerScript.log("getServerBaseSecurityLevel() returned " + formatNumber(server.baseDifficulty, 3) + " for " + server.hostname);
|
||||
}
|
||||
return server.baseDifficulty;
|
||||
},
|
||||
@@ -1357,13 +1424,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerMinSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerMinSecurityLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerMinSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerMinSecurityLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerMinSecurityLevel == null) {
|
||||
workerScript.scriptRef.log("getServerMinSecurityLevel() returned " + formatNumber(server.minDifficulty, 3) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerMinSecurityLevel");
|
||||
if (failOnHacknetServer(server, "getServerMinSecurityLevel")) { return 1; }
|
||||
if (workerScript.shouldLog("getServerMinSecurityLevel")) {
|
||||
workerScript.log("getServerMinSecurityLevel() returned " + formatNumber(server.minDifficulty, 3) + " for " + server.hostname);
|
||||
}
|
||||
return server.minDifficulty;
|
||||
},
|
||||
@@ -1372,13 +1436,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerRequiredHackingLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerRequiredHackingLevel", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerRequiredHackingLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerRequiredHackingLevel() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerRequiredHackingLevel == null) {
|
||||
workerScript.scriptRef.log("getServerRequiredHackingLevel returned " + formatNumber(server.requiredHackingSkill, 0) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerRequiredHackingLevel");
|
||||
if (failOnHacknetServer(server, "getServerRequiredHackingLevel")) { return 1; }
|
||||
if (workerScript.shouldLog("getServerRequiredHackingLevel")) {
|
||||
workerScript.log("getServerRequiredHackingLevel returned " + formatNumber(server.requiredHackingSkill, 0) + " for " + server.hostname);
|
||||
}
|
||||
return server.requiredHackingSkill;
|
||||
},
|
||||
@@ -1387,13 +1448,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerMaxMoney", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerMaxMoney", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerMaxMoney() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerMaxMoney() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerMaxMoney == null) {
|
||||
workerScript.scriptRef.log("getServerMaxMoney() returned " + formatNumber(server.moneyMax, 0) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerMaxMoney");
|
||||
if (failOnHacknetServer(server, "getServerMaxMoney")) { return 0; }
|
||||
if (workerScript.shouldLog("getServerMaxMoney")) {
|
||||
workerScript.log("getServerMaxMoney() returned " + formatNumber(server.moneyMax, 0) + " for " + server.hostname);
|
||||
}
|
||||
return server.moneyMax;
|
||||
},
|
||||
@@ -1402,13 +1460,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerGrowth", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerGrowth", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerGrowth() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerGrowth() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerGrowth == null) {
|
||||
workerScript.scriptRef.log("getServerGrowth() returned " + formatNumber(server.serverGrowth, 0) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerGrowth");
|
||||
if (failOnHacknetServer(server, "getServerGrowth")) { return 1; }
|
||||
if (workerScript.shouldLog("getServerGrowth")) {
|
||||
workerScript.log("getServerGrowth() returned " + formatNumber(server.serverGrowth, 0) + " for " + server.hostname);
|
||||
}
|
||||
return server.serverGrowth;
|
||||
},
|
||||
@@ -1417,13 +1472,10 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerNumPortsRequired", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerNumPortsRequired", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerNumPortsRequired() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerNumPortsRequired() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerNumPortsRequired == null) {
|
||||
workerScript.scriptRef.log("getServerNumPortsRequired() returned " + formatNumber(server.numOpenPortsRequired, 0) + " for " + server.hostname);
|
||||
const server = safeGetServer(ip, "getServerNumPortsRequired");
|
||||
if (failOnHacknetServer(server, "getServerNumPortsRequired")) { return 5; }
|
||||
if (workerScript.shouldLog("getServerNumPortsRequired")) {
|
||||
workerScript.log("getServerNumPortsRequired() returned " + formatNumber(server.numOpenPortsRequired, 0) + " for " + server.hostname);
|
||||
}
|
||||
return server.numOpenPortsRequired;
|
||||
},
|
||||
@@ -1432,13 +1484,9 @@ function NetscriptFunctions(workerScript) {
|
||||
return updateStaticRam("getServerRam", CONSTANTS.ScriptGetServerRamCost);
|
||||
}
|
||||
updateDynamicRam("getServerRam", CONSTANTS.ScriptGetServerRamCost);
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("getServerRam() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getServerRam() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.getServerRam == null) {
|
||||
workerScript.scriptRef.log("getServerRam() returned [" + formatNumber(server.maxRam, 2) + "GB, " + formatNumber(server.ramUsed, 2) + "GB]");
|
||||
const server = safeGetServer(ip, "getServerRam");
|
||||
if (workerScript.shouldLog("getServerRam")) {
|
||||
workerScript.log("getServerRam() returned [" + formatNumber(server.maxRam, 2) + "GB, " + formatNumber(server.ramUsed, 2) + "GB]");
|
||||
}
|
||||
return [server.maxRam, server.ramUsed];
|
||||
},
|
||||
@@ -2267,56 +2315,14 @@ function NetscriptFunctions(workerScript) {
|
||||
if (ip == null || ip === "") {
|
||||
ip = workerScript.serverIp;
|
||||
}
|
||||
var s = getServer(ip);
|
||||
if (s == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid server specified for rm(): ${ip}`);
|
||||
const s = safeGetServer(ip, "rm");
|
||||
|
||||
const status = s.removeFile(fn);
|
||||
if (!status.res) {
|
||||
workerScript.log(status.msg);
|
||||
}
|
||||
|
||||
if (fn.endsWith(".exe")) {
|
||||
for (var i = 0; i < s.programs.length; ++i) {
|
||||
if (s.programs[i] === fn) {
|
||||
s.programs.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (isScriptFilename(fn)) {
|
||||
for (var i = 0; i < s.scripts.length; ++i) {
|
||||
if (s.scripts[i].filename === fn) {
|
||||
//Check that the script isnt currently running
|
||||
for (var j = 0; j < s.runningScripts.length; ++j) {
|
||||
if (s.runningScripts[j].filename === fn) {
|
||||
workerScript.scriptRef.log("Cannot delete a script that is currently running!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
s.scripts.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (fn.endsWith(".lit")) {
|
||||
for (var i = 0; i < s.messages.length; ++i) {
|
||||
var f = s.messages[i];
|
||||
if (!(f instanceof Message) && isString(f) && f === fn) {
|
||||
s.messages.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (fn.endsWith(".txt")) {
|
||||
for (var i = 0; i < s.textFiles.length; ++i) {
|
||||
if (s.textFiles[i].fn === fn) {
|
||||
s.textFiles.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (fn.endsWith(".cct")) {
|
||||
for (var i = 0; i < s.contracts.length; ++i) {
|
||||
if (s.contracts[i].fn === fn) {
|
||||
s.contracts.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return status.res;
|
||||
},
|
||||
scriptRunning : function(scriptname, ip) {
|
||||
if (workerScript.checkingRam) {
|
||||
@@ -2583,30 +2589,30 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
var costMult, expMult;
|
||||
switch(universityName.toLowerCase()) {
|
||||
case Locations.AevumSummitUniversity.toLowerCase():
|
||||
if (Player.city != Locations.Aevum) {
|
||||
case LocationName.AevumSummitUniversity.toLowerCase():
|
||||
if (Player.city != CityName.Aevum) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot study at Summit University because you are not in Aevum. universityCourse() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.AevumSummitUniversity;
|
||||
Player.gotoLocation(LocationName.AevumSummitUniversity);
|
||||
costMult = 4;
|
||||
expMult = 3;
|
||||
break;
|
||||
case Locations.Sector12RothmanUniversity.toLowerCase():
|
||||
if (Player.city != Locations.Sector12) {
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase():
|
||||
if (Player.city != CityName.Sector12) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot study at Rothman University because you are not in Sector-12. universityCourse() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.Sector12RothmanUniversity;
|
||||
Player.location = LocationName.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (Player.city != Locations.Volhaven) {
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (Player.city != CityName.Volhaven) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot study at ZB Institute of Technology because you are not in Volhaven. universityCourse() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.VolhavenZBInstituteOfTechnology;
|
||||
Player.location = LocationName.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
expMult = 4;
|
||||
break;
|
||||
@@ -2671,48 +2677,48 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
var costMult, expMult;
|
||||
switch(gymName.toLowerCase()) {
|
||||
case Locations.AevumCrushFitnessGym.toLowerCase():
|
||||
if (Player.city != Locations.Aevum) {
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase():
|
||||
if (Player.city != CityName.Aevum) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot workout at Crush Fitness because you are not in Aevum. gymWorkout() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.AevumCrushFitnessGym;
|
||||
Player.location = LocationName.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.AevumSnapFitnessGym.toLowerCase():
|
||||
if (Player.city != Locations.Aevum) {
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase():
|
||||
if (Player.city != CityName.Aevum) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot workout at Snap Fitness because you are not in Aevum. gymWorkout() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.AevumSnapFitnessGym;
|
||||
Player.location = LocationName.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
expMult = 5;
|
||||
break;
|
||||
case Locations.Sector12IronGym.toLowerCase():
|
||||
if (Player.city != Locations.Sector12) {
|
||||
case LocationName.Sector12IronGym.toLowerCase():
|
||||
if (Player.city != CityName.Sector12) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot workout at Iron Gym because you are not in Sector-12. gymWorkout() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.Sector12IronGym;
|
||||
Player.location = LocationName.Sector12IronGym;
|
||||
costMult = 1;
|
||||
expMult = 1;
|
||||
break;
|
||||
case Locations.Sector12PowerhouseGym.toLowerCase():
|
||||
if (Player.city != Locations.Sector12) {
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
||||
if (Player.city != CityName.Sector12) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot workout at Powerhouse Gym because you are not in Sector-12. gymWorkout() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.Sector12PowerhouseGym;
|
||||
Player.location = LocationName.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
expMult = 10;
|
||||
break;
|
||||
case Locations.VolhavenMilleniumFitnessGym.toLowerCase():
|
||||
if (Player.city != Locations.Volhaven) {
|
||||
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
|
||||
if (Player.city != CityName.Volhaven) {
|
||||
workerScript.scriptRef.log("ERROR: You cannot workout at Millenium Fitness Gym because you are not in Volhaven. gymWorkout() failed");
|
||||
return false;
|
||||
}
|
||||
Player.location = Locations.VolhavenMilleniumFitnessGym;
|
||||
Player.location = LocationName.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
expMult = 4;
|
||||
break;
|
||||
@@ -2763,12 +2769,12 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
switch(cityname) {
|
||||
case Locations.Aevum:
|
||||
case Locations.Chongqing:
|
||||
case Locations.Sector12:
|
||||
case Locations.NewTokyo:
|
||||
case Locations.Ishima:
|
||||
case Locations.Volhaven:
|
||||
case CityName.Aevum:
|
||||
case CityName.Chongqing:
|
||||
case CityName.Sector12:
|
||||
case CityName.NewTokyo:
|
||||
case CityName.Ishima:
|
||||
case CityName.Volhaven:
|
||||
if(Player.money.lt(CONSTANTS.TravelCost)) {
|
||||
workerScript.scriptRef.log("ERROR: not enough money to travel with travelToCity().");
|
||||
throw makeRuntimeRejectMsg(workerScript, "ERROR: not enough money to travel with travelToCity().");
|
||||
@@ -2817,10 +2823,6 @@ function NetscriptFunctions(workerScript) {
|
||||
AddToAllServers(darkweb);
|
||||
SpecialServerIps.addIp("Darkweb Server", darkweb.ip);
|
||||
|
||||
const purchaseTor = document.getElementById("location-purchase-tor");
|
||||
purchaseTor.setAttribute("class", "a-link-button-bought");
|
||||
purchaseTor.innerHTML = "TOR Router - Purchased";
|
||||
|
||||
Player.getHomeComputer().serversOnNetwork.push(darkweb.ip);
|
||||
darkweb.serversOnNetwork.push(Player.getHomeComputer().ip);
|
||||
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
|
||||
@@ -3586,29 +3588,8 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
//Set Location to slums
|
||||
switch(Player.city) {
|
||||
case Locations.Aevum:
|
||||
Player.location = Locations.AevumSlums;
|
||||
break;
|
||||
case Locations.Chongqing:
|
||||
Player.location = Locations.ChongqingSlums;
|
||||
break;
|
||||
case Locations.Sector12:
|
||||
Player.location = Locations.Sector12Slums;
|
||||
break;
|
||||
case Locations.NewTokyo:
|
||||
Player.location = Locations.NewTokyoSlums;
|
||||
break;
|
||||
case Locations.Ishima:
|
||||
Player.location = Locations.IshimaSlums;
|
||||
break;
|
||||
case Locations.Volhaven:
|
||||
Player.location = Locations.VolhavenSlums;
|
||||
break;
|
||||
default:
|
||||
console.log("Invalid Player.city value");
|
||||
}
|
||||
// Set Location to slums
|
||||
Player.gotoLocation(LocationName.Slums);
|
||||
|
||||
const crime = findCrime(crimeRoughName.toLowerCase());
|
||||
if(crime == null) { // couldn't find crime
|
||||
@@ -4730,7 +4711,7 @@ function NetscriptFunctions(workerScript) {
|
||||
answer = String(answer);
|
||||
}
|
||||
|
||||
const serv = safeGetServer(ip, "codingcontract.attempt()");
|
||||
const serv = safeGetServer(ip, "codingcontract.attempt");
|
||||
if (contract.isSolution(answer)) {
|
||||
const reward = Player.gainCodingContractReward(contract.reward, contract.getDifficulty());
|
||||
workerScript.log(`Successfully completed Coding Contract ${fn}. Reward: ${reward}`);
|
||||
@@ -5080,6 +5061,70 @@ function NetscriptFunctions(workerScript) {
|
||||
workRepGain: sl.getRepGain(),
|
||||
}
|
||||
},
|
||||
getSleeveAugmentations : function(sleeveNumber=0) {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("getSleeveAugmentations", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
}
|
||||
if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "getSleeveAugmentations() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10");
|
||||
}
|
||||
updateDynamicRam("getSleeveAugmentations", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
|
||||
workerScript.log(`ERROR: sleeve.getSleeveAugmentations(${sleeveNumber}) failed because it is an invalid sleeve number.`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const augs = [];
|
||||
for (let i = 0; i < Player.sleeves[sleeveNumber].augmentations.length; i++) {
|
||||
augs.push(Player.sleeves[sleeveNumber].augmentations[i].name);
|
||||
}
|
||||
return augs;
|
||||
},
|
||||
getSleevePurchasableAugs : function(sleeveNumber=0) {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("getSleevePurchasableAugs", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
}
|
||||
if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "getSleevePurchasableAugs() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10");
|
||||
}
|
||||
updateDynamicRam("getSleevePurchasableAugs", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
|
||||
workerScript.log(`ERROR: sleeve.getSleevePurchasableAugs(${sleeveNumber}) failed because it is an invalid sleeve number.`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const purchasableAugs = findSleevePurchasableAugs(Player.sleeves[sleeveNumber], Player);
|
||||
const augs = [];
|
||||
for (let i = 0; i < purchasableAugs.length; i++) {
|
||||
const aug = purchasableAugs[i];
|
||||
augs.push({
|
||||
name: aug.name,
|
||||
cost: aug.startingCost,
|
||||
});
|
||||
}
|
||||
|
||||
return augs;
|
||||
},
|
||||
purchaseSleeveAug : function(sleeveNumber=0, augName="") {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("purchaseSleeveAug", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
}
|
||||
if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "purchaseSleeveAug() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10");
|
||||
}
|
||||
updateDynamicRam("purchaseSleeveAug", CONSTANTS.ScriptSleeveBaseRamCost);
|
||||
if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
|
||||
workerScript.log(`ERROR: sleeve.purchaseSleeveAug(${sleeveNumber}) failed because it is an invalid sleeve number.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const aug = Augmentations[augName];
|
||||
if (!aug) {
|
||||
workerScript.log(`ERROR: sleeve.purchaseSleeveAug(${sleeveNumber}) failed because ${augName} is not a valid aug.`);
|
||||
}
|
||||
|
||||
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
|
||||
}
|
||||
} // End sleeve
|
||||
} //End return
|
||||
} //End NetscriptFunction()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user