Merge pull request #459 from danielyxie/dev

v0.40.3
This commit is contained in:
danielyxie 2018-09-15 12:05:30 -05:00 committed by GitHub
commit e425bcfe6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 5608 additions and 2500 deletions

13
.editorconfig Normal file

@ -0,0 +1,13 @@
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[package.json]
indent_size = 2
[md]
trim_trailing_whitespace = false

@ -17,29 +17,29 @@ heard:
scripting perhaps there is something that is conflicting with the
browser's Javascript interaction. So please do not be afraid to open a
[new issue](https://github.com/danielyxie/bitburner/issues/new).
## Reporting Bugs
The recommended method for reporting a bug is by opening a
[Github Issue](https://github.com/danielyxie/bitburner/issues).
Before submitting a bug report, please check to make sure the bug has not
already been reported as an [Issue](https://github.com/danielyxie/bitburner/issues).
## Reporting Bugs
The recommended method for reporting a bug is by opening a
[Github Issue](https://github.com/danielyxie/bitburner/issues).
Before submitting a bug report, please check to make sure the bug has not
already been reported as an [Issue](https://github.com/danielyxie/bitburner/issues).
#### How to Submit a Good Bug Report
* **Use a clear and descriptive title** for the issue
* **State your browser, your browser's version, and your computer's OS**
* **Provide instructions on how to reproduce the bug** in as much detail
* **Provide instructions on how to reproduce the bug** in as much detail
as possible. If you cannot reliably reproduce the bug, then just try
your best to explain what was happening when the bug occurred
* **Provide any scripts** that triggered the bug if the issue is Netscript-related
* **Open your browser's Dev Console and report any error-related output**
that may be printed there. The Dev Console can be opened on most modern
* **Open your browser's Dev Console and report any error-related output**
that may be printed there. The Dev Console can be opened on most modern
browsers by pressing F12
## As a Developer
Anyone is welcome to contribute to Bitburner code. However, please read
the [license](https://github.com/danielyxie/bitburner/blob/dev/license.txt)
Anyone is welcome to contribute to Bitburner code. However, please read
the [license](https://github.com/danielyxie/bitburner/blob/dev/license.txt)
and the [readme](https://github.com/danielyxie/bitburner/blob/dev/README.md)
before doing so.
@ -48,22 +48,22 @@ To contribute to Bitburner code, you will need to have
called `npm` is installed as well.
#### What are you Allowed to Contribute?
Not all code contributions will be accepted. The safest way to ensure
that you don't waste time working on something that gets rejected is to
run your idea(s)/plan(s) past [danielyxie](https://github.com/danielyxie) first.
Not all code contributions will be accepted. The safest way to ensure
that you don't waste time working on something that gets rejected is to
run your idea(s)/plan(s) past [danielyxie](https://github.com/danielyxie) first.
You can contact him through:
* Github
* Discord
* [Reddit](https://www.reddit.com/user/chapt3r/)
Otherwise, here are some general guidelines for determining what types of changes
are okay to contribute:
Otherwise, here are some general guidelines for determining what types of
changes are okay to contribute:
##### Contributions that Will Most Likely Be Accepted
* Bug Fixes
* Quality-of-Life Changes
* Adding a new, commonly-requested Netscript function
* Adding a new, commonly-requested Netscript function
* Fixing or improving UI elements
* Adding game settings/options
* Adding a new Terminal command
@ -73,8 +73,8 @@ are okay to contribute:
* Changes that directly affect the game's balance
* New gameplay mechanics
#### Submitting a Pull Request
When submitting a pull request with your code contributions, please abide by
#### Submitting a Pull Request
When submitting a pull request with your code contributions, please abide by
the following rules:
- Work in a branch forked from `dev` to isolate the new code
@ -86,14 +86,18 @@ the following rules:
_danielyxie/bitburner_ and the base is _dev_.
- If your changes affect the game's UI, attach some screenshots or GIFs showing
the changes to the UI
- If your changes affect Netscript, provide some
scripts that can be used to test the Netscript changes.
- Do not check in any bundled files (`dist\*.bundle.js`). These will be
updated as part of official releases.
- If your changes affect Netscript, provide some
scripts that can be used to test the Netscript changes.
- Ensure you have run `npm run lint` to make sure your changes conform to the
rules enforced across the code base. The command will fail if any of the
linters find a violation.
- Do not check in any bundled files (`dist\*.bundle.js`) or the `index.html`
in the root of the repository. These will be updated as part of official
releases.
## As a Documentor
To contribute to BitBurner documentation, you will need to have Python
installed, along with [Sphinx](http://www.sphinx-doc.org).
To contribute to and view your changes to the BitBurner documentation, you will
need to have Python installed, along with [Sphinx](http://www.sphinx-doc.org).
Before submitting your code for a pull request, please try to follow these
rules:
@ -103,3 +107,5 @@ rules:
- Rebase your branch if necessary
- When submitting the pull request, make sure that the base fork is
_danielyxie/bitburner_ and the base is _dev_.
- Do not check in any generated files under `doc\`. The documentation is built
automatically by ReadTheDocs.

@ -1,2 +1,17 @@
$fontFamily: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
$defaultFontSize: 16px;
/* COLORS */
$hacker-green: #adff2f;
$success-green: #3adb76;
$alert-red: #ff2929;
$money-gold: #ffd700;
$light-yellow: #faffdf;
/* Attributes */
$my-stat-hp-color: #dd3434;
$my-stat-money-color: $money-gold;
$my-stat-hack-color: $hacker-green;
$my-stat-physical: $light-yellow;
$my-stat-cha-color: #a671d1;
$my-stat-int-color: #6495ed;

@ -11,11 +11,11 @@
position: absolute; /* Stay in place */
right: 0;
top: 0;
height: 400px; /* Full height */
height: 450px;
padding: 10px;
border: 5px solid #fff;
width: 20%;
overflow: auto; /* Enable scroll if needed */
width: 23%;
overflow: hidden;
background-color: #444; /* Fallback color */
color: #fff;
@ -29,6 +29,9 @@
margin: 4px;
color: #fff;
background-color: #444;
font-size: $defaultFontSize * 0.875;
max-height: 350px;
overflow-y: auto;
}
#interactive-tutorial-exit,
@ -38,7 +41,7 @@
@include boxShadow(1px 1px 3px #000);
color: #aaa;
font-size: $defaultFontSize * 1.25;
font-size: $defaultFontSize * 1.125;
font-weight: bold;
background-color: #000;
@ -50,6 +53,7 @@
}
}
/*
#interactive-tutorial-exit {
float: left;
}
@ -58,6 +62,16 @@
margin-right: 20%;
float: right;
}
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0;
}
#interactive-tutorial-back {
float: left;
}
#interactive-tutorial-next {
float: right;

@ -92,3 +92,20 @@
left: 50%;
}
}
.killAllMessage {
position: absolute;
top: 95%;
left: 50%;
-webkit-transform: translateX(-50%);
-moz-transform: translateX(-50%);
-ms-transform: translateX(-50%);
-o-transform: translateX(-50%);
transform: translateX(-50%);
}
.killAllMessageWrapperHidden {
display: none;
}
.killAllMessageWrapperShow {
display: block;
}

@ -22,17 +22,15 @@
#script-editor-container {
background-color: transparent;
}
#javascript-editor {
margin: 10px;
height: 80%;
width: 100%;
margin-left: 6px;
padding-left: 6px;
padding-top: 6px;
padding-bottom: 6px;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
@ -101,9 +99,7 @@
resize: none;
color: #fff;
margin: 4px;
padding: 2px;
border: 2px solid var(--my-highlight-color);
}
@ -204,38 +200,37 @@
.active-scripts-script-header {
background-color: #555;
color: var(--my-font-color);
padding: 4px;
padding-left: 10px;
padding: 4px 25px 4px 10px;
cursor: pointer;
width: auto;
text-align: left;
border: none;
outline: none;
}
position: relative;
.active-scripts-script-header:hover,
.active-scripts-script-header.active:hover {
background-color: #666;
}
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
.active-scripts-script-header.active {
background-color: #555;
}
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
.active-scripts-script-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
}
&:hover,
&.active:hover {
background-color: #666;
}
.active-scripts-script-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
&.active {
background-color: #555;
}
}
.active-scripts-script-panel {
@ -244,16 +239,13 @@
width: auto;
display: none;
margin-bottom: 6px;
}
.active-scripts-script-panel p,
.active-scripts-script-panel h2,
.active-scripts-script-panel ul,
.active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
p, h2, ul, li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}
.active-scripts-button {
@ -266,13 +258,13 @@
margin: 4px;
padding: 4px;
background-color: #000;
}
.active-scripts-button:hover,
.active-scripts-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
}
/* Hacknet Nodes */
@ -291,6 +283,16 @@
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 {
@ -316,34 +318,24 @@
display: inline-block;
}
.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: 6px;
width: 34vw;
border: 2px solid var(--my-highlight-color);
}
.hacknet-node-container {
display: inline-table;
}
.hacknet-node-container .row {
display: table-row;
height: 30px;
}
.row {
display: table-row;
height: 30px;
.hacknet-node-container .row p {
display: table-cell;
}
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: $defaultFontSize * 4;
.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 {
@ -412,8 +404,8 @@
#faction-container p,
#faction-container pre {
padding: 6px;
margin: 6px;
padding: 4px 6px;
margin: 4px 6px;
}
#faction-container pre {
@ -426,17 +418,14 @@
}
/* Faction Augmentations */
#faction-augmentations-container{
#faction-augmentations-container {
position: fixed;
padding-top: 10px;
}
#faction-augmentations-container p,
#faction-augmentations-container a,
#faction-augmentations-container ul,
#faction-augmentations-container h1{
margin: 8px;
padding: 4px;
p, a, ul, h1 {
margin: 8px;
padding: 4px;
}
}
/* World */
@ -451,11 +440,20 @@
padding-top: 10px;
}
.augmentations-list button,
.augmentations-list div {
color: var(--my-font-color);
padding: 8px;
text-decoration: none;
.augmentations-list {
button,
div {
color: var(--my-font-color);
text-decoration: none;
}
button {
padding: 2px 5px;
}
div {
padding: 6px;
}
}
/* Tutorial */
@ -510,11 +508,12 @@
padding: 6px;
}
#location-container * {
#location-container > * {
margin: 10px 5px 10px 5px;
}
#location-job-reputation, #location-company-favor {
#location-job-reputation,
#location-company-favor {
display: inline;
}
@ -522,7 +521,13 @@
#infiltration-container {
position: fixed;
padding: 6px;
span {
margin: 0;
padding: 0;
}
}
#infiltration-left-panel,
#infiltration-right-panel {
display: inline-block;
@ -543,6 +548,10 @@
margin: 4px;
}
#infiltration-buttons {
margin-top: 20px;
}
#infiltration-buttons .a-link-button {
display: inline;
width: 25%;
@ -552,6 +561,7 @@
#stock-market-container {
position: fixed;
padding: 6px;
p {
font-size: $defaultFontSize * 0.8125;
}
@ -559,9 +569,9 @@
font-size: $defaultFontSize * 0.875;
}
h2 {
margin-top:10px;
margin-left:10px;
display:block;
margin-top: 10px;
margin-left: 10px;
display: block;
}
}
@ -583,8 +593,8 @@
}
#stock-market-watchlist-filter {
width:50%;
margin-left:10px;
width: 50%;
margin-left: 10px;
}
.stock-market-input {

@ -8,8 +8,8 @@
z-index: 10; /* Sit on top */
left: 0;
top: 0;
align-items:center;
justify-content:center;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: auto;
@ -21,7 +21,7 @@
padding: 12px;
border: 5px solid var(--my-highlight-color);
width: 70%;
max-height:80%;
max-height: 80%;
/*
margin: auto;
height:auto;
@ -32,7 +32,7 @@
bottom:0;
right:0;
*/
overflow-y:auto;
overflow-y: auto;
color: var(--my-font-color);
}
@ -87,6 +87,11 @@
z-index: 2;
background-color: var(--my-background-color);
padding: 10px;
p span {
padding: 0;
margin: 0;
}
}
.dialog-box-close-button {
@ -138,6 +143,11 @@
margin: 8px;
}
#infiltration-box-content span {
padding: 0;
margin: 0;
}
#infiltration-faction-select {
background-color: #000;
}
@ -158,8 +168,8 @@
border: 5px solid var(--my-highlight-color);
color: var(--my-font-color);
width: 80%;
max-height:80%;
overflow-y:auto;
max-height: 80%;
overflow-y: auto;
}
#game-options-left-panel,

@ -3,10 +3,11 @@
@import "reset";
:root{
:root {
--my-font-color: #6f3;
--my-background-color: #000;
--my-highlight-color: #fff;
--my-prompt-color: #f92672;
}
body {
@ -83,7 +84,7 @@ tr:focus {
display: block;
color: #e6e6e6;
background-color: #555;
padding: 16px;
padding: 12px 8px;
text-decoration: none;
}
@ -105,21 +106,36 @@ tr:focus {
background-color: #aaa;
}
#hacking-menu-header-li,
#character-menu-header-li,
#world-menu-header-li,
#help-menu-header-li {
position: relative;
}
/* Accordion Outline */
.mainmenu-accordion-header {
outline: 2px solid #fff;
}
/* Plus and minus signs */
.mainmenu-accordion-header:after {
content: '\02795';
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
.mainmenu-accordion-header.opened:after {
content: "\2796";
font-size: $defaultFontSize * 0.8125;
position: absolute;
bottom: 25%;
right: 3px;
color: transparent;
text-shadow: 0 0 0 #fff;
}
.mainmenu-accordion-header.opened {
background-color: #222;
&:after {
content: "\2796";
}
}
/* Slide down transition */
@ -129,11 +145,6 @@ tr:focus {
transition: max-height 0.2s ease-out;
}
/* Borders */
.mainmenu-accordion-header {
border: 2px solid #fff;
}
/* Make html links ("a" elements) nice looking buttons with this class */
a:link,
a:visited {
@ -144,7 +155,7 @@ a:visited {
text-decoration: none;
background-color: #555;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
@ -152,14 +163,14 @@ a:visited {
-ms-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
.a-link-button:hover {
background-color: #666;
}
&:hover {
background-color: #666;
}
.a-link-button:active {
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
&:active {
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
}
}
/* Make anchor tags ("a" elements) inactive (not clickable) */
@ -167,20 +178,22 @@ a:visited {
text-decoration: none;
background-color: #333;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
cursor: default;
}
.a-link-button-inactive:hover .tooltiptext,
.a-link-button-inactive:hover .tooltiptexthigh,
.a-link-button-inactive:hover .tooltiptextleft {
visibility: visible;
}
&:hover {
.tooltiptext,
.tooltiptexthigh,
.tooltiptextleft {
visibility: visible;
}
}
.a-link-button-inactive:active {
pointer-events: none;
&:active {
pointer-events: none;
}
}
/* Make anchor tags ("a" elements) for activated actions */
@ -188,20 +201,22 @@ a:visited {
text-decoration: none;
background-color: #0a0;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #0a0;
cursor: default;
}
.a-link-button-bought:hover .tooltiptext,
.a-link-button-bought:hover .tooltiptexthigh,
.a-link-button-bought:hover .tooltiptextleft {
visibility: visible;
}
&:hover {
.tooltiptext,
.tooltiptexthigh,
.tooltiptextleft {
visibility: visible;
}
}
.a-link-button-bought:active {
pointer-events: none;
&:active {
pointer-events: none;
}
}
.dropdown {
@ -218,9 +233,9 @@ a:visited {
#create-program-tab {
position: relative;
}
#create-program-notification {
font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */
top: 0;
right: 0;
@ -242,22 +257,23 @@ a:visited {
/* Tool tips (when hovering over an element */
.tooltip {
position: relative;
display: inline-block;
}
position: relative;
.tooltip .tooltiptext {
visibility: hidden;
width: 300px;
background-color: var(--my-background-color);
border: 2px solid var(--my-highlight-color);
color: #fff;
text-align: center;
padding: 4px;
left: 101%;
.tooltiptext {
visibility: hidden;
width: 300px;
background-color: var(--my-background-color);
border: 2px solid var(--my-highlight-color);
color: #fff;
text-align: center;
padding: 4px;
left: 101%;
position: absolute;
z-index: 99;
pointer-events: none;
position: absolute;
z-index: 99;
}
}
/* Same thing as a normal tooltip except its a bit higher */
@ -276,7 +292,7 @@ a:visited {
z-index: 99;
}
/* Similar to a normal tooltip except its positioned on the left of the elemnt
/* Similar to a normal tooltip except its positioned on the left of the element
rather than the right to avoid exceeding the elements normal width */
.tooltip .tooltiptextleft {
visibility: hidden;
@ -364,7 +380,7 @@ a:visited {
/* Blinking Cursor */
/* ----- blinking cursor animation ----- */
.typed-cursor{
.typed-cursor {
opacity: 1;
-webkit-animation: blink 0.95s infinite;
-moz-animation: blink 0.95s infinite;
@ -400,16 +416,16 @@ a:visited {
}
/* Status text */
@-webkit-keyframes status-text{
from{
@-webkit-keyframes status-text {
from {
opacity: 1;
}
to{
to {
opacity: 0;
}
}
.status-text{
.status-text {
display: inline-block;
height: 15%;
position: fixed;
@ -437,47 +453,78 @@ a:visited {
#character-overview-wrapper {
position: relative;
}
#character-overview-container {
display: none;
position: absolute; /* Stay in place */
right: 0;
top: 0;
height: auto; /* Full height */
padding: 8px;
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: 19%;
width: auto;
max-width: 280px;
overflow: auto; /* Enable scroll if needed */
background-color: #444; /* Fallback color */
background-color: rgba(57, 54, 54, 0.9); /* Fallback color */
z-index: 1;
}
#character-overview-text {
color: #fff;
background-color: #444;
color: $my-stat-physical;
table {
border-collapse: collapse;
margin: auto;
}
td {
padding: 2px;
vertical-align: middle;
}
}
.character-stat-text {
color: #fff;
background-color: #444;
}
.character-stat-cell {
.character-stat-cell {
text-align: right;
}
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px;
}
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px;
}
#character-hp-wrapper { color: $my-stat-hp-color; }
#character-money-wrapper { color: $my-stat-money-color; }
#character-hack-wrapper { color: $my-stat-hack-color; }
#character-cha-wrapper { color: $my-stat-cha-color; }
#character-int-wrapper { color: $my-stat-int-color; }
#character-overview-save-button,
#character-overview-options-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
color: #cecece;
display: inline-block;
font-size: $defaultFontSize * 0.875;
font-weight: bold;
height: 22px;
height: 25px;
background-color: #000;
padding: 5px 8px;
}
.character-quick-options {
padding-top: 5px;
margin-top: 10px;
text-align: center;
}
#character-overview-save-button:hover,
@ -489,18 +536,15 @@ a:visited {
cursor: pointer;
}
#character-overview-options-button {
display: inline;
}
/* Scan analyze links from AutoLink */
.scan-analyze-link {
cursor: pointer;
color: #fff;
text-decoration: underline;
}
.scan-analyze-link:hover {
text-decoration: none;
&:hover {
text-decoration: none;
}
}
/* Accordion menus (Header with collapsible panel) */
@ -509,37 +553,37 @@ a:visited {
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
padding: 4px 6px;
cursor: pointer;
width: 80%;
text-align: left;
border: none;
outline: none;
}
position: relative;
.accordion-header.active,
.accordion-header:hover {
background-color: #555;
}
&.active,
&:hover {
background-color: #555;
}
.accordion-header.active:hover {
background-color: #666;
}
&.active:hover {
background-color: #666;
}
.accordion-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
color: transparent;
text-shadow: 0 0 0 #fff;
position: absolute;
bottom: 5px;
right: 6px;
}
.accordion-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
}
.accordion-panel {
@ -551,13 +595,10 @@ a:visited {
background-color: #555;
overflow-y: auto;
overflow-x: none;
}
.accordion-panel div,
.accordion-panel ul,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555;
div, ul, p, ul > li {
background-color: #555;
}
}
/* override the global <span> styling */
@ -567,3 +608,34 @@ a:visited {
margin: 0;
padding: 0;
}
/* Helper Classes */
.hacker-green {
color: $hacker-green;
}
.money-gold {
color: $money-gold;
}
.light-yellow {
color: $light-yellow;
}
.failure {
color: $alert-red;
text-shadow: 0 0 0 $alert-red;
}
.success {
color: $success-green;
text-shadow: 0 0 0 $success-green;
}
.physical-yellow {
color: $my-stat-physical;
}
.charisma-purple {
color: $my-stat-cha-color;
}

@ -19,6 +19,12 @@
overflow-y: scroll;
background-color: var(--my-background-color);
table-layout: fixed;
.prompt {
color: var(--my-prompt-color);
margin: 0;
padding: 0;
}
}
#terminal-input {
@ -50,6 +56,12 @@
display: flex;
}
#terminal-input-td textarea {
overflow: hidden;
resize: none;
height: auto;
}
#terminal-input-header {
white-space: pre;
}

File diff suppressed because one or more lines are too long

480
dist/engine.css vendored

@ -1,3 +1,7 @@
/* COLORS */
/* Attributes */
/* COLORS */
/* Attributes */
* {
font-size: 16px;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; }
@ -13,7 +17,8 @@
:root {
--my-font-color: #6f3;
--my-background-color: #000;
--my-highlight-color: #fff; }
--my-highlight-color: #fff;
--my-prompt-color: #f92672; }
body {
background-color: var(--my-background-color); }
@ -81,7 +86,7 @@ tr:focus {
display: block;
color: #e6e6e6;
background-color: #555;
padding: 16px;
padding: 12px 8px;
text-decoration: none; }
/* Hovering makes them lighter */
@ -99,19 +104,31 @@ tr:focus {
.mainmenu > li a.active:hover {
background-color: #aaa; }
#hacking-menu-header-li,
#character-menu-header-li,
#world-menu-header-li,
#help-menu-header-li {
position: relative; }
/* Accordion Outline */
.mainmenu-accordion-header {
outline: 2px solid #fff; }
/* Plus and minus signs */
.mainmenu-accordion-header:after {
content: '\2795';
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.mainmenu-accordion-header.opened:after {
content: "\2796"; }
font-size: 13px;
position: absolute;
bottom: 25%;
right: 3px;
color: transparent;
text-shadow: 0 0 0 #fff; }
.mainmenu-accordion-header.opened {
background-color: #222; }
.mainmenu-accordion-header.opened:after {
content: "\2796"; }
/* Slide down transition */
.mainmenu-accordion-panel {
@ -119,10 +136,6 @@ tr:focus {
opacity: 1;
transition: max-height 0.2s ease-out; }
/* Borders */
.mainmenu-accordion-header {
border: 2px solid #fff; }
/* Make html links ("a" elements) nice looking buttons with this class */
a:link,
a:visited {
@ -132,57 +145,51 @@ a:visited {
text-decoration: none;
background-color: #555;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
-moz-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none; }
.a-link-button:hover {
background-color: #666; }
.a-link-button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
.a-link-button:hover {
background-color: #666; }
.a-link-button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
/* Make anchor tags ("a" elements) inactive (not clickable) */
.a-link-button-inactive {
text-decoration: none;
background-color: #333;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
cursor: default; }
.a-link-button-inactive:hover .tooltiptext,
.a-link-button-inactive:hover .tooltiptexthigh,
.a-link-button-inactive:hover .tooltiptextleft {
visibility: visible; }
.a-link-button-inactive:active {
pointer-events: none; }
.a-link-button-inactive:hover .tooltiptext,
.a-link-button-inactive:hover .tooltiptexthigh,
.a-link-button-inactive:hover .tooltiptextleft {
visibility: visible; }
.a-link-button-inactive:active {
pointer-events: none; }
/* Make anchor tags ("a" elements) for activated actions */
.a-link-button-bought {
text-decoration: none;
background-color: #0a0;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #0a0;
cursor: default; }
.a-link-button-bought:hover .tooltiptext,
.a-link-button-bought:hover .tooltiptexthigh,
.a-link-button-bought:hover .tooltiptextleft {
visibility: visible; }
.a-link-button-bought:active {
pointer-events: none; }
.a-link-button-bought:hover .tooltiptext,
.a-link-button-bought:hover .tooltiptexthigh,
.a-link-button-bought:hover .tooltiptextleft {
visibility: visible; }
.a-link-button-bought:active {
pointer-events: none; }
.dropdown {
color: #fff;
@ -217,20 +224,20 @@ a:visited {
/* Tool tips (when hovering over an element */
.tooltip {
position: relative;
display: inline-block; }
.tooltip .tooltiptext {
visibility: hidden;
width: 300px;
background-color: var(--my-background-color);
border: 2px solid var(--my-highlight-color);
color: #fff;
text-align: center;
padding: 4px;
left: 101%;
position: absolute;
z-index: 99; }
display: inline-block;
position: relative; }
.tooltip .tooltiptext {
visibility: hidden;
width: 300px;
background-color: var(--my-background-color);
border: 2px solid var(--my-highlight-color);
color: #fff;
text-align: center;
padding: 4px;
left: 101%;
pointer-events: none;
position: absolute;
z-index: 99; }
/* Same thing as a normal tooltip except its a bit higher */
.tooltip .tooltiptexthigh {
@ -246,7 +253,7 @@ a:visited {
position: absolute;
z-index: 99; }
/* Similar to a normal tooltip except its positioned on the left of the elemnt
/* Similar to a normal tooltip except its positioned on the left of the element
rather than the right to avoid exceeding the elements normal width */
.tooltip .tooltiptextleft {
visibility: hidden;
@ -436,18 +443,24 @@ a:visited {
top: 0;
height: auto;
/* Full height */
padding: 8px;
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: 19%;
width: auto;
max-width: 280px;
overflow: auto;
/* Enable scroll if needed */
background-color: #444;
background-color: rgba(57, 54, 54, 0.9);
/* Fallback color */
z-index: 1; }
#character-overview-text {
color: #fff;
background-color: #444; }
color: #faffdf; }
#character-overview-text table {
border-collapse: collapse;
margin: auto; }
#character-overview-text td {
padding: 2px;
vertical-align: middle; }
.character-stat-text {
color: #fff;
@ -456,6 +469,30 @@ a:visited {
.character-stat-cell {
text-align: right; }
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px; }
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px; }
#character-hp-wrapper {
color: #dd3434; }
#character-money-wrapper {
color: #ffd700; }
#character-hack-wrapper {
color: #adff2f; }
#character-cha-wrapper {
color: #a671d1; }
#character-int-wrapper {
color: #6495ed; }
#character-overview-save-button,
#character-overview-options-button {
-webkit-border-radius: 12px;
@ -464,14 +501,17 @@ a:visited {
-webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #aaa;
color: #cecece;
display: inline-block;
font-size: 14px;
font-weight: bold;
height: 22px;
background-color: #000; }
height: 25px;
background-color: #000;
padding: 5px 8px; }
.character-quick-options {
padding-top: 5px; }
margin-top: 10px;
text-align: center; }
#character-overview-save-button:hover,
#character-overview-save-button:focus,
@ -481,17 +521,13 @@ a:visited {
text-decoration: none;
cursor: pointer; }
#character-overview-options-button {
display: inline; }
/* Scan analyze links from AutoLink */
.scan-analyze-link {
cursor: pointer;
color: #fff;
text-decoration: underline; }
.scan-analyze-link:hover {
text-decoration: none; }
.scan-analyze-link:hover {
text-decoration: none; }
/* Accordion menus (Header with collapsible panel) */
.accordion-header {
@ -499,35 +535,30 @@ a:visited {
font-size: 20px;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
padding: 4px 6px;
cursor: pointer;
width: 80%;
text-align: left;
border: none;
outline: none; }
.accordion-header.active,
.accordion-header:hover {
background-color: #555; }
.accordion-header.active:hover {
background-color: #666; }
.accordion-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.accordion-header.active:after {
content: "\2796";
/* "minus" sign (-) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
outline: none;
position: relative; }
.accordion-header.active, .accordion-header:hover {
background-color: #555; }
.accordion-header.active:hover {
background-color: #666; }
.accordion-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
float: right;
color: transparent;
text-shadow: 0 0 0 #fff;
position: absolute;
bottom: 5px;
right: 6px; }
.accordion-header.active:after {
content: "\2796";
/* "minus" sign (-) */ }
.accordion-panel {
margin: 0 6px 6px 6px;
@ -538,12 +569,8 @@ a:visited {
background-color: #555;
overflow-y: auto;
overflow-x: none; }
.accordion-panel div,
.accordion-panel ul,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555; }
.accordion-panel div, .accordion-panel ul, .accordion-panel p, .accordion-panel ul > li {
background-color: #555; }
/* override the global <span> styling */
#active-scripts-total-production-active,
@ -552,6 +579,32 @@ a:visited {
margin: 0;
padding: 0; }
/* Helper Classes */
.hacker-green {
color: #adff2f; }
.money-gold {
color: #ffd700; }
.light-yellow {
color: #faffdf; }
.failure {
color: #ff2929;
text-shadow: 0 0 0 #ff2929; }
.success {
color: #3adb76;
text-shadow: 0 0 0 #3adb76; }
.physical-yellow {
color: #faffdf; }
.charisma-purple {
color: #a671d1; }
/* COLORS */
/* Attributes */
#terminal-container {
position: fixed;
margin-left: 10%;
@ -570,6 +623,10 @@ a:visited {
overflow-y: scroll;
background-color: var(--my-background-color);
table-layout: fixed; }
#terminal .prompt {
color: var(--my-prompt-color);
margin: 0;
padding: 0; }
#terminal-input {
background-color: var(--my-background-color);
@ -596,12 +653,19 @@ a:visited {
#terminal-input-td {
display: flex; }
#terminal-input-td textarea {
overflow: hidden;
resize: none;
height: auto; }
#terminal-input-header {
white-space: pre; }
#terminal-input-text-box {
flex: 1 1 auto; }
/* COLORS */
/* Attributes */
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
terminal which has its own page) */
.generic-menupage-container {
@ -776,36 +840,30 @@ a:visited {
.active-scripts-script-header {
background-color: #555;
color: var(--my-font-color);
padding: 4px;
padding-left: 10px;
padding: 4px 25px 4px 10px;
cursor: pointer;
width: auto;
text-align: left;
border: none;
outline: none; }
.active-scripts-script-header:hover,
.active-scripts-script-header.active:hover {
background-color: #666; }
.active-scripts-script-header.active {
background-color: #555; }
.active-scripts-script-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
color: var(--my-font-color);
float: right;
margin-left: 5px; }
.active-scripts-script-header.active:after {
content: "\2796";
/* "minus" sign (-) */
font-size: 13px;
color: var(--my-font-color);
float: right;
margin-left: 5px; }
outline: none;
position: relative; }
.active-scripts-script-header:after {
content: '\2795';
/* "plus" sign (+) */
font-size: 13px;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px; }
.active-scripts-script-header.active:after {
content: "\2796";
/* "minus" sign (-) */ }
.active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
background-color: #666; }
.active-scripts-script-header.active {
background-color: #555; }
.active-scripts-script-panel {
padding: 0 18px;
@ -813,15 +871,11 @@ a:visited {
width: auto;
display: none;
margin-bottom: 6px; }
.active-scripts-script-panel p,
.active-scripts-script-panel h2,
.active-scripts-script-panel ul,
.active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%; }
.active-scripts-script-panel p, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%; }
.active-scripts-button {
-webkit-border-radius: 12px;
@ -836,12 +890,10 @@ a:visited {
margin: 4px;
padding: 4px;
background-color: #000; }
.active-scripts-button:hover,
.active-scripts-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
.active-scripts-button:hover, .active-scripts-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
/* Hacknet Nodes */
#hacknet-nodes-container {
@ -857,6 +909,14 @@ a:visited {
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;
@ -876,31 +936,19 @@ a:visited {
#hacknet-nodes-purchase-button {
display: inline-block; }
.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: 6px;
width: 34vw;
border: 2px solid var(--my-highlight-color); }
.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; }
.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; }
@ -957,8 +1005,8 @@ a:visited {
#faction-container p,
#faction-container pre {
padding: 6px;
margin: 6px; }
padding: 4px 6px;
margin: 4px 6px; }
#faction-container pre {
width: 70%;
@ -977,13 +1025,9 @@ a:visited {
#faction-augmentations-container {
position: fixed;
padding-top: 10px; }
#faction-augmentations-container p,
#faction-augmentations-container a,
#faction-augmentations-container ul,
#faction-augmentations-container h1 {
margin: 8px;
padding: 4px; }
#faction-augmentations-container p, #faction-augmentations-container a, #faction-augmentations-container ul, #faction-augmentations-container h1 {
margin: 8px;
padding: 4px; }
/* World */
#world-container li {
@ -998,9 +1042,14 @@ a:visited {
.augmentations-list button,
.augmentations-list div {
color: var(--my-font-color);
padding: 8px;
text-decoration: none; }
.augmentations-list button {
padding: 2px 5px; }
.augmentations-list div {
padding: 6px; }
/* Tutorial */
#tutorial-container {
position: fixed;
@ -1043,16 +1092,20 @@ a:visited {
margin: 10px;
padding: 6px; }
#location-container * {
#location-container > * {
margin: 10px 5px 10px 5px; }
#location-job-reputation, #location-company-favor {
#location-job-reputation,
#location-company-favor {
display: inline; }
/* Infiltration */
#infiltration-container {
position: fixed;
padding: 6px; }
#infiltration-container span {
margin: 0;
padding: 0; }
#infiltration-left-panel,
#infiltration-right-panel {
@ -1071,6 +1124,9 @@ a:visited {
#infiltration-right-panel p {
margin: 4px; }
#infiltration-buttons {
margin-top: 20px; }
#infiltration-buttons .a-link-button {
display: inline;
width: 25%; }
@ -1139,6 +1195,8 @@ a:visited {
float: left;
background-color: #555; }
/* COLORS */
/* Attributes */
/* Both Work in progress and BitNode stuff */
.generic-fullscreen-container {
color: var(--my-font-color);
@ -1195,6 +1253,8 @@ a:visited {
.bitnode-destroyed:hover {
color: #fff; }
/* COLORS */
/* Attributes */
/* Pop-up boxes */
.popup-box-container {
display: none;
@ -1277,6 +1337,10 @@ a:visited {
z-index: 2;
background-color: var(--my-background-color);
padding: 10px; }
.dialog-box-content p span,
#log-box-content p span {
padding: 0;
margin: 0; }
.dialog-box-close-button {
-webkit-border-radius: 12px;
@ -1323,6 +1387,10 @@ a:visited {
padding: 8px;
margin: 8px; }
#infiltration-box-content span {
padding: 0;
margin: 0; }
#infiltration-faction-select {
background-color: #000; }
@ -1375,6 +1443,8 @@ a:visited {
#import-game-file-selector {
display: none; }
/* COLORS */
/* Attributes */
/* interactivetutorial.css */
#interactive-tutorial-wrapper {
position: relative; }
@ -1385,13 +1455,11 @@ a:visited {
/* Stay in place */
right: 0;
top: 0;
height: 400px;
/* Full height */
height: 450px;
padding: 10px;
border: 5px solid #fff;
width: 20%;
overflow: auto;
/* Enable scroll if needed */
width: 23%;
overflow: hidden;
background-color: #444;
/* Fallback color */
color: #fff; }
@ -1402,7 +1470,10 @@ a:visited {
padding: 4px;
margin: 4px;
color: #fff;
background-color: #444; }
background-color: #444;
font-size: 14px;
max-height: 350px;
overflow-y: auto; }
#interactive-tutorial-exit,
#interactive-tutorial-next,
@ -1414,7 +1485,7 @@ a:visited {
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #aaa;
font-size: 20px;
font-size: 18px;
font-weight: bold;
background-color: #000; }
#interactive-tutorial-exit:hover, #interactive-tutorial-exit:focus,
@ -1426,16 +1497,29 @@ a:visited {
text-decoration: none;
cursor: pointer; }
/*
#interactive-tutorial-exit {
float: left; }
float: left;
}
#interactive-tutorial-back {
margin-right: 20%;
float: right; }
margin-right: 20%;
float: right;
}
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0; }
#interactive-tutorial-back {
float: left; }
#interactive-tutorial-next {
float: right; }
/* COLORS */
/* Attributes */
* {
font-size: 16px;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; }
@ -1448,6 +1532,8 @@ a:visited {
box-sizing: border-box;
vertical-align: top; }
/* COLORS */
/* Attributes */
@-webkit-keyframes LOADERSPINNER {
0% {
-webkit-transform: translate(-50%, -50%) rotate(0deg); }
@ -1604,6 +1690,24 @@ a:visited {
top: 50%;
left: 50%; }
.killAllMessage {
position: absolute;
top: 95%;
left: 50%;
-webkit-transform: translateX(-50%);
-moz-transform: translateX(-50%);
-ms-transform: translateX(-50%);
-o-transform: translateX(-50%);
transform: translateX(-50%); }
.killAllMessageWrapperHidden {
display: none; }
.killAllMessageWrapperShow {
display: block; }
/* COLORS */
/* Attributes */
/* css for Missions */
/* Hacking missions */
#mission-container {
@ -1720,6 +1824,8 @@ a:visited {
.hack-mission-action-buttons-container {
border: 2px solid #fff; }
/* COLORS */
/* Attributes */
#cmpy-mgmt-container p,
#cmpy-mgmt-container a,
#cmpy-mgmt-container div {
@ -1830,6 +1936,8 @@ a:visited {
.cmpy-mgmt-advertising-info {
font-size: 12px; }
/* COLORS */
/* Attributes */
#bladeburner-container a,
#bladeburner-container div,
#bladeburner-container p,

119
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,31 @@
Changelog
=========
v0.40.3 - 9/15/2018
-------------------
* Bladeburner Changes:
* Increased the effect that agi and dexterity have on action time
* Starting number of contracts/operations available will be slightly lower
* Random events will now happen slightly more often
* Slightly increased the rate at which the Overclock skill point cost increases
* The maximum volatility of stocks is now randomized (randomly generated within a certain range every time the game resets)
* Increased the range of possible values for initial stock prices
* b1t_flum3.exe program can now be created immediately at Hacking level 1 (rather than hacking level 5)
* UI improvements for the character overview panel and the left-hand menu (by mat-jaworski)
* General UI improvements for displays and Terminal (by mat-jaworski)
* Added optional parameters to the getHackTime(), getGrowTime(), and getWeakenTime() Netscript functions
* Added isLogEnabled() and getScriptLogs() Netscript functions
* Added donateToFaction() Singularity function
* Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0
* Added tryWrite() Netscript function
* When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished
* Added a setting in .fconf for enabling line-wrap in the Terminal input
* Adding a game option for changing the locale that most numbers are displayed in (this mostly applies for whenever money is displayed)
* The randomized parameters of many high-level servers can now take on a higher range of values
* Many 'foreign' servers (hackable servers that you don't own) now have a randomized amount of RAM
* Added 'wget' Terminal command
* Improved the introductory tutorial
v0.40.2 - 8/27/2018
-------------------
* Bladeburner Changes:
@ -17,9 +42,9 @@ v0.40.2 - 8/27/2018
* There are now, on average, more Synthoid communities in a city
* If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation
* Stock Market Changes:
*Added a watchlist filter feature to the UI that allows you to specify which stocks to show
*Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks
*Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript
* Added a watchlist filter feature to the UI that allows you to specify which stocks to show
* Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks
* Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript
* There is now a setting for enabling/disabling the popup that appears when you are hospitalized
* Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-)
* Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-)

@ -3,9 +3,10 @@ Netscript Documentation
Netscript is the programming language used in the world of Bitburner.
When you write scripts in Bitburner, they are written in the Netscript language.
Netscript is simply a tiny subset of Javascript. This means that Netscript's
syntax is almost idental to Javascript's, but it does not implement many of the
features that Javascript has.
Netscript is simply a subset of `JavaScript <https://developer.mozilla.org/en-US/docs/Web/JavaScript>`_,.
This means that Netscript's syntax is
identical to that of JavaScript, but it does not implement some of the features
that JavaScript has.
If you have any requests or suggestions to improve the Netscript language, feel free
to reach out to the developer!

@ -2,14 +2,18 @@
Netscript 1.0
=============
Netscript 1.0 is implemented using modified version of Neil Fraser's
Netscript 1.0 is implemented using a modified version of Neil Fraser's
`JS-Interpreter <https://github.com/NeilFraser/JS-Interpreter>`_.
This interpreter was created for ES5, which means that the code written
for Netscript 1.0 must be compliant for that version. However, some additional
ES6+ features are implemented through polyfills.
This is an ES5 JavaScript interpreter. This means that (almost) any JavaScript feature
that is available in ES5 is also available in Netscript 1.0. However, this also means
that the interpreter does not natively support any JavaScript features introduced in versions
ES6 or after.
Netscript 1.0 scripts end with the ".script" extension.
If you are confused by the ES5/ES6/etc. terminology, consider reading this:
`WTF is ES6, ES8, ES2017, ECMAScript... <https://codeburst.io/javascript-wtf-is-es6-es8-es-2017-ecmascript-dca859e4821c>`_
Netscript 1.0 scripts end with the ".script" extension in their filenames.
Which ES6+ features are supported?
----------------------------------

@ -43,3 +43,19 @@ getBitNodeMultipliers
mults = getBitNodeMultipliers();
print(mults.ServerMaxMoney);
print(mults.HackExpGain);
getHackTime, getGrowTime, & getWeakenTime
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :js:func:`getHackTime`, :js:func:`getGrowTime`, and :js:func:`getWeakenTime`
all take an additional third optional parameter for specifying a specific intelligence
level to see how that would affect the hack/grow/weaken times. This parameter
defaults to your current intelligence level.
(Intelligence is unlocked after obtaining Source-File 5).
The function signatures are then::
getHackTime(hostname/ip[, hackLvl=current level, intLvl=current level])
getGrowTime(hostname/ip[, hackLvl=current level, intLvl=current level])
getWeakenTime(hostname/ip[, hackLvl=current level, intLvl=current level])

@ -129,6 +129,28 @@ enableLog
Re-enables logging for the given function. If 'ALL' is passed into this function
as an argument, then it will revert the effects of disableLog('ALL')
isLogEnabled
^^^^^^^^^^^^
.. js:function:: isLogEnabled(fn)
:param string fn: Name of function to check
Returns a boolean indicating whether or not logging is enabled for that
function (or 'ALL')
getScriptLogs
^^^^^^^^^^^^^
.. js:function:: getScriptLogs()
Returns the script's logs. The logs are returned as an array, where each
line is an element in the array. The most recently logged line is at the
end of the array.
Note that there is a maximum number of lines that a script stores in its logs.
This is configurable in the game's options.
scan
^^^^
@ -659,14 +681,14 @@ getNextHacknetNodeCost
.. js:function:: getNextHacknetNodeCost()
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
purchaseHacknetNode
^^^^^^^^^^^^^^^^^^^
.. js:function:: purchaseHacknetNode()
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
getPurchasedServerCost
^^^^^^^^^^^^^^^^^^^^^^
@ -768,10 +790,22 @@ write
when writing to a port.
If the first argument is a string, then it specifies the name of a text file (.txt) and this function will write *data* to that text file. If the
specified text file does not exist, then it will be created. The third argument *mode, defines how the data will be written to the text file. If *mode*
specified text file does not exist, then it will be created. The third argument *mode*, defines how the data will be written to the text file. If *mode*
is set to "w", then the data is written in "write" mode which means that it will overwrite all existing data on the text file. If *mode* is set to
any other value then the data will be written in "append" mode which means that the data will be added at the end of the text file.
tryWrite
^^^^^^^^
.. js:function:: tryWrite(port, data="")
:param number port: Port to be written to
:param string data: Data to try to write
:returns: True if the data is successfully written to the port, and false otherwise
Attempts to write data to the specified Netscript Port. If the port is full, the data will
not be written. Otherwise, the data will be written normally
read
^^^^
@ -813,6 +847,17 @@ clear
If the *port/fn* argument is a string, then it specifies the name of a text file (.txt) and will delete all data from that text file.
getPortHandle
^^^^^^^^^^^^^
.. js:function:: getPortHandle(port)
:param number port: Port number
Get a handle to a Netscript Port. See more details here: :ref:`netscript_ports`
**WARNING:** Port Handles only work in :ref:`netscriptjs`. They will not work in :ref:`netscript1`.
rm
^^
@ -879,30 +924,42 @@ getScriptRam
getHackTime
^^^^^^^^^^^
.. js:function:: getHackTime(hostname/ip)
.. js:function:: getHackTime(hostname/ip[, hackLvl=current level])
:param string hostname/ip: Hostname or IP of target server
:param number hackLvl: Optional hacking level for the calculation. Defaults to player's current hacking level
Returns the amount of time in seconds it takes to execute the *hack()* Netscript function on the target server.
The function takes in an optional *hackLvl* parameter that can be specified
to see what the hack time would be at different hacking levels.
getGrowTime
^^^^^^^^^^^
.. js:function:: getGrowTime(hostname/ip)
.. js:function:: getGrowTime(hostname/ip[, hackLvl=current level])
:param string hostname/ip: Hostname or IP of target server
:param number hackLvl: Optional hacking level for the calculation. Defaults to player's current hacking level
Returns the amount of time in seconds it takes to execute the *grow()* Netscript function on the target server.
The function takes in an optional *hackLvl* parameter that can be specified
to see what the grow time would be at different hacking levels.
getWeakenTime
^^^^^^^^^^^^^
.. js:function:: getWeakenTime(hostname/ip)
.. js:function:: getWeakenTime(hostname/ip[, hackLvl=current level])
:param string hostname/ip: Hostname or IP of target server
:param number hackLvl: Optional hacking level for the calculation. Defaults to player's current hacking level
Returns the amount of time in seconds it takes to execute the *weaken()* Netscript function on the target server.
The function takes in an optional *hackLvl* parameter that can be specified
to see what the weaken time would be at different hacking levels.
getScriptIncome
^^^^^^^^^^^^^^^

@ -1,14 +1,18 @@
.. _netscript_misc:
Netscript Miscellaneous
=======================
.. _netscript_ports:
Netscript Ports
---------------
Netscript ports are endpoints that can be used to communicate between scripts.
Netscript Ports are endpoints that can be used to communicate between scripts.
A port is implemented as a sort of serialized queue, where you can only write
and read one element at a time from the port. When you read data from a port,
the element that is read is removed from the port.
The :js:func:`read`, :js:func:`write`, :js:func:`clear`, and :js:func:`peek`
The :js:func:`read`, :js:func:`write`, :js:func:`tryWrite`, :js:func:`clear`, and :js:func:`peek`
Netscript functions can be used to interact with ports.
Right now, there are only 20 ports for Netscript, denoted by the number 1
@ -55,9 +59,11 @@ And the data in port 1 will look like::
**Port Handles**
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
This handle allows you to access several new port-related functions and the
port's underlying data structure, which is just a Javascript array. The functions are:
port's underlying data structure, which is just a JavaScript array. The functions are:
.. js:method:: NetscriptPort.write(data)
@ -205,36 +211,12 @@ to specify a namespace for the import::
Note that exporting functions is not required.
Javascript Math Module
----------------------
The `Javascript Math Module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math>`_ is
supported in Netscript and is used in the same way::
numThreads = Math.floor(getServerRam("foodnstuff")[1] / 3.4);
Javascript Date Module
----------------------
The `Javascript Date Module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date>`_ is supported in Netscript.
However, since the 'new' operator does not work in Netscript, only the Date module's static functions can be used:
* now()
* UTC()
* Parse()
* Maybe some others I don't know about
Example::
time = Date.now();
Javascript Number Module
------------------------
The `Javascript Number module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number>`_ is supported in Netscript.
Example::
tprint(Number.isInteger(1)); //True
tprint(Number.isInteger(1.534059)); //False
Standard, Built-In JavaScript Objects
-------------------------------------
Standard built-in JavaScript objects such as
`Math <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math>`_,
`Date <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date>`_,
`Number <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number>`_,
and others are supported as expected based on which version
of Netscript you use (i.e. :ref:`netscript1` will support built-in objects that are
defined in ES5, and :ref:`netscriptjs` will support whatever your browser supports).

@ -311,17 +311,17 @@ getCompanyFavor
This function will return the amount of favor you have at the specified company.
If the company passed in as an argument is invalid, -1 will be returned.
getCompanyFavorGain
-------------------
.. js:function:: getCompanyFavorGain(companyName)
:param string companyName: Name of the company. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function will return the amount of favor you will gain for the specified company
This function will return the amount of favor you will gain for the specified company
when you reset by installing Augmentations.
checkFactionInvitations
@ -398,18 +398,31 @@ getFactionFavor
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function returns the amount of favor you have for the specified faction.
getFactionFavorGain
-------------------
.. js:function:: getFactionFavorGain(factionName)
:param string factionName: Name of faction. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function returns the amount of favor you will gain for the specified faction when you reset by installing Augmentations.
donateToFaction
---------------
.. js:function:: donateToFaction(factionName, donateAmt)
:param string factionName: Name of faction to donate to. CASE-SENSITIVE
:param number donateAmt: Amount of money to donate
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.
Attempts to donate money to the specified faction in exchange for reputation.
Returns true if you successfully donate the money, and false otherwise.
createProgram
-------------

@ -412,3 +412,18 @@ Then it could be removed using::
$ unalias "r"
It is not necessary to differentiate between global and non-global aliases when using 'unalias'
wget
^^^^
$ wget [url] [target file]
Retrieves data from a url and downloads it to a file on the current server.
The data can only be downloaded to a script (.script, .ns, .js) or a text file
(.txt). If the target file already exists, it will be overwritten by this command.
Note that will not be possible to download data from many websites because they
do not allow cross-origin origin sharing (CORS). This includes websites such
as gist and pastebin. One notable site it will work on is rawgithub. Example::
$ wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt

@ -8,13 +8,11 @@
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
<link rel="manifest" href="dist/site.webmanifest">
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000">
<link rel="shortcut icon" href="favicon.ico">
<meta name="apple-mobile-web-app-title" content="Bitburner">
<meta name="application-name" content="Bitburner">
<meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="dist/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" type="text/css" href="dist/engine.css" />
<!-- Google Analytics -->
<script>
@ -22,11 +20,12 @@
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
</script>
<script>
ga('create', 'UA-100157497-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<link rel="shortcut icon" href="favicon.ico"><link href="dist/engine.css" rel="stylesheet"></head>
<body>
<div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container">
@ -169,11 +168,11 @@
<!-- Terminal page -->
<div id="terminal-container">
<table id="terminal">
<tr id="terminal-input">
<tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
</td>
</tr>
</tr>
</table>
</div>
@ -188,9 +187,9 @@
provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p>
<p id="active-scripts-total-prod">Total online production of
Active scripts: <span id="active-scripts-total-production-active">$0.000</span> / sec<br />
Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total">$0.000</span>
(<span id="active-scripts-total-prod-aug-avg">$0.000</span> / sec)</p>
Active scripts: <span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br/>
Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span class="money-gold"><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span>)</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;">
</ul>
</div>
@ -202,20 +201,20 @@
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>
<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>
<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>
<br/>
<div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money"></span><br/>
<span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span>
<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>
@ -452,7 +451,7 @@
<div id="create-program-container" class="generic-menupage-container">
<p id="create-program-page-text">
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating on a program you can cancel
can vary based on how complex the program is. If you are working on creating a program you can cancel
at any time. Your progress will be saved and you can continue later.
</p>
@ -472,8 +471,7 @@
<!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container">
<a id="tutorial-getting-started-link" class="a-link-button"
href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-getting-started-link" class="a-link-button" href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a>
<a id="tutorial-hacking-link" class="a-link-button"> Hacking </a>
<a id="tutorial-scripts-link" class="a-link-button"> Scripts </a>
@ -482,8 +480,7 @@
<a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a>
<a id="tutorial-factions-link" class="a-link-button"> Factions </a>
<a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a>
<a id="tutorial-shortcuts-link" class="a-link-button"
href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-shortcuts-link" class="a-link-button" href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-back-button" class="a-link-button"> Back </a>
<p id="tutorial-text"> </p>
@ -625,9 +622,9 @@
<!-- 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
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>
@ -673,7 +670,7 @@
<div id="stock-market-container" class="generic-menupage-container">
<p>
Welcome to the World Stock Exchange (WSE)! <br><br>
Welcome to the World Stock Exchange (WSE)! <br/><br/>
To begin trading, you must first purchase an account. WSE accounts will persist
after you 'reset' by installing Augmentations.
@ -686,7 +683,7 @@
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies.
<br><br>
<br/><br/>
If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations.
</p>
@ -696,7 +693,7 @@
<p>
Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies.
<br><br>
<br/><br/>
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
retain that access even after you 'reset' by installing Augmentations.
</p>
@ -714,8 +711,8 @@
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br><br>
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"> </input>
<br/><br/>
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;">
</ul>
@ -756,7 +753,7 @@
<p id="faction-invitation-box-text"> </p>
<p id="faction-invitation-box-message"> </p>
<p id="faction-invitation-box-warning">
Would you like to join? <br> <br>
Would you like to join? <br/> <br/>
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<span id="faction-invitation-box-yes" class="popup-box-button"> Yes </span>
@ -769,8 +766,8 @@
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p>
<span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br><br>
<select id="infiltration-faction-select"> </select> <br>
<span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br/><br/>
<select id="infiltration-faction-select"> </select> <br/>
<span id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </span>
</div>
@ -840,8 +837,8 @@
</table>
</div>
<div class="character-quick-options">
<span id="character-overview-save-button"> Save Game </span>
<span id="character-overview-options-button"> Options </span>
<span id="character-overview-save-button">Save Game</span>
<span id="character-overview-options-button">Options</span>
</div>
</div>
</div>
@ -856,7 +853,7 @@
<div id="game-options-content" class="game-options-box">
<span id="game-options-close-button">&times;</span>
<h1> Game Options </h1>
<br>
<br/>
<div id="game-options-left-panel">
<!-- Netscript execution time -->
<fieldset>
@ -868,7 +865,7 @@
</span>
</label>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<input type="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -927,7 +924,7 @@
<!-- Suppress faction invites -->
<fieldset>
<label for="settingsSuppressFactionInvites" class="tooltip">Suppress Faction Invites:
<span class="tooltiptext">
<span class="tooltiptexthigh">
If this is set, then any faction invites you receive will not appear as popups
on the screen. Your outstanding faction invites can be viewed in the 'Factions' page.
</span>
@ -938,7 +935,7 @@
<!-- Suppress travel confirmation -->
<fieldset>
<label for="settingsSuppressTravelConfirmation" class="tooltip">Suppress Travel Confirmation:
<span class="tooltiptext">
<span class="tooltiptexthigh">
If this is set, the confirmation message before traveling will not show up. You will automatically be deducted the travel cost as soon as you click.
</span>
</label>
@ -949,7 +946,7 @@
<!-- Suppress buy aug confirmation -->
<fieldset>
<label for="settingsSuppressBuyAugmentationConfirmation" class="tooltip">Suppress buy augmentation confirmation:
<span class="tooltiptext">
<span class="tooltiptexthigh">
If this is set, the confirmation message before buying augmentation will not show up.
</span>
</label>
@ -959,7 +956,7 @@
<!-- Hospitalization Popup -->
<fieldset>
<label for="settingsSuppressHospitalizationPopup" class="tooltip">Suppress Hospitalization popup:
<span class="tooltiptext">
<span class="tooltiptexthigh">
If this is set, a popup message will no longer be shown when you are hospitalized after taking too much damage.
</span>
</label>
@ -969,7 +966,7 @@
<!-- Disable Terminal and Navigation Shortcuts -->
<fieldset>
<label for="settingsDisableHotkeys" class="tooltip">Disable Hotkeys:
<span class="tooltiptext">
<span class="tooltiptexthigh">
If this is set, then most hotkeys (keyboard shortcuts) in the game are disabled.
This includes Terminal commands, hotkeys to navigate between different parts of the game,
and the "Save and Close (Ctrl + b)" hotkey in the Text Editor.
@ -978,6 +975,32 @@
<input type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</fieldset>
<!-- Locale for displaying numbers -->
<fieldset>
<label for="settingsLocale" class="tooltip">Locale:
<span class="tooltiptexthigh">
Sets the locale for displaying numbers. Defaults to 'en'
</span>
</label>
<select name="settingsLocale" id="settingsLocale">
<option value="en">en</option>
<option value="bg">bg</option>
<option value="cs">cs</option>
<option value="da-dk">da-dk</option>
<option value="de">de</option>
<option value="en-au">en-au</option>
<option value="en-gb">en-gb</option>
<option value="es">es</option>
<option value="fr">fr</option>
<option value="hu">hu</option>
<option value="it">it</option>
<option value="lv">lv</option>
<option value="no">no</option>
<option value="pl">pl</option>
<option value="ru">ru</option>
</select>
</fieldset>
<!-- Donate button -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick">
@ -998,15 +1021,15 @@
<a id="import-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Import Game </a>
<a id="debug-delete-scripts-link" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Delete Active Scripts
<span class="tooltiptext">
<span class="tooltiptextleft">
Debug option used to forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
using this, save the game and then reload the page.
</span>
</a>
<a id="debug-soft-reset" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Soft Reset
<span class="tooltiptext">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation
<span class="tooltiptextleft">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>
</a>
</div>
@ -1018,8 +1041,15 @@
<div id="loader" class="loaderoverlay">
<div class="loaderspinner"></div>
<div class="loaderlabel">Loading Bitburner...</div>
<div id="killAllMessageWrapper" class="killAllMessage killAllMessageWrapperHidden">
<script>setTimeout(function(){
var w = document.getElementById('killAllMessageWrapper');
if (w == null) {return;}
w.classList.remove("killAllMessageWrapperHidden");
w.classList.add("killAllMessageWrapperShow");
}, 2000);</script>
<p>If the game fails to load, consider <a href="?noScripts">killing all scripts</a></p>
</div>
</div>
<script src="dist/vendor.bundle.js"></script>
<script src="dist/engine.bundle.js"></script>
</body>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script></body>
</html>

@ -60,7 +60,7 @@ var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*";
let NetscriptFunctions =
"hack|sleep|grow|weaken|print|tprint|scan|nuke|brutessh|ftpcrack|" + //Netscript functions
"clearLog|disableLog|enableLog|" +
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
"scp|ls|hasRootAccess|" +
"getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" +
@ -73,22 +73,28 @@ let NetscriptFunctions =
"deleteServer|getPurchasedServers|" +
"getPurchasedServerLimit|getPurchasedServerMaxRam|" +
"getPurchasedServerCost|" +
"purchaseServer|round|write|read|peek|clear|rm|getPortHandle|" +
"purchaseServer|round|write|tryWrite|read|peek|clear|rm|getPortHandle|" +
"scriptRunning|scriptKill|getScriptName|getScriptRam|" +
"getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" +
"getTimeSinceLastAug|prompt|" +
//Singularity Functions
"universityCourse|getCharacterInformation|" +
"gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" +
"getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" +
"getCompanyFavor|stopAction|getFactionFavor|" +
"getFavorToDonate|getFactionFavorGain|getCompanyFavorGain|" +
"checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" +
"donateToFaction|" +
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" +
"getAugmentationCost|purchaseAugmentation|" +
"installAugmentations|" +
//TIX API
"getStockPrice|getStockPosition|buyStock|sellStock|shortStock|sellShort|" +
"placeOrder|cancelOrder|" +
//Hacknet Node API
"hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" +
"upgradeLevel|upgradeRam|upgradeCore|getLevelUpgradeCost|" +
@ -101,7 +107,7 @@ let NetscriptFunctions =
"getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" +
"setActionAutolevel|setActionLevel|" +
"getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" +
"upgradeSkill|getTeamSize|" +
"upgradeSkill|getTeamSize|getCity|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime";

321
package-lock.json generated

@ -608,6 +608,11 @@
"postcss-value-parser": "3.3.0"
}
},
"autosize": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.2.tgz",
"integrity": "sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA=="
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
@ -800,6 +805,12 @@
"multicast-dns-service-types": "1.1.0"
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"boom": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
@ -1080,6 +1091,16 @@
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
"dev": true
},
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
"dev": true,
"requires": {
"no-case": "2.3.2",
"upper-case": "1.1.3"
}
},
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
@ -1679,6 +1700,23 @@
}
}
},
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
"dev": true,
"requires": {
"source-map": "0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@ -2181,6 +2219,30 @@
"source-list-map": "2.0.0"
}
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"dev": true,
"requires": {
"boolbase": "1.0.0",
"css-what": "2.1.0",
"domutils": "1.5.1",
"nth-check": "1.0.1"
},
"dependencies": {
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"dev": true,
"requires": {
"dom-serializer": "0.1.0",
"domelementtype": "1.3.0"
}
}
}
},
"css-selector-tokenizer": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
@ -2192,6 +2254,12 @@
"regexpu-core": "1.0.0"
}
},
"css-what": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
"integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=",
"dev": true
},
"cssesc": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
@ -2524,6 +2592,23 @@
"esutils": "2.0.2"
}
},
"dom-converter": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz",
"integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=",
"dev": true,
"requires": {
"utila": "0.3.3"
},
"dependencies": {
"utila": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
"integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
"dev": true
}
}
},
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
@ -4790,12 +4875,86 @@
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
"dev": true
},
"html-minifier": {
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz",
"integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==",
"dev": true,
"requires": {
"camel-case": "3.0.0",
"clean-css": "4.2.1",
"commander": "2.17.1",
"he": "1.1.1",
"param-case": "2.1.1",
"relateurl": "0.2.7",
"uglify-js": "3.4.8"
},
"dependencies": {
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"uglify-js": {
"version": "3.4.8",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.8.tgz",
"integrity": "sha512-WatYTD84gP/867bELqI2F/2xC9PQBETn/L+7RGq9MQOA/7yFBNvY1UwXqvtILeE6n0ITwBXxp34M0/o70dzj6A==",
"dev": true,
"requires": {
"commander": "2.17.1",
"source-map": "0.6.1"
}
}
}
},
"html-tags": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz",
"integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
"dev": true
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"dev": true,
"requires": {
"html-minifier": "3.5.20",
"loader-utils": "0.2.17",
"lodash": "4.17.10",
"pretty-error": "2.1.1",
"tapable": "1.0.0",
"toposort": "1.0.7",
"util.promisify": "1.0.0"
},
"dependencies": {
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
"loader-utils": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
"integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
"dev": true,
"requires": {
"big.js": "3.2.0",
"emojis-list": "2.1.0",
"json5": "0.5.1",
"object-assign": "4.1.1"
}
}
}
},
"htmlparser2": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
@ -6380,6 +6539,12 @@
"signal-exit": "3.0.2"
}
},
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
"dev": true
},
"lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
@ -6996,6 +7161,15 @@
"integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==",
"dev": true
},
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"dev": true,
"requires": {
"lower-case": "1.1.4"
}
},
"node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@ -7603,6 +7777,15 @@
}
}
},
"nth-check": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
"integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
"dev": true,
"requires": {
"boolbase": "1.0.0"
}
},
"num2fraction": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@ -7726,6 +7909,16 @@
"object-keys": "1.0.11"
}
},
"object.getownpropertydescriptors": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
"integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
"dev": true,
"requires": {
"define-properties": "1.1.2",
"es-abstract": "1.12.0"
}
},
"object.omit": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
@ -7926,6 +8119,15 @@
"readable-stream": "2.3.4"
}
},
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"dev": true,
"requires": {
"no-case": "2.3.2"
}
},
"parse-asn1": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
@ -8979,6 +9181,16 @@
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
"dev": true
},
"pretty-error": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
"integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=",
"dev": true,
"requires": {
"renderkid": "2.0.1",
"utila": "0.4.0"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -9355,6 +9567,12 @@
"jsesc": "0.5.0"
}
},
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
"dev": true
},
"remark": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz",
@ -9417,6 +9635,81 @@
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"dev": true
},
"renderkid": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz",
"integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=",
"dev": true,
"requires": {
"css-select": "1.2.0",
"dom-converter": "0.1.4",
"htmlparser2": "3.3.0",
"strip-ansi": "3.0.1",
"utila": "0.3.3"
},
"dependencies": {
"domhandler": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz",
"integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=",
"dev": true,
"requires": {
"domelementtype": "1.3.0"
}
},
"domutils": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz",
"integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=",
"dev": true,
"requires": {
"domelementtype": "1.3.0"
}
},
"htmlparser2": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
"integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=",
"dev": true,
"requires": {
"domelementtype": "1.3.0",
"domhandler": "2.1.0",
"domutils": "1.1.6",
"readable-stream": "1.0.34"
}
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"readable-stream": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
},
"utila": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
"integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
"dev": true
}
}
},
"repeat-element": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
@ -11688,6 +11981,12 @@
}
}
},
"toposort": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
"integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=",
"dev": true
},
"tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
@ -12227,6 +12526,12 @@
"integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==",
"dev": true
},
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
"dev": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@ -12372,6 +12677,22 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"util.promisify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
"integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
"dev": true,
"requires": {
"define-properties": "1.1.2",
"object.getownpropertydescriptors": "2.0.3"
}
},
"utila": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
"dev": true
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

@ -11,6 +11,7 @@
"ajv": "^5.1.5",
"ajv-keywords": "^2.0.0",
"async": "^2.6.1",
"autosize": "^4.0.2",
"bluebird": "^3.5.1",
"brace": "^0.11.1",
"decimal.js": "7.2.3",
@ -45,6 +46,7 @@
"eslint": "^4.19.1",
"eslint-plugin-node": "^6.0.1",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5",
"js-beautify": "^1.5.10",

@ -2,6 +2,7 @@ import {workerScripts,
killWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import {getServer} from "./Server";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
import {arrayToString} from "../utils/helpers/arrayToString";
@ -10,7 +11,6 @@ import {createProgressBarText} from "../utils/helpers/createProgressBarText"
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getElementById} from "../utils/uiHelpers/getElementById";
import {logBoxCreate} from "../utils/LogBox";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElement} from "../utils/uiHelpers/removeElement";
@ -242,9 +242,9 @@ function updateActiveScriptsItems(maxTasks=150) {
}
}
getElementById("active-scripts-total-production-active").innerText = numeral(total).format('$0.000a');
getElementById("active-scripts-total-prod-aug-total").innerText = numeral(Player.scriptProdSinceLastAug).format('$0.000a');
getElementById("active-scripts-total-prod-aug-avg").innerText = numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a');
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.format(total, '$0.000a');
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.format(Player.scriptProdSinceLastAug, '$0.000a');
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.format(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000), '$0.000a');
return total;
}

@ -7,6 +7,7 @@ import {Faction, Factions, factionExists,
import {Locations} from "./Locations";
import {Player} from "./Player";
import {hackWorldDaemon, redPillFlag} from "./RedPill";
import {numeralWrapper} from "./ui/numeralFormat";
import {KEY} from "../utils/helpers/keyCodes";
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
@ -14,7 +15,6 @@ import {dialogBoxCreate} from "../utils/DialogBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {addOffset} from "../utils/helpers/addOffset";
import {appendLineBreaks} from "../utils/uiHelpers/appendLineBreaks";
import {clearObject} from "../utils/helpers/clearObject";
@ -44,9 +44,9 @@ var DifficultyToTimeFactor = 10; //Action Difficulty divided by this to ge
var DiffMultExponentialFactor = 0.28;
var DiffMultLinearFactor = 650;
var EffAgiLinearFactor = 90e3;
var EffDexLinearFactor = 90e3;
var EffAgiExponentialFactor = 0.031;
var EffAgiLinearFactor = 40e3;
var EffDexLinearFactor = 40e3;
var EffAgiExponentialFactor = 0.032;
var EffDexExponentialFactor = 0.03;
var BaseRecruitmentTimeNeeded = 300; //Base time needed (s) to complete a Recruitment action
@ -659,7 +659,7 @@ function Bladeburner(params={}) {
this.storedCycles = 0;
this.randomEventCounter = getRandomInt(300, 600); //5-10 minutes
this.randomEventCounter = getRandomInt(240, 600); //4-10 minutes
//These times are in seconds
this.actionTimeToComplete = 0; //0 or -1 is an infinite running action (like training)
@ -734,7 +734,7 @@ Bladeburner.prototype.create = function() {
"whatever city you are currently in.",
baseDifficulty:125,difficultyFac:1.02,rewardFac:1.041,
rankGain:0.3, hpLoss:0.5,
count:getRandomInt(100, 500), countGrowth:getRandomInt(5, 75)/10,
count:getRandomInt(25, 500), countGrowth:getRandomInt(5, 75)/10,
weights:{hack:0,str:0.05,def:0.05,dex:0.35,agi:0.35,cha:0.1, int:0.05},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.9, int:1},
isStealth:true
@ -746,7 +746,7 @@ Bladeburner.prototype.create = function() {
"current city, and will also increase its chaos level.",
baseDifficulty:250, difficultyFac:1.04,rewardFac:1.085,
rankGain:0.9, hpLoss:1,
count:getRandomInt(25, 750), countGrowth:getRandomInt(5, 75)/10,
count:getRandomInt(5, 500), countGrowth:getRandomInt(5, 75)/10,
weights:{hack:0,str:0.15,def:0.15,dex:0.25,agi:0.25,cha:0.1, int:0.1},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.8, int:0.9},
isKill:true
@ -758,7 +758,7 @@ Bladeburner.prototype.create = function() {
"city, and will also increase its chaos level.",
baseDifficulty:200, difficultyFac:1.03, rewardFac:1.065,
rankGain:0.6, hpLoss:1,
count:getRandomInt(50, 1000), countGrowth:getRandomInt(5,75)/10,
count:getRandomInt(5, 500), countGrowth:getRandomInt(5,75)/10,
weights:{hack:0,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0.1, int:0.1},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.8, int:0.9},
isKill:true
@ -773,7 +773,7 @@ Bladeburner.prototype.create = function() {
"You will NOT lose HP from failed Investigation ops.",
baseDifficulty:400, difficultyFac:1.03,rewardFac:1.07,reqdRank:25,
rankGain:2.2, rankLoss:0.2,
count:getRandomInt(50, 200), countGrowth:getRandomInt(10, 40)/10,
count:getRandomInt(1, 250), countGrowth:getRandomInt(10, 40)/10,
weights:{hack:0.25,str:0.05,def:0.05,dex:0.2,agi:0.1,cha:0.25, int:0.1},
decays:{hack:0.85,str:0.9,def:0.9,dex:0.9,agi:0.9,cha:0.7, int:0.9},
isStealth:true
@ -786,7 +786,7 @@ Bladeburner.prototype.create = function() {
"data.",
baseDifficulty:500, difficultyFac:1.04, rewardFac:1.09, reqdRank:100,
rankGain:4.4, rankLoss:0.4, hpLoss:2,
count:getRandomInt(25, 300), countGrowth:getRandomInt(10, 40)/10,
count:getRandomInt(1, 250), countGrowth:getRandomInt(10, 40)/10,
weights:{hack:0.2,str:0.05,def:0.05,dex:0.2,agi:0.2,cha:0.2, int:0.1},
decays:{hack:0.8,str:0.9,def:0.9,dex:0.9,agi:0.9,cha:0.7, int:0.9},
isStealth:true
@ -797,7 +797,7 @@ Bladeburner.prototype.create = function() {
"notorious Synthoid criminals.",
baseDifficulty:650, difficultyFac:1.04, rewardFac:1.095, reqdRank:500,
rankGain:5.5, rankLoss:0.5, hpLoss:2.5,
count:getRandomInt(25,400), countGrowth:getRandomInt(3, 40)/10,
count:getRandomInt(1, 300), countGrowth:getRandomInt(3, 40)/10,
weights:{hack:0.25,str:0.05,def:0.05,dex:0.25,agi:0.1,cha:0.2, int:0.1},
decays:{hack:0.8,str:0.85,def:0.85,dex:0.85,agi:0.85,cha:0.7, int:0.9},
isStealth:true
@ -809,7 +809,7 @@ Bladeburner.prototype.create = function() {
"in order for this Operation to be successful",
baseDifficulty:800, difficultyFac:1.045, rewardFac:1.1, reqdRank:3000,
rankGain:55,rankLoss:2.5,hpLoss:50,
count:getRandomInt(25, 150), countGrowth:getRandomInt(2, 40)/10,
count:getRandomInt(1, 200), countGrowth:getRandomInt(2, 40)/10,
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
decays:{hack:0.7,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.9},
isKill:true
@ -821,7 +821,7 @@ Bladeburner.prototype.create = function() {
"drawing any attention. Stealth and discretion are key.",
baseDifficulty:1000, difficultyFac:1.05, rewardFac:1.11, reqdRank:20e3,
rankGain:22, rankLoss:2, hpLoss:10,
count:getRandomInt(25, 250), countGrowth:getRandomInt(1, 20)/10,
count:getRandomInt(1, 250), countGrowth:getRandomInt(1, 20)/10,
weights:{hack:0.1,str:0.1,def:0.1,dex:0.3,agi:0.3,cha:0, int:0.1},
decays:{hack:0.7,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.9},
isStealth:true, isKill:true
@ -833,7 +833,7 @@ Bladeburner.prototype.create = function() {
"in the Synthoid communities.",
baseDifficulty:1500, difficultyFac:1.06, rewardFac:1.14, reqdRank:50e3,
rankGain:44, rankLoss:4, hpLoss:5,
count:getRandomInt(25, 200), countGrowth:getRandomInt(1, 20)/10,
count:getRandomInt(1, 200), countGrowth:getRandomInt(1, 20)/10,
weights:{hack:0.1,str:0.1,def:0.1,dex:0.3,agi:0.3,cha:0, int:0.1},
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.8},
isStealth:true, isKill:true
@ -900,7 +900,7 @@ Bladeburner.prototype.process = function() {
this.randomEventCounter -= seconds;
if (this.randomEventCounter <= 0) {
this.randomEvent();
this.randomEventCounter = getRandomInt(300, 600);
this.randomEventCounter = getRandomInt(240, 600);
}
this.processAction(seconds);
@ -1191,7 +1191,7 @@ Bladeburner.prototype.completeAction = function() {
if (isOperation && this.logging.ops) {
this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
} else if (!isOperation && this.logging.contracts) {
this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeral(moneyGain).format("$0.000a"));
this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeralWrapper.format(moneyGain, "$0.000a"));
}
}
isOperation ? this.completeOperation(true) : this.completeContract(true);
@ -1833,7 +1833,7 @@ Bladeburner.prototype.createOverviewContent = function() {
"Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed."
});
DomElems.overviewSkillPoints = createElement("p", {display:"block"});
DomElems.overviewAugSuccessMult = createElement("p", {display:"block"});
DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"});
@ -2207,10 +2207,10 @@ Bladeburner.prototype.updateOverviewContent = function() {
"Team Size: " + formatNumber(this.teamSize, 0) + "<br>" +
"Team Members Lost: " + formatNumber(this.teamLost, 0) + "<br><br>" +
"Num Times Hospitalized: " + this.numHosp + "<br>" +
"Money Lost From Hospitalizations: " + numeral(this.moneyLost).format("$0.000a") + "<br><br>" +
"Money Lost From Hospitalizations: " + numeralWrapper.format(this.moneyLost, "$0.000a") + "<br><br>" +
"Current City: " + this.city + "<br>";
DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeral(this.getCurrentCity().popEst).format("0.000a");
DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeralWrapper.format(this.getCurrentCity().popEst, "0.000a");
DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0);
DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0);
@ -3703,7 +3703,7 @@ function initBladeburner() {
name:SkillNames.Overclock,
desc:"Each level of this skill decreases the time it takes " +
"to attempt a Contract, Operation, and BlackOp by 1% (Max Level: 95)",
baseCost:5, costInc:1, maxLvl:95,
baseCost:5, costInc:1.1, maxLvl:95,
actionTime:1
});
Skills[SkillNames.EvasiveSystem] = new Skill({

@ -1,5 +1,6 @@
import {Player} from "./Player";
import numeral from "numeral/min/numeral.min";
import {numeralWrapper} from "./ui/numeralFormat";
function CharacterOverview() {
this.hp = document.getElementById("character-hp-text");
@ -33,7 +34,7 @@ CharacterOverview.prototype.update = function() {
let changed = false;
changed = replaceAndChanged(this.hp, Player.hp + " / " + Player.max_hp) || changed;
changed = replaceAndChanged(this.money, numeral(Player.money.toNumber()).format('($0.000a)')) || changed;
changed = replaceAndChanged(this.money, numeralWrapper.format(Player.money.toNumber(), '($0.000a)')) || changed;
changed = replaceAndChanged(this.hack, (Player.hacking_skill).toLocaleString()) || changed;
changed = replaceAndChanged(this.str, (Player.strength).toLocaleString()) || changed;
changed = replaceAndChanged(this.def, (Player.defense).toLocaleString()) || changed;

@ -5,11 +5,12 @@ import {Locations} from "./Locations";
import {Player} from "./Player";
import Decimal from "decimal.js";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearSelector} from "../utils/uiHelpers/clearSelector";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking";
@ -453,46 +454,46 @@ var IndustryStartingCosts = {
var IndustryDescriptions = {
Energy: "Engage in the production and distribution of energy.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Energy).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Energy, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Utilities: "Distributes water and provides wastewater services.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Utilities).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Utilities, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Agriculture: "Cultive crops and breed livestock to produce food.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Agriculture).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Agriculture, "$0.000a") + "<br>" +
"Recommended starting Industry: YES",
Fishing: "Produce food through the breeding and processing of fish and fish products<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Fishing).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Fishing, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Mining: "Extract and process metals from the earth.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Mining).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Mining, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Food: "Create your own restaurants all around the world.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Food).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Food, "$0.000a") + "<br>" +
"Recommended starting Industry: YES",
Tobacco: "Create and distribute tobacco and tobacco-related products.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Tobacco).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Tobacco, "$0.000a") + "<br>" +
"Recommended starting Industry: YES",
Chemical: "Product industrial chemicals<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Chemical).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Chemical, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Pharmaceutical: "Discover, develop, and create new pharmaceutical drugs.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Pharmaceutical).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Pharmaceutical, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Computer: "Develop and manufacture new computer hardware and networking infrastructures.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Computer).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Computer, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Robotics: "Develop and create robots.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Robotics).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Robotics, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
Software: "Develop computer software and create AI Cores.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Software).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Software, "$0.000a") + "<br>" +
"Recommended starting Industry: YES",
Healthcare: "Create and manage hospitals.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.Healthcare).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Healthcare, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
RealEstate: "Develop and manage real estate properties.<br><br>" +
"Starting cost: " + numeral(IndustryStartingCosts.RealEstate).format("$0.000a") + "<br>" +
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.RealEstate, "$0.000a") + "<br>" +
"Recommended starting Industry: NO",
}
@ -1776,7 +1777,7 @@ Employee.prototype.createUI = function(panel, corporation) {
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
"Creativity: " + formatNumber(effCre, 3) + "<br>" +
"Efficiency: " + formatNumber(effEff, 3) + "<br>" +
"Salary: " + numeral(this.sal).format("$0.000a") + "/ s<br>",
"Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s<br>",
}));
//Selector for employee position
@ -1826,7 +1827,7 @@ Employee.prototype.updateUI = function(panel, corporation) {
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
"Creativity: " + formatNumber(effCre, 3) + "<br>" +
"Efficiency: " + formatNumber(effEff, 3) + "<br>" +
"Salary: " + numeral(this.sal).format("$0.000a") + "/ s<br>";
"Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s<br>";
}
Employee.prototype.toJSON = function() {
@ -1963,7 +1964,7 @@ OfficeSpace.prototype.findEmployees = function(parentRefs) {
"Experience: " + formatNumber(employee.exp, 1) + "<br>" +
"Creativity: " + formatNumber(employee.cre, 1) + "<br>" +
"Efficiency: " + formatNumber(employee.eff, 1) + "<br>" +
"Salary: " + numeral(employee.sal).format('$0.000a') + " \ s<br>",
"Salary: " + numeralWrapper.format(employee.sal, '$0.000a') + " \ s<br>",
clickListener:()=>{
office.hireEmployee(employee, parentRefs);
removeElementById("cmpy-mgmt-hire-employee-popup");
@ -2155,7 +2156,7 @@ Warehouse.prototype.createUI = function(parentRefs) {
//Upgrade warehouse size button
var upgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, this.level+1);
industryWarehouseUpgradeSizeButton = createElement("a", {
innerText:"Upgrade Warehouse Size - " + numeral(upgradeCost).format('$0.000a'),
innerText:"Upgrade Warehouse Size - " + numeralWrapper.format(upgradeCost, '$0.000a'),
display:"inline-block",
class: company.funds.lt(upgradeCost) ? "a-link-button-inactive" : "a-link-button",
clickListener:()=>{
@ -2721,9 +2722,9 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
"Aesthetics: " + formatNumber(product.aes, 3) + "<br>" +
"Features: " + formatNumber(product.fea, 3) +
cmpAndDmdText + "</span></p><br>" +
"<p class='tooltip'>Est. Production Cost: " + numeral(product.pCost / ProductProductionCostRatio).format("$0.000a") +
"<p class='tooltip'>Est. Production Cost: " + numeralWrapper.format(product.pCost / ProductProductionCostRatio, "$0.000a") +
"<span class='tooltiptext'>An estimate of the material cost it takes to create this Product.</span></p><br>" +
"<p class='tooltip'>Est. Market Price: " + numeral(product.pCost + product.rat / product.mku).format("$0.000a") +
"<p class='tooltip'>Est. Market Price: " + numeralWrapper.format(product.pCost + product.rat / product.mku, "$0.000a") +
"<span class='tooltiptext'>An estimate of how much consumers are willing to pay for this product. " +
"Setting the sale price above this may result in less sales. Setting the sale price below this may result " +
"in more sales.</span></p>"
@ -2740,7 +2741,7 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
if (isString(product.sCost)) {
sellInnerTextString += (" @ " + product.sCost);
} else {
sellInnerTextString += (" @ " + numeral(product.sCost).format("$0.000a"));
sellInnerTextString += (" @ " + numeralWrapper.format(product.sCost, "$0.000a"));
}
}
div.appendChild(createElement("a", {
@ -3175,9 +3176,9 @@ Corporation.prototype.getInvestment = function() {
noBtn.addEventListener("click", ()=>{
return yesNoBoxClose();
});
yesNoBoxCreate("An investment firm has offered you " + numeral(funding).format('$0.000a') +
" in funding in exchange for a " + numeral(percShares*100).format("0.000a") +
"% stake in the company (" + numeral(investShares).format('0.000a') + " shares).<br><br>" +
yesNoBoxCreate("An investment firm has offered you " + numeralWrapper.format(funding, '$0.000a') +
" in funding in exchange for a " + numeralWrapper.format(percShares*100, "0.000a") +
"% stake in the company (" + numeralWrapper.format(investShares, '0.000a') + " shares).<br><br>" +
"Do you accept or reject this offer?");
}
@ -3188,11 +3189,11 @@ Corporation.prototype.goPublic = function() {
innerHTML: "Enter the number of shares you would like to issue " +
"for your IPO. These shares will be publicly sold " +
"and you will no longer own them. Your Corporation will receive " +
numeral(initialSharePrice).format('$0.000a') + " per share " +
numeralWrapper.format(initialSharePrice, '$0.000a') + " per share " +
"(the IPO money will be deposited directly into your Corporation's funds).<br><br>" +
"Furthermore, issuing more shares now will help drive up " +
"your company's stock price in the future.<br><br>" +
"You have a total of " + numeral(this.numShares).format("0.000a") + " of shares that you can issue.",
"You have a total of " + numeralWrapper.format(this.numShares, "0.000a") + " of shares that you can issue.",
});
var yesBtn;
var input = createElement("input", {
@ -3635,7 +3636,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
innerHTML: "Enter the number of shares you would like to sell. The money from " +
"selling your shares will go directly to you (NOT your Corporation). " +
"The current price of your " +
"company's stock is " + numeral(currentStockPrice).format("$0.000a"),
"company's stock is " + numeralWrapper.format(currentStockPrice, "$0.000a"),
});
var profitIndicator = createElement("p", {});
var input = createElement("input", {
@ -3648,7 +3649,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
profitIndicator.innerText = "You don't have this many shares to sell!";
} else {
profitIndicator.innerText = "Sell " + numShares + " shares for a total of " +
numeral(numShares * currentStockPrice).format('$0.000a');
numeralWrapper.format(numShares * currentStockPrice, '$0.000a');
}
}
});
@ -3702,7 +3703,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
innerHTML: "Enter the number of shares you would like to buy back at market price. To purchase " +
"these shares, you must use your own money (NOT your Corporation's funds). " +
"The current price of your " +
"company's stock is " + numeral(currentStockPrice).format("$0.000a") +
"company's stock is " + numeralWrapper.format(currentStockPrice, "$0.000a") +
". Your company currently has " + formatNumber(this.issuedShares, 3) + " outstanding stock shares",
});
var costIndicator = createElement("p", {});
@ -3719,7 +3720,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
} else {
console.log("here");
costIndicator.innerText = "Purchase " + numShares + " shares for a total of " +
numeral(numShares * currentStockPrice).format('$0.000a');
numeralWrapper.format(numShares * currentStockPrice, '$0.000a');
}
}
});
@ -3734,7 +3735,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
} else if (shares * tempStockPrice > Player.money) {
dialogBoxCreate("ERROR: You do not have enough money to purchase this many shares (you need " +
numeral(shares * tempStockPrice).format("$0.000a") + ")");
numeralWrapper.format(shares * tempStockPrice, "$0.000a") + ")");
} else {
this.numShares += shares;
if (isNaN(this.issuedShares)) {
@ -3958,7 +3959,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
upgradeContainer.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div", width:"45%",
innerHTML:upgrade[2] + " - " + numeral(upgrade[1]).format("$0.000a"),
innerHTML:upgrade[2] + " - " + numeralWrapper.format(upgrade[1], "$0.000a"),
tooltip: upgrade[3],
clickListener:()=>{
if (corp.funds.lt(upgrade[1])) {
@ -3990,7 +3991,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
var cost = baseCost * Math.pow(priceMult, corp.upgrades[i]);
upgradeContainer.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div", width:"45%",
innerHTML:upgrade[4] + " - " + numeral(cost).format("$0.000a"),
innerHTML:upgrade[4] + " - " + numeralWrapper.format(cost, "$0.000a"),
tooltip:upgrade[5],
clickListener:()=>{
if (corp.funds.lt(cost)) {
@ -4018,14 +4019,14 @@ Corporation.prototype.updateCorporationOverviewContent = function() {
totalExpenses = new Decimal(0);
var profit = this.revenue.minus(this.expenses).toNumber(),
profitStr = profit >= 0 ? numeral(profit).format("$0.000a") : "-" + numeral(-1 * profit).format("$0.000a");
profitStr = profit >= 0 ? numeralWrapper.format(profit, "$0.000a") : "-" + numeralWrapper.format(-1 * profit, "$0.000a");
var txt = "Total Funds: " + numeral(totalFunds.toNumber()).format('$0.000a') + "<br>" +
"Total Revenue: " + numeral(this.revenue.toNumber()).format("$0.000a") + " / s<br>" +
"Total Expenses: " + numeral(this.expenses.toNumber()).format("$0.000a") + "/ s<br>" +
var txt = "Total Funds: " + numeralWrapper.format(totalFunds.toNumber(), '$0.000a') + "<br>" +
"Total Revenue: " + numeralWrapper.format(this.revenue.toNumber(), "$0.000a") + " / s<br>" +
"Total Expenses: " + numeralWrapper.format(this.expenses.toNumber(), "$0.000a") + "/ s<br>" +
"Total Profits: " + profitStr + " / s<br>" +
"Publicly Traded: " + (this.public ? "Yes" : "No") + "<br>" +
"Owned Stock Shares: " + numeral(this.numShares).format('0.000a') + "<br>" +
"Owned Stock Shares: " + numeralWrapper.format(this.numShares, '0.000a') + "<br>" +
"Stock Price: " + (this.public ? "$" + formatNumber(this.sharePrice, 2) : "N/A") + "<br><br>";
var prodMult = this.getProductionMultiplier(),
@ -4071,7 +4072,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
var popupId = "cmpy-mgmt-expand-city-popup";
var text = createElement("p", {
innerText: "Would you like to expand into a new city by opening an office? " +
"This would cost " + numeral(OfficeInitialCost).format('$0.000a'),
"This would cost " + numeralWrapper.format(OfficeInitialCost, '$0.000a'),
});
var citySelector = createElement("select", {class: "dropdown", margin:"5px"});
for (var cityName in division.offices) {
@ -4180,7 +4181,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
}
industryOverviewUpgrades.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div", display:"inline-block",
innerHTML:upgrade[4] + ' - ' + numeral(cost).format("$0.000a"),
innerHTML:upgrade[4] + ' - ' + numeralWrapper.format(cost, "$0.000a"),
tooltip:upgrade[5],
clickListener:()=>{
if (corp.funds.lt(cost)) {
@ -4428,7 +4429,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
var confirmBtn = createElement("a", {
class: this.funds.lt(upgradeCost) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by 3",
tooltip:numeral(upgradeCost).format("$0.000a"),
tooltip:numeralWrapper.format(upgradeCost, "$0.000a"),
clickListener:()=>{
if (this.funds.lt(upgradeCost)) {
dialogBoxCreate("You don't have enough company funds to purchase this upgrade!");
@ -4445,7 +4446,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
var confirmBtn15 = createElement("a", {
class: this.funds.lt(upgradeCost15) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by 15",
tooltip:numeral(upgradeCost15).format("$0.000a"),
tooltip:numeralWrapper.format(upgradeCost15, "$0.000a"),
clickListener:()=>{
if (this.funds.lt(upgradeCost15)) {
dialogBoxCreate("You don't have enough company funds to purchase this upgrade!");
@ -4462,7 +4463,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
var confirmBtnMax = createElement("a", {
class:this.funds.lt(upgradeCostMax) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by MAX (" + maxNum*OfficeInitialSize + ")",
tooltip:numeral(upgradeCostMax).format("$0.000a"),
tooltip:numeralWrapper.format(upgradeCostMax, "$0.000a"),
clickListener:()=>{
if (this.funds.lt(upgradeCostMax)) {
dialogBoxCreate("You don't have enough company funds to purchase this upgrade!");
@ -4511,7 +4512,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
totalCostTxt.innerText = "Invalid value entered!"
} else {
var totalCost = input.value * office.employees.length;
totalCostTxt.innerText = "Throwing this party will cost a total of " + numeral(totalCost).format('$0.000a');
totalCostTxt.innerText = "Throwing this party will cost a total of " + numeralWrapper.format(totalCost, '$0.000a');
}
},
onkeyup:(e)=>{
@ -4726,7 +4727,7 @@ Corporation.prototype.updateDivisionContent = function(division) {
var vechain = (this.unlockUpgrades[4] === 1);
//Industry Overview Text
var profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber(),
profitStr = profit >= 0 ? numeral(profit).format("$0.000a") : "-" + numeral(-1 * profit).format("$0.000a");
profitStr = profit >= 0 ? numeralWrapper.format(profit, "$0.000a") : "-" + numeralWrapper.format(-1 * profit, "$0.000a");
var advertisingInfo = "";
if (vechain) {
var advertisingFactors = division.getAdvertisingFactors();
@ -4745,12 +4746,12 @@ Corporation.prototype.updateDivisionContent = function(division) {
removeChildrenFromElement(industryOverviewText);
industryOverviewText.appendChild(createElement("p", {
innerHTML:"Industry: " + division.type + " (Corp Funds: " + numeral(this.funds.toNumber()).format("$0.000a") + ")<br><br>" +
innerHTML:"Industry: " + division.type + " (Corp Funds: " + numeralWrapper.format(this.funds.toNumber(), "$0.000a") + ")<br><br>" +
"Awareness: " + formatNumber(division.awareness, 3) + "<br>" +
"Popularity: " + formatNumber(division.popularity, 3) + "<br>" +
advertisingInfo + "<br>" +
"Revenue: " + numeral(division.lastCycleRevenue.toNumber()).format("$0.000a") + " / s<br>" +
"Expenses: " + numeral(division.lastCycleExpenses.toNumber()).format("$0.000a") + " /s<br>" +
"Revenue: " + numeralWrapper.format(division.lastCycleRevenue.toNumber(), "$0.000a") + " / s<br>" +
"Expenses: " + numeralWrapper.format(division.lastCycleExpenses.toNumber(), "$0.000a") + " /s<br>" +
"Profit: " + profitStr + " / s<br><br>"
}));
industryOverviewText.appendChild(createElement("p", {

@ -1,11 +1,14 @@
let CONSTANTS = {
Version: "0.40.2",
Version: "0.40.3",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
//the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
MaxSkillLevel: 975,
//Milliseconds per game cycle
MilliPerCycle: 200,
//How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 200e3,
@ -32,6 +35,7 @@ let CONSTANTS = {
/* Faction and Company favor */
BaseFavorToDonate: 150,
DonateMoneyToRepDivisor: 1e6,
FactionReputationToFavorBase: 500,
FactionReputationToFavorMult: 1.02,
CompanyReputationToFavorBase: 500,
@ -493,33 +497,31 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>",
LatestUpdate:
"v0.40.2<br>" +
"------------------------------<br>" +
"* Bladeburner Changes:<br>" +
"*** Added getBonusTime(), getSkillUpgradeCost(), and getCity() Netscript functions to the API<br>" +
"*** Buffed the effects of many Bladeburner Augmentations<br>" +
"*** The Blade's Simulacrum Augmentation requires significantly less reputation but slightly more money<br>" +
"*** Slightly increased the amount of successes needed for a Contract/Operation in order to increase its max level<br>" +
"*** Increased the amount of money gained from Contracts by ~25%<br>" +
"*** Increased the base amount of rank gained from Operations by 10%<br>" +
"*** Significantly increased the 'randomness' in determining a Contract/Operation's initial count and rate of count increase<br>" +
"*** The number (count) of Operations should now increase significantly faster<br>" +
"*** There are now, on average, more Synthoid communities in a city<br>" +
"*** If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation<br>" +
"------------------------------<br>" +
"* Stock Market Changes:<br>" +
"***Added a watchlist filter feature to the UI that allows you to specify which stocks to show<br>" +
"***Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks<br>" +
"***Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript<br>" +
"------------------------------<br>" +
"* There is now a setting for enabling/disabling the popup that appears when you are hospitalized<br>" +
"* Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-)<br>" +
"* Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-)<br>" +
"* Bug Fix: Bladeburner skill cost multiplier should now properly increase in BitNode-12 (by hydroflame)<br>" +
"* Bug Fix: 'document', 'hacknet', and 'window' keywords should no longer be counted multiple times in RAM calculations<br>" +
"* Bug Fix: Joining factions through Singularity functions should now prevent you from joining opposing factions<br>" +
"* Bug Fix: Four Sigma should no longer have two 'Speech Enhancement' Augmentations (by Kline-)<br>"
`v0.40.3<br>
-----------------------------------------------<br>
* Bladeburner Changes:<br>
*** Increased the effect that agi and dexterity have on action time<br>
*** Starting number of contracts/operations available will be slightly lower<br>
*** Random events will now happen slightly more often<br>
*** Slightly increased the rate at which the Overclock skill point cost increases<br>
-----------------------------------------------<br>
* The maximum volatility of stocks is now randomized (randomly generated within a certain range every time the game resets)<br>
* Increased the range of possible values for initial stock prices<br>
* b1t_flum3.exe program can now be created immediately at Hacking level 1 (rather than hacking level 5)<br>
* UI improvements for the character overview panel and the left-hand menu (by mat-jaworski)<br>
* General UI improvements for displays and Terminal (by mat-jaworski)<br>
* Added optional parameters to the getHackTime(), getGrowTime(), and getWeakenTime() Netscript functions<br>
* Added isLogEnabled() and getScriptLogs() Netscript functions<br>
* Added donateToFaction() Singularity function<br>
* Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0<br>
* Added tryWrite() Netscript function<br>
* When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished<br>
* Added a setting in .fconf for enabling line-wrap in the Terminal input<br>
* Adding a game option for changing the locale that most numbers are displayed in (this mostly applies for whenever money is displayed)<br>
* The randomized parameters of many high-level servers can now take on a higher range of values<br>
* Many 'foreign' servers (hackable servers that you don't own) now have a randomized amount of RAM<br>
* Added 'wget' Terminal command<br>
* Improved the introductory tutorial`
}
export {CONSTANTS};

@ -83,9 +83,9 @@ const Programs = {
time: CONSTANTS.MillisecondsPerQuarterHour,
}),
BitFlume: new Program("b1t_flum3.exe", {
level: 5,
level: 1,
tooltip:"This program creates a portal to the BitNode Nexus (allows you to restart and switch BitNodes)",
req: function() {return Player.sourceFiles.length > 0 && Player.hacking_skill >= 5},
req: function() {return Player.sourceFiles.length > 0 && Player.hacking_skill >= 1},
time: CONSTANTS.MillisecondsPerFiveMinutes / 5,
}),
// special because you can't create it.

@ -10,13 +10,13 @@ import {Player} from "./Player";
import {Settings} from "./Settings";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../utils/uiHelpers/createElement";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox";
@ -115,7 +115,7 @@ function factionExists(name) {
// This might change in the future for balance
function initFactions() {
for(const name in FactionInfos) {
resetFaction(new Faction(name));
resetFaction(new Faction(name));
}
}
@ -319,7 +319,7 @@ function displayFactionContent(factionName) {
if (isNaN(amt)) {
donateRepGain.innerText = "Invalid donate amount entered!";
} else {
var repGain = amt / 1e6 * Player.faction_rep_mult;
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
donateRepGain.innerText = "This donation will result in " +
formatNumber(repGain, 3) + " reputation gain";
}
@ -335,9 +335,9 @@ function displayFactionContent(factionName) {
dialogBoxCreate("You cannot afford to donate this much money!");
} else {
Player.loseMoney(amt);
var repGain = amt / 1e6 * Player.faction_rep_mult;
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain;
dialogBoxCreate("You just donated " + numeral(amt).format("$0.000a") + " to " +
dialogBoxCreate("You just donated " + numeralWrapper.format(amt, "$0.000a") + " to " +
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
displayFactionContent(factionName);
}
@ -603,10 +603,10 @@ function createFactionAugmentationDisplayElements(augmentationsList, augs, facti
pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button");
pElem.innerHTML = "UNLOCKED - " + numeral(aug.baseCost * factionInfo.augmentationPriceMult).format("$0.000a");
pElem.innerHTML = "UNLOCKED - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
} else {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeral(aug.baseCost * factionInfo.augmentationPriceMult).format("$0.000a");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
pElem.style.color = "red";
}
aDiv.appendChild(aElem);

@ -66,26 +66,43 @@ class FactionInfo {
export const FactionInfos: IMap<FactionInfo> = {
// Endgame
Illuminati: new FactionInfo(
"Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. And " +
"from this chaos, we are the Invisible hand that guides them to order.",
[], true, true, true, false),
"Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. " +
"And from this chaos, we are the Invisible hand that guides them to order. ",
[],
true,
true,
true,
false),
Daedalus: new FactionInfo(
"Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.",
[], true, true, true, false),
[],
true,
true,
true,
false),
"The Covenant": new FactionInfo(
"Surrender yourself. Give up your empty individuality to become part of something great, something eternal. " +
"Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.<br>" +
"<br>" +
"Only then can you discover immortality.",
[], true, true, true, false),
[],
true,
true,
true,
false),
// Megacorporations, each forms its own faction
ECorp: new FactionInfo(
"ECorp's mission is simple: to connect the world of today with the technology of tomorrow. With our wide range " +
"of Internet-related software and commercial hardware, ECorp makes the world's information universally accessible.",
[], true, true, true, true),
"ECorp's mission is simple: to connect the world of today with the technology of tomorrow. With our wide " +
"range of Internet-related software and commercial hardware, ECorp makes the world's information " +
"universally accessible.",
[],
true,
true,
true,
true),
MegaCorp: new FactionInfo(
"MegaCorp does things that others don't. We imagine. We create. We invent. We build things that others have " +
@ -93,66 +110,110 @@ export const FactionInfos: IMap<FactionInfo> = {
"unprecendented scale, in ways that no other company can.<br>" +
"<br>" +
"In our labs and factories and on the ground with customers, MegaCorp is ushering in a new era for the world.",
[], true, true, true, true),
[],
true,
true,
true,
true),
"Bachman & Associates": new FactionInfo(
"Where Law and Business meet - thats where we are. <br>" +
"Where Law and Business meet - thats where we are.<br>" +
"<br>" +
"Legal Insight - Business Instinct - Experience Innovation",
[], true, true, true, true),
[],
true,
true,
true,
true),
"Blade Industries": new FactionInfo(
"Augmentation is salvation",
[], true, true, true, true),
[],
true,
true,
true,
true),
NWO: new FactionInfo(
"The human being does not truly desire freedom. It wants to be observed, understood, and judged. It wants to be " +
"given purpose and direction in its life. That is why humans created God. And that is why humans created " +
"The human being does not truly desire freedom. It wants to be observed, understood, and judged. It wants to " +
"be given purpose and direction in its life. That is why humans created God. And that is why humans created " +
"civilization - not because of willingness, but because of a need to be incorporated into higher orders of " +
"structure and meaning.",
[], true, true, true, true),
[],
true,
true,
true,
true),
"Clarke Incorporated": new FactionInfo(
"Unlocking the power of the genome",
[], true, true, true, true),
[],
true,
true,
true,
true),
"OmniTek Incorporated": new FactionInfo(
"Simply put, our mission is to design and build robots that make a difference",
[], true, true, true, true),
[],
true,
true,
true,
true),
"Four Sigma": new FactionInfo(
"The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven by " +
"deep learning and innovative ideas. And improved by iteration. That's Four Sigma.",
[], true, true, true, true),
"The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven " +
"by deep learning and innovative ideas. And improved by iteration. That's Four Sigma.",
[],
true,
true,
true,
true),
"KuaiGong International": new FactionInfo(
"Dream big. Work hard. Make history.",
[], true, true, true, true),
[],
true,
true,
true,
true),
// Other Corporations
"Fulcrum Secret Technologies": new FactionInfo(
"The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it " +
"would be necessary to create them. And now we can.",
[], true, true, false, true),
[],
true,
true,
false,
true),
// Hacker groups
BitRunners: new FactionInfo(
"Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's all " +
"transformed into bits, stored in bits, communicated through bits. Its impossible for any person to move, to " +
"live, to operate at any level without the use of bits. And when a person moves, lives, and operates, they leave " +
"behind their bits, mere traces of seemingly meaningless fragments of information. But these bits can be " +
"Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's " +
"all transformed into bits, stored in bits, communicated through bits. Its impossible for any person to move, " +
"to live, to operate at any level without the use of bits. And when a person moves, lives, and operates, they " +
"leave behind their bits, mere traces of seemingly meaningless fragments of information. But these bits can be " +
"reconstructed. Transformed. Used.<br>" +
"<br>" +
"Those who run the bits, run the world",
[], true, true, false, false),
[],
true,
true,
false,
false),
"The Black Hand": new FactionInfo(
"The world, so afraid of strong government, now has no government. Only power - Digital power. Financial power. " +
"Technological power. And those at the top rule with an invisible hand. They built a society where the rich get " +
"richer, and everyone else suffers.<br>" +
"The world, so afraid of strong government, now has no government. Only power - Digital power. Financial " +
"power. Technological power. And those at the top rule with an invisible hand. They built a society where the " +
"rich get richer, and everyone else suffers.<br>" +
"<br>" +
"So much pain. So many lives. Their darkness must end.",
[], true, true, true, false),
[],
true,
true,
true,
false),
NiteSec: new FactionInfo(
" __..__ <br>" +
@ -190,40 +251,108 @@ export const FactionInfos: IMap<FactionInfo> = {
" d .dNITESEC $ | <br>" +
" :bp.__.gNITESEC$$ :$ ; <br>" +
" NITESECNITESECNIT $$b : <br>",
[], true, true, false, false),
[],
true,
true,
false,
false),
// City factions, essentially governments
Aevum: new FactionInfo(
"The Silicon City",
["Chongqing", "New Tokyo", "Ishima", "Volhaven"], true, true, true, true),
[
"Chongqing",
"New Tokyo",
"Ishima",
"Volhaven",
],
true,
true,
true,
true),
Chongqing: new FactionInfo(
"Serve the people",
["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
[
"Sector-12",
"Aevum",
"Volhaven",
],
true,
true,
true,
true),
Ishima: new FactionInfo(
"The East Asian Order of the Future",
["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
[
"Sector-12",
"Aevum",
"Volhaven",
],
true,
true,
true,
true),
"New Tokyo": new FactionInfo(
"Asia's World City",
["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
[
"Sector-12",
"Aevum",
"Volhaven",
],
true,
true,
true,
true),
"Sector-12": new FactionInfo(
"The City of the Future",
["Chongqing", "New Tokyo", "Ishima", "Volhaven"], true, true, true, true),
[
"Chongqing",
"New Tokyo",
"Ishima",
"Volhaven",
],
true,
true,
true,
true),
Volhaven: new FactionInfo(
"Benefit, Honour, and Glory",
["Chongqing", "Sector-12", "New Tokyo", "Aevum", "Ishima"], true, true, true, true),
[
"Chongqing",
"Sector-12",
"New Tokyo",
"Aevum",
"Ishima",
],
true,
true,
true,
true),
// Criminal Organizations/Gangs
"Speakers for the Dead": new FactionInfo(
"It is better to reign in hell than to serve in heaven.",
[], true, true, true, true),
[],
true,
true,
true,
true),
"The Dark Army": new FactionInfo(
"The World doesn't care about right or wrong. It's all about power.",
[], true, true, true, false),
[],
true,
true,
true,
false),
"The Syndicate": new FactionInfo(
"Honor holds you back",
[], true, true, true, true),
[],
true,
true,
true,
true),
Silhouette: new FactionInfo(
"Corporations have filled the void of power left behind by the collapse of Western government. The issue is " +
@ -231,35 +360,64 @@ export const FactionInfos: IMap<FactionInfo> = {
"corporations, you don't even know who you're working for.<br>" +
"<br>" +
"That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.",
[], true, true, true, false),
[],
true,
true,
true,
false),
Tetrads: new FactionInfo(
"Following the Mandate of Heaven and Carrying out the Way",
[], false, false, true, true),
[],
false,
false,
true,
true),
"Slum Snakes": new FactionInfo(
"Slum Snakes rule!",
[], false, false, true, true),
[],
false,
false,
true,
true),
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
Netburners: new FactionInfo(
"~~//*>H4CK|\|3T 8URN3R5**>?>\\~~",
[], true, true, false, false),
[],
true,
true,
false,
false),
"Tian Di Hui": new FactionInfo(
"Obey Heaven and Work Righteousness",
[], true, true, false, true),
[],
true,
true,
false,
true),
CyberSec: new FactionInfo(
"The Internet is the first thing that humanity has built that humanity doesnt understand, the largest " +
"experiment in anarchy that we have ever had. And as the world becomes increasingly dominated by the internet, " +
"society approaches the brink of total chaos. We serve only to protect society, to protect humanity, to protect " +
"the world from its imminent collapse.",
[], true, true, false, false),
"society approaches the brink of total chaos. We serve only to protect society, to protect humanity, to " +
"protect the world from its imminent collapse.",
[],
true,
true,
false,
false),
// Special Factions
Bladeburners: new FactionInfo(
"It's too bad they won't live. But then again, who does?<br><br>Note that for this faction, reputation can only " +
"be gained through Bladeburner actions. Completing Bladeburner contracts/operations will increase your reputation.",
[], false, false, false, false),
"It's too bad they won't live. But then again, who does?<br><br>Note that for this faction, reputation can " +
"only be gained through Bladeburner actions. Completing Bladeburner contracts/operations will increase your " +
"reputation.",
[],
false,
false,
false,
false),
};

@ -1,8 +1,9 @@
import {parse, Node} from "../utils/acorn";
var FconfSettings = {
ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false,
ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false,
WRAP_INPUT: false,
}
var FconfComments = {
@ -15,6 +16,11 @@ var FconfComments = {
"http://bitburner.readthedocs.io/en/latest/shortcuts.html",
ENABLE_TIMESTAMPS: "Terminal commands and log entries will be timestamped. The timestamp\n" +
"will have the format: M/D h:m",
WRAP_INPUT: "Wrap Terminal Input. If this is enabled, then when a Terminal command is\n" +
"too long and overflows, then it will wrap to the next line instead of\n" +
"side-scrolling\n\n" +
"Note that after you enable/disable this, you'll have to run a command\n" +
"before its effect takes place.",
}
//Parse Fconf settings from the config text
@ -74,6 +80,7 @@ function parseFconfSetting(setting, value) {
switch(setting) {
case "ENABLE_BASH_HOTKEYS":
case "ENABLE_TIMESTAMPS":
case "WRAP_INPUT":
var value = value.toLowerCase();
if (value === "1" || value === "true" || value === "y") {
value = true;

@ -3,6 +3,7 @@ import {Engine} from "./engine";
import {Faction, Factions,
displayFactionContent} from "./Faction";
import {Player} from "./Player";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
@ -10,7 +11,6 @@ import {createAccordionElement} from "../utils/uiHelpers/createA
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getRandomInt} from "../utils/helpers/getRandomInt";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
@ -892,7 +892,7 @@ function createGangMemberUpgradePanel(memberObj) {
var upg = upgradeArray[j];
(function (upg, div, memberObj) {
div.appendChild(createElement("a", {
innerText:upg.name + " - " + numeral(upg.cost).format("$0.000a"),
innerText:upg.name + " - " + numeralWrapper.format(upg.cost, "$0.000a"),
class:"a-link-button", margin:"2px", padding:"2px", display:"block",
fontSize:"12px",
tooltip:upg.desc,
@ -1411,12 +1411,12 @@ function updateGangMemberDisplayElement(memberObj) {
var stats = document.getElementById(name + "gang-member-stats-text");
if (stats) {
stats.innerHTML =
"Hacking: " + formatNumber(memberObj.hack, 0) + " (" + numeral(memberObj.hack_exp).format('(0.00a)') + " exp)<br>" +
"Strength: " + formatNumber(memberObj.str, 0) + " (" + numeral(memberObj.str_exp).format('(0.00a)') + " exp)<br>" +
"Defense: " + formatNumber(memberObj.def, 0) + " (" + numeral(memberObj.def_exp).format('(0.00a)') + " exp)<br>" +
"Dexterity: " + formatNumber(memberObj.dex, 0) + " (" + numeral(memberObj.dex_exp).format('(0.00a)') + " exp)<br>" +
"Agility: " + formatNumber(memberObj.agi, 0) + " (" + numeral(memberObj.agi_exp).format('(0.00a)') + " exp)<br>" +
"Charisma: " + formatNumber(memberObj.cha, 0) + " (" + numeral(memberObj.cha_exp).format('(0.00a)') + " exp)<br>";
"Hacking: " + formatNumber(memberObj.hack, 0) + " (" + numeralWrapper.format(memberObj.hack_exp, '(0.00a)') + " exp)<br>" +
"Strength: " + formatNumber(memberObj.str, 0) + " (" + numeralWrapper.format(memberObj.str_exp, '(0.00a)') + " exp)<br>" +
"Defense: " + formatNumber(memberObj.def, 0) + " (" + numeralWrapper.format(memberObj.def_exp, '(0.00a)') + " exp)<br>" +
"Dexterity: " + formatNumber(memberObj.dex, 0) + " (" + numeralWrapper.format(memberObj.dex_exp, '(0.00a)') + " exp)<br>" +
"Agility: " + formatNumber(memberObj.agi, 0) + " (" + numeralWrapper.format(memberObj.agi_exp, '(0.00a)') + " exp)<br>" +
"Charisma: " + formatNumber(memberObj.cha, 0) + " (" + numeralWrapper.format(memberObj.cha_exp, '(0.00a)') + " exp)<br>";
}
var gainInfo = document.getElementById(name + "gang-member-gain-info");

92
src/Hacking.js Normal file

@ -0,0 +1,92 @@
import { BitNodeMultipliers } from "./BitNodeMultipliers";
import { Player } from "./Player";
import { Server } from "./Server";
/**
* Returns the chance the player has to successfully hack a server
*/
export function calculateHackingChance(server) {
const hackFactor = 1.75;
const intFactor = 0.2;
const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (hackFactor * Player.hacking_skill) + (intFactor * Player.intelligence);
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
const chance = skillChance * difficultyMult * Player.hacking_chance_mult;
if (chance > 1) { return 1; }
if (chance < 0) { return 0; }
return chance;
}
/**
* Returns the amount of hacking experience the player will gain upon
* successfully hacking a server
*/
export function calculateHackingExpGain(server) {
const baseExpGain = 3;
const diffFactor = 0.3;
if (server.baseDifficulty == null) {
server.baseDifficulty = server.hackDifficulty;
}
var expGain = baseExpGain;
expGain += (server.baseDifficulty * Player.hacking_exp_mult * diffFactor);
return expGain * BitNodeMultipliers.HackExpGain;
}
/**
* Returns the percentage of money that will be stolen from a server if
* it is successfully hacked (returns the decimal form, not the actual percent value)
*/
export function calculatePercentMoneyHacked(server) {
// Adjust if needed for balancing. This is the divisor for the final calculation
const balanceFactor = 240;
const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (Player.hacking_skill - (server.requiredHackingSkill - 1)) / Player.hacking_skill;
const percentMoneyHacked = difficultyMult * skillMult * Player.hacking_money_mult / balanceFactor;
if (percentMoneyHacked < 0) { return 0; }
if (percentMoneyHacked > 1) { return 1; }
return percentMoneyHacked * BitNodeMultipliers.ScriptHackMoney;
}
/**
* Returns time it takes to complete a hack on a server, in seconds
*/
export function calculateHackingTime(server, hack, int) {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500;
const baseSkill = 50;
const diffFactor = 2.5;
const intFactor = 0.1;
if (hack == null) {hack = Player.hacking_skill;}
if (int == null) {int = Player.intelligence;}
var skillFactor = (diffFactor * difficultyMult + baseDiff);
// tslint:disable-next-line
skillFactor /= (hack + baseSkill + (intFactor * int));
const hackTimeMultiplier = 5;
const hackingTime = hackTimeMultiplier * skillFactor / Player.hacking_speed_mult;
return hackingTime;
}
/**
* Returns time it takes to complete a grow operation on a server, in seconds
*/
export function calculateGrowTime(server, hack, int) {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server, hack, int);
}
/**
* Returns time it takes to complete a weaken operation on a server, in seconds
*/
export function calculateWeakenTime(server, hack, int) {
const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server, hack, int);
}

@ -2,7 +2,7 @@ import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {Player} from "./Player";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
@ -245,8 +245,8 @@ Reviver.constructors.HacknetNode = HacknetNode;
function purchaseHacknet() {
/* INTERACTIVE TUTORIAL */
if (iTutorialIsRunning) {
if (currITutorialStep == iTutorialSteps.HacknetNodesIntroduction) {
if (ITutorial.isRunning) {
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
iTutorialNextStep();
} else {
return;
@ -446,7 +446,7 @@ function updateHacknetNodesContent() {
//Update player's money
updateText("hacknet-nodes-player-money", "$" + formatNumber(Player.money.toNumber(), 2));
updateText("hacknet-nodes-total-production", "$" + formatNumber(Player.totalHacknetNodeProduction, 2) + " / second");
updateText("hacknet-nodes-total-production", "$" + formatNumber(Player.totalHacknetNodeProduction, 2) + " / sec");
//Update information in each owned hacknet node
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
@ -548,7 +548,7 @@ function updateHacknetNodeDomElement(nodeObj) {
updateText("hacknet-node-name-" + nodeName, nodeName);
updateText("hacknet-node-total-production-" + nodeName, "$" + formatNumber(nodeObj.totalMoneyGenerated, 2));
updateText("hacknet-node-production-rate-" + nodeName, "($" + formatNumber(nodeObj.moneyGainRatePerSecond, 2) + " / second)");
updateText("hacknet-node-production-rate-" + nodeName, "($" + formatNumber(nodeObj.moneyGainRatePerSecond, 2) + " / sec)");
updateText("hacknet-node-level-" + nodeName, nodeObj.level);
updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB");
updateText("hacknet-node-cores-" + nodeName, nodeObj.cores);

@ -32,7 +32,8 @@ export const TerminalHelpText: string =
"tail [script] [args...] Displays dynamic logs for the specified script<br>" +
"theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" +
"top Displays all running scripts and their RAM usage<br>" +
'unalias "[alias name]" Deletes the specified alias<br>';
'unalias "[alias name]" Deletes the specified alias<br>' +
"wget [url] [target file] Retrieves code/text from a web server<br>";
interface IMap<T> {
[key: string]: T;
@ -214,5 +215,11 @@ export const HelpTexts: IMap<string> = {
"Then it could be removed using:<br><br>" +
'unalias "r"<br><br>' +
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
wget: "wget [url] [target file]<br>" +
"Retrieves data from a URL and downloads it to a file on the current server. The data can only " +
"be downloaded to a script (.script, .ns, .js) or a text file (.txt). If the file already exists, " +
"it will be overwritten by this command.<br><br>" +
"Note that it will not be possible to download data from many websites because they do not allow " +
"cross-origin resource sharing (CORS). Example:<br><br>" +
"wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
};

@ -162,7 +162,7 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKill(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY killed the security bots! Unfortunately you alerted the " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> killed the security bots! Unfortunately you alerted the " +
"rest of the facility's security. The facility's security " +
"level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
Player.karma -= 1;
@ -170,7 +170,7 @@ function nextInfiltrationLevel(inst) {
return false;
} else {
var dmgTaken = Math.max(1, Math.round(1.5 * inst.securityLevel / Player.defense));
writeInfiltrationStatusText("You FAILED to kill the security bots. The bots fight back " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to kill the security bots. The bots fight back " +
"and raise the alarm! You take " + dmgTaken + " damage and " +
"the facility's security level increases by " +
formatNumber((res[1]*100)-100, 2).toString() + "%");
@ -186,12 +186,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationAssassinate(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY assassinated the security bots without being detected!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> assassinated the security bots without being detected!");
Player.karma -= 1;
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to assassinate the security bots. The bots have not detected " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to assassinate the security bots. The bots have not detected " +
"you but are now more alert for an intruder. The facility's security level " +
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
@ -209,7 +209,7 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKill(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY killed the security guard! Unfortunately you alerted the " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> killed the security guard! Unfortunately you alerted the " +
"rest of the facility's security. The facility's security " +
"level has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
Player.karma -= 3;
@ -218,7 +218,7 @@ function nextInfiltrationLevel(inst) {
return false;
} else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
writeInfiltrationStatusText("You FAILED to kill the security guard. The guard fights back " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to kill the security guard. The guard fights back " +
"and raises the alarm! You take " + dmgTaken + " damage and " +
"the facility's security level has increased by " +
formatNumber((res[1]*100)-100, 2).toString() + "%");
@ -236,13 +236,13 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationAssassinate(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY assassinated the security guard without being detected!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> assassinated the security guard without being detected!");
Player.karma -= 3;
++Player.numPeopleKilled;
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to assassinate the security guard. The guard has not detected " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to assassinate the security guard. The guard has not detected " +
"you but is now more alert for an intruder. The facility's security level " +
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
@ -259,14 +259,14 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKnockout(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY knocked out the security guard! " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> knocked out the security guard! " +
"Unfortunately you made a lot of noise and alerted other security.");
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst);
return false;
} else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
writeInfiltrationStatusText("You FAILED to knockout the security guard. The guard " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to knockout the security guard. The guard " +
"raises the alarm and fights back! You take " + dmgTaken + " damage and " +
"the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(dmgTaken)) {
@ -282,13 +282,13 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationStealthKnockout(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY knocked out the security guard without making " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> knocked out the security guard without making " +
"any noise!");
endInfiltrationLevel(inst);
return false;
} else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
writeInfiltrationStatusText("You FAILED to stealthily knockout the security guard. The guard " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to stealthily knockout the security guard. The guard " +
"raises the alarm and fights back! You take " + dmgTaken + " damage and " +
"the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(dmgTaken)) {
@ -304,12 +304,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationHack(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY hacked and disabled the security system!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> hacked and disabled the security system!");
writeInfiltrationStatusText("The facility's security level increased by " + ((res[1]*100) - 100).toString() + "%");
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to hack the security system. The facility's " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to hack the security system. The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
updateInfiltrationButtons(inst, scenario);
@ -321,12 +321,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationDestroySecurity(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY and violently destroy the security system!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> and violently destroy the security system!");
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to destroy the security system. The facility's " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to destroy the security system. The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
updateInfiltrationButtons(inst, scenario);
@ -338,11 +338,11 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationSneak(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY sneak past the security undetected!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> sneak past the security undetected!");
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED and were detected while trying to sneak past security! The facility's " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> and were detected while trying to sneak past security! The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
updateInfiltrationButtons(inst, scenario);
@ -354,12 +354,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationPickLockedDoor(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY pick the locked door!");
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> pick the locked door!");
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to pick the locked door. The facility's security level " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to pick the locked door. The facility's security level " +
"increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
updateInfiltrationButtons(inst, scenario);
@ -377,13 +377,13 @@ function nextInfiltrationLevel(inst) {
}
var res = attemptInfiltrationBribe(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY bribed a guard to let you through " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> bribed a guard to let you through " +
"to the next clearance level for $" + bribeAmt);
Player.loseMoney(bribeAmt);
endInfiltrationLevel(inst);
return false;
} else {
writeInfiltrationStatusText("You FAILED to bribe a guard! The guard is alerting " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to bribe a guard! The guard is alerting " +
"other security guards about your presence! The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
}
@ -396,12 +396,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;}
var res = attemptInfiltrationEscape(inst);
if (res[0]) {
writeInfiltrationStatusText("You SUCCESSFULLY escape from the facility with the stolen classified " +
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> escape from the facility with the stolen classified " +
"documents and company secrets!");
endInfiltration(inst, true);
return false;
} else {
writeInfiltrationStatusText("You FAILED to escape from the facility. You took 1 damage. The facility's " +
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to escape from the facility. You took 1 damage. The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(1)) {
endInfiltration(inst, false);
@ -429,9 +429,9 @@ function endInfiltrationLevel(inst) {
BitNodeMultipliers.InfiltrationMoney;
inst.secretsStolen.push(baseSecretValue);
dialogBoxCreate("You found and stole a set of classified documents from the company. " +
"These classified secrets could probably be sold for money ($" +
formatNumber(secretMoneyValue, 2) + "), or they " +
"could be given to factions for reputation (" + formatNumber(secretValue, 3) + " rep)");
"These classified secrets could probably be sold for money (<span class='money-gold'>$" +
formatNumber(secretMoneyValue, 2) + "</span>), or they " +
"could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
}
//Increase security level based on difficulty
@ -468,18 +468,22 @@ function updateInfiltrationLevelText(inst) {
}
var expMultiplier = 2 * inst.clearanceLevel / inst.maxClearanceLevel;
// TODO: fix this to not rely on <pre> and whitespace for formatting...
/* eslint-disable no-irregular-whitespace */
document.getElementById("infiltration-level-text").innerHTML =
"Facility name: " + inst.companyName + "<br>" +
"Clearance Level: " + inst.clearanceLevel + "<br>" +
"Security Level: " + formatNumber(inst.securityLevel, 3) + "<br><br>" +
"Total reputation value of secrets stolen: " + formatNumber(totalValue, 3) + "<br>" +
"Total monetary value of secrets stolen: $" + formatNumber(totalMoneyValue, 2) + "<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);
"Facility name:    " + inst.companyName + "<br>" +
"Clearance Level:  " + inst.clearanceLevel + "<br>" +
"Security Level:   " + formatNumber(inst.securityLevel, 3) + "<br><br>" +
"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);
/* eslint-enable no-irregular-whitespace */
}
function updateInfiltrationButtons(inst, scenario) {

@ -1,55 +1,75 @@
import {Engine} from "./engine";
import {Player} from "./Player";
import {dialogBoxCreate} from "../utils/DialogBox";
import {Settings} from "./Settings";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
/* InteractiveTutorial.js */
let iTutorialSteps = {
Start: "Start",
GoToCharacterPage: "Click on the Character page menu link",
CharacterPage: "Introduction to Character page",
CharacterGoToTerminalPage: "Click on the Terminal link",
TerminalIntro: "Introduction to terminal interface",
TerminalHelp: "Using the help command to display all options in terminal",
TerminalLs: "Use the ls command to show all programs/scripts. Right now we have NUKE.exe",
TerminalScan: "Using the scan command to display all available connections",
TerminalScanAnalyze1: "Use the scan-analyze command to show hacking related information",
TerminalScanAnalyze2: "Use the scan-analyze command with a depth of 3",
TerminalConnect: "Using the telnet/connect command to connect to another server",
TerminalAnalyze: "Use the analyze command to display details about this server",
TerminalNuke: "Use the NUKE Program to gain root access to a server",
TerminalManualHack: "Use the hack command to manually hack a server",
TerminalHackingMechanics: "Briefly explain hacking mechanics",
TerminalCreateScript: "Create a script using nano",
TerminalTypeScript: "This occurs in the Script Editor page...type the script then save and close",
TerminalFree: "Use the free command to check RAM",
TerminalRunScript: "Use the run command to run a script",
TerminalGoToActiveScriptsPage: "Go to the ActiveScriptsPage",
ActiveScriptsPage: "Introduction to the Active Scripts Page",
ActiveScriptsToTerminal: "Go from Active Scripts Page Back to Terminal",
TerminalTailScript: "Use the tail command to show a script's logs",
GoToHacknetNodesPage: "Go to the Hacknet Nodes page",
HacknetNodesIntroduction: "Introduction to Hacknet Nodesm and have user purchase one",
HacknetNodesGoToWorldPage: "Go to the world page",
WorldDescription: "Tell the user to explore..theres a lot of different stuff to do out there",
TutorialPageInfo: "The tutorial page contains a lot of info on different subjects",
End: "End",
//Ordered array of keys to Interactive Tutorial Steps
const orderedITutorialSteps = [
"Start",
"GoToCharacterPage", //Click on 'Stats' page
"CharacterPage", //Introduction to 'Stats' page
"CharacterGoToTerminalPage", //Go back to Terminal
"TerminalIntro", //Introduction to Terminal
"TerminalHelp", //Using 'help' Terminal command
"TerminalLs", //Using 'ls' Terminal command
"TerminalScan", //Using 'scan' Terminal command
"TerminalScanAnalyze1", //Using 'scan-analyze' Terminal command
"TerminalScanAnalyze2", //Using 'scan-analyze 3' Terminal command
"TerminalConnect", //Connecting to foodnstuff
"TerminalAnalyze", //Analyzing foodnstuff
"TerminalNuke", //NUKE foodnstuff
"TerminalManualHack", //Hack foodnstuff
"TerminalHackingMechanics", //Explanation of hacking mechanics
"TerminalCreateScript", //Create a script using 'nano'
"TerminalTypeScript", //Script Editor page - Type script and then save & close
"TerminalFree", //Using 'Free' Terminal command
"TerminalRunScript", //Running script using 'run' Terminal command
"TerminalGoToActiveScriptsPage",
"ActiveScriptsPage",
"ActiveScriptsToTerminal",
"TerminalTailScript",
"GoToHacknetNodesPage",
"HacknetNodesIntroduction",
"HacknetNodesGoToWorldPage",
"WorldDescription",
"TutorialPageInfo",
"End"
]
//Create an 'enum' for the Steps
const iTutorialSteps = {};
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
iTutorialSteps[orderedITutorialSteps[i]] = i;
}
var currITutorialStep = iTutorialSteps.Start;
var iTutorialIsRunning = false;
var ITutorial = {
currStep: 0, //iTutorialSteps.Start
isRunning: false,
//Keeps track of whether each step has been done
stepIsDone: {},
}
function iTutorialStart() {
//Initialize Interactive Tutorial state by settings 'done' for each state to false
ITutorial.stepIsDone = {};
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
ITutorial.stepIsDone[i] = false;
}
Engine.loadTerminalContent();
//Don't autosave during this interactive tutorial
Engine.Counters.autoSaveCounter = 999000000000;
Engine.Counters.autoSaveCounter = Infinity;
console.log("Interactive Tutorial started");
currITutorialStep = iTutorialSteps.Start;
iTutorialIsRunning = true;
ITutorial.currStep = 0;
ITutorial.isRunning = true;
document.getElementById("interactive-tutorial-container").style.display = "block";
iTutorialEvaluateStep();
//Exit tutorial button
var exitButton = clearEventListeners("interactive-tutorial-exit");
exitButton.addEventListener("click", function() {
@ -59,142 +79,150 @@ function iTutorialStart() {
//Back button
var backButton = clearEventListeners("interactive-tutorial-back");
backButton.style.display = "none";
backButton.addEventListener("click", function() {
iTutorialPrevStep();
return false;
});
//Next button
var nextButton = clearEventListeners("interactive-tutorial-next");
nextButton.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
iTutorialEvaluateStep();
}
function iTutorialEvaluateStep() {
if (!iTutorialIsRunning) {console.log("Interactive Tutorial not running"); return;}
switch(currITutorialStep) {
if (!ITutorial.isRunning) {console.log("Interactive Tutorial not running"); return;}
//Disable and clear main menu
var terminalMainMenu = clearEventListeners("terminal-menu-link");
var statsMainMenu = clearEventListeners("stats-menu-link");
var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
var hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
var cityMainMenu = clearEventListeners("city-menu-link");
var tutorialMainMenu = clearEventListeners("tutorial-menu-link");
terminalMainMenu.removeAttribute("class");
statsMainMenu.removeAttribute("class");
activeScriptsMainMenu.removeAttribute("class");
hacknetMainMenu.removeAttribute("class");
cityMainMenu.removeAttribute("class");
tutorialMainMenu.removeAttribute("class");
//Interactive Tutorial Next button
var nextBtn = document.getElementById("interactive-tutorial-next");
switch(ITutorial.currStep) {
case iTutorialSteps.Start:
Engine.loadTerminalContent();
iTutorialSetText("Welcome to Bitburner, a cyberpunk-themed incremental RPG! " +
"The game takes place in a dark, dystopian future...The year is 2077...<br><br>" +
"This tutorial will show you the basics of the game. " +
"You may skip the tutorial at any time.");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.GoToCharacterPage:
Engine.loadTerminalContent();
iTutorialSetText("Let's start by heading to the Stats page. Click the 'Stats' tab on " +
"the main navigation menu (left-hand side of the screen)");
nextBtn.style.display = "none";
//No next button
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
//Flash Character tab
document.getElementById("stats-menu-link").setAttribute("class", "flashing-button");
//Initialize everything necessary to open the "Character" page
var charaterMainMenuButton = document.getElementById("stats-menu-link");
charaterMainMenuButton.addEventListener("click", function() {
//Flash 'Stats' menu and set its tutorial click handler
statsMainMenu.setAttribute("class", "flashing-button");
statsMainMenu.addEventListener("click", function() {
Engine.loadCharacterContent();
iTutorialNextStep(); //Opening the character page will go to the next step
clearEventListeners("stats-menu-link");
return false;
});
break;
case iTutorialSteps.CharacterPage:
Engine.loadCharacterContent();
iTutorialSetText("The Stats page shows a lot of important information about your progress, " +
"such as your skills, money, and bonuses/multipliers. ")
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.CharacterGoToTerminalPage:
Engine.loadCharacterContent();
iTutorialSetText("Let's head to your computer's terminal by clicking the 'Terminal' tab on the " +
"main navigation menu.");
//No next button
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
nextBtn.style.display = "none";
document.getElementById("terminal-menu-link").setAttribute("class", "flashing-button");
//Initialize everything necessary to open the 'Terminal' Page
var terminalMainMenuButton = document.getElementById("terminal-menu-link");
terminalMainMenuButton.addEventListener("click", function() {
//Flash 'Terminal' menu and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
clearEventListeners("terminal-menu-link");
return false;
});
break;
case iTutorialSteps.TerminalIntro:
Engine.loadTerminalContent();
iTutorialSetText("The Terminal is used to interface with your home computer as well as " +
"all of the other machines around the world.");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.TerminalHelp:
Engine.loadTerminalContent();
iTutorialSetText("Let's try it out. Start by entering the 'help' command into the Terminal " +
"(Don't forget to press Enter after typing the command)");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalLs:
Engine.loadTerminalContent();
iTutorialSetText("The 'help' command displays a list of all available Terminal commands, how to use them, " +
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalScan:
Engine.loadTerminalContent();
iTutorialSetText("'ls' is a basic command that shows all of the contents (programs/scripts) " +
"on the computer. Right now, it shows that you have a program called 'NUKE.exe' on your computer. " +
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
"to other machines throughout the world. Let's do that now by first entering " +
"the 'scan' command. ");
//next step triggered by terminal command
"the 'scan' command.");
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze1:
Engine.loadTerminalContent();
iTutorialSetText("The 'scan' command shows all available network connections. In other words, " +
"it displays a list of all servers that can be connected to from your " +
"current machine. A server is identified by either its IP or its hostname. <br><br> " +
"That's great and all, but there's so many servers. Which one should you go to? " +
"The 'scan-analyze' command gives some more detailed information about servers on the " +
"network. Try it now");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze2:
Engine.loadTerminalContent();
iTutorialSetText("You just ran 'scan-analyze' with a depth of one. This command shows more detailed " +
"information about each server that you can connect to (servers that are a distance of " +
"one node away). <br><br> It is also possible to run 'scan-analyze' with " +
"a higher depth. Let's try a depth of two with the following command: 'scan-analyze 2'.")
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalConnect:
Engine.loadTerminalContent();
iTutorialSetText("Now you can see information about all servers that are up to two nodes away, as well " +
"as figure out how to navigate to those servers through the network. You can only connect to " +
"a server that is one node away. To connect to a machine, use the 'connect [ip/hostname]' command. You can type in " +
"the ip or the hostname, but dont use both.<br><br>" +
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalAnalyze:
Engine.loadTerminalContent();
iTutorialSetText("You are now connected to another machine! What can you do now? You can hack it!<br><br> In the year 2077, currency has " +
"become digital and decentralized. People and corporations store their money " +
"on servers and computers. Using your hacking abilities, you can hack servers " +
"to steal money and gain experience. <br><br> " +
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalNuke:
Engine.loadTerminalContent();
iTutorialSetText("When the 'analyze' command finishes running it will show useful information " +
"about hacking the server. <br><br> For this server, the required hacking skill is only 1, " +
"which means you can hack it right now. However, in order to hack a server " +
@ -203,14 +231,16 @@ function iTutorialEvaluateStep() {
"open ports.<br><br> The 'analyze' results shows that there do not need to be any open ports " +
"on this machine for the NUKE virus to work, so go ahead and run the virus using the " +
"'run NUKE.exe' command.");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalManualHack:
Engine.loadTerminalContent();
iTutorialSetText("You now have root access! You can hack the server using the 'hack' command. " +
"Try doing that now.");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalHackingMechanics:
Engine.loadTerminalContent();
iTutorialSetText("You are now attempting to hack the server. Note that performing a hack takes time and " +
"only has a certain percentage chance " +
"of success. This time and success chance is determined by a variety of factors, including " +
@ -220,30 +250,25 @@ function iTutorialEvaluateStep() {
"the server's security level.<br><br>The amount of money on a server is not limitless. So, if " +
"you constantly hack a server and deplete its money, then you will encounter " +
"diminishing returns in your hacking.");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.TerminalCreateScript:
Engine.loadTerminalContent();
iTutorialSetText("Hacking is the core mechanic of the game and is necessary for progressing. However, " +
"you don't want to be hacking manually the entire time. You can automate your hacking " +
"by writing scripts!<br><br>To create a new script or edit an existing one, you can use the 'nano' " +
"command. Scripts must end with the '.script' extension. Let's make a script now by " +
"entering 'nano foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
" will end a command like hack early)");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalTypeScript:
Engine.loadScriptEditorContent("foodnstuff.script", "");
iTutorialSetText("This is the script editor. You can use it to program your scripts. Scripts are " +
"written in the Netscript language, a programming language created for " +
"this game. <strong style='background-color:#444;'>There are details about the Netscript language in the documentation, which " +
"can be accessed in the 'Tutorial' tab on the main navigation menu. I highly suggest you check " +
"it out after this tutorial. </strong> For now, just copy " +
"it out after this tutorial.</strong> For now, just copy " +
"and paste the following code into the script editor: <br><br>" +
"while(true) { <br>" +
"&nbsp;&nbsp;hack('foodnstuff'); <br>" +
@ -251,21 +276,24 @@ function iTutorialEvaluateStep() {
"For anyone with basic programming experience, this code should be straightforward. " +
"This script will continuously hack the 'foodnstuff' server.<br><br>" +
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
//next step triggered in saveAndCloseScriptEditor() (Script.js)
nextBtn.style.display = "none"; //next step triggered in saveAndCloseScriptEditor() (Script.js)
break;
case iTutorialSteps.TerminalFree:
Engine.loadTerminalContent();
iTutorialSetText("Now we'll run the script. Scripts require a certain amount of RAM to run, and can be " +
"run on any machine which you have root access to. Different servers have different " +
"amounts of RAM. You can also purchase more RAM for your home server.<br><br>To check how much " +
"RAM is available on this machine, enter the 'free' command.");
//next step triggered by terminal commmand
nextBtn.style.display = "none"; //next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalRunScript:
Engine.loadTerminalContent();
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
"script. Let's run our script using 'run foodnstuff.script'.");
//next step triggered by terminal commmand
nextBtn.style.display = "none"; //next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
Engine.loadTerminalContent();
iTutorialSetText("Your script is now running! The script might take a few seconds to 'fully start up'. " +
"Your scripts will continuously run in the background and will automatically stop if " +
"the code ever completes (the 'foodnstuff.script' will never complete because it " +
@ -274,117 +302,114 @@ function iTutorialEvaluateStep() {
"much slower rate. <br><br> " +
"Let's check out some statistics for our running scripts by clicking the " +
"'Active Scripts' link in the main navigation menu.");
document.getElementById("active-scripts-menu-link").setAttribute("class", "flashing-button");
var activeScriptsMainMenuButton = document.getElementById("active-scripts-menu-link");
activeScriptsMainMenuButton.addEventListener("click", function() {
nextBtn.style.display = "none";
//Flash 'Active Scripts' menu and set its tutorial click handler
activeScriptsMainMenu.setAttribute("class", "flashing-button");
activeScriptsMainMenu.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
iTutorialNextStep();
clearEventListeners("active-scripts-menu-link");
return false;
});
break;
case iTutorialSteps.ActiveScriptsPage:
Engine.loadActiveScriptsContent();
iTutorialSetText("This page displays stats/information about all of your scripts that are " +
"running across every existing server. You can use this to gauge how well " +
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal'" +
"link.");
document.getElementById("terminal-menu-link").setAttribute("class", "flashing-button");
//Initialize everything necessary to open the 'Terminal' Page
var terminalMainMenuButton = clearEventListeners("terminal-menu-link");
terminalMainMenuButton.addEventListener("click", function() {
nextBtn.style.display = "none";
//Flash 'Terminal' button and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
clearEventListeners("terminal-menu-link");
return false;
});
break;
case iTutorialSteps.ActiveScriptsToTerminal:
Engine.loadTerminalContent();
iTutorialSetText("One last thing about scripts, each active script contains logs that detail " +
"what it's doing. We can check these logs using the 'tail' command. Do that " +
"now for the script we just ran by typing 'tail foodnstuff.script'");
//next step triggered by terminal command
nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalTailScript:
Engine.loadTerminalContent();
iTutorialSetText("The log for this script won't show much right now (it might show nothing at all) because it " +
"just started running...but check back again in a few minutes! <br><br>" +
"This pretty much covers the basics of hacking. To learn more about writing " +
"scripts using the Netscript language, select the 'Tutorial' link in the " +
"main navigation menu to look at the documentation. For now, let's move on " +
"to something else!");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
"main navigation menu to look at the documentation. " +
"<strong style='background-color:#444;'>If you are an experienced JavaScript " +
"developer, I would highly suggest you check out the section on " +
"NetscriptJS/Netscript 2.0.</strong><br><br>For now, let's move on to something else!");
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.GoToHacknetNodesPage:
Engine.loadTerminalContent();
iTutorialSetText("Hacking is not the only way to earn money. One other way to passively " +
"earn money is by purchasing and upgrading Hacknet Nodes. Let's go to " +
"the 'Hacknet Nodes' page through the main navigation menu now.");
document.getElementById("hacknet-nodes-menu-link").setAttribute("class", "flashing-button");
var hacknetNodesButton = clearEventListeners("hacknet-nodes-menu-link");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
hacknetNodesButton.addEventListener("click", function() {
nextBtn.style.display = "none";
//Flash 'Hacknet' menu and set its tutorial click handler
hacknetMainMenu.setAttribute("class", "flashing-button");
hacknetMainMenu.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
iTutorialNextStep();
clearEventListeners("hacknet-nodes-menu-link");
return false;
});
break;
case iTutorialSteps.HacknetNodesIntroduction:
Engine.loadHacknetNodesContent();
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
"existing ones. Let's purchase a new one now.");
//Next step triggered by purchaseHacknet() (HacknetNode.js)
nextBtn.style.display = "none"; //Next step triggered by purchaseHacknet() (HacknetNode.js)
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
Engine.loadHacknetNodesContent();
iTutorialSetText("You just purchased a Hacknet Node! This Hacknet Node will passively " +
"earn you money over time, both online and offline. When you get enough " +
" money, you can upgrade " +
"your newly-purchased Hacknet Node below.<br><br>" +
"Let's go to the 'City' page through the main navigation menu.");
document.getElementById("city-menu-link").setAttribute("class", "flashing-button");
var worldButton = clearEventListeners("city-menu-link");
worldButton.addEventListener("click", function() {
nextBtn.style.display = "none";
//Flash 'City' menu and set its tutorial click handler
cityMainMenu.setAttribute("class", "flashing-button");
cityMainMenu.addEventListener("click", function() {
Engine.loadWorldContent();
iTutorialNextStep();
clearEventListeners("city-menu-link");
return false;
});
break;
case iTutorialSteps.WorldDescription:
Engine.loadWorldContent();
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 " +
"you explore and discover!<br><br>" +
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
document.getElementById("tutorial-menu-link").setAttribute("class", "flashing-button");
var tutorialButton = clearEventListeners("tutorial-menu-link");
tutorialButton.addEventListener("click", function() {
nextBtn.style.display = "none";
//Flash 'Tutorial' menu and set its tutorial click handler
tutorialMainMenu.setAttribute("class", "flashing-button");
tutorialMainMenu.addEventListener("click", function() {
Engine.loadTutorialContent();
iTutorialNextStep();
clearEventListeners("tutorial-menu-link");
return false;
});
break;
case iTutorialSteps.TutorialPageInfo:
Engine.loadTutorialContent();
iTutorialSetText("This page contains a lot of different documentation about the game's " +
"content and mechanics. <strong style='background-color:#444;'> I know it's a lot, but I highly suggest you read " +
"(or at least skim) through this before you start playing</strong>. That's the end of the tutorial. " +
"Hope you enjoy the game!");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.innerHTML = "Finish Tutorial";
var backButton = clearEventListeners("interactive-tutorial-back");
backButton.style.display = "none";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
nextBtn.style.display = "inline-block";
nextBtn.innerHTML = "Finish Tutorial";
break;
case iTutorialSteps.End:
iTutorialEnd();
@ -392,264 +417,85 @@ function iTutorialEvaluateStep() {
default:
throw new Error("Invalid tutorial step");
}
if (ITutorial.stepIsDone[ITutorial.currStep] === true) {
nextBtn.style.display = "inline-block";
}
}
//Go to the next step and evaluate it
function iTutorialNextStep() {
switch(currITutorialStep) {
case iTutorialSteps.Start:
currITutorialStep = iTutorialSteps.GoToCharacterPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.GoToCharacterPage:
//Special behavior for certain steps
if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
document.getElementById("stats-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.CharacterPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.CharacterPage:
currITutorialStep = iTutorialSteps.CharacterGoToTerminalPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.CharacterGoToTerminalPage:
document.getElementById("terminal-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.TerminalIntro;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalIntro:
currITutorialStep = iTutorialSteps.TerminalHelp;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalHelp:
currITutorialStep = iTutorialSteps.TerminalLs;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalLs:
currITutorialStep = iTutorialSteps.TerminalScan;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalScan:
currITutorialStep = iTutorialSteps.TerminalScanAnalyze1;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalScanAnalyze1:
currITutorialStep = iTutorialSteps.TerminalScanAnalyze2;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalScanAnalyze2:
currITutorialStep = iTutorialSteps.TerminalConnect;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalConnect:
currITutorialStep = iTutorialSteps.TerminalAnalyze;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalAnalyze:
currITutorialStep = iTutorialSteps.TerminalNuke;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalNuke:
currITutorialStep = iTutorialSteps.TerminalManualHack;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalManualHack:
currITutorialStep = iTutorialSteps.TerminalHackingMechanics;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalHackingMechanics:
currITutorialStep = iTutorialSteps.TerminalCreateScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalCreateScript:
currITutorialStep = iTutorialSteps.TerminalTypeScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalTypeScript:
currITutorialStep = iTutorialSteps.TerminalFree;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalFree:
currITutorialStep = iTutorialSteps.TerminalRunScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalRunScript:
currITutorialStep = iTutorialSteps.TerminalGoToActiveScriptsPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
document.getElementById("active-scripts-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.ActiveScriptsPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.ActiveScriptsPage:
document.getElementById("terminal-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.ActiveScriptsToTerminal;
iTutorialEvaluateStep();
break;
case iTutorialSteps.ActiveScriptsToTerminal:
currITutorialStep = iTutorialSteps.TerminalTailScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalTailScript:
currITutorialStep = iTutorialSteps.GoToHacknetNodesPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.GoToHacknetNodesPage:
document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.HacknetNodesIntroduction;
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesIntroduction:
currITutorialStep = iTutorialSteps.HacknetNodesGoToWorldPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
document.getElementById("city-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.WorldDescription;
iTutorialEvaluateStep();
break;
case iTutorialSteps.WorldDescription:
document.getElementById("tutorial-menu-link").removeAttribute("class");
currITutorialStep = iTutorialSteps.TutorialPageInfo;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TutorialPageInfo:
currITutorialStep = iTutorialSteps.End;
iTutorialEvaluateStep();
break;
case iTutorialSteps.End:
break;
default:
throw new Error("Invalid tutorial step");
}
if (ITutorial.currStep === iTutorialSteps.CharacterGoToTerminalPage) {
document.getElementById("terminal-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.TerminalGoToActiveScriptsPage) {
document.getElementById("active-scripts-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.ActiveScriptsPage) {
document.getElementById("terminal-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.GoToHacknetNodesPage) {
document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.HacknetNodesGoToWorldPage) {
document.getElementById("city-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.WorldDescription) {
document.getElementById("tutorial-menu-link").removeAttribute("class");
}
ITutorial.stepIsDone[ITutorial.currStep] = true;
if (ITutorial.currStep < iTutorialSteps.End) {
ITutorial.currStep += 1;
}
iTutorialEvaluateStep();
}
//Go to previous step and evaluate
function iTutorialPrevStep() {
switch(currITutorialStep) {
case iTutorialSteps.Start:
currITutorialStep = iTutorialSteps.Start;
iTutorialEvaluateStep();
break;
case iTutorialSteps.GoToCharacterPage:
currITutorialStep = iTutorialSteps.Start;
iTutorialEvaluateStep();
break;
case iTutorialSteps.CharacterPage:
currITutorialStep = iTutorialSteps.GoToCharacterPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.CharacterGoToTerminalPage:
currITutorialStep = iTutorialSteps.CharacterPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalIntro:
currITutorialStep = iTutorialSteps.CharacterGoToTerminalPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalHelp:
currITutorialStep = iTutorialSteps.TerminalIntro;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalLs:
currITutorialStep = iTutorialSteps.TerminalHelp;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalScan:
currITutorialStep = iTutorialSteps.TerminalLs;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalConnect:
currITutorialStep = iTutorialSteps.TerminalScan;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalAnalyze:
currITutorialStep = iTutorialSteps.TerminalConnect;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalNuke:
currITutorialStep = iTutorialSteps.TerminalAnalyze;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalManualHack:
currITutorialStep = iTutorialSteps.TerminalNuke;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalHackingMechanics:
currITutorialStep = iTutorialSteps.TerminalManualHack;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalCreateScript:
currITutorialStep = iTutorialSteps.TerminalManualHack;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalTypeScript:
currITutorialStep = iTutorialSteps.TerminalCreateScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalFree:
currITutorialStep = iTutorialSteps.TerminalTypeScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalRunScript:
currITutorialStep = iTutorialSteps.TerminalFree;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
currITutorialStep = iTutorialSteps.TerminalRunScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.ActiveScriptsPage:
currITutorialStep = iTutorialSteps.TerminalGoToActiveScriptsPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.ActiveScriptsToTerminal:
currITutorialStep = iTutorialSteps.ActiveScriptsPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalTailScript:
currITutorialStep = iTutorialSteps.ActiveScriptsToTerminal;
iTutorialEvaluateStep();
break;
case iTutorialSteps.GoToHacknetNodesPage:
currITutorialStep = iTutorialSteps.TerminalTailScript;
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesIntroduction:
currITutorialStep = iTutorialSteps.GoToHacknetNodesPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
currITutorialStep = iTutorialSteps.HacknetNodesIntroduction;
iTutorialEvaluateStep();
break;
case iTutorialSteps.WorldDescription:
currITutorialStep = iTutorialSteps.HacknetNodesGoToWorldPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TutorialPageInfo:
currITutorialStep = iTutorialSteps.WorldDescription;
iTutorialEvaluateStep();
break;
case iTutorialSteps.End:
break;
default:
throw new Error("Invalid tutorial step");
if (ITutorial.currStep > iTutorialSteps.Start) {
ITutorial.currStep -= 1;
}
iTutorialEvaluateStep();
}
function iTutorialEnd() {
//Re-enable auto save
Engine.Counters.autoSaveCounter = 300;
if (Settings.AutosaveInterval === 0) {
Engine.Counters.autoSaveCounter = Infinity;
} else {
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
}
console.log("Ending interactive tutorial");
Engine.init();
currITutorialStep = iTutorialSteps.End;
iTutorialIsRunning = false;
ITutorial.currStep = iTutorialSteps.End;
ITutorial.isRunning = false;
document.getElementById("interactive-tutorial-container").style.display = "none";
dialogBoxCreate("If you are new to the game, the following links may be useful for you!<br><br>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner' target='_blank'>Getting Started Guide</a>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Bitburner_Wiki' target='_blank'>Wiki</a><br><br>" +
"The Beginner's Guide to Hacking was added to your home computer! It contains some tips/pointers for starting out with the game. " +
"To read it, go to Terminal and enter<br><br>cat hackers-starting-handbook.lit");
//Create a popup with final introductory stuff
var popupId = "interactive-tutorial-ending-popup";
var txt = createElement("p", {
innerHTML:
"If you are new to the game, the following links may be useful for you!<br><br>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner' target='_blank'>Getting Started Guide</a>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Bitburner_Wiki' target='_blank'>Wiki</a>" +
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/' target='_blank'>Documentation</a><br><br>" +
"The Beginner's Guide to Hacking was added to your home computer! It contains some tips/pointers for starting out with the game. " +
"To read it, go to Terminal and enter<br><br>cat hackers-starting-handbook.lit"
});
var gotitBtn = createElement("a", {
class:"a-link-button", float:"right", padding:"6px", innerText:"Got it!",
clickListener:()=>{
removeElementById(popupId);
}
});
createPopup(popupId, [txt, gotitBtn]);
Player.getHomeComputer().messages.push("hackers-starting-handbook.lit");
}
@ -660,5 +506,4 @@ function iTutorialSetText(txt) {
textBox.parentElement.scrollTop = 0; // this resets scroll position
}
export {iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, currITutorialStep,
iTutorialIsRunning};
export {iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, ITutorial};

@ -23,7 +23,11 @@ function initLiterature() {
var title, fn, txt;
title = "The Beginner's Guide to Hacking";
fn = "hackers-starting-handbook.lit";
txt = "When starting out, hacking is the most profitable way to earn money and progress. This " +
txt = "Some resources:<br><br>" +
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscriptlearntoprogram.html' target='_blank' style='margin:4px'>Learn to Program</a><br><br>" +
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscriptjs.html' target='_blank' style='margin:4px'>For Experienced JavaScript Developers: NetscriptJS</a><br><br>" +
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscript.html' target='_blank' style='margin:4px'>Netscript Documentation</a><br><br>" +
"When starting out, hacking is the most profitable way to earn money and progress. This " +
"is a brief collection of tips/pointers on how to make the most out of your hacking scripts.<br><br>" +
"-hack() and grow() both work by percentages. hack() steals a certain percentage of the " +
"money on a server, and grow() increases the amount of money on a server by some percentage (multiplicatively)<br><br>" +

@ -15,10 +15,11 @@ import {purchaseServer,
import {Settings} from "./Settings";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createRandomIp} from "../utils/IPAddress";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton,
@ -1001,7 +1002,7 @@ function displayLocationContent() {
slumsDealDrugs.innerHTML = "Deal Drugs (" + (drugsChance*100).toFixed(3) + "% chance of success)";
slumsDealDrugs.innerHTML += '<span class="tooltiptext"> Attempt to deal drugs </span>';
slumsBondForgery.style.display = "block";
slumsBondForgery.innerHTML = "Bond Forgery(" + (bondChance*100).toFixed(3) + "% chance of success)";
slumsBondForgery.innerHTML = "Bond Forgery (" + (bondChance*100).toFixed(3) + "% chance of success)";
slumsBondForgery.innerHTML += "<span class='tooltiptext'> Attempt to forge corporate bonds</span>";
slumsTrafficArms.style.display = "block";
slumsTrafficArms.innerHTML = "Traffick Illegal Arms (" + (armsChance*100).toFixed(3) + "% chance of success)";
@ -1788,7 +1789,7 @@ function initLocationButtons() {
});
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 " + numeral(cost).format('$0.000a'));
"This will cost " + numeralWrapper.format(cost, '$0.000a'));
});
purchaseHomeCores.addEventListener("click", function(e) {
@ -1825,7 +1826,7 @@ function initLocationButtons() {
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 " + (Player.getHomeComputer().cpuCores + 1) + ") will " +
"cost " + numeral(cost).format('$0.000a'));
"cost " + numeralWrapper.format(cost, '$0.000a'));
});
travelToAevum.addEventListener("click", function(e) {

@ -911,61 +911,5 @@ function isScriptErrorMessage(msg) {
return true;
}
//The same as Player's calculateHackingChance() function but takes in the server as an argument
function scriptCalculateHackingChance(server) {
var difficultyMult = (100 - server.hackDifficulty) / 100;
var skillMult = (1.75 * Player.hacking_skill) + (0.2 * Player.intelligence);
var skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
var chance = skillChance * difficultyMult * Player.hacking_chance_mult;
if (chance > 1) {return 1;}
if (chance < 0) {return 0;}
else {return chance;}
}
//The same as Player's calculateHackingTime() function but takes in the server as an argument
function scriptCalculateHackingTime(server) {
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
var hackingTime = 5 * skillFactor / Player.hacking_speed_mult; //This is in seconds
return hackingTime;
}
//The same as Player's calculateExpGain() function but takes in the server as an argument
function scriptCalculateExpGain(server) {
if (server.baseDifficulty == null) {
server.baseDifficulty = server.hackDifficulty;
}
return (server.baseDifficulty * Player.hacking_exp_mult * 0.3 + 3) * BitNodeMultipliers.HackExpGain;
}
//The same as Player's calculatePercentMoneyHacked() function but takes in the server as an argument
function scriptCalculatePercentMoneyHacked(server) {
var difficultyMult = (100 - server.hackDifficulty) / 100;
var skillMult = (Player.hacking_skill - (server.requiredHackingSkill - 1)) / Player.hacking_skill;
var percentMoneyHacked = difficultyMult * skillMult * Player.hacking_money_mult / 240;
if (percentMoneyHacked < 0) {return 0;}
if (percentMoneyHacked > 1) {return 1;}
return percentMoneyHacked * BitNodeMultipliers.ScriptHackMoney;
}
//Amount of time to execute grow() in milliseconds
function scriptCalculateGrowTime(server) {
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
var growTime = 16 * skillFactor / Player.hacking_speed_mult; //This is in seconds
return growTime * 1000;
}
//Amount of time to execute weaken() in milliseconds
function scriptCalculateWeakenTime(server) {
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
var weakenTime = 20 * skillFactor / Player.hacking_speed_mult; //This is in seconds
return weakenTime * 1000;
}
export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateHackingChance, scriptCalculateHackingTime,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime, evaluate,
export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript, evaluate,
isScriptErrorMessage, killNetscriptDelay, evaluateImport};

@ -13,6 +13,12 @@ import {Companies, Company, CompanyPosition,
import {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram";
import {DarkWebItems} from "./DarkWeb";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import {AllGangs} from "./Gang";
import {Factions, Faction, joinFaction,
factionExists, purchaseAugmentation} from "./Faction";
@ -28,11 +34,12 @@ import {Server, getServer, AddToAllServers,
GetServerByHostname} from "./Server";
import {Settings} from "./Settings";
import {SpecialServerIps} from "./SpecialServerIps";
import {Stock} from "./Stock";
import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
sellStock, updateStockPrices, displayStockMarketContent,
updateStockTicker, updateStockPlayerPosition,
Stock, shortStock, sellShort, OrderTypes,
shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket";
import {post} from "./ui/postToTerminal";
import {TextFile, getTextFile, createTextFile} from "./TextFile";
@ -42,10 +49,8 @@ import {unknownBladeburnerActionErrorMessage,
checkBladeburnerAccess} from "./NetscriptBladeburner.js";
import {WorkerScript, workerScripts,
killWorkerScript, NetscriptPorts} from "./NetscriptWorker";
import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
scriptCalculateHackingChance, scriptCalculateHackingTime,
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime} from "./NetscriptEvaluator";
import {makeRuntimeRejectMsg, netscriptDelay,
runScriptFromScript} from "./NetscriptEvaluator";
import {NetscriptPort} from "./NetscriptPort";
import Decimal from "decimal.js";
@ -116,6 +121,7 @@ var possibleLogs = {
applyToCompany: true,
joinFaction: true,
workForFaction: true,
donateToFaction: true,
createProgram: true,
commitCrime: true,
shortStock: true,
@ -292,7 +298,7 @@ function NetscriptFunctions(workerScript) {
}
//Calculate the hacking time
var hackingTime = scriptCalculateHackingTime(server); //This is in seconds
var hackingTime = calculateHackingTime(server); //This is in seconds
//No root access or skill level too low
if (server.hasAdminRights == false) {
@ -308,14 +314,14 @@ function NetscriptFunctions(workerScript) {
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() {
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = scriptCalculateHackingChance(server);
var hackChance = calculateHackingChance(server);
var rand = Math.random();
var expGainedOnSuccess = scriptCalculateExpGain(server) * threads;
var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success!
const percentHacked = scriptCalculatePercentMoneyHacked(server);
const percentHacked = calculatePercentMoneyHacked(server);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) {
//Server has a 'max money' of 0 (probably).
@ -390,18 +396,18 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Cannot grow this server (" + server.hostname + ") because user does not have root access");
}
var growTime = scriptCalculateGrowTime(server);
var growTime = calculateGrowTime(server);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.grow == null) {
workerScript.scriptRef.log("Executing grow() on server " + server.hostname + " in " + formatNumber(growTime/1000, 3) + " seconds (t=" + threads + ")");
workerScript.scriptRef.log("Executing grow() on server " + server.hostname + " in " + formatNumber(growTime, 3) + " seconds (t=" + threads + ")");
}
return netscriptDelay(growTime, workerScript).then(function() {
return netscriptDelay(growTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable;
server.moneyAvailable += (1 * threads); //It can be grown even if it has no money
var growthPercentage = processSingleServerGrowth(server, 450 * threads);
const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads);
var expGain = scriptCalculateExpGain(server) * threads;
var expGain = calculateHackingExpGain(server) * threads;
if (growthPercentage == 1) {
expGain = 0;
}
@ -437,16 +443,16 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Cannot weaken this server (" + server.hostname + ") because user does not have root access");
}
var weakenTime = scriptCalculateWeakenTime(server);
var weakenTime = calculateWeakenTime(server);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.weaken == null) {
workerScript.scriptRef.log("Executing weaken() on server " + server.hostname + " in " +
formatNumber(weakenTime/1000, 3) + " seconds (t=" + threads + ")");
formatNumber(weakenTime, 3) + " seconds (t=" + threads + ")");
}
return netscriptDelay(weakenTime, workerScript).then(function() {
return netscriptDelay(weakenTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
server.weaken(CONSTANTS.ServerWeakenAmount * threads);
workerScript.scriptRef.recordWeaken(server.ip, threads);
var expGain = scriptCalculateExpGain(server) * threads;
var expGain = calculateHackingExpGain(server) * threads;
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.weaken == null) {
workerScript.scriptRef.log("Server security level on " + server.hostname + " weakened to " + server.hackDifficulty +
". Gained " + formatNumber(expGain, 4) + " hacking exp (t=" + threads + ")");
@ -502,6 +508,17 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("Enabled logging for " + fn);
}
},
isLogEnabled : function(fn) {
if (workerScript.checkingRam) {return 0;}
if (possibleLogs[fn] === undefined) {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument to isLogEnabled: " + fn);
}
return workerScript.disableLogs[fn] ? false : true;
},
getScriptLogs : function() {
if (workerScript.checkingRam) {return 0;}
return workerScript.scriptRef.logs.slice();
},
nuke : function(ip){
if (workerScript.checkingRam) {
return updateStaticRam("nuke", CONSTANTS.ScriptPortProgramRamCost);
@ -1843,6 +1860,25 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for write: " + port);
}
},
tryWrite : function(port, data="") {
if (workerScript.checkingRam) {
return updateStaticRam("tryWrite", CONSTANTS.ScriptReadWriteRamCost);
}
updateDynamicRam("tryWrite", CONSTANTS.ScriptReadWriteRamCost);
if (!isNaN(port)) {
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: tryWrite() called on invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid.");
}
var port = NetscriptPorts[port-1];
if (port == null || !(port instanceof NetscriptPort)) {
throw makeRuntimeRejectMsg(workerScript, "Could not find port: " + port + ". This is a bug contact the game developer");
}
return port.tryWrite(data);
} else {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for tryWrite: " + port);
}
},
read : function(port) {
if (workerScript.checkingRam) {
return updateStaticRam("read", CONSTANTS.ScriptReadWriteRamCost);
@ -2047,7 +2083,7 @@ function NetscriptFunctions(workerScript) {
}
return 0;
},
getHackTime : function(ip) {
getHackTime : function(ip, hack, int) {
if (workerScript.checkingRam) {
return updateStaticRam("getHackTime", CONSTANTS.ScriptGetHackTimeRamCost);
}
@ -2057,9 +2093,9 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getHackTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getHackTime() failed. Invalid IP or hostname passed in: " + ip);
}
return scriptCalculateHackingTime(server); //Returns seconds
return calculateHackingTime(server, hack, int); //Returns seconds
},
getGrowTime : function(ip) {
getGrowTime : function(ip, hack, int) {
if (workerScript.checkingRam) {
return updateStaticRam("getGrowTime", CONSTANTS.ScriptGetHackTimeRamCost);
}
@ -2069,9 +2105,9 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getGrowTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getGrowTime() failed. Invalid IP or hostname passed in: " + ip);
}
return scriptCalculateGrowTime(server) / 1000; //Returns seconds
return calculateGrowTime(server, hack, int); //Returns seconds
},
getWeakenTime : function(ip) {
getWeakenTime : function(ip, hack, int) {
if (workerScript.checkingRam) {
return updateStaticRam("getWeakenTime", CONSTANTS.ScriptGetHackTimeRamCost);
}
@ -2081,7 +2117,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getWeakenTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getWeakenTime() failed. Invalid IP or hostname passed in: " + ip);
}
return scriptCalculateWeakenTime(server) / 1000; //Returns seconds
return calculateWeakenTime(server, hack, int); //Returns seconds
},
getScriptIncome : function(scriptname, ip) {
if (workerScript.checkingRam) {
@ -3032,7 +3068,7 @@ function NetscriptFunctions(workerScript) {
return Factions[name].favor;
},
getFactionFavorGain: function(name){
getFactionFavorGain: function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) {
@ -3053,6 +3089,45 @@ function NetscriptFunctions(workerScript) {
return Factions[name].getFavorGain()[0];
},
donateToFaction : function(name, amt) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) {
return updateStaticRam("donateToFaction", ramCost);
}
updateDynamicRam("donateToFaction", ramCost);
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run donateToFaction(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return;
}
}
if (!factionExists(name)) {
workerScript.log(`ERROR: Faction specified in donateToFaction() does not exist: ${name}`);
return false;
}
if (typeof amt !== 'number' || amt <= 0) {
workerScript.log(`ERROR: Invalid donation amount specified in donateToFaction(): ${amt}. Must be numeric and positive`);
return false;
}
if (Player.money.lt(amt)) {
workerScript.log(`ERROR: You do not have enough money to donate $${amt} to ${name}`);
return false;
}
var repNeededToDonate = Math.round(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
if (Factions[name].favor < repNeededToDonate) {
workerScript.log(`ERROR: You do not have enough favor to donate to this faction. Have ${Factions[name].favor}, need ${repNeededToDonate}`);
return false;
}
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
Factions[name].playerReputation += repGain;
Player.loseMoney(amt);
if (workerScript.shouldLog("donateToFaction")) {
workerScript.log(`$${amt} donated to ${name} for ${repGain} reputation`);
}
return true;
},
createProgram : function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}

@ -21,15 +21,18 @@ import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile";
import Decimal from "decimal.js";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createRandomIp} from "../utils/IPAddress";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {formatNumber,
convertTimeMsToTimeElapsedString} from "../utils/StringHelperFunctions";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
function PlayerObject() {
//Skills and stats
this.hacking_skill = 1;
@ -119,11 +122,6 @@ function PlayerObject() {
this.crime_money_mult = 1;
this.crime_success_mult = 1;
//Flag to let the engine know the player is starting an action
// Current actions: hack, analyze
this.startAction = false;
this.actionTime = 0;
//Flags/variables for working (Company, Faction, Creating Program, Taking Class)
this.isWorking = false;
this.workType = "";
@ -265,9 +263,6 @@ PlayerObject.prototype.prestigeAugmentation = function() {
this.queuedAugmentations = [];
this.startAction = false;
this.actionTime = 0;
this.isWorking = false;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
@ -349,9 +344,6 @@ PlayerObject.prototype.prestigeSourceFile = function() {
this.queuedAugmentations = [];
this.augmentations = [];
this.startAction = false;
this.actionTime = 0;
this.isWorking = false;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
@ -498,74 +490,10 @@ PlayerObject.prototype.resetMultipliers = function() {
this.bladeburner_success_chance_mult = 1;
}
//Calculates the chance of hacking a server
//The formula is:
// (2 * hacking_chance_multiplier * hacking_skill - requiredLevel) 100 - difficulty
// ----------------------------------------------------------- * -----------------
// (2 * hacking_chance_multiplier * hacking_skill) 100
PlayerObject.prototype.calculateHackingChance = function() {
var difficultyMult = (100 - this.getCurrentServer().hackDifficulty) / 100;
var skillMult = (1.75 * this.hacking_skill) + (0.2 * this.intelligence);
var skillChance = (skillMult - this.getCurrentServer().requiredHackingSkill) / skillMult;
var chance = skillChance * difficultyMult * this.hacking_chance_mult;
if (chance > 1) {return 1;}
if (chance < 0) {return 0;}
return chance;
}
//Calculate the time it takes to hack a server in seconds. Returns the time
//The formula is:
// (2.5 * requiredLevel * difficulty + 200)
// ----------------------------------- * hacking_speed_multiplier
// hacking_skill + 100
PlayerObject.prototype.calculateHackingTime = function() {
var difficultyMult = this.getCurrentServer().requiredHackingSkill * this.getCurrentServer().hackDifficulty;
var skillFactor = (2.5 * difficultyMult + 200) / (this.hacking_skill + 100 + (0.1 * this.intelligence));
return 5 * skillFactor / this.hacking_speed_mult;
}
//Calculates the PERCENTAGE of a server's money that the player will hack from the server if successful
//The formula is:
// (hacking_skill - (requiredLevel-1)) 100 - difficulty
// --------------------------------------* ----------------------- * hacking_money_multiplier
// hacking_skill 100
PlayerObject.prototype.calculatePercentMoneyHacked = function() {
var difficultyMult = (100 - this.getCurrentServer().hackDifficulty) / 100;
var skillMult = (this.hacking_skill - (this.getCurrentServer().requiredHackingSkill - 1)) / this.hacking_skill;
var percentMoneyHacked = difficultyMult * skillMult * this.hacking_money_mult / 240;
console.log("Percent money hacked calculated to be: " + percentMoneyHacked);
if (percentMoneyHacked < 0) {return 0;}
if (percentMoneyHacked > 1) {return 1;}
return percentMoneyHacked * BitNodeMultipliers.ManualHackMoney;
}
//Returns how much EXP the player gains on a successful hack
//The formula is:
// difficulty * requiredLevel * hacking_multiplier
PlayerObject.prototype.calculateExpGain = function() {
var s = this.getCurrentServer();
if (s.baseDifficulty == null) {
s.baseDifficulty = s.hackDifficulty;
}
return (s.baseDifficulty * this.hacking_exp_mult * 0.3 + 3) * BitNodeMultipliers.HackExpGain;
}
//Hack/Analyze a server. Return the amount of time the hack will take. This lets the Terminal object know how long to disable itself for
//This assumes that the server being hacked is not purchased by the player, that the player's hacking skill is greater than the
//required hacking skill and that the player has admin rights.
PlayerObject.prototype.hack = function() {
this.actionTime = this.calculateHackingTime();
console.log("Hacking time: " + this.actionTime);
this.startAction = true; //Set the startAction flag so the engine starts the hacking process
}
PlayerObject.prototype.analyze = function() {
this.actionTime = 1;
this.startAction = true;
}
PlayerObject.prototype.hasProgram = function(programName) {
var home = Player.getHomeComputer();
if (home == null) {return false;}
for (var i = 0; i < home.programs.length; ++i) {
if (programName.toLowerCase() == home.programs[i].toLowerCase()) {return true;}
}
@ -688,6 +616,7 @@ PlayerObject.prototype.resetWorkStatus = function() {
this.workChaExpGainRate = 0;
this.workRepGainRate = 0;
this.workMoneyGainRate = 0;
this.workMoneyLossRate = 0;
this.workHackExpGained = 0;
this.workStrExpGained = 0;
@ -709,24 +638,109 @@ PlayerObject.prototype.resetWorkStatus = function() {
document.getElementById("work-in-progress-text").innerHTML = "";
}
PlayerObject.prototype.gainWorkExp = function() {
this.gainHackingExp(this.workHackExpGained);
this.gainStrengthExp(this.workStrExpGained);
this.gainDefenseExp(this.workDefExpGained);
this.gainDexterityExp(this.workDexExpGained);
this.gainAgilityExp(this.workAgiExpGained);
this.gainCharismaExp(this.workChaExpGained);
PlayerObject.prototype.processWorkEarnings = function(numCycles=1) {
var hackExpGain = this.workHackExpGainRate * numCycles;
var strExpGain = this.workStrExpGainRate * numCycles;
var defExpGain = this.workDefExpGainRate * numCycles;
var dexExpGain = this.workDexExpGainRate * numCycles;
var agiExpGain = this.workAgiExpGainRate * numCycles;
var chaExpGain = this.workChaExpGainRate * numCycles;
this.gainHackingExp(hackExpGain);
this.gainStrengthExp(strExpGain);
this.gainDefenseExp(defExpGain);
this.gainDexterityExp(dexExpGain);
this.gainAgilityExp(agiExpGain);
this.gainCharismaExp(chaExpGain);
this.workHackExpGained += hackExpGain;
this.workStrExpGained += strExpGain;
this.workDefExpGained += defExpGain;
this.workDexExpGained += dexExpGain;
this.workAgiExpGained += agiExpGain;
this.workChaExpGained += chaExpGain;
this.workRepGained += this.workRepGainRate * numCycles;
this.workMoneyGained += this.workMoneyGainRate * numCycles;
this.workMoneyGained -= this.workMoneyLossRate * numCycles;
}
/* Working for Company */
PlayerObject.prototype.startWork = function() {
this.resetWorkStatus();
this.isWorking = true;
this.workType = CONSTANTS.WorkTypeCompany;
this.workHackExpGainRate = this.getWorkHackExpGain();
this.workStrExpGainRate = this.getWorkStrExpGain();
this.workDefExpGainRate = this.getWorkDefExpGain();
this.workDexExpGainRate = this.getWorkDexExpGain();
this.workAgiExpGainRate = this.getWorkAgiExpGain();
this.workChaExpGainRate = this.getWorkChaExpGain();
this.workRepGainRate = this.getWorkRepGain();
this.workMoneyGainRate = this.getWorkMoneyGain();
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours;
//Remove all old event listeners from Cancel button
var newCancelButton = clearEventListeners("work-in-progress-cancel-button");
newCancelButton.innerHTML = "Cancel Work";
newCancelButton.addEventListener("click", function() {
Player.finishWork(true);
return false;
});
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
}
PlayerObject.prototype.work = function(numCycles) {
//Cap the number of cycles being processed to whatever would put you at
//the work time limit (8 hours)
var overMax = false;
if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / Engine._idleSpeed);
}
this.timeWorked += Engine._idleSpeed * numCycles;
this.workRepGainRate = this.getWorkRepGain();
this.processWorkEarnings(numCycles);
//If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
return this.finishWork(false);
}
var comp = Companies[this.companyName], companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.log("ERROR: Could not find Company: " + this.companyName);
} else {
companyRep = comp.playerReputation;
}
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName +
" at " + this.companyName + " (Current Company Reputation: " +
formatNumber(companyRep, 0) + ")<br><br>" +
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * CYCLES_PER_SEC, 4) + " / sec) reputation for this company <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * CYCLES_PER_SEC, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * CYCLES_PER_SEC, 4) + " / sec) charisma exp <br><br>" +
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish, " +
"but you will only gain half of the reputation you've earned so far."
}
PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
//Since the work was cancelled early, player only gains half of what they've earned so far
if (cancelled) {
this.workRepGained /= 2;
}
this.gainWorkExp();
var company = Companies[this.companyName];
company.playerReputation += (this.workRepGained);
@ -773,91 +787,6 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
this.resetWorkStatus();
}
PlayerObject.prototype.startWork = function() {
this.resetWorkStatus();
this.isWorking = true;
this.workType = CONSTANTS.WorkTypeCompany;
this.workHackExpGainRate = this.getWorkHackExpGain();
this.workStrExpGainRate = this.getWorkStrExpGain();
this.workDefExpGainRate = this.getWorkDefExpGain();
this.workDexExpGainRate = this.getWorkDexExpGain();
this.workAgiExpGainRate = this.getWorkAgiExpGain();
this.workChaExpGainRate = this.getWorkChaExpGain();
this.workRepGainRate = this.getWorkRepGain();
this.workMoneyGainRate = this.getWorkMoneyGain();
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours;
//Remove all old event listeners from Cancel button
var newCancelButton = clearEventListeners("work-in-progress-cancel-button");
newCancelButton.innerHTML = "Cancel Work";
newCancelButton.addEventListener("click", function() {
Player.finishWork(true);
return false;
});
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
}
PlayerObject.prototype.work = function(numCycles) {
this.workRepGainRate = this.getWorkRepGain();
this.workHackExpGained += this.workHackExpGainRate * numCycles;
this.workStrExpGained += this.workStrExpGainRate * numCycles;
this.workDefExpGained += this.workDefExpGainRate * numCycles;
this.workDexExpGained += this.workDexExpGainRate * numCycles;
this.workAgiExpGained += this.workAgiExpGainRate * numCycles;
this.workChaExpGained += this.workChaExpGainRate * numCycles;
this.workRepGained += this.workRepGainRate * numCycles;
this.workMoneyGained += this.workMoneyGainRate * numCycles;
var cyclesPerSec = 1000 / Engine._idleSpeed;
this.timeWorked += Engine._idleSpeed * numCycles;
//If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
if (this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
var maxCycles = CONSTANTS.GameCyclesPer8Hours;
this.workHackExpGained = this.workHackExpGainRate * maxCycles;
this.workStrExpGained = this.workStrExpGainRate * maxCycles;
this.workDefExpGained = this.workDefExpGainRate * maxCycles;
this.workDexExpGained = this.workDexExpGainRate * maxCycles;
this.workAgiExpGained = this.workAgiExpGainRate * maxCycles;
this.workChaExpGained = this.workChaExpGainRate * maxCycles;
this.workRepGained = this.workRepGainRate * maxCycles;
this.workMoneyGained = this.workMoneyGainRate * maxCycles;
this.finishWork(false);
return;
}
var comp = Companies[this.companyName], companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.log("ERROR: Could not find Company: " + this.companyName);
} else {
companyRep = comp.playerReputation;
}
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName +
" at " + this.companyName + " (Current Company Reputation: " +
formatNumber(companyRep, 0) + ")<br><br>" +
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * cyclesPerSec, 4) + " / sec) reputation for this company <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * cyclesPerSec, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * cyclesPerSec, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma exp <br><br>" +
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish, " +
"but you will only gain half of the reputation you've earned so far."
}
PlayerObject.prototype.startWorkPartTime = function() {
this.resetWorkStatus();
this.isWorking = true;
@ -886,57 +815,50 @@ PlayerObject.prototype.startWorkPartTime = function() {
}
PlayerObject.prototype.workPartTime = function(numCycles) {
this.workRepGainRate = this.getWorkRepGain();
this.workHackExpGained += this.workHackExpGainRate * numCycles;
this.workStrExpGained += this.workStrExpGainRate * numCycles;
this.workDefExpGained += this.workDefExpGainRate * numCycles;
this.workDexExpGained += this.workDexExpGainRate * numCycles;
this.workAgiExpGained += this.workAgiExpGainRate * numCycles;
this.workChaExpGained += this.workChaExpGainRate * numCycles;
this.workRepGained += this.workRepGainRate * numCycles;
this.workMoneyGained += this.workMoneyGainRate * numCycles;
var cyclesPerSec = 1000 / Engine._idleSpeed;
//Cap the number of cycles being processed to whatever would put you at the
//work time limit (8 hours)
var overMax = false;
if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / Engine._idleSpeed);
}
this.timeWorked += Engine._idleSpeed * numCycles;
this.workRepGainRate = this.getWorkRepGain();
this.processWorkEarnings(numCycles);
//If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
if (this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
var maxCycles = CONSTANTS.GameCyclesPer8Hours;
this.workHackExpGained = this.workHackExpGainRate * maxCycles;
this.workStrExpGained = this.workStrExpGainRate * maxCycles;
this.workDefExpGained = this.workDefExpGainRate * maxCycles;
this.workDexExpGained = this.workDexExpGainRate * maxCycles;
this.workAgiExpGained = this.workAgiExpGainRate * maxCycles;
this.workChaExpGained = this.workChaExpGainRate * maxCycles;
this.workRepGained = this.workRepGainRate * maxCycles;
this.workMoneyGained = this.workMoneyGainRate * maxCycles;
this.finishWorkPartTime();
return;
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
return this.finishWorkPartTime();
}
var comp = Companies[this.companyName], companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.log("ERROR: Could not find Company: " + this.companyName);
} else {
companyRep = comp.playerReputation;
}
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName +
" at " + Player.companyName + "<br><br>" +
" at " + Player.companyName + " (Current Company Reputation: " +
formatNumber(companyRep, 0) + ")<br><br>" +
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * cyclesPerSec, 4) + " / sec) reputation for this company <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * cyclesPerSec, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * cyclesPerSec, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma exp <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * CYCLES_PER_SEC, 4) + " / sec) reputation for this company <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * CYCLES_PER_SEC, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * CYCLES_PER_SEC, 4) + " / sec) charisma exp <br><br>" +
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish, <br>" +
"and there will be no penalty because this is a part-time job.";
}
PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
this.gainWorkExp();
var company = Companies[this.companyName];
company.playerReputation += (this.workRepGained);
@ -978,51 +900,6 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
}
/* Working for Faction */
PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
this.gainWorkExp();
var faction = Factions[this.currentWorkFactionName];
faction.playerReputation += (this.workRepGained);
this.gainMoney(this.workMoneyGained);
this.updateSkillLevels();
var txt = "You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " <br><br> " +
"You earned a total of: <br>" +
"$" + formatNumber(this.workMoneyGained, 2) + "<br>" +
formatNumber(this.workRepGained, 4) + " reputation for the faction <br>" +
formatNumber(this.workHackExpGained, 4) + " hacking exp <br>" +
formatNumber(this.workStrExpGained, 4) + " strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " agility exp <br>" +
formatNumber(this.workChaExpGained, 4) + " charisma exp<br>";
if (!sing) {dialogBoxCreate(txt);}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadFactionContent();
displayFactionContent(faction.name);
if (sing) {
var res="You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " +
"You earned " +
formatNumber(this.workRepGained, 4) + " rep, " +
formatNumber(this.workHackExpGained, 4) + " hacking exp, " +
formatNumber(this.workStrExpGained, 4) + " str exp, " +
formatNumber(this.workDefExpGained, 4) + " def exp, " +
formatNumber(this.workDexExpGained, 4) + " dex exp, " +
formatNumber(this.workAgiExpGained, 4) + " agi exp, and " +
formatNumber(this.workChaExpGained, 4) + " cha exp.";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
}
PlayerObject.prototype.startFactionWork = function(faction) {
//Update reputation gain rate to account for faction favor
var favorMult = 1 + (faction.favor / 100);
@ -1117,52 +994,81 @@ PlayerObject.prototype.workForFaction = function(numCycles) {
this.workRepGainRate *= favorMult;
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
this.workHackExpGained += this.workHackExpGainRate * numCycles;
this.workStrExpGained += this.workStrExpGainRate * numCycles;
this.workDefExpGained += this.workDefExpGainRate * numCycles;
this.workDexExpGained += this.workDexExpGainRate * numCycles;
this.workAgiExpGained += this.workAgiExpGainRate * numCycles;
this.workChaExpGained += this.workChaExpGainRate * numCycles;
this.workRepGained += this.workRepGainRate * numCycles;
this.workMoneyGained += this.workMoneyGainRate * numCycles;
var cyclesPerSec = 1000 / Engine._idleSpeed;
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
var overMax = false;
if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer20Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / Engine._idleSpeed);
}
this.timeWorked += Engine._idleSpeed * numCycles;
this.processWorkEarnings(numCycles);
//If timeWorked == 20 hours, then finish. You can only work for the faction for 20 hours
if (this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
var maxCycles = CONSTANTS.GameCyclesPer20Hours;
this.timeWorked = CONSTANTS.MillisecondsPer20Hours;
this.workHackExpGained = this.workHackExpGainRate * maxCycles;
this.workStrExpGained = this.workStrExpGainRate * maxCycles;
this.workDefExpGained = this.workDefExpGainRate * maxCycles;
this.workDexExpGained = this.workDexExpGainRate * maxCycles;
this.workAgiExpGained = this.workAgiExpGainRate * maxCycles;
this.workChaExpGained = this.workChaExpGainRate * maxCycles;
this.workRepGained = this.workRepGainRate * maxCycles;
this.workMoneyGained = this.workMoneyGainRate * maxCycles;
this.finishFactionWork(false);
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
return this.finishWork(false);
}
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently " + this.currentWorkFactionDescription + " for your faction " + faction.name +
" (Current Faction Reputation: " + formatNumber(faction.playerReputation, 0) + "). " +
" (Current Faction Reputation: " + formatNumber(faction.playerReputation, 0) + "). <br>" +
"You have been doing this for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " (" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * cyclesPerSec, 4) + " / sec) reputation for this faction <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * cyclesPerSec, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * cyclesPerSec, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma exp <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " (" + formatNumber(this.workMoneyGainRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * CYCLES_PER_SEC, 4) + " / sec) reputation for this faction <br><br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * CYCLES_PER_SEC, 4) + " / sec) hacking exp <br><br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * CYCLES_PER_SEC, 4) + " / sec) charisma exp <br><br>" +
"You will automatically finish after working for 20 hours. You can cancel earlier if you wish.<br>" +
"There is no penalty for cancelling earlier.";
}
PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
var faction = Factions[this.currentWorkFactionName];
faction.playerReputation += (this.workRepGained);
this.gainMoney(this.workMoneyGained);
this.updateSkillLevels();
var txt = "You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " <br><br> " +
"You earned a total of: <br>" +
"$" + formatNumber(this.workMoneyGained, 2) + "<br>" +
formatNumber(this.workRepGained, 4) + " reputation for the faction <br>" +
formatNumber(this.workHackExpGained, 4) + " hacking exp <br>" +
formatNumber(this.workStrExpGained, 4) + " strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " agility exp <br>" +
formatNumber(this.workChaExpGained, 4) + " charisma exp<br>";
if (!sing) {dialogBoxCreate(txt);}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadFactionContent();
displayFactionContent(faction.name);
if (sing) {
var res="You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " +
"You earned " +
formatNumber(this.workRepGained, 4) + " rep, " +
formatNumber(this.workHackExpGained, 4) + " hacking exp, " +
formatNumber(this.workStrExpGained, 4) + " str exp, " +
formatNumber(this.workDefExpGained, 4) + " def exp, " +
formatNumber(this.workDexExpGained, 4) + " dex exp, " +
formatNumber(this.workAgiExpGained, 4) + " agi exp, and " +
formatNumber(this.workChaExpGained, 4) + " cha exp.";
this.resetWorkStatus();
return res;
}
this.resetWorkStatus();
}
//Money gained per game cycle
PlayerObject.prototype.getWorkMoneyGain = function() {
@ -1438,36 +1344,25 @@ PlayerObject.prototype.takeClass = function(numCycles) {
this.timeWorked += Engine._idleSpeed * numCycles;
var className = this.className;
this.workHackExpGained += this.workHackExpGainRate * numCycles;
this.workStrExpGained += this.workStrExpGainRate * numCycles;
this.workDefExpGained += this.workDefExpGainRate * numCycles;
this.workDexExpGained += this.workDexExpGainRate * numCycles;
this.workAgiExpGained += this.workAgiExpGainRate * numCycles;
this.workChaExpGained += this.workChaExpGainRate * numCycles;
this.workRepGained += this.workRepGainRate * numCycles;
this.workMoneyGained += this.workMoneyGainRate * numCycles;
this.workMoneyGained -= this.workMoneyLossRate * numCycles;
var cyclesPerSec = 1000 / Engine._idleSpeed;
this.processWorkEarnings(numCycles);
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You have been " + className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"This has cost you: <br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyLossRate * cyclesPerSec, 2) + " / sec) <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyLossRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
"You have gained: <br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * cyclesPerSec, 4) + " / sec) hacking exp <br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * cyclesPerSec, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br>" +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma exp <br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * CYCLES_PER_SEC, 4) + " / sec) hacking exp <br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br>" +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * CYCLES_PER_SEC, 4) + " / sec) charisma exp <br>" +
"You may cancel at any time";
}
//The 'sing' argument defines whether or not this function was called
//through a Singularity Netscript function
PlayerObject.prototype.finishClass = function(sing=false) {
this.gainWorkExp();
this.gainIntelligenceExp(CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.timeWorked / 1000));
if (this.workMoneyGained > 0) {
@ -1592,7 +1487,7 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
if (this.committingCrimeThruSingFn) {
if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime successful! Gained " +
numeral(this.workMoneyGained).format("$0.000a") + ", " +
numeralWrapper.format(this.workMoneyGained, "$0.000a") + ", " +
formatNumber(this.workHackExpGained, 3) + " hack exp, " +
formatNumber(this.workStrExpGained, 3) + " str exp, " +
formatNumber(this.workDefExpGained, 3) + " def exp, " +
@ -1642,7 +1537,12 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
}
}
this.gainWorkExp();
this.gainHackingExp(this.workHackExpGained);
this.gainStrengthExp(this.workStrExpGained);
this.gainDefenseExp(this.workDefExpGained);
this.gainDexterityExp(this.workDexExpGained);
this.gainAgilityExp(this.workAgiExpGained);
this.gainCharismaExp(this.workChaExpGained);
}
this.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null;
@ -1701,7 +1601,7 @@ PlayerObject.prototype.hospitalize = function() {
dialogBoxCreate(
"You were in critical condition! You were taken to the hospital where " +
"luckily they were able to save your life. You were charged " +
numeral(this.max_hp * CONSTANTS.HospitalCostPerHp).format('$0.000a')
numeralWrapper.format(this.max_hp * CONSTANTS.HospitalCostPerHp, '$0.000a')
);
}
@ -2049,7 +1949,7 @@ PlayerObject.prototype.reapplyAllSourceFiles = function() {
//those requirements and will return an array of all factions that the Player should
//receive an invitation to
PlayerObject.prototype.checkForFactionInvitations = function() {
let invitedFactions = []; //Array which will hold all Factions th eplayer should be invited to
let invitedFactions = []; //Array which will hold all Factions the player should be invited to
var numAugmentations = this.augmentations.length;

@ -451,7 +451,7 @@ function loadImportedGame(saveObj, saveString) {
var popupId = "import-game-restart-game-notice";
var txt = createElement("p", {
innerText:"Imported game! I would suggest saving the game and then reloading the page " +
innerText:"Imported game! You need to SAVE the game and then RELOAD the page " +
"to make sure everything runs smoothly"
});
var gotitBtn = createElement("a", {
@ -516,9 +516,9 @@ function loadImportedGame(saveObj, saveString) {
Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop
dialogBoxCreate("While you were offline, your scripts generated $" +
formatNumber(offlineProductionFromScripts, 2) + " and your Hacknet Nodes generated $" +
formatNumber(offlineProductionFromHacknetNodes, 2));
dialogBoxCreate("While you were offline, your scripts generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromScripts, 2) + "</span> and your Hacknet Nodes generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromHacknetNodes, 2) + "</span>");
return true;
}

32
src/Script.js Normal file → Executable file

@ -21,7 +21,7 @@ import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings} from "./Fconf";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript,
@ -31,15 +31,14 @@ import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings";
import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {compareArrays} from "../utils/helpers/compareArrays";
import {createElement} from "../utils/uiHelpers/createElement";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo";
@ -252,7 +251,7 @@ function updateScriptEditorContent() {
var codeCopy = code.repeat(1);
var ramUsage = calculateRamUsage(codeCopy);
if (ramUsage !== -1) {
scriptEditorRamText.innerText = "RAM: " + formatNumber(ramUsage, 2).toString() + "GB";
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB";
} else {
scriptEditorRamText.innerText = "RAM: Syntax Error";
}
@ -281,8 +280,9 @@ function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
if (iTutorialIsRunning && currITutorialStep == iTutorialSteps.TerminalTypeScript) {
if (filename != "foodnstuff.script") {
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "foodnstuff.script") {
dialogBoxCreate("Leave the script name as 'foodnstuff'!");
return;
}
@ -291,7 +291,23 @@ function saveAndCloseScriptEditor() {
dialogBoxCreate("Please copy and paste the code from the tutorial!");
return;
}
iTutorialNextStep();
//Save the script
let s = Player.getCurrentServer();
for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript();
Engine.loadTerminalContent();
return iTutorialNextStep();
}
}
//If the current script does NOT exist, create a new one
let script = new Script();
script.saveScript();
s.scripts.push(script);
return iTutorialNextStep();
}
if (filename == "") {
@ -858,7 +874,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
console.log(runningScriptObj.filename + " called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
runningScriptObj.log("Called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
var growth = processSingleServerGrowth(serv, timesGrown * 450);
runningScriptObj.log(serv.hostname + " grown by " + formatNumber(growth * 100 - 100, 6) + "% from grow() calls made while offline");
runningScriptObj.log(serv.hostname + " grown by " + numeralWrapper.format(growth * 100 - 100, '0.000000%') + " from grow() calls made while offline");
}
}

@ -2,10 +2,10 @@ import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram";
import {Player} from "./Player";
import {RunningScript, Script} from "./Script";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
import {SpecialServerIps} from "./SpecialServerIps";
import {getRandomInt} from "../utils/helpers/getRandomInt";
import {createRandomIp, ipExists} from "../utils/IPAddress";
import {serverMetadata} from "./data/servers";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
@ -125,652 +125,83 @@ Server.fromJSON = function(value) {
Reviver.constructors.Server = Server;
function initForeignServers() {
//MegaCorporations
var ECorpServer = new Server({
ip:createRandomIp(), hostname:"ecorp", organizationName:"ECorp",
requiredHackingSkill:getRandomInt(1150, 1300), moneyAvailable:getRandomInt(30e9, 70e9),
hackDifficulty:99,serverGrowth:99, numOpenPortsRequired: 5,
});
AddToAllServers(ECorpServer);
var MegaCorpServer = new Server({
ip:createRandomIp(), hostname:"megacorp", organizationName:"MegaCorp",
requiredHackingSkill:getRandomInt(1150, 1300), moneyAvailable:getRandomInt(40e9, 60e9),
hackDifficulty:99, serverGrowth:99, numOpenPortsRequired:5
});
AddToAllServers(MegaCorpServer);
var BachmanAndAssociatesServer = new Server({
ip:createRandomIp(), hostname:"b-and-a", organizationName:"Bachman & Associates",
requiredHackingSkill:getRandomInt(1000, 1050), moneyAvailable:getRandomInt(20e9, 25e9),
hackDifficulty:getRandomInt(75, 85), serverGrowth:getRandomInt(65, 75), numOpenPortsRequired:5
});
AddToAllServers(BachmanAndAssociatesServer);
var BladeIndustriesServer = new Server({
ip:createRandomIp(), hostname:"blade", organizationName:"Blade Industries", maxRam:128,
requiredHackingSkill:getRandomInt(1000, 1100), moneyAvailable:getRandomInt(12e9, 20e9),
hackDifficulty:getRandomInt(90, 95), serverGrowth:getRandomInt(60, 75), numOpenPortsRequired:5
});
BladeIndustriesServer.messages.push("beyond-man.lit");
AddToAllServers(BladeIndustriesServer);
var NWOServer = new Server({
ip:createRandomIp(), hostname:"nwo", organizationName:"New World Order",
requiredHackingSkill:getRandomInt(1000, 1200), moneyAvailable:getRandomInt(25e9, 35e9),
hackDifficulty:99, serverGrowth:getRandomInt(75, 85), numOpenPortsRequired:5
});
NWOServer.messages.push("the-hidden-world.lit");
AddToAllServers(NWOServer);
var ClarkeIncorporatedServer = new Server({
ip:createRandomIp(), hostname:"clarkeinc", organizationName:"Clarke Incorporated",
requiredHackingSkill:getRandomInt(1000, 1200), moneyAvailable:getRandomInt(15e9, 25e9),
hackDifficulty:getRandomInt(50, 60), serverGrowth:getRandomInt(50, 70), numOpenPortsRequired:5
});
ClarkeIncorporatedServer.messages.push("beyond-man.lit");
ClarkeIncorporatedServer.messages.push("cost-of-immortality.lit");
AddToAllServers(ClarkeIncorporatedServer);
var OmniTekIncorporatedServer = new Server({
ip:createRandomIp(), hostname:"omnitek", organizationName:"OmniTek Incorporated", maxRam:256,
requiredHackingSkill:getRandomInt(900, 1100), moneyAvailable:getRandomInt(15e9, 20e9),
hackDifficulty:getRandomInt(90, 99), serverGrowth:getRandomInt(95, 99), numOpenPortsRequired:5
});
OmniTekIncorporatedServer.messages.push("coded-intelligence.lit");
OmniTekIncorporatedServer.messages.push("history-of-synthoids.lit");
AddToAllServers(OmniTekIncorporatedServer);
var FourSigmaServer = new Server({
ip:createRandomIp(), hostname:"4sigma", organizationName:"FourSigma",
requiredHackingSkill:getRandomInt(950, 1200), moneyAvailable:getRandomInt(15e9, 25e9),
hackDifficulty:getRandomInt(60, 70), serverGrowth:getRandomInt(75, 99), numOpenPortsRequired:5
});
AddToAllServers(FourSigmaServer);
var KuaiGongInternationalServer = new Server({
ip:createRandomIp(), hostname:"kuai-gong", organizationName:"KuaiGong International",
requiredHackingSkill:getRandomInt(1000, 1250), moneyAvailable:getRandomInt(20e9, 30e9),
hackDifficulty:getRandomInt(95, 99), serverGrowth:getRandomInt(90, 99), numOpenPortsRequired:5,
});
AddToAllServers(KuaiGongInternationalServer);
//Technology and communications companies (large targets)
var FulcrumTechnologiesServer = new Server({
ip:createRandomIp(), hostname:"fulcrumtech", organizationName:"Fulcrum Technologies", maxRam:512,
requiredHackingSkill:getRandomInt(1000, 1200), moneyAvailable:getRandomInt(1.4e9, 1.8e9),
hackDifficulty:getRandomInt(85, 95), serverGrowth:getRandomInt(80, 99), numOpenPortsRequired:5
});
FulcrumTechnologiesServer.messages.push("simulated-reality.lit");
AddToAllServers(FulcrumTechnologiesServer);
var FulcrumSecretTechnologiesServer = new Server({
ip:createRandomIp(), hostname:"fulcrumassets", organizationName:"Fulcrum Technologies Assets",
requiredHackingSkill:getRandomInt(1200, 1500), moneyAvailable:1e6,
hackDifficulty:99, serverGrowth:1, numOpenPortsRequired:5
});
AddToAllServers(FulcrumSecretTechnologiesServer);
SpecialServerIps.addIp(SpecialServerNames.FulcrumSecretTechnologies, FulcrumSecretTechnologiesServer.ip);
var StormTechnologiesServer = new Server({
ip:createRandomIp(), hostname:"stormtech", organizationName:"Storm Technologies",
requiredHackingSkill:getRandomInt(900, 1050), moneyAvailable:getRandomInt(1e9, 1.2e9),
hackDifficulty:getRandomInt(80, 90), serverGrowth:getRandomInt(70, 90), numOpenPortsRequired:5
});
AddToAllServers(StormTechnologiesServer);
var DefCommServer = new Server({
ip:createRandomIp(), hostname:"defcomm", organizationName:"DefComm",
requiredHackingSkill:getRandomInt(900, 1000), moneyAvailable:getRandomInt(800e6, 950e6),
hackDifficulty:getRandomInt(85, 95), serverGrowth:getRandomInt(50, 70), numOpenPortsRequired:5
});
AddToAllServers(DefCommServer);
var InfoCommServer = new Server({
ip:createRandomIp(), hostname:"infocomm", organizationName:"InfoComm",
requiredHackingSkill:getRandomInt(875, 950), moneyAvailable:getRandomInt(600e6, 900e6),
hackDifficulty:getRandomInt(70, 90), serverGrowth:getRandomInt(35, 75), numOpenPortsRequired:5
});
AddToAllServers(InfoCommServer);
var HeliosLabsServer = new Server({
ip:createRandomIp(), hostname:"helios", organizationName:"Helios Labs", maxRam:128,
requiredHackingSkill:getRandomInt(800, 900), moneyAvailable:getRandomInt(550e6, 750e6),
hackDifficulty:getRandomInt(85, 95), serverGrowth:getRandomInt(70, 80), numOpenPortsRequired:5
});
HeliosLabsServer.messages.push("beyond-man.lit");
AddToAllServers(HeliosLabsServer);
var VitaLifeServer = new Server({
ip:createRandomIp(), hostname:"vitalife", organizationName:"VitaLife", maxRam:64,
requiredHackingSkill:getRandomInt(775, 900), moneyAvailable:getRandomInt(700e6, 800e6),
hackDifficulty:getRandomInt(80, 90), serverGrowth:getRandomInt(60, 80), numOpenPortsRequired:5
});
VitaLifeServer.messages.push("A-Green-Tomorrow.lit");
AddToAllServers(VitaLifeServer);
var IcarusMicrosystemsServer = new Server({
ip:createRandomIp(), hostname:"icarus", organizationName:"Icarus Microsystems",
requiredHackingSkill:getRandomInt(850, 925), moneyAvailable:getRandomInt(900e6, 1000e6),
hackDifficulty:getRandomInt(85, 95), serverGrowth:getRandomInt(85, 95), numOpenPortsRequired:5
});
AddToAllServers(IcarusMicrosystemsServer);
var UniversalEnergyServer = new Server({
ip:createRandomIp(), hostname:"univ-energy", organizationName:"Universal Energy", maxRam:64,
requiredHackingSkill:getRandomInt(800, 900), moneyAvailable:getRandomInt(1.1e9, 1.2e9),
hackDifficulty:getRandomInt(80, 90), serverGrowth:getRandomInt(80, 90), numOpenPortsRequired:4
});
AddToAllServers(UniversalEnergyServer);
var TitanLabsServer = new Server({
ip:createRandomIp(), hostname:"titan-labs", organizationName:"Titan Laboratories", maxRam:64,
requiredHackingSkill:getRandomInt(800, 875), moneyAvailable:getRandomInt(750e6, 900e6),
hackDifficulty:getRandomInt(70, 80), serverGrowth:getRandomInt(60, 80), numOpenPortsRequired:5
});
TitanLabsServer.messages.push("coded-intelligence.lit");
AddToAllServers(TitanLabsServer);
var MicrodyneTechnologiesServer = new Server({
ip:createRandomIp(), hostname:"microdyne", organizationName:"Microdyne Technologies", maxRam:32,
requiredHackingSkill:getRandomInt(800, 875), moneyAvailable:getRandomInt(500e6, 700e6),
hackDifficulty:getRandomInt(65, 75), serverGrowth:getRandomInt(70, 90), numOpenPortsRequired:5
});
MicrodyneTechnologiesServer.messages.push("synthetic-muscles.lit");
AddToAllServers(MicrodyneTechnologiesServer);
var TaiYangDigitalServer = new Server({
ip:createRandomIp(), hostname:"taiyang-digital", organizationName:"Taiyang Digital",
requiredHackingSkill:getRandomInt(850, 950), moneyAvailable:getRandomInt(800e6, 900e6),
hackDifficulty:getRandomInt(70, 80), serverGrowth:getRandomInt(70, 80), numOpenPortsRequired:5
});
TaiYangDigitalServer.messages.push("A-Green-Tomorrow.lit");
TaiYangDigitalServer.messages.push("brighter-than-the-sun.lit");
AddToAllServers(TaiYangDigitalServer);
var GalacticCyberSystemsServer = new Server({
ip:createRandomIp(), hostname:"galactic-cyber", organizationName:"Galactic Cybersystems",
requiredHackingSkill:getRandomInt(825, 875), moneyAvailable:getRandomInt(750e6, 850e6),
hackDifficulty:getRandomInt(55, 65), serverGrowth:getRandomInt(70, 90), numOpenPortsRequired:5
});
AddToAllServers(GalacticCyberSystemsServer);
//Defense Companies ("Large" Companies)
var AeroCorpServer = new Server({
ip:createRandomIp(), hostname:"aerocorp", organizationName:"AeroCorp",
requiredHackingSkill:getRandomInt(850, 925), moneyAvailable:getRandomInt(1e9, 1.2e9),
hackDifficulty:getRandomInt(80, 90), serverGrowth:getRandomInt(55, 65), numOpenPortsRequired:5
});
AeroCorpServer.messages.push("man-and-machine.lit");
AddToAllServers(AeroCorpServer);
var OmniaCybersystemsServer = new Server({
ip:createRandomIp(), hostname:"omnia", organizationName:"Omnia Cybersystems", maxRam:64,
requiredHackingSkill:getRandomInt(850, 950), moneyAvailable:getRandomInt(900e6, 1e9),
hackDifficulty:getRandomInt(85, 95), serverGrowth:getRandomInt(60, 70), numOpenPortsRequired:5
});
OmniaCybersystemsServer.messages.push("history-of-synthoids.lit");
AddToAllServers(OmniaCybersystemsServer);
var ZBDefenseServer = new Server({
ip:createRandomIp(), hostname:"zb-def", organizationName:"ZB Defense Industries",
requiredHackingSkill:getRandomInt(775, 825), moneyAvailable:getRandomInt(900e6, 1.1e9),
hackDifficulty:getRandomInt(55, 65), serverGrowth:getRandomInt(65, 75), numOpenPortsRequired:4
});
ZBDefenseServer.messages.push("synthetic-muscles.lit");
AddToAllServers(ZBDefenseServer);
var AppliedEnergeticsServer = new Server({
ip:createRandomIp(), hostname:"applied-energetics", organizationName:"Applied Energetics",
requiredHackingSkill:getRandomInt(775, 850), moneyAvailable:getRandomInt(700e6, 1e9),
hackDifficulty:getRandomInt(60, 80), serverGrowth:getRandomInt(70, 75), numOpenPortsRequired:4
});
AddToAllServers(AppliedEnergeticsServer);
var SolarisSpaceSystemsServer = new Server({
ip:createRandomIp(), hostname:"solaris", organizationName:"Solaris Space Systems", maxRam:64,
requiredHackingSkill:getRandomInt(750, 850), moneyAvailable:getRandomInt(700e6, 900e6),
hackDifficulty:getRandomInt(70, 80), serverGrowth:getRandomInt(70, 80), numOpenPortsRequired:5
});
SolarisSpaceSystemsServer.messages.push("A-Green-Tomorrow.lit");
SolarisSpaceSystemsServer.messages.push("the-failed-frontier.lit");
AddToAllServers(SolarisSpaceSystemsServer);
var DeltaOneServer = new Server({
ip:createRandomIp(), hostname:"deltaone", organizationName:"Delta One",
requiredHackingSkill:getRandomInt(800, 900), moneyAvailable:getRandomInt(1.3e9, 1.7e9),
hackDifficulty:getRandomInt(75, 85), serverGrowth:getRandomInt(50, 70), numOpenPortsRequired:5
});
AddToAllServers(DeltaOneServer);
//Health, medicine, pharmaceutical companies ("Large" targets)
var GlobalPharmaceuticalsServer = new Server({
ip:createRandomIp(), hostname:"global-pharm", organizationName:"Global Pharmaceuticals", maxRam:32,
requiredHackingSkill:getRandomInt(750, 850), moneyAvailable:getRandomInt(1.5e9, 1.75e9),
hackDifficulty:getRandomInt(75, 85), serverGrowth:getRandomInt(80, 90), numOpenPortsRequired:4
});
GlobalPharmaceuticalsServer.messages.push("A-Green-Tomorrow.lit");
AddToAllServers(GlobalPharmaceuticalsServer);
var NovaMedicalServer = new Server({
ip:createRandomIp(), hostname:"nova-med", organizationName:"Nova Medical",
requiredHackingSkill:getRandomInt(775, 850), moneyAvailable:getRandomInt(1.1e9, 1.25e9),
hackDifficulty:getRandomInt(60, 80), serverGrowth:getRandomInt(65, 85), numOpenPortsRequired:4
});
AddToAllServers(NovaMedicalServer);
var ZeusMedicalServer = new Server({
ip:createRandomIp(), hostname:"zeus-med", organizationName:"Zeus Medical",
requiredHackingSkill:getRandomInt(800, 850), moneyAvailable:getRandomInt(1.3e9, 1.5e9),
hackDifficulty:getRandomInt(70, 90), serverGrowth:getRandomInt(70, 80), numOpenPortsRequired:5
});
AddToAllServers(ZeusMedicalServer);
var UnitaLifeGroupServer = new Server({
ip:createRandomIp(), hostname:"unitalife", organizationName:"UnitaLife Group", maxRam:32,
requiredHackingSkill:getRandomInt(775, 825), moneyAvailable:getRandomInt(1e9, 1.1e9),
hackDifficulty:getRandomInt(70, 80), serverGrowth:getRandomInt(70, 80), numOpenPortsRequired:4
});
AddToAllServers(UnitaLifeGroupServer);
//"Medium level" targets
var LexoCorpServer = new Server({
ip:createRandomIp(), hostname:"lexo-corp", organizationName:"Lexo Corporation", maxRam:32,
requiredHackingSkill:getRandomInt(650, 750), moneyAvailable:getRandomInt(700e6, 800e6),
hackDifficulty:getRandomInt(60, 80), serverGrowth:getRandomInt(55, 65), numOpenPortsRequired:4
});
AddToAllServers(LexoCorpServer);
var RhoConstructionServer = new Server({
ip:createRandomIp(), hostname:"rho-construction", organizationName:"Rho Construction",
requiredHackingSkill:getRandomInt(475, 525), moneyAvailable:getRandomInt(500e6, 700e6),
hackDifficulty:getRandomInt(40, 60), serverGrowth:getRandomInt(40, 60), numOpenPortsRequired:3
});
AddToAllServers(RhoConstructionServer);
var AlphaEnterprisesServer = new Server({
ip:createRandomIp(), hostname:"alpha-ent", organizationName:"Alpha Enterprises", maxRam:32,
requiredHackingSkill:getRandomInt(500, 600), moneyAvailable:getRandomInt(600e6, 750e6),
hackDifficulty:getRandomInt(50, 70), serverGrowth:getRandomInt(50, 60),numOpenPortsRequired:4
});
AlphaEnterprisesServer.messages.push("sector-12-crime.lit");
AddToAllServers(AlphaEnterprisesServer);
var AevumPoliceServer = new Server({
ip:createRandomIp(), hostname:"aevum-police", organizationName:"Aevum Police Network", maxRam:32,
requiredHackingSkill:getRandomInt(400, 450), moneyAvailable:getRandomInt(200e6, 400e6),
hackDifficulty:getRandomInt(70, 80), serverGrowth:getRandomInt(30, 50), numOpenPortsRequired:4
});
AddToAllServers(AevumPoliceServer);
var RothmanUniversityServer = new Server({
ip:createRandomIp(), hostname:"rothman-uni", organizationName:"Rothman University Network", maxRam:64,
requiredHackingSkill:getRandomInt(370, 430), moneyAvailable:getRandomInt(175e6, 250e6),
hackDifficulty:getRandomInt(45, 55), serverGrowth:getRandomInt(35, 45), numOpenPortsRequired:3
});
RothmanUniversityServer.messages.push("secret-societies.lit");
RothmanUniversityServer.messages.push("the-failed-frontier.lit");
RothmanUniversityServer.messages.push("tensions-in-tech-race.lit");
AddToAllServers(RothmanUniversityServer);
var ZBInstituteOfTechnologyServer = new Server({
ip:createRandomIp(), hostname:"zb-institute", organizationName:"ZB Institute of Technology Network", maxRam:64,
requiredHackingSkill:getRandomInt(725, 775), moneyAvailable:getRandomInt(800e6, 1.1e9),
hackDifficulty:getRandomInt(65, 85), serverGrowth:getRandomInt(75, 85), numOpenPortsRequired:5
});
AddToAllServers(ZBInstituteOfTechnologyServer);
var SummitUniversityServer = new Server({
ip:createRandomIp(), hostname:"summit-uni", organizationName:"Summit University Network", maxRam:32,
requiredHackingSkill:getRandomInt(425, 475), moneyAvailable:getRandomInt(200e6, 350e6),
hackDifficulty:getRandomInt(45, 65), serverGrowth:getRandomInt(40, 60), numOpenPortsRequired:3
});
SummitUniversityServer.messages.push("secret-societies.lit");
SummitUniversityServer.messages.push("the-failed-frontier.lit");
SummitUniversityServer.messages.push("synthetic-muscles.lit");
AddToAllServers(SummitUniversityServer);
var SysCoreSecuritiesServer = new Server({
ip:createRandomIp(), hostname:"syscore", organizationName:"SysCore Securities",
requiredHackingSkill:getRandomInt(550, 650), moneyAvailable:getRandomInt(400e6, 600e6),
hackDifficulty:getRandomInt(60, 80), serverGrowth:getRandomInt(60, 70), numOpenPortsRequired:4
});
AddToAllServers(SysCoreSecuritiesServer);
var CatalystVenturesServer = new Server({
ip:createRandomIp(), hostname:"catalyst", organizationName:"Catalyst Ventures",
requiredHackingSkill:getRandomInt(400, 450), moneyAvailable:getRandomInt(300e6, 550e6),
hackDifficulty:getRandomInt(60, 70), serverGrowth:getRandomInt(25, 55), numOpenPortsRequired:3,
});
CatalystVenturesServer.messages.push("tensions-in-tech-race.lit");
AddToAllServers(CatalystVenturesServer);
var TheHubServer = new Server({
ip:createRandomIp(), hostname:"the-hub", organizationName:"The Hub",
requiredHackingSkill:getRandomInt(275, 325), moneyAvailable:getRandomInt(150e6, 200e6),
hackDifficulty:getRandomInt(35, 45), serverGrowth:getRandomInt(45, 55), numOpenPortsRequired:2
});
AddToAllServers(TheHubServer);
var CompuTekServer = new Server({
ip:createRandomIp(), hostname:"comptek", organizationName:"CompuTek",
requiredHackingSkill:getRandomInt(300, 400), moneyAvailable:getRandomInt(220e6, 250e6),
hackDifficulty:getRandomInt(55, 65), serverGrowth:getRandomInt(45, 65), numOpenPortsRequired:3
});
CompuTekServer.messages.push("man-and-machine.lit");
AddToAllServers(CompuTekServer);
var NetLinkTechnologiesServer = new Server({
ip:createRandomIp(), hostname:"netlink", organizationName:"NetLink Technologies", maxRam:64,
requiredHackingSkill:getRandomInt(375, 425), moneyAvailable:275e6,
hackDifficulty:getRandomInt(60, 80), serverGrowth:getRandomInt(45, 75), numOpenPortsRequired:3
});
NetLinkTechnologiesServer.messages.push("simulated-reality.lit");
AddToAllServers(NetLinkTechnologiesServer);
var JohnsonOrthopedicsServer = new Server({
ip:createRandomIp(), hostname:"johnson-ortho", organizationName:"Johnson Orthopedics",
requiredHackingSkill:getRandomInt(250, 300), moneyAvailable:getRandomInt(70e6, 85e6),
hackDifficulty:getRandomInt(35, 65), serverGrowth:getRandomInt(35, 65), numOpenPortsRequired:2
});
AddToAllServers(JohnsonOrthopedicsServer);
//"Low level" targets
var FoodNStuffServer = new Server({
ip:createRandomIp(), hostname:"foodnstuff", organizationName:"Food N Stuff Supermarket", maxRam:16,
requiredHackingSkill:1, moneyAvailable:2e6,
hackDifficulty:10, serverGrowth:5, numOpenPortsRequired:0
});
FoodNStuffServer.messages.push("sector-12-crime.lit");
AddToAllServers(FoodNStuffServer);
var SigmaCosmeticsServer = new Server({
ip:createRandomIp(), hostname:"sigma-cosmetics", organizationName:"Sigma Cosmetics", maxRam:16,
requiredHackingSkill:5, moneyAvailable:2.3e6,
hackDifficulty:10, serverGrowth:10, numOpenPortsRequired:0
});
AddToAllServers(SigmaCosmeticsServer);
var JoesGunsServer = new Server({
ip:createRandomIp(), hostname:"joesguns", organizationName:"Joe's Guns", maxRam:16,
requiredHackingSkill:10, moneyAvailable:2.5e6,
hackDifficulty:15, serverGrowth:20, numOpenPortsRequired:0
});
AddToAllServers(JoesGunsServer);
var Zer0NightclubServer = new Server({
ip:createRandomIp(), hostname:"zer0", organizationName:"ZER0 Nightclub", maxRam:32,
requiredHackingSkill:75, moneyAvailable:7.5e6,
hackDifficulty:25, serverGrowth:40, numOpenPortsRequired:1
});
AddToAllServers(Zer0NightclubServer);
var NectarNightclubServer = new Server({
ip:createRandomIp(), hostname:"nectar-net", organizationName:"Nectar Nightclub Network", maxRam:16,
requiredHackingSkill:20, moneyAvailable:2.75e6,
hackDifficulty:20, serverGrowth:25, numOpenPortsRequired:0
});
AddToAllServers(NectarNightclubServer);
var NeoNightclubServer = new Server({
ip:createRandomIp(), hostname:"neo-net", organizationName:"Neo Nightclub Network", maxRam:32,
requiredHackingSkill:50, moneyAvailable:5e6,
hackDifficulty:25, serverGrowth:25, numOpenPortsRequired:1
});
NeoNightclubServer.messages.push("the-hidden-world.lit");
AddToAllServers(NeoNightclubServer);
var SilverHelixServer = new Server({
ip:createRandomIp(), hostname:"silver-helix", organizationName:"Silver Helix", maxRam:64,
requiredHackingSkill:150, moneyAvailable:45e6,
hackDifficulty:30, serverGrowth:30, numOpenPortsRequired:2
});
SilverHelixServer.messages.push("new-triads.lit");
AddToAllServers(SilverHelixServer);
var HongFangTeaHouseServer = new Server({
ip:createRandomIp(), hostname:"hong-fang-tea", organizationName:"HongFang Teahouse", maxRam:16,
requiredHackingSkill:30, moneyAvailable:3e6,
hackDifficulty:15, serverGrowth:20, numOpenPortsRequired:0
});
HongFangTeaHouseServer.messages.push("brighter-than-the-sun.lit");
AddToAllServers(HongFangTeaHouseServer);
var HaraKiriSushiBarServer = new Server({
ip:createRandomIp(), hostname:"harakiri-sushi", organizationName:"HaraKiri Sushi Bar Network",maxRam:16,
requiredHackingSkill:40, moneyAvailable:4e6,
hackDifficulty:15, serverGrowth:40, numOpenPortsRequired:0
});
AddToAllServers(HaraKiriSushiBarServer);
var PhantasyServer = new Server({
ip:createRandomIp(), hostname:"phantasy", organizationName:"Phantasy Club", maxRam:32,
requiredHackingSkill:100, moneyAvailable:24e6,
hackDifficulty:20, serverGrowth:35, numOpenPortsRequired:2
});
AddToAllServers(PhantasyServer);
var MaxHardwareServer = new Server({
ip:createRandomIp(), hostname:"max-hardware", organizationName:"Max Hardware Store", maxRam:32,
requiredHackingSkill:80, moneyAvailable:10e6,
hackDifficulty:15, serverGrowth:30, numOpenPortsRequired:1,
});
AddToAllServers(MaxHardwareServer);
var OmegaSoftwareServer = new Server({
ip:createRandomIp(), hostname:"omega-net", organizationName:"Omega Software", maxRam:32,
requiredHackingSkill:getRandomInt(180, 220), moneyAvailable:getRandomInt(60e6, 70e6),
hackDifficulty:getRandomInt(25, 35), serverGrowth:getRandomInt(30, 40), numOpenPortsRequired:2
});
OmegaSoftwareServer.messages.push("the-new-god.lit");
AddToAllServers(OmegaSoftwareServer);
//Gyms
var CrushFitnessGymServer = new Server({
ip:createRandomIp(), hostname:"crush-fitness", organizationName:"Crush Fitness",
requiredHackingSkill:getRandomInt(225, 275), moneyAvailable:getRandomInt(40e6, 60e6),
hackDifficulty:getRandomInt(35, 45), serverGrowth:getRandomInt(27, 33), numOpenPortsRequired:2
});
AddToAllServers(CrushFitnessGymServer);
var IronGymServer = new Server({
ip:createRandomIp(), hostname:"iron-gym", organizationName:"Iron Gym Network", maxRam:32,
requiredHackingSkill:100, moneyAvailable:20e6,
hackDifficulty:30, serverGrowth:20, numOpenPortsRequired:1
});
AddToAllServers(IronGymServer);
var MilleniumFitnessGymServer = new Server({
ip:createRandomIp(), hostname:"millenium-fitness", organizationName:"Millenium Fitness Network",
requiredHackingSkill:getRandomInt(475, 525), moneyAvailable:250e6,
hackDifficulty:getRandomInt(45, 55), serverGrowth:getRandomInt(25, 45), numOpenPortsRequired:3,
});
AddToAllServers(MilleniumFitnessGymServer);
var PowerhouseGymServer = new Server({
ip:createRandomIp(), hostname:"powerhouse-fitness", organizationName:"Powerhouse Fitness",
requiredHackingSkill:getRandomInt(950, 1100), moneyAvailable:900e6,
hackDifficulty:getRandomInt(55, 65), serverGrowth:getRandomInt(50, 60), numOpenPortsRequired:5,
});
AddToAllServers(PowerhouseGymServer);
var SnapFitnessGymServer = new Server({
ip:createRandomIp(), hostname:"snap-fitness", organizationName:"Snap Fitness",
requiredHackingSkill:getRandomInt(675, 800), moneyAvailable:450e6,
hackDifficulty:getRandomInt(40, 60), serverGrowth:getRandomInt(40, 60), numOpenPortsRequired:4
});
AddToAllServers(SnapFitnessGymServer);
//Faction servers, cannot hack money from these
var BitRunnersServer = new Server({
ip:createRandomIp(), hostname:"run4theh111z", organizationName:"The Runners", maxRam:128,
requiredHackingSkill:getRandomInt(505, 550), moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:4
});
BitRunnersServer.messages.push("simulated-reality.lit");
BitRunnersServer.messages.push("the-new-god.lit");
AddToAllServers(BitRunnersServer);
SpecialServerIps.addIp(SpecialServerNames.BitRunnersServer, BitRunnersServer.ip);
var TheBlackHandServer = new Server({
ip:createRandomIp(), hostname:"I.I.I.I", organizationName:"I.I.I.I", maxRam:64,
requiredHackingSkill:getRandomInt(340, 365), moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:3,
});
TheBlackHandServer.messages.push("democracy-is-dead.lit");
AddToAllServers(TheBlackHandServer);
SpecialServerIps.addIp(SpecialServerNames.TheBlackHandServer, TheBlackHandServer.ip);
var NiteSecServer = new Server({
ip:createRandomIp(), hostname:"avmnite-02h", organizationName:"NiteSec", maxRam:32,
requiredHackingSkill:getRandomInt(202, 220), moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:2
});
NiteSecServer.messages.push("democracy-is-dead.lit");
AddToAllServers(NiteSecServer);
SpecialServerIps.addIp(SpecialServerNames.NiteSecServer, NiteSecServer.ip);
var DarkArmyServer = new Server({
ip:createRandomIp(), hostname:".", organizationName:".", maxRam:16,
requiredHackingSkill:getRandomInt(505, 550), moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:4
});
AddToAllServers(DarkArmyServer);
SpecialServerIps.addIp(SpecialServerNames.TheDarkArmyServer, DarkArmyServer.ip);
var CyberSecServer = new Server({
ip:createRandomIp(), hostname:"CSEC", organizationName:"CyberSec", maxRam:8,
requiredHackingSkill:getRandomInt(51, 60), moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:1
});
CyberSecServer.messages.push("democracy-is-dead.lit");
AddToAllServers(CyberSecServer);
SpecialServerIps.addIp(SpecialServerNames.CyberSecServer, CyberSecServer.ip);
var DaedalusServer = new Server({
ip:createRandomIp(), hostname:"The-Cave", organizationName:"Helios",
requiredHackingSkill:925, moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:5
});
DaedalusServer.messages.push("alpha-omega.lit");
AddToAllServers(DaedalusServer);
SpecialServerIps.addIp(SpecialServerNames.DaedalusServer, DaedalusServer.ip);
//Super special Servers
var WorldDaemon = new Server({
ip:createRandomIp(), hostname:SpecialServerNames.WorldDaemon, organizationName:SpecialServerNames.WorldDaemon,
requiredHackingSkill:3000, moneyAvailable:0,
hackDifficulty:0, serverGrowth:0, numOpenPortsRequired:5
});
AddToAllServers(WorldDaemon);
SpecialServerIps.addIp(SpecialServerNames.WorldDaemon, WorldDaemon.ip);
/* Create a randomized network for all the foreign servers */
//Groupings for creating a randomized network
var NetworkGroup1 = [IronGymServer, FoodNStuffServer, SigmaCosmeticsServer, JoesGunsServer, HongFangTeaHouseServer, HaraKiriSushiBarServer];
var NetworkGroup2 = [MaxHardwareServer, NectarNightclubServer, Zer0NightclubServer, CyberSecServer];
var NetworkGroup3 = [OmegaSoftwareServer, PhantasyServer, SilverHelixServer, NeoNightclubServer];
var NetworkGroup4 = [CrushFitnessGymServer, NetLinkTechnologiesServer, CompuTekServer, TheHubServer, JohnsonOrthopedicsServer, NiteSecServer];
var NetworkGroup5 = [CatalystVenturesServer, SysCoreSecuritiesServer, SummitUniversityServer, ZBInstituteOfTechnologyServer, RothmanUniversityServer, TheBlackHandServer];
var NetworkGroup6 = [LexoCorpServer, RhoConstructionServer, AlphaEnterprisesServer, AevumPoliceServer, MilleniumFitnessGymServer];
var NetworkGroup7 = [GlobalPharmaceuticalsServer, AeroCorpServer, GalacticCyberSystemsServer, SnapFitnessGymServer];
var NetworkGroup8 = [DeltaOneServer, UnitaLifeGroupServer, OmniaCybersystemsServer];
var NetworkGroup9 = [ZeusMedicalServer, SolarisSpaceSystemsServer, UniversalEnergyServer, IcarusMicrosystemsServer, DefCommServer];
var NetworkGroup10 = [NovaMedicalServer, ZBDefenseServer, TaiYangDigitalServer, InfoCommServer];
var NetworkGroup11 = [AppliedEnergeticsServer, MicrodyneTechnologiesServer, TitanLabsServer, BitRunnersServer];
var NetworkGroup12 = [VitaLifeServer, HeliosLabsServer, StormTechnologiesServer, FulcrumTechnologiesServer];
var NetworkGroup13 = [KuaiGongInternationalServer, FourSigmaServer, OmniTekIncorporatedServer, DarkArmyServer];
var NetworkGroup14 = [PowerhouseGymServer, ClarkeIncorporatedServer, NWOServer, BladeIndustriesServer, BachmanAndAssociatesServer];
var NetworkGroup15 = [FulcrumSecretTechnologiesServer, MegaCorpServer, ECorpServer, DaedalusServer];
for (var i = 0; i < NetworkGroup2.length; i++) {
var randomServerFromPrevGroup = NetworkGroup1[Math.floor(Math.random() * NetworkGroup1.length)];
NetworkGroup2[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup2[i].ip);
const networkLayers = [];
for (let i = 0; i < 15; i++) {
networkLayers.push([]);
}
for (var i = 0; i < NetworkGroup3.length; i++) {
var randomServerFromPrevGroup = NetworkGroup2[Math.floor(Math.random() * NetworkGroup2.length)];
NetworkGroup3[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup3[i].ip);
// Essentially any property that is of type 'number | IMinMaxRange'
const propertiesToPatternMatch = [
"hackDifficulty",
"moneyAvailable",
"requiredHackingSkill",
"serverGrowth"
];
const toNumber = (value) => {
switch (typeof value) {
case 'number':
return value;
case 'object':
return getRandomInt(value.min, value.max);
default:
throw Error(`Do not know how to convert the type '${typeof value}' to a number`);
}
}
for (var i = 0; i < NetworkGroup4.length; i++) {
var randomServerFromPrevGroup = NetworkGroup3[Math.floor(Math.random() * NetworkGroup3.length)];
NetworkGroup4[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup4[i].ip);
for (const metadata of serverMetadata) {
const serverParams = {
hostname: metadata.hostname,
ip: createRandomIp(),
numOpenPortsRequired: metadata.numOpenPortsRequired,
organizationName: metadata.organizationName
};
if (metadata.maxRamExponent !== undefined) {
serverParams.maxRam = Math.pow(2, toNumber(metadata.maxRamExponent));
}
for (const prop of propertiesToPatternMatch) {
if (metadata[prop] !== undefined) {
serverParams[prop] = toNumber(metadata[prop]);
}
}
const server = new Server(serverParams);
for (const filename of (metadata.literature || [])) {
server.messages.push(filename);
}
if (metadata.specialName !== undefined) {
SpecialServerIps.addIp(metadata.specialName, server.ip);
}
AddToAllServers(server);
if (metadata.networkLayer !== undefined) {
networkLayers[toNumber(metadata.networkLayer) - 1].push(server);
}
}
for (var i = 0; i < NetworkGroup5.length; i++) {
var randomServerFromPrevGroup = NetworkGroup4[Math.floor(Math.random() * NetworkGroup4.length)];
NetworkGroup5[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup5[i].ip);
}
/* Create a randomized network for all the foreign servers */
const linkComputers = (server1, server2) => {
server1.serversOnNetwork.push(server2.ip);
server2.serversOnNetwork.push(server1.ip);
};
for (var i = 0; i < NetworkGroup6.length; i++) {
var randomServerFromPrevGroup = NetworkGroup5[Math.floor(Math.random() * NetworkGroup5.length)];
NetworkGroup6[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup6[i].ip);
}
const getRandomArrayItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
for (var i = 0; i < NetworkGroup7.length; i++) {
var randomServerFromPrevGroup = NetworkGroup6[Math.floor(Math.random() * NetworkGroup6.length)];
NetworkGroup7[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup7[i].ip);
}
const linkNetworkLayers = (network1, selectServer) => {
for (const server of network1) {
linkComputers(server, selectServer());
}
};
for (var i = 0; i < NetworkGroup8.length; i++) {
var randomServerFromPrevGroup = NetworkGroup7[Math.floor(Math.random() * NetworkGroup7.length)];
NetworkGroup8[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup8[i].ip);
}
for (var i = 0; i < NetworkGroup9.length; i++) {
var randomServerFromPrevGroup = NetworkGroup8[Math.floor(Math.random() * NetworkGroup8.length)];
NetworkGroup9[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup9[i].ip);
}
for (var i = 0; i < NetworkGroup10.length; i++) {
var randomServerFromPrevGroup = NetworkGroup9[Math.floor(Math.random() * NetworkGroup9.length)];
NetworkGroup10[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup10[i].ip);
}
for (var i = 0; i < NetworkGroup11.length; i++) {
var randomServerFromPrevGroup = NetworkGroup10[Math.floor(Math.random() * NetworkGroup10.length)];
NetworkGroup11[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup11[i].ip);
}
for (var i = 0; i < NetworkGroup12.length; i++) {
var randomServerFromPrevGroup = NetworkGroup11[Math.floor(Math.random() * NetworkGroup11.length)];
NetworkGroup12[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup12[i].ip);
}
for (var i = 0; i < NetworkGroup13.length; i++) {
var randomServerFromPrevGroup = NetworkGroup12[Math.floor(Math.random() * NetworkGroup12.length)];
NetworkGroup13[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup13[i].ip);
}
for (var i = 0; i < NetworkGroup14.length; i++) {
var randomServerFromPrevGroup = NetworkGroup13[Math.floor(Math.random() * NetworkGroup13.length)];
NetworkGroup14[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup14[i].ip);
}
for (var i = 0; i < NetworkGroup15.length; i++) {
var randomServerFromPrevGroup = NetworkGroup14[Math.floor(Math.random() * NetworkGroup14.length)];
NetworkGroup15[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup15[i].ip);
}
//Connect the first tier of servers to the player's home computer
for (var i = 0; i < NetworkGroup1.length; i++) {
Player.getHomeComputer().serversOnNetwork.push(NetworkGroup1[i].ip);
NetworkGroup1[i].serversOnNetwork.push(Player.homeComputer);
// Connect the first tier of servers to the player's home computer
linkNetworkLayers(networkLayers[0], () => Player.getHomeComputer());
for (let i = 1; i < networkLayers.length; i++) {
linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1]));
}
}

@ -19,6 +19,11 @@ interface IDefaultSettings {
*/
DisableHotkeys: boolean;
/**
* Locale used for display numbers
*/
Locale: string;
/**
* Limit the number of log entries for each script being executed on each server.
*/
@ -39,6 +44,11 @@ interface IDefaultSettings {
*/
SuppressFactionInvites: boolean;
/**
* Whether to show a popup message when player is hospitalized from taking too much damage
*/
SuppressHospitalizationPopup: boolean;
/**
* Whether the user should be shown a dialog box whenever they receive a new message file.
*/
@ -48,11 +58,6 @@ interface IDefaultSettings {
* Whether the user should be asked to confirm travelling between cities.
*/
SuppressTravelConfirmation: boolean;
/**
* Whether to show a popup message when player is hospitalized from taking too much damage
*/
SuppressHospitalizationPopup: boolean;
}
/**
@ -91,13 +96,14 @@ const defaultSettings: IDefaultSettings = {
AutosaveInterval: 60,
CodeInstructionRunTime: 50,
DisableHotkeys: false,
Locale: "en",
MaxLogCapacity: 50,
MaxPortCapacity: 50,
SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false,
SuppressHospitalizationPopup: false,
SuppressMessages: false,
SuppressTravelConfirmation: false,
SuppressHospitalizationPopup: false,
};
/**
@ -110,13 +116,14 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
DisableHotkeys: defaultSettings.DisableHotkeys,
EditorKeybinding: "ace",
EditorTheme: "Monokai",
Locale: "en",
MaxLogCapacity: defaultSettings.MaxLogCapacity,
MaxPortCapacity: defaultSettings.MaxPortCapacity,
SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation,
SuppressFactionInvites: defaultSettings.SuppressFactionInvites,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
SuppressMessages: defaultSettings.SuppressMessages,
SuppressTravelConfirmation: defaultSettings.SuppressTravelConfirmation,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
ThemeBackgroundColor: "#000000",
ThemeFontColor: "#66ff33",
ThemeHighlightColor: "#ffffff",

98
src/Stock.ts Normal file

@ -0,0 +1,98 @@
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
/**
* Represents the valuation of a company in the World Stock Exchange.
*/
export class Stock {
/**
* Initializes a Stock from a JSON save state
*/
static fromJSON(value: any): Stock {
return Generic_fromJSON(Stock, value.data);
}
/**
* Bear or bull (more likely to go up or down, based on otlkMag)
*/
b: boolean;
/**
* Maximum volatility
*/
readonly mv: number;
/**
* Name of the company that the stock is for
*/
readonly name: string;
/**
* Outlook magnitude. Represents the stock's forecast and likelihood
* of increasing/decreasing (based on whether its in bear or bull mode)
*/
otlkMag: number;
/**
* Average price of stocks that the player owns in the LONG position
*/
playerAvgPx: number;
/**
* Average price of stocks that the player owns in the SHORT position
*/
playerAvgShortPx: number;
/**
* Number of shares the player owns in the LONG position
*/
playerShares: number;
/**
* Number of shares the player owns in the SHORT position
*/
playerShortShares: number;
/**
* The HTML element that displays the stock's info in the UI
*/
posTxtEl: HTMLElement | null;
/**
* Stock's share price
*/
price: number;
/**
* The stock's ticker symbol
*/
readonly symbol: string;
constructor(name: string = "",
symbol: string = "",
mv: number = 1,
b: boolean = true,
otlkMag: number = 0,
initPrice: number = 10e3) {
this.name = name;
this.symbol = symbol;
this.price = initPrice;
this.playerShares = 0;
this.playerAvgPx = 0;
this.playerShortShares = 0;
this.playerAvgShortPx = 0;
this.mv = mv;
this.b = b;
this.otlkMag = otlkMag;
this.posTxtEl = null;
}
/**
* Serialize the Stock to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("Stock", this);
}
}
Reviver.constructors.Stock = Stock;

@ -3,13 +3,14 @@ import {Locations} from "./Locations";
import {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions";
import {WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import {Stock} from "./Stock";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {Page, routing} from "./ui/navigationTracking";
import numeral from "numeral/min/numeral.min";
import {numeralWrapper} from "./ui/numeralFormat";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getRandomInt} from "../utils/helpers/getRandomInt";
import {KEY} from "../utils/helpers/keyCodes";
@ -24,32 +25,6 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
let StockPriceCap = 1e9; //Put a limit on how high a price can go
function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
this.symbol = symbol;
this.name = name;
this.price = initPrice;
this.playerShares = 0;
this.playerAvgPx = 0;
this.playerShortShares = 0;
this.playerAvgShortPx = 0;
this.mv = mv;
this.b = b;
this.otlkMag = otlkMag;
this.posTxtEl = null;
}
Stock.prototype.toJSON = function() {
return Generic_toJSON("Stock", this);
}
Stock.fromJSON = function(value) {
return Generic_fromJSON(Stock, value.data);
}
Reviver.constructors.Stock = Stock;
var OrderTypes = {
LimitBuy: "Limit Buy Order",
LimitSell: "Limit Sell Order",
@ -112,7 +87,7 @@ function cancelOrder(params, workerScript=null) {
//Order properties are passed in. Need to look for the order
var stockOrders = StockMarket["Orders"][params.stock.symbol];
var orderTxt = params.stock.symbol + " - " + params.shares + " @ " +
numeral(params.price).format('$0.000a');
numeralWrapper.format(params.price, '$0.000a');
for (var i = 0; i < stockOrders.length; ++i) {
var order = stockOrders[i];
if (params.shares === order.shares &&
@ -253,135 +228,135 @@ function initStockMarket() {
}
var ecorp = Locations.AevumECorp;
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], 0.45, true, 19, getRandomInt(20000, 25000));
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], getRandomInt(40, 50)/100, true, 19, getRandomInt(17e3, 28e3));
StockMarket[ecorp] = ecorpStk;
var megacorp = Locations.Sector12MegaCorp;
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], 0.45, true, 19, getRandomInt(25000, 33000));
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], getRandomInt(40,50)/100, true, 19, getRandomInt(24e3, 34e3));
StockMarket[megacorp] = megacorpStk;
var blade = Locations.Sector12BladeIndustries;
var bladeStk = new Stock(blade, StockSymbols[blade], 0.75, true, 13, getRandomInt(15000, 22000));
var bladeStk = new Stock(blade, StockSymbols[blade], getRandomInt(70, 80)/100, true, 13, getRandomInt(12e3, 25e3));
StockMarket[blade] = bladeStk;
var clarke = Locations.AevumClarkeIncorporated;
var clarkeStk = new Stock(clarke, StockSymbols[clarke], 0.7, true, 12, getRandomInt(15000, 20000));
var clarkeStk = new Stock(clarke, StockSymbols[clarke], getRandomInt(65, 75)/100, true, 12, getRandomInt(10e3, 25e3));
StockMarket[clarke] = clarkeStk;
var omnitek = Locations.VolhavenOmniTekIncorporated;
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], 0.65, true, 12, getRandomInt(35000, 40000));
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], getRandomInt(60, 70)/100, true, 12, getRandomInt(32e3, 43e3));
StockMarket[omnitek] = omnitekStk;
var foursigma = Locations.Sector12FourSigma;
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], 1.05, true, 17, getRandomInt(60000, 70000));
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], getRandomInt(100, 110)/100, true, 17, getRandomInt(50e3, 80e3));
StockMarket[foursigma] = foursigmaStk;
var kuaigong = Locations.ChongqingKuaiGongInternational;
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], 0.8, true, 10, getRandomInt(20000, 24000));
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], getRandomInt(75, 85)/100, true, 10, getRandomInt(16e3, 28e3));
StockMarket[kuaigong] = kuaigongStk;
var fulcrum = Locations.AevumFulcrumTechnologies;
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], 1.25, true, 16, getRandomInt(30000, 35000));
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], getRandomInt(120, 130)/100, true, 16, getRandomInt(29e3, 36e3));
StockMarket[fulcrum] = fulcrumStk;
var storm = Locations.IshimaStormTechnologies;
var stormStk = new Stock(storm, StockSymbols[storm], 0.85, true, 7, getRandomInt(21000, 24000));
var stormStk = new Stock(storm, StockSymbols[storm], getRandomInt(80, 90)/100, true, 7, getRandomInt(20e3, 25e3));
StockMarket[storm] = stormStk;
var defcomm = Locations.NewTokyoDefComm;
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], 0.65, true, 10, getRandomInt(10000, 15000));
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], getRandomInt(60, 70)/100, true, 10, getRandomInt(6e3, 19e3));
StockMarket[defcomm] = defcommStk;
var helios = Locations.VolhavenHeliosLabs;
var heliosStk = new Stock(helios, StockSymbols[helios], 0.6, true, 9, getRandomInt(12000, 16000));
var heliosStk = new Stock(helios, StockSymbols[helios], getRandomInt(55, 65)/100, true, 9, getRandomInt(10e3, 18e3));
StockMarket[helios] = heliosStk;
var vitalife = Locations.NewTokyoVitaLife;
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], 0.75, true, 7, getRandomInt(10000, 12000));
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], getRandomInt(70, 80)/100, true, 7, getRandomInt(8e3, 14e3));
StockMarket[vitalife] = vitalifeStk;
var icarus = Locations.Sector12IcarusMicrosystems;
var icarusStk = new Stock(icarus, StockSymbols[icarus], 0.65, true, 7.5, getRandomInt(16000, 20000));
var icarusStk = new Stock(icarus, StockSymbols[icarus], getRandomInt(60, 70)/100, true, 7.5, getRandomInt(12e3, 24e3));
StockMarket[icarus] = icarusStk;
var universalenergy = Locations.Sector12UniversalEnergy;
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], 0.55, true, 10, getRandomInt(20000, 25000));
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], getRandomInt(50, 60)/100, true, 10, getRandomInt(16e3, 29e3));
StockMarket[universalenergy] = universalenergyStk;
var aerocorp = Locations.AevumAeroCorp;
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], 0.6, true, 6, getRandomInt(10000, 15000));
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], getRandomInt(55, 65)/100, true, 6, getRandomInt(8e3, 17e3));
StockMarket[aerocorp] = aerocorpStk;
var omnia = Locations.VolhavenOmniaCybersystems;
var omniaStk = new Stock(omnia, StockSymbols[omnia], 0.7, true, 4.5, getRandomInt(9000, 12000));
var omniaStk = new Stock(omnia, StockSymbols[omnia], getRandomInt(65, 75)/100, true, 4.5, getRandomInt(6e3, 15e3));
StockMarket[omnia] = omniaStk;
var solaris = Locations.ChongqingSolarisSpaceSystems;
var solarisStk = new Stock(solaris, StockSymbols[solaris], 0.75, true, 8.5, getRandomInt(18000, 24000));
var solarisStk = new Stock(solaris, StockSymbols[solaris], getRandomInt(70, 80)/100, true, 8.5, getRandomInt(14e3, 28e3));
StockMarket[solaris] = solarisStk;
var globalpharm = Locations.NewTokyoGlobalPharmaceuticals;
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], 0.6, true, 10.5, getRandomInt(18000, 24000));
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], getRandomInt(55, 65)/100, true, 10.5, getRandomInt(12e3, 30e3));
StockMarket[globalpharm] = globalpharmStk;
var nova = Locations.IshimaNovaMedical;
var novaStk = new Stock(nova, StockSymbols[nova], 0.75, true, 5, getRandomInt(18000, 24000));
var novaStk = new Stock(nova, StockSymbols[nova], getRandomInt(70, 80)/100, true, 5, getRandomInt(15e3, 27e3));
StockMarket[nova] = novaStk;
var watchdog = Locations.AevumWatchdogSecurity;
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], 2.5, true, 1.5, getRandomInt(5000, 7500));
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], getRandomInt(240, 260)/100, true, 1.5, getRandomInt(4e3, 8.5e3));
StockMarket[watchdog] = watchdogStk;
var lexocorp = Locations.VolhavenLexoCorp;
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], 1.25, true, 6, getRandomInt(5000, 7500));
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], getRandomInt(115, 135)/100, true, 6, getRandomInt(4.5e3, 8e3));
StockMarket[lexocorp] = lexocorpStk;
var rho = Locations.AevumRhoConstruction;
var rhoStk = new Stock(rho, StockSymbols[rho], 0.6, true, 1, getRandomInt(3000, 6000));
var rhoStk = new Stock(rho, StockSymbols[rho], getRandomInt(50, 70)/100, true, 1, getRandomInt(2e3, 7e3));
StockMarket[rho] = rhoStk;
var alpha = Locations.Sector12AlphaEnterprises;
var alphaStk = new Stock(alpha, StockSymbols[alpha], 1.9, true, 10, getRandomInt(5000, 7500));
var alphaStk = new Stock(alpha, StockSymbols[alpha], getRandomInt(175, 205)/100, true, 10, getRandomInt(4e3, 8.5e3));
StockMarket[alpha] = alphaStk;
var syscore = Locations.VolhavenSysCoreSecurities;
var syscoreStk = new Stock(syscore, StockSymbols[syscore], 1.6, true, 3, getRandomInt(4000, 7000))
var syscoreStk = new Stock(syscore, StockSymbols[syscore], getRandomInt(150, 170)/100, true, 3, getRandomInt(3e3, 8e3));
StockMarket[syscore] = syscoreStk;
var computek = Locations.VolhavenCompuTek;
var computekStk = new Stock(computek, StockSymbols[computek], 0.9, true, 4, getRandomInt(2000, 5000));
var computekStk = new Stock(computek, StockSymbols[computek], getRandomInt(80, 100)/100, true, 4, getRandomInt(1e3, 6e3));
StockMarket[computek] = computekStk;
var netlink = Locations.AevumNetLinkTechnologies;
var netlinkStk = new Stock(netlink, StockSymbols[netlink], 4.2, true, 1, getRandomInt(2000, 4000));
var netlinkStk = new Stock(netlink, StockSymbols[netlink], getRandomInt(400, 430)/100, true, 1, getRandomInt(1e3, 5e3));
StockMarket[netlink] = netlinkStk;
var omega = Locations.IshimaOmegaSoftware;
var omegaStk = new Stock(omega, StockSymbols[omega], 1, true, 0.5, getRandomInt(3000, 6000));
var omegaStk = new Stock(omega, StockSymbols[omega], getRandomInt(90, 110)/100, true, 0.5, getRandomInt(1e3, 8e3));
StockMarket[omega] = omegaStk;
var fns = Locations.Sector12FoodNStuff;
var fnsStk = new Stock(fns, StockSymbols[fns], 0.75, false, 1, getRandomInt(1000, 4000));
var fnsStk = new Stock(fns, StockSymbols[fns], getRandomInt(70, 80)/100, false, 1, getRandomInt(500, 4.5e3));
StockMarket[fns] = fnsStk;
var sigmacosm = "Sigma Cosmetics";
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], 2.8, true, 0, getRandomInt(2000, 3000));
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], getRandomInt(260, 300)/100, true, 0, getRandomInt(1.5e3, 3.5e3));
StockMarket[sigmacosm] = sigmacosmStk;
var joesguns = "Joes Guns";
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], 3.8, true, 1, getRandomInt(500, 1000));
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], getRandomInt(360, 400)/100, true, 1, getRandomInt(250, 1.5e3));
StockMarket[joesguns] = joesgunsStk;
var catalyst = "Catalyst Ventures";
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], 1.45, true, 13.5, getRandomInt(500, 1000));
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], getRandomInt(120, 175)/100, true, 13.5, getRandomInt(250, 1.5e3));
StockMarket[catalyst] = catalystStk;
var microdyne = "Microdyne Technologies";
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], 0.75, true, 8, getRandomInt(20000, 25000));
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], getRandomInt(70, 80)/100, true, 8, getRandomInt(15e3, 30e3));
StockMarket[microdyne] = microdyneStk;
var titanlabs = "Titan Laboratories";
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], 0.6, true, 11, getRandomInt(15000, 20000));
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], getRandomInt(50, 70)/100, true, 11, getRandomInt(12e3, 24e3));
StockMarket[titanlabs] = titanlabsStk;
var orders = {};
@ -435,7 +410,7 @@ function buyStock(stock, shares) {
var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
dialogBoxCreate("You do not have enough money to purchase this. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
numeralWrapper.format(totalPrice + CONSTANTS.StockMarketCommission, '($0.000a)') + ".");
return false;
}
@ -445,9 +420,9 @@ function buyStock(stock, shares) {
stock.playerShares += shares;
stock.playerAvgPx = newTotal / stock.playerShares;
updateStockPlayerPosition(stock);
dialogBoxCreate("Bought " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
dialogBoxCreate("Bought " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
return true;
}
@ -468,9 +443,9 @@ function sellStock(stock, shares) {
stock.playerAvgPx = 0;
}
updateStockPlayerPosition(stock);
dialogBoxCreate("Sold " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(gains).format('($0.000a)') + ".");
dialogBoxCreate("Sold " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeralWrapper.format(gains, '($0.000a)') + ".");
return true;
}
@ -494,10 +469,10 @@ function shortStock(stock, shares, workerScript=null) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have enough " +
"money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
numeralWrapper.format(totalPrice + CONSTANTS.StockMarketCommission, '($0.000a)') + ".");
} else {
dialogBoxCreate("You do not have enough money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
numeralWrapper.format(totalPrice + CONSTANTS.StockMarketCommission, '($0.000a)') + ".");
}
return false;
@ -511,14 +486,14 @@ function shortStock(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock);
if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.shortStock == null) {
workerScript.scriptRef.log("Bought a short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
workerScript.scriptRef.log("Bought a short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
}
} else {
dialogBoxCreate("Bought a short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
dialogBoxCreate("Bought a short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
}
return true;
}
@ -555,14 +530,14 @@ function sellShort(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock);
if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.sellShort == null) {
workerScript.scriptRef.log("Sold your short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)') + ".");
workerScript.scriptRef.log("Sold your short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeralWrapper.format(origCost + profit, '($0.000a)') + ".");
}
} else {
dialogBoxCreate("Sold your short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)') + ".");
dialogBoxCreate("Sold your short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeralWrapper.format(origCost + profit, '($0.000a)') + ".");
}
return true;
@ -721,7 +696,7 @@ function displayStockMarketContent() {
//Purchase WSE Account button
var wseAccountButton = clearEventListeners("stock-market-buy-account");
stylePurchaseButton(wseAccountButton, CONSTANTS.WSEAccountCost, Player.hasWseAccount,
"Buy WSE Account - " + numeral(CONSTANTS.WSEAccountCost).format('($0.000a)'),
"Buy WSE Account - " + numeralWrapper.format(CONSTANTS.WSEAccountCost, '($0.000a)'),
"WSE Account - Purchased");
wseAccountButton.addEventListener("click", function() {
Player.hasWseAccount = true;
@ -735,7 +710,7 @@ function displayStockMarketContent() {
//Purchase TIX API Access account
var tixApiAccessButton = clearEventListeners("stock-market-buy-tix-api");
stylePurchaseButton(tixApiAccessButton, CONSTANTS.TIXAPICost, Player.hasTixApiAccess,
"Buy Trade Information eXchange (TIX) API Access - " + numeral(CONSTANTS.TIXAPICost).format('($0.000a)'),
"Buy Trade Information eXchange (TIX) API Access - " + numeralWrapper.format(CONSTANTS.TIXAPICost, '($0.000a)'),
"TIX API Access - Purchased");
tixApiAccessButton.addEventListener("click", function() {
Player.hasTixApiAccess = true;
@ -747,7 +722,7 @@ function displayStockMarketContent() {
//Purchase Four Sigma Market Data Feed
var marketDataButton = clearEventListeners("stock-market-buy-4s-data");
stylePurchaseButton(marketDataButton, CONSTANTS.MarketData4SCost, Player.has4SData,
"Buy 4S Market Data Access - " + numeral(CONSTANTS.MarketData4SCost).format('($0.000a)'),
"Buy 4S Market Data Access - " + numeralWrapper.format(CONSTANTS.MarketData4SCost, '($0.000a)'),
"4S Market Data - Purchased");
marketDataButton.addEventListener("click", function() {
Player.has4SData = true;
@ -785,7 +760,7 @@ function displayStockMarketContent() {
//Purchase Four Sigma Market Data TIX API (Requires TIX API Access)
var marketDataTixButton = clearEventListeners("stock-market-buy-4s-tix-api");
stylePurchaseButton(marketDataTixButton, CONSTANTS.MarketDataTixApi4SCost, Player.has4SDataTixApi,
"Buy 4S Market Data TIX API Access - " + numeral(CONSTANTS.MarketDataTixApi4SCost).format('($0.000a)'),
"Buy 4S Market Data TIX API Access - " + numeralWrapper.format(CONSTANTS.MarketDataTixApi4SCost, '($0.000a)'),
"4S Market Data TIX API - Purchased");
if (Player.hasTixApiAccess) {
marketDataTixButton.addEventListener("click", function() {
@ -848,7 +823,7 @@ function displayStockMarketContent() {
console.log("Creating Stock Market UI");
commissionText.innerHTML =
"Commission Fees: Every transaction you make has a " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " commission fee.<br><br>" +
numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " commission fee.<br><br>" +
"WARNING: When you reset after installing Augmentations, the Stock Market is reset. " +
"This means all your positions are lost, so make sure to sell your stocks before installing " +
"Augmentations!";
@ -1045,7 +1020,7 @@ function createStockTicker(stock) {
var li = document.createElement("li"), hdr = document.createElement("button");
hdr.classList.add("accordion-header");
hdr.setAttribute("id", tickerId + "-hdr");
hdr.innerHTML = stock.name + " - " + stock.symbol + " - " + numeral(stock.price).format('($0.000a)');
hdr.innerHTML = stock.name + " - " + stock.symbol + " - " + numeralWrapper.format(stock.price, '($0.000a)');
//Div for entire panel
var stockDiv = document.createElement("div");
@ -1322,9 +1297,9 @@ function updateStockTicker(stock, increase) {
}
return;
}
let hdrText = stock.name + " (" + stock.symbol + ") - " + numeral(stock.price).format('($0.000a)');
let hdrText = stock.name + " (" + stock.symbol + ") - " + numeralWrapper.format(stock.price, '($0.000a)');
if (Player.has4SData) {
hdrText += " - Volatility: " + numeral(stock.mv).format('0,0.00') + "%" +
hdrText += " - Volatility: " + numeralWrapper.format(stock.mv, '0,0.00') + "%" +
" - Price Forecast: ";
if (stock.b) {
hdrText += "+".repeat(Math.floor(stock.otlkMag/10) + 1);
@ -1388,21 +1363,21 @@ function updateStockPlayerPosition(stock) {
"<h1 class='tooltip stock-market-position-text'>Long Position: " +
"<span class='tooltiptext'>Shares in the long position will increase " +
"in value if the price of the corresponding stock increases</span></h1>" +
"<br>Shares: " + numeral(stock.playerShares).format('0,0') +
"<br>Average Price: " + numeral(stock.playerAvgPx).format('$0.000a') +
" (Total Cost: " + numeral(totalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(gains).format('$0.000a') +
" (" + numeral(percentageGains).format('0.00%') + ")<br><br>";
"<br>Shares: " + numeralWrapper.format(stock.playerShares, '0,0') +
"<br>Average Price: " + numeralWrapper.format(stock.playerAvgPx, '$0.000a') +
" (Total Cost: " + numeralWrapper.format(totalCost, '$0.000a') + ")" +
"<br>Profit: " + numeralWrapper.format(gains, '$0.000a') +
" (" + numeralWrapper.format(percentageGains, '0.00%') + ")<br><br>";
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
stock.posTxtEl.innerHTML +=
"<h1 class='tooltip stock-market-position-text'>Short Position: " +
"<span class='tooltiptext'>Shares in short position will increase " +
"in value if the price of the corresponding stock decreases</span></h1>" +
"<br>Shares: " + numeral(stock.playerShortShares).format('0,0') +
"<br>Average Price: " + numeral(stock.playerAvgShortPx).format('$0.000a') +
" (Total Cost: " + numeral(shortTotalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(shortGains).format('$0.000a') +
" (" + numeral(shortPercentageGains).format('0.00%') + ")" +
"<br>Shares: " + numeralWrapper.format(stock.playerShortShares, '0,0') +
"<br>Average Price: " + numeralWrapper.format(stock.playerAvgShortPx, '$0.000a') +
" (Total Cost: " + numeralWrapper.format(shortTotalCost, '$0.000a') + ")" +
"<br>Profit: " + numeralWrapper.format(shortGains, '$0.000a') +
" (" + numeralWrapper.format(shortPercentageGains, '0.00%') + ")" +
"<br><br><h1 class='stock-market-position-text'>Orders: </h1>";
}
@ -1465,7 +1440,7 @@ function updateStockOrderList(stock) {
var posText = (order.pos === PositionTypes.Long ? "Long Position" : "Short Position");
li.style.color = "white";
li.innerText = order.type + " - " + posText + " - " +
order.shares + " @ " + numeral(order.price).format('($0.000a)');
order.shares + " @ " + numeralWrapper.format(order.price, '($0.000a)');
var cancelButton = document.createElement("span");
cancelButton.classList.add("stock-market-order-cancel-btn");

@ -10,17 +10,18 @@ import {executeDarkwebTerminalCommand,
import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings,
createFconf} from "./Fconf";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import {TerminalHelpText, HelpTexts} from "./HelpText";
import {iTutorialNextStep, iTutorialSteps,
iTutorialIsRunning,
currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {showLiterature} from "./Literature";
import {showMessage, Message} from "./Message";
import {scriptCalculateHackingTime,
scriptCalculateGrowTime,
scriptCalculateWeakenTime} from "./NetscriptEvaluator";
import {killWorkerScript, addWorkerScript} from "./NetscriptWorker";
import numeral from "numeral/min/numeral.min";
import {Player} from "./Player";
import {hackWorldDaemon} from "./RedPill";
import {findRunningScript, RunningScript,
@ -32,10 +33,10 @@ import {Settings} from "./Settings";
import {SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps";
import {TextFile, getTextFile} from "./TextFile";
import {containsAllStrings, longestCommonStart,
formatNumber} from "../utils/StringHelperFunctions";
import {containsAllStrings,
longestCommonStart} from "../utils/StringHelperFunctions";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {KEY} from "../utils/helpers/keyCodes";
import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString";
@ -48,6 +49,7 @@ import {yesNoBoxCreate,
import {post, hackProgressBarPost,
hackProgressPost} from "./ui/postToTerminal";
import autosize from 'autosize';
import * as JSZip from 'jszip';
import * as FileSaver from 'file-saver';
@ -64,12 +66,12 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val();
post(
"[" +
var command = terminalInput.value;
post(
"<span class='prompt'>[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
" ~]&gt;</span> " + command
);
if (command.length > 0) {
@ -513,16 +515,27 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
let Terminal = {
//Flags to determine whether the player is currently running a hack or an analyze
hackFlag: false,
analyzeFlag: false,
hackFlag: false,
analyzeFlag: false,
actionStarted: false,
actionTime: 0,
commandHistory: [],
commandHistoryIndex: 0,
resetTerminalInput: function() {
document.getElementById("terminal-input-td").innerHTML =
"<div id='terminal-input-header'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" +
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
if (FconfSettings.WRAP_INPUT) {
document.getElementById("terminal-input-td").innerHTML =
"<div id='terminal-input-header' class='prompt'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" +
'<textarea type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
//Auto re-size the line element as it wraps
autosize(document.getElementById("terminal-input-text-box"));
} else {
document.getElementById("terminal-input-td").innerHTML =
"<div id='terminal-input-header' class='prompt'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" +
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
}
var hdr = document.getElementById("terminal-input-header");
hdr.style.display = "inline";
},
@ -620,6 +633,32 @@ let Terminal = {
}
},
startHack: function() {
Terminal.hackFlag = true;
//Hacking through Terminal should be faster than hacking through a script
Terminal.actionTime = calculateHackingTime(Player.getCurrentServer()) / 4;
Terminal.startAction();
},
startAnalyze: function() {
Terminal.analyzeFlag = true;
Terminal.actionTime = 1;
post("Analyzing system...");
Terminal.startAction();
},
startAction: function() {
Terminal.actionStarted = true;
hackProgressPost("Time left:");
hackProgressBarPost("[");
//Disable terminal
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
},
finishAction: function(cancelled = false) {
if (Terminal.hackFlag) {
Terminal.finishHack(cancelled);
@ -634,10 +673,10 @@ let Terminal = {
var server = Player.getCurrentServer();
//Calculate whether hack was successful
var hackChance = Player.calculateHackingChance();
var hackChance = calculateHackingChance(server);
var rand = Math.random();
console.log("Hack success chance: " + hackChance + ", rand: " + rand);
var expGainedOnSuccess = Player.calculateExpGain();
var expGainedOnSuccess = calculateHackingExpGain(server);
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success!
if (SpecialServerIps[SpecialServerNames.WorldDaemon] &&
@ -649,7 +688,7 @@ let Terminal = {
return;
}
server.manuallyHacked = true;
var moneyGained = Player.calculatePercentMoneyHacked();
var moneyGained = calculatePercentMoneyHacked(server);
moneyGained = Math.floor(server.moneyAvailable * moneyGained);
if (moneyGained <= 0) {moneyGained = 0;} //Safety check
@ -661,11 +700,11 @@ let Terminal = {
server.fortify(CONSTANTS.ServerFortifyAmount);
post("Hack successful! Gained $" + formatNumber(moneyGained, 2) + " and " + formatNumber(expGainedOnSuccess, 4) + " hacking EXP");
post("Hack successful! Gained " + numeralWrapper.format(moneyGained, '($0,0.00)') + " and " + numeralWrapper.format(expGainedOnSuccess, '0.0000') + " hacking EXP");
} else { //Failure
//Player only gains 25% exp for failure? TODO Can change this later to balance
Player.gainHackingExp(expGainedOnFailure)
post("Failed to hack " + server.hostname + ". Gained " + formatNumber(expGainedOnFailure, 4) + " hacking EXP");
post("Failed to hack " + server.hostname + ". Gained " + numeralWrapper.format(expGainedOnFailure, '0.0000') + " hacking EXP");
}
}
@ -680,43 +719,45 @@ let Terminal = {
finishAnalyze: function(cancelled = false) {
if (cancelled == false) {
post(Player.getCurrentServer().hostname + ": ");
post("Organization name: " + Player.getCurrentServer().organizationName);
let currServ = Player.getCurrentServer();
post(currServ.hostname + ": ");
post("Organization name: " + currServ.organizationName);
var rootAccess = "";
if (Player.getCurrentServer().hasAdminRights) {rootAccess = "YES";}
if (currServ.hasAdminRights) {rootAccess = "YES";}
else {rootAccess = "NO";}
post("Root Access: " + rootAccess);
post("Required hacking skill: " + Player.getCurrentServer().requiredHackingSkill);
post("Estimated server security level: " + formatNumber(addOffset(Player.getCurrentServer().hackDifficulty, 5), 3));
post("Estimated chance to hack: " + formatNumber(addOffset(Player.calculateHackingChance() * 100, 5), 2) + "%");
post("Estimated time to hack: " + formatNumber(addOffset(Player.calculateHackingTime(), 5), 3) + " seconds");
post("Estimated total money available on server: $" + formatNumber(addOffset(Player.getCurrentServer().moneyAvailable, 5), 2));
post("Required number of open ports for NUKE: " + Player.getCurrentServer().numOpenPortsRequired);
if (Player.getCurrentServer().sshPortOpen) {
post("Required hacking skill: " + currServ.requiredHackingSkill);
post("Server security level: " + numeralWrapper.format(currServ.hackDifficulty, '0.000a'));
post("Chance to hack: " + numeralWrapper.format(calculateHackingChance(currServ) * 100, '0.00%'));
post("Time to hack: " + numeralWrapper.format(calculateHackingTime(currServ), '0.000') + " seconds");
post("Total money available on server: $" + numeralWrapper.format(currServ.moneyAvailable, '$0,0.00'));
post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired);
if (currServ.sshPortOpen) {
post("SSH port: Open")
} else {
post("SSH port: Closed")
}
if (Player.getCurrentServer().ftpPortOpen) {
if (currServ.ftpPortOpen) {
post("FTP port: Open")
} else {
post("FTP port: Closed")
}
if (Player.getCurrentServer().smtpPortOpen) {
if (currServ.smtpPortOpen) {
post("SMTP port: Open")
} else {
post("SMTP port: Closed")
}
if (Player.getCurrentServer().httpPortOpen) {
if (currServ.httpPortOpen) {
post("HTTP port: Open")
} else {
post("HTTP port: Closed")
}
if (Player.getCurrentServer().sqlPortOpen) {
if (currServ.sqlPortOpen) {
post("SQL port: Open")
} else {
post("SQL port: Closed")
@ -731,7 +772,56 @@ let Terminal = {
$('input[class=terminal-input]').prop('disabled', false);
},
executeCommand: function(command) {
writeToScriptFile : function(server, fn, code) {
var ret = {success: false, overwritten: false};
if (!isScriptFilename(fn) || !(server instanceof Server)) { return ret; }
//Check if the script already exists, and overwrite it if it does
for (let i = 0; i < server.scripts.length; ++i) {
if (fn === server.scripts[i].filename) {
let script = server.scripts[i];
script.code = code;
script.updateRamUsage();
script.module = "";
ret.overwritten = true;
ret.success = true;
return ret;
}
}
//Otherwise, create a new script
var newScript = new Script();
newScript.filename = fn;
newScript.code = code;
newScript.updateRamUsage();
newScript.server = server.ip;
server.scripts.push(newScript);
ret.success = true;
return ret;
},
writeToTextFile : function(server, fn, txt) {
var ret = {success: false, overwritten: false};
if (!fn.endsWith("txt") || !(server instanceof Server)) { return ret; }
//Check if the text file already exists, and overwrite if it does
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === fn) {
ret.overwritten = true;
server.textFiles[i].text = txt;
ret.success = true;
return ret;
}
}
//Otherwise create a new text file
var newFile = new TextFile(fn, txt);
server.textFiles.push(newFile);
ret.success = true;
return ret;
},
executeCommand : function(command) {
command = command.trim();
//Replace all extra whitespace in command with a single space
command = command.replace(/\s\s+/g, ' ');
@ -762,11 +852,11 @@ let Terminal = {
if (commandArray.length == 0) {return;}
/****************** Interactive Tutorial Terminal Commands ******************/
if (iTutorialIsRunning) {
if (ITutorial.isRunning) {
var foodnstuffServ = GetServerByHostname("foodnstuff");
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
switch(currITutorialStep) {
switch(ITutorial.currStep) {
case iTutorialSteps.TerminalHelp:
if (commandArray[0] == "help") {
post(TerminalHelpText);
@ -816,17 +906,7 @@ let Terminal = {
if (commandArray.length != 1) {
post("Incorrect usage of analyze command. Usage: analyze"); return;
}
//Analyze the current server for information
Terminal.analyzeFlag = true;
post("Analyzing system...");
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.analyze();
//Disable terminal
//Terminal.resetTerminalInput();
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
Terminal.startAnalyze();
iTutorialNextStep();
} else {
post("Bad command. Please follow the tutorial");
@ -842,15 +922,7 @@ let Terminal = {
break;
case iTutorialSteps.TerminalManualHack:
if (commandArray.length == 1 && commandArray[0] == "hack") {
Terminal.hackFlag = true;
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.hack();
//Disable terminal
//Terminal.resetTerminalInput();
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
Terminal.startHack();
iTutorialNextStep();
} else {post("Bad command. Please follow the tutorial");}
break;
@ -921,17 +993,7 @@ let Terminal = {
if (commandArray.length != 1) {
post("Incorrect usage of analyze command. Usage: analyze"); return;
}
//Analyze the current server for information
Terminal.analyzeFlag = true;
post("Analyzing system...");
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.analyze();
//Disable terminal
//Terminal.resetTerminalInput();
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
Terminal.startAnalyze();
break;
case "buy":
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
@ -1084,15 +1146,7 @@ let Terminal = {
} else if (Player.getCurrentServer().requiredHackingSkill > Player.hacking_skill) {
post("Your hacking skill is not high enough to attempt hacking this machine. Try analyzing the machine to determine the required hacking skill");
} else {
Terminal.hackFlag = true;
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.hack();
//Disable terminal
//Terminal.resetTerminalInput();
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
Terminal.startHack();
}
break;
case "help":
@ -1191,7 +1245,7 @@ let Terminal = {
var scriptBaseRamUsage = currServ.scripts[i].ramUsage;
var ramUsage = scriptBaseRamUsage * numThreads;
post("This script requires " + formatNumber(ramUsage, 2) + "GB of RAM to run for " + numThreads + " thread(s)");
post("This script requires " + numeralWrapper.format(ramUsage, '0.00') + " GB of RAM to run for " + numThreads + " thread(s)");
return;
}
}
@ -1346,6 +1400,7 @@ let Terminal = {
post("Incorrect usage of scan-analyze command. usage: scan-analyze [depth]");
}
break;
/* eslint-disable no-case-declarations */
case "scp":
if (commandArray.length != 2) {
post("Incorrect usage of scp command. Usage: scp [file] [destination hostname/ip]");
@ -1389,8 +1444,7 @@ let Terminal = {
}
}
destServer.messages.push(scriptname);
post(scriptname + " copied over to " + destServer.hostname);
return;
return post(scriptname + " copied over to " + destServer.hostname);
}
//Scp for txt files
@ -1406,18 +1460,15 @@ let Terminal = {
if (!found) {return post("Error: no such file exists!");}
for (var i = 0; i < destServer.textFiles.length; ++i) {
if (destServer.textFiles[i].fn === scriptname) {
//Overwrite
destServer.textFiles[i].text = txtFile.text;
post("WARNING: " + scriptname + " already exists on " + destServer.hostname +
"and will be overwriten");
return post(scriptname + " copied over to " + destServer.hostname);
}
let tRes = Terminal.writeToTextFile(destServer, txtFile.fn, txtFile.text);
if (!tRes.success) {
return post("Error: scp failed");
}
var newFile = new TextFile(txtFile.fn, txtFile.text);
destServer.textFiles.push(newFile);
return post(scriptname + " copied over to " + destServer.hostname);
if (tRes.overwritten) {
post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwriten`);
return post(`${scriptname} overwritten on ${destServer.hostname}`);
}
return post(`${scriptname} copied over to ${destServer.hostname}`);
}
//Get the current script
@ -1433,27 +1484,17 @@ let Terminal = {
return;
}
//Overwrite script if it exists
for (var i = 0; i < destServer.scripts.length; ++i) {
if (scriptname == destServer.scripts[i].filename) {
post("WARNING: " + scriptname + " already exists on " + destServer.hostname + " and will be overwritten");
var oldScript = destServer.scripts[i];
oldScript.code = sourceScript.code;
oldScript.ramUsage = sourceScript.ramUsage;
oldScript.module = "";
post(scriptname + " overwriten on " + destServer.hostname);
return;
}
let sRes = Terminal.writeToScriptFile(destServer, scriptname, sourceScript.code);
if (!sRes.success) {
return post(`Error: scp failed`);
}
var newScript = new Script();
newScript.filename = scriptname;
newScript.code = sourceScript.code;
newScript.ramUsage = sourceScript.ramUsage;
newScript.destServer = ip;
destServer.scripts.push(newScript);
post(scriptname + " copied over to " + destServer.hostname);
break;
if (sRes.overwritten) {
post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwritten`);
return post(`${scriptname} overwritten on ${destServer.hostname}`);
}
post(`${scriptname} copied over to ${destServer.hostname}`);
break;
/* eslint-enable no-case-declarations */
case "sudov":
if (commandArray.length != 1) {
post("Incorrect number of arguments. Usage: sudov"); return;
@ -1556,7 +1597,7 @@ let Terminal = {
var spacesThread = Array(numSpacesThread+1).join(" ");
//Calculate and transform RAM usage
ramUsage = formatNumber(script.scriptRef.ramUsage * script.threads, 2).toString() + "GB";
ramUsage = numeralWrapper.format(script.scriptRef.ramUsage * script.threads, '0.00') + " GB";
var entry = [script.filename, spacesScript, script.threads, spacesThread, ramUsage];
post(entry.join(""));
@ -1577,6 +1618,40 @@ let Terminal = {
}
}
break;
/* eslint-disable no-case-declarations */
case "wget":
if (commandArray.length !== 2) {
return post("Incorrect usage of wget command. Usage: wget [url] [target file]");
}
var args = commandArray[1].split(" ");
if (args.length !== 2) {
return post("Incorrect usage of wget command. Usage: wget [url] [target file]");
}
let url = args[0];
let target = args[1];
if (!isScriptFilename(target) && !target.endsWith(".txt")) {
return post(`wget failed: Invalid target file. Target file must be script or text file`);
}
$.get(url, function(data) {
let res;
if (isScriptFilename(target)) {
res = Terminal.writeToScriptFile(s, target, data);
} else {
res = Terminal.writeToTextFile(s, target, data);
}
if (!res.success) {
return post("wget failed");
}
if (res.overwritten) {
return post(`wget successfully retrieved content and overwrote ${target}`);
}
return post(`wget successfully retrieved content to new file ${target}`);
}, 'text').fail(function(e) {
return post("wget failed: " + JSON.stringify(e));
})
break;
/* eslint-enable no-case-declarations */
default:
post("Command not found");
}
@ -1769,9 +1844,9 @@ let Terminal = {
if (commandArray.length != 1) {
post("Incorrect usage of free command. Usage: free"); return;
}
post("Total: " + formatNumber(Player.getCurrentServer().maxRam, 2) + " GB");
post("Used: " + formatNumber(Player.getCurrentServer().ramUsed, 2) + " GB");
post("Available: " + formatNumber(Player.getCurrentServer().maxRam - Player.getCurrentServer().ramUsed, 2) + " GB");
post("Total: " + numeralWrapper.format(Player.getCurrentServer().maxRam, '0.00') + " GB");
post("Used: " + numeralWrapper.format(Player.getCurrentServer().ramUsed, '0.00') + " GB");
post("Available: " + numeralWrapper.format(Player.getCurrentServer().maxRam - Player.getCurrentServer().ramUsed, '0.00') + " GB");
},
//First called when the "run [program]" command is called. Checks to see if you
@ -1809,7 +1884,7 @@ let Terminal = {
* @returns {void}
*/
/**
* @type {Object.<string, ProgramHandler}
* @type {Object.<string, ProgramHandler}
*/
const programHandlers = {};
programHandlers[Programs.NukeProgram.name] = (server) => {
@ -1892,9 +1967,9 @@ let Terminal = {
post("Server base security level: " + targetServer.baseDifficulty);
post("Server current security level: " + targetServer.hackDifficulty);
post("Server growth rate: " + targetServer.serverGrowth);
post("Netscript hack() execution time: " + formatNumber(scriptCalculateHackingTime(targetServer), 1) + "s");
post("Netscript grow() execution time: " + formatNumber(scriptCalculateGrowTime(targetServer)/1000, 1) + "s");
post("Netscript weaken() execution time: " + formatNumber(scriptCalculateWeakenTime(targetServer)/1000, 1) + "s");
post("Netscript hack() execution time: " + numeralWrapper.format(scriptCalculateHackingTime(targetServer), '0.0') + "s");
post("Netscript grow() execution time: " + numeralWrapper.format(scriptCalculateGrowTime(targetServer), '0.0') + "s");
post("Netscript weaken() execution time: " + numeralWrapper.format(scriptCalculateWeakenTime(targetServer), '0.0') + "s");
};
programHandlers[Programs.AutoLink.name] = () => {
post("This executable cannot be run.");
@ -1920,7 +1995,7 @@ let Terminal = {
if(!fulfilled) {
post("Augmentations: " + Player.augmentations.length + " / 30");
post("Money: " + numeral(Player.money.toNumber()).format('($0.000a)') + " / " + numeral(1e11).format('($0.000a)'));
post("Money: " + numeralWrapper.format(Player.money.toNumber(), '($0.000a)') + " / " + numeralWrapper.format(1e11, '($0.000a)'));
post("One path below must be fulfilled...");
post("----------HACKING PATH----------");
post("Hacking skill: " + Player.hacking_skill + " / 2500");
@ -2053,6 +2128,7 @@ let Terminal = {
}
}
post("ERROR: No such script");
}
};

1543
src/data/servers.ts Normal file

File diff suppressed because it is too large Load Diff

@ -5,13 +5,14 @@ import {clearEventListeners} from "../utils/uiHelpers/clearEv
import {createElement} from "../utils/uiHelpers/createElement";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {removeLoadingScreen} from "../utils/uiHelpers/removeLoadingScreen";
import numeral from "numeral/min/numeral.min";
import {numeralWrapper} from "./ui/numeralFormat";
import {formatNumber,
convertTimeMsToTimeElapsedString,
replaceAt} from "../utils/StringHelperFunctions";
import {loxBoxCreate, logBoxUpdateText,
logBoxOpened} from "../utils/LogBox";
import {updateActiveScriptsItems} from "./ActiveScriptsUI";
import {Augmentations, installAugmentations,
initAugmentations, AugmentationNames,
@ -150,7 +151,7 @@ $(document).keydown(function(e) {
}
});
let Engine = {
const Engine = {
version: "",
Debug: true,
overview: new CharacterOverview(),
@ -548,11 +549,11 @@ let Engine = {
displayCharacterOverviewInfo: function() {
Engine.overview.update();
const save = document.getElementById("character-overview-save-button");
const flashClass = "flashing-button";
if(!Settings.AutosaveInterval) {
if(!Settings.AutosaveInterval) {
save.classList.add(flashClass);
} else {
save.classList.remove(flashClass);
@ -570,7 +571,7 @@ let Engine = {
var intText = "";
if (Player.intelligence > 0) {
intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + "<br><br><br>";
intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + '<br>';
}
let bitNodeTimeText = "";
@ -584,21 +585,21 @@ let Engine = {
'Current City: ' + Player.city + '<br><br>' +
'Employer: ' + Player.companyName + '<br>' +
'Job Title: ' + companyPosition + '<br><br>' +
'Money: $' + formatNumber(Player.money.toNumber(), 2)+ '<br><br><br>' +
'Money: $' + formatNumber(Player.money.toNumber(), 2) + '<br><br><br>' +
'<b>Stats</b><br><br>' +
'Hacking Level: ' + (Player.hacking_skill).toLocaleString() +
" (" + numeral(Player.hacking_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeralWrapper.format(Player.hacking_exp, '(0.000a)') + ' experience)<br>' +
'Strength: ' + (Player.strength).toLocaleString() +
" (" + numeral(Player.strength_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeralWrapper.format(Player.strength_exp, '(0.000a)') + ' experience)<br>' +
'Defense: ' + (Player.defense).toLocaleString() +
" (" + numeral(Player.defense_exp).format('(0.000a)')+ ' experience)<br>' +
' (' + numeralWrapper.format(Player.defense_exp, '(0.000a)') + ' experience)<br>' +
'Dexterity: ' + (Player.dexterity).toLocaleString() +
" (" + numeral(Player.dexterity_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeralWrapper.format(Player.dexterity_exp, '(0.000a)') + ' experience)<br>' +
'Agility: ' + (Player.agility).toLocaleString() +
" (" + numeral(Player.agility_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeralWrapper.format(Player.agility_exp, '(0.000a)') + ' experience)<br>' +
'Charisma: ' + (Player.charisma).toLocaleString() +
" (" + numeral(Player.charisma_exp).format('(0.000a)') + ' experience)<br>' +
intText +
' (' + numeralWrapper.format(Player.charisma_exp, '(0.000a)') + ' experience)<br>' +
intText + '<br><br>' +
'<b>Multipliers</b><br><br>' +
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
@ -923,14 +924,14 @@ let Engine = {
Player.playtimeSinceLastBitnode += time;
//Start Manual hack
if (Player.startAction == true) {
Engine._totalActionTime = Player.actionTime;
Engine._actionTimeLeft = Player.actionTime;
if (Terminal.actionStarted === true) {
Engine._totalActionTime = Terminal.actionTime;
Engine._actionTimeLeft = Terminal.actionTime;
Engine._actionInProgress = true;
Engine._actionProgressBarCount = 1;
Engine._actionProgressStr = "[ ]";
Engine._actionTimeStr = "Time left: ";
Player.startAction = false;
Terminal.actionStarted = false;
}
//Working
@ -1324,9 +1325,9 @@ let Engine = {
Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop
removeLoadingScreen();
dialogBoxCreate("While you were offline, your scripts generated $" +
formatNumber(offlineProductionFromScripts, 2) + " and your Hacknet Nodes generated $" +
formatNumber(offlineProductionFromHacknetNodes, 2));
dialogBoxCreate("While you were offline, your scripts generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromScripts, 2) + "</span> and your Hacknet Nodes generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromHacknetNodes, 2) + "</span>");
//Close main menu accordions for loaded game
var visibleMenuTabs = [terminal, createScript, activeScripts, stats,
hacknetnodes, city, tutorial, options, dev];

1059
src/index.html Normal file

File diff suppressed because it is too large Load Diff

39
src/ui/numeralFormat.js Normal file

@ -0,0 +1,39 @@
import numeral from "numeral";
import 'numeral/locales/bg';
import 'numeral/locales/cs';
import 'numeral/locales/da-dk';
import 'numeral/locales/de';
import 'numeral/locales/en-au';
import 'numeral/locales/en-gb';
import 'numeral/locales/es';
import 'numeral/locales/fr';
import 'numeral/locales/hu';
import 'numeral/locales/it';
import 'numeral/locales/lv';
import 'numeral/locales/no';
import 'numeral/locales/pl';
import 'numeral/locales/ru';
/* eslint-disable class-methods-use-this */
class NumeralFormatter {
constructor() {
this.defaultLocale = 'en';
}
updateLocale(l) {
if (numeral.locale(l) == null) {
console.warn(`Invalid locale for numeral: ${l}`);
numeral.locale(this.defaultLocale);
return false;
}
return true;
}
format(n, format) {
return numeral(n).format(format);
}
}
export const numeralWrapper = new NumeralFormatter();

@ -1,6 +1,9 @@
import {Engine} from "../engine";
import {Settings} from "../Settings";
import {numeralWrapper} from "./NumeralFormat";
function setSettingsLabels() {
var nsExecTime = document.getElementById("settingsNSExecTimeRangeValLabel");
var nsLogLimit = document.getElementById("settingsNSLogRangeValLabel");
@ -12,6 +15,7 @@ function setSettingsLabels() {
var suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup");
var autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel");
var disableHotkeys = document.getElementById("settingsDisableHotkeys");
var locale = document.getElementById("settingsLocale");
//Initialize values on labels
nsExecTime.innerHTML = Settings.CodeInstructionRunTime + "ms";
@ -24,8 +28,10 @@ function setSettingsLabels() {
suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup;
autosaveInterval.innerHTML = Settings.AutosaveInterval;
disableHotkeys.checked = Settings.DisableHotkeys;
locale.value = Settings.Locale;
numeralWrapper.updateLocale(Settings.Locale); //Initialize locale
//Set handlers for when input changes
//Set handlers for when input changes for sliders
var nsExecTimeInput = document.getElementById("settingsNSExecTimeRangeVal");
var nsLogRangeInput = document.getElementById("settingsNSLogRangeVal");
var nsPortRangeInput = document.getElementById("settingsNSPortRangeVal");
@ -60,6 +66,7 @@ function setSettingsLabels() {
}
};
//Set handlers for when settings change on checkboxes
suppressMsgs.onclick = function() {
Settings.SuppressMessages = this.checked;
};
@ -75,7 +82,7 @@ function setSettingsLabels() {
suppressBuyAugmentationConfirmation.onclick = function() {
Settings.SuppressBuyAugmentationConfirmation = this.checked;
};
suppressHospitalizationPopup.onclick = function() {
Settings.SuppressHospitalizationPopup = this.checked;
}
@ -84,6 +91,19 @@ function setSettingsLabels() {
Settings.DisableHotkeys = this.checked;
}
//Locale selector
locale.onchange = function() {
if (!numeralWrapper.updateLocale(locale.value)) {
console.warn(`Invalid locale for numeral: ${locale.value}`);
let defaultValue = 'en';
Settings.Locale = defaultValue;
locale.value = defaultValue;
return;
}
Settings.Locale = locale.value;
}
//Theme
if (Settings.ThemeHighlightColor == null || Settings.ThemeFontColor == null || Settings.ThemeBackgroundColor == null) {
console.log("ERROR: Cannot find Theme Settings");

@ -48,6 +48,7 @@
"ignore-params",
"ignore-properties"
],
"no-magic-numbers": [true, -1, 0, 1, 2, 10, 100],
"no-null-keyword": false,
"no-unsafe-any": false,
"object-literal-key-quotes": [
@ -59,6 +60,7 @@
"allow-declarations",
"allow-named-functions"
],
"triple-equals": [true, "allow-null-check", "allow-undefined-check"],
"typedef": [
true,
"call-signatures",
@ -73,4 +75,4 @@
]
},
"rulesDirectory": []
}
}

@ -57,9 +57,9 @@ function infiltrationBoxCreate(inst) {
CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue * BitNodeMultipliers.InfiltrationMoney;
infiltrationSetText("You can sell the classified documents and secrets " +
"you stole from " + inst.companyName + " for $" +
formatNumber(moneyValue, 2) + " on the black market or you can give it " +
"to a faction to gain " + formatNumber(facValue, 3) + " reputation with " +
"you stole from " + inst.companyName + " for <span class='money-gold'>$" +
formatNumber(moneyValue, 2) + "</span> on the black market or you can give it " +
"to a faction to gain <span class='light-yellow'>" + formatNumber(facValue, 3) + " reputation</span> with " +
"that faction.");
var selector = document.getElementById("infiltration-faction-select");
selector.innerHTML = "";
@ -85,7 +85,7 @@ function infiltrationBoxCreate(inst) {
if (!e.isTrusted) {return false;}
Player.gainMoney(moneyValue);
dialogBoxCreate("You sold the classified information you stole from " + inst.companyName +
" for $" + moneyValue + " on the black market!<br><br>" +
" for <span class='money-gold'>$" + formatNumber(moneyValue, 2) + "</span> on the black market!<br><br>" +
"You gained:<br>" +
formatNumber(inst.hackingExpGained, 3) + " hacking exp<br>" +
formatNumber(inst.strExpGained, 3) + " str exp<br>" +
@ -111,7 +111,7 @@ function infiltrationBoxCreate(inst) {
}
faction.playerReputation += facValue;
dialogBoxCreate("You gave the classified information you stole from " + inst.companyName +
" to " + facName + " and gained " + formatNumber(facValue, 3) + " reputation with the faction. <br><br>" +
" to " + facName + " and gained <span class='light-yellow'>" + formatNumber(facValue, 3) + " reputation</span> with the faction. <br><br>" +
"You gained:<br>" +
formatNumber(inst.hackingExpGained, 3) + " hacking exp<br>" +
formatNumber(inst.strExpGained, 3) + " str exp<br>" +

@ -7,4 +7,4 @@ export function Generic_toJSON(ctorName: string, obj: object, keys?: string[]):
export function Reviver(key, value: IReviverValue);
export namespace Reviver {
export var constructors: any;
}
}

@ -34,11 +34,14 @@ export function createProgressBarText(params: IProgressBarConfiguration) {
};
// tslint:disable-next-line:prefer-object-spread
const derivedParams: IProgressBarConfigurationMaterialized = Object.assign({}, defaultParams, params);
const derived: IProgressBarConfigurationMaterialized = Object.assign({}, defaultParams, params);
// Ensure it is 0..1
derived.progress = Math.max(Math.min(derived.progress, 1), 0);
const bars: number = Math.floor(derivedParams.progress / (1 / derivedParams.totalTicks));
const dashes: number = derivedParams.totalTicks - bars;
// This way there is always at least one bar filled in...
const bars: number = Math.max(Math.floor(derived.progress / (1 / derived.totalTicks)), 1);
const dashes: number = Math.max(derived.totalTicks - bars, 0);
// String.prototype.repeat isn't completley supported, but good enough for our purposes
return `[${"|".repeat(bars + 1)}${"-".repeat(dashes + 1)}]`;
return `[${"|".repeat(bars)}${"-".repeat(dashes)}]`;
}

@ -1,3 +1,6 @@
/**
* Returns a MM/DD HH:MM timestamp for the current time
*/
export function getTimestamp() {
const d: Date = new Date();
// A negative slice value takes from the end of the string rather than the beginning.

@ -1,6 +1,7 @@
var path = require('path');
var webpack = require('webpack');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env, argv) => ({
plugins: [
@ -16,6 +17,45 @@ module.exports = (env, argv) => ({
jQuery: "jquery",
$: "jquery"
}),
new HtmlWebpackPlugin({
title: "Bitburner" + (argv.mode === 'development' ? ' - development' : ""),
template: "src/index.html",
favicon: "favicon.ico",
googleAnalytics: {
trackingId: 'UA-100157497-1'
},
meta: {},
minify: argv.mode === 'development' ? false : {
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: false,
collapseWhitespace: false,
conservativeCollapse: false,
html5: true,
includeAutoGeneratedTags: false,
keepClosingSlash: true,
minifyCSS: false,
minifyJS: false,
minifyURLs: false,
preserveLineBreaks: false,
preventAttributesEscaping: false,
processConditionalComments: false,
quoteCharacter: "\"",
removeAttributeQuotes: false,
removeComments: false,
removeEmptyAttributes: false,
removeEmptyElements: false,
removeOptionalTags: false,
removeScriptTypeAttributes: false,
removeStyleLinkTypeAttributes: false,
removeTagWhitespace: false,
sortAttributes: false,
sortClassName: false,
useShortDoctype: false
},
excludeChunks: [
"tests/tests"
]
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"