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

@ -57,8 +57,8 @@ You can contact him through:
* Discord * Discord
* [Reddit](https://www.reddit.com/user/chapt3r/) * [Reddit](https://www.reddit.com/user/chapt3r/)
Otherwise, here are some general guidelines for determining what types of changes Otherwise, here are some general guidelines for determining what types of
are okay to contribute: changes are okay to contribute:
##### Contributions that Will Most Likely Be Accepted ##### Contributions that Will Most Likely Be Accepted
* Bug Fixes * Bug Fixes
@ -88,12 +88,16 @@ the following rules:
the changes to the UI the changes to the UI
- If your changes affect Netscript, provide some - If your changes affect Netscript, provide some
scripts that can be used to test the Netscript changes. scripts that can be used to test the Netscript changes.
- Do not check in any bundled files (`dist\*.bundle.js`). These will be - Ensure you have run `npm run lint` to make sure your changes conform to the
updated as part of official releases. 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 ## As a Documentor
To contribute to BitBurner documentation, you will need to have Python To contribute to and view your changes to the BitBurner documentation, you will
installed, along with [Sphinx](http://www.sphinx-doc.org). 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 Before submitting your code for a pull request, please try to follow these
rules: rules:
@ -103,3 +107,5 @@ rules:
- Rebase your branch if necessary - Rebase your branch if necessary
- When submitting the pull request, make sure that the base fork is - When submitting the pull request, make sure that the base fork is
_danielyxie/bitburner_ and the base is _dev_. _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'; $fontFamily: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
$defaultFontSize: 16px; $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 */ position: absolute; /* Stay in place */
right: 0; right: 0;
top: 0; top: 0;
height: 400px; /* Full height */ height: 450px;
padding: 10px; padding: 10px;
border: 5px solid #fff; border: 5px solid #fff;
width: 20%; width: 23%;
overflow: auto; /* Enable scroll if needed */ overflow: hidden;
background-color: #444; /* Fallback color */ background-color: #444; /* Fallback color */
color: #fff; color: #fff;
@ -29,6 +29,9 @@
margin: 4px; margin: 4px;
color: #fff; color: #fff;
background-color: #444; background-color: #444;
font-size: $defaultFontSize * 0.875;
max-height: 350px;
overflow-y: auto;
} }
#interactive-tutorial-exit, #interactive-tutorial-exit,
@ -38,7 +41,7 @@
@include boxShadow(1px 1px 3px #000); @include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
font-size: $defaultFontSize * 1.25; font-size: $defaultFontSize * 1.125;
font-weight: bold; font-weight: bold;
background-color: #000; background-color: #000;
@ -50,6 +53,7 @@
} }
} }
/*
#interactive-tutorial-exit { #interactive-tutorial-exit {
float: left; float: left;
} }
@ -58,6 +62,16 @@
margin-right: 20%; margin-right: 20%;
float: right; float: right;
} }
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0;
}
#interactive-tutorial-back {
float: left;
}
#interactive-tutorial-next { #interactive-tutorial-next {
float: right; float: right;

@ -92,3 +92,20 @@
left: 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;
}

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

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

@ -3,10 +3,11 @@
@import "reset"; @import "reset";
:root{ :root {
--my-font-color: #6f3; --my-font-color: #6f3;
--my-background-color: #000; --my-background-color: #000;
--my-highlight-color: #fff; --my-highlight-color: #fff;
--my-prompt-color: #f92672;
} }
body { body {
@ -83,7 +84,7 @@ tr:focus {
display: block; display: block;
color: #e6e6e6; color: #e6e6e6;
background-color: #555; background-color: #555;
padding: 16px; padding: 12px 8px;
text-decoration: none; text-decoration: none;
} }
@ -105,21 +106,36 @@ tr:focus {
background-color: #aaa; 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 */ /* Plus and minus signs */
.mainmenu-accordion-header:after { .mainmenu-accordion-header:after {
content: '\02795'; content: '\02795';
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right; float: right;
margin-left: 5px; font-size: $defaultFontSize * 0.8125;
} position: absolute;
bottom: 25%;
.mainmenu-accordion-header.opened:after { right: 3px;
content: "\2796"; color: transparent;
text-shadow: 0 0 0 #fff;
} }
.mainmenu-accordion-header.opened { .mainmenu-accordion-header.opened {
background-color: #222; background-color: #222;
&:after {
content: "\2796";
}
} }
/* Slide down transition */ /* Slide down transition */
@ -129,11 +145,6 @@ tr:focus {
transition: max-height 0.2s ease-out; 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 */ /* Make html links ("a" elements) nice looking buttons with this class */
a:link, a:link,
a:visited { a:visited {
@ -144,7 +155,7 @@ a:visited {
text-decoration: none; text-decoration: none;
background-color: #555; background-color: #555;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #333; border: 1px solid #333;
@ -152,14 +163,14 @@ a:visited {
-ms-user-select: none; -ms-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
}
.a-link-button:hover { &:hover {
background-color: #666; background-color: #666;
} }
.a-link-button:active { &:active {
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6)); @include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
}
} }
/* Make anchor tags ("a" elements) inactive (not clickable) */ /* Make anchor tags ("a" elements) inactive (not clickable) */
@ -167,20 +178,22 @@ a:visited {
text-decoration: none; text-decoration: none;
background-color: #333; background-color: #333;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #333; border: 1px solid #333;
cursor: default; cursor: default;
}
.a-link-button-inactive:hover .tooltiptext, &:hover {
.a-link-button-inactive:hover .tooltiptexthigh, .tooltiptext,
.a-link-button-inactive:hover .tooltiptextleft { .tooltiptexthigh,
visibility: visible; .tooltiptextleft {
} visibility: visible;
}
}
.a-link-button-inactive:active { &:active {
pointer-events: none; pointer-events: none;
}
} }
/* Make anchor tags ("a" elements) for activated actions */ /* Make anchor tags ("a" elements) for activated actions */
@ -188,20 +201,22 @@ a:visited {
text-decoration: none; text-decoration: none;
background-color: #0a0; background-color: #0a0;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #0a0; border: 1px solid #0a0;
cursor: default; cursor: default;
}
.a-link-button-bought:hover .tooltiptext, &:hover {
.a-link-button-bought:hover .tooltiptexthigh, .tooltiptext,
.a-link-button-bought:hover .tooltiptextleft { .tooltiptexthigh,
visibility: visible; .tooltiptextleft {
} visibility: visible;
}
}
.a-link-button-bought:active { &:active {
pointer-events: none; pointer-events: none;
}
} }
.dropdown { .dropdown {
@ -218,9 +233,9 @@ a:visited {
#create-program-tab { #create-program-tab {
position: relative; position: relative;
} }
#create-program-notification { #create-program-notification {
font-size: $defaultFontSize * 0.625; font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */ position: absolute; /* Position the badge within the relatively positioned button */
top: 0; top: 0;
right: 0; right: 0;
@ -242,22 +257,23 @@ a:visited {
/* Tool tips (when hovering over an element */ /* Tool tips (when hovering over an element */
.tooltip { .tooltip {
position: relative;
display: inline-block; display: inline-block;
} position: relative;
.tooltip .tooltiptext { .tooltiptext {
visibility: hidden; visibility: hidden;
width: 300px; width: 300px;
background-color: var(--my-background-color); background-color: var(--my-background-color);
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
color: #fff; color: #fff;
text-align: center; text-align: center;
padding: 4px; padding: 4px;
left: 101%; left: 101%;
position: absolute; pointer-events: none;
z-index: 99; position: absolute;
z-index: 99;
}
} }
/* Same thing as a normal tooltip except its a bit higher */ /* Same thing as a normal tooltip except its a bit higher */
@ -276,7 +292,7 @@ a:visited {
z-index: 99; 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 */ rather than the right to avoid exceeding the elements normal width */
.tooltip .tooltiptextleft { .tooltip .tooltiptextleft {
visibility: hidden; visibility: hidden;
@ -364,7 +380,7 @@ a:visited {
/* Blinking Cursor */ /* Blinking Cursor */
/* ----- blinking cursor animation ----- */ /* ----- blinking cursor animation ----- */
.typed-cursor{ .typed-cursor {
opacity: 1; opacity: 1;
-webkit-animation: blink 0.95s infinite; -webkit-animation: blink 0.95s infinite;
-moz-animation: blink 0.95s infinite; -moz-animation: blink 0.95s infinite;
@ -400,16 +416,16 @@ a:visited {
} }
/* Status text */ /* Status text */
@-webkit-keyframes status-text{ @-webkit-keyframes status-text {
from{ from {
opacity: 1; opacity: 1;
} }
to{ to {
opacity: 0; opacity: 0;
} }
} }
.status-text{ .status-text {
display: inline-block; display: inline-block;
height: 15%; height: 15%;
position: fixed; position: fixed;
@ -437,47 +453,78 @@ a:visited {
#character-overview-wrapper { #character-overview-wrapper {
position: relative; position: relative;
} }
#character-overview-container { #character-overview-container {
display: none; display: none;
position: absolute; /* Stay in place */ position: absolute; /* Stay in place */
right: 0; right: 0;
top: 0; top: 0;
height: auto; /* Full height */ height: auto; /* Full height */
padding: 8px; padding: 10px 2px;
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
width: 19%; width: auto;
max-width: 280px;
overflow: auto; /* Enable scroll if needed */ overflow: auto; /* Enable scroll if needed */
background-color: #444; /* Fallback color */ background-color: rgba(57, 54, 54, 0.9); /* Fallback color */
z-index: 1; z-index: 1;
} }
#character-overview-text { #character-overview-text {
color: #fff; color: $my-stat-physical;
background-color: #444;
table {
border-collapse: collapse;
margin: auto;
}
td {
padding: 2px;
vertical-align: middle;
}
} }
.character-stat-text { .character-stat-text {
color: #fff; color: #fff;
background-color: #444; background-color: #444;
} }
.character-stat-cell {
.character-stat-cell {
text-align: right; 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-save-button,
#character-overview-options-button { #character-overview-options-button {
@include borderRadius(12px); @include borderRadius(12px);
@include boxShadow(1px 1px 3px #000); @include boxShadow(1px 1px 3px #000);
color: #cecece;
color: #aaa; display: inline-block;
font-size: $defaultFontSize * 0.875; font-size: $defaultFontSize * 0.875;
font-weight: bold; font-weight: bold;
height: 22px; height: 25px;
background-color: #000; background-color: #000;
padding: 5px 8px;
} }
.character-quick-options { .character-quick-options {
padding-top: 5px; margin-top: 10px;
text-align: center;
} }
#character-overview-save-button:hover, #character-overview-save-button:hover,
@ -489,18 +536,15 @@ a:visited {
cursor: pointer; cursor: pointer;
} }
#character-overview-options-button {
display: inline;
}
/* Scan analyze links from AutoLink */ /* Scan analyze links from AutoLink */
.scan-analyze-link { .scan-analyze-link {
cursor: pointer; cursor: pointer;
color: #fff; color: #fff;
text-decoration: underline; text-decoration: underline;
}
.scan-analyze-link:hover { &:hover {
text-decoration: none; text-decoration: none;
}
} }
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
@ -509,37 +553,37 @@ a:visited {
font-size: $defaultFontSize * 1.25; font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 6px; padding: 4px 6px;
cursor: pointer; cursor: pointer;
width: 80%; width: 80%;
text-align: left; text-align: left;
border: none; border: none;
outline: none; outline: none;
} position: relative;
.accordion-header.active, &.active,
.accordion-header:hover { &:hover {
background-color: #555; background-color: #555;
} }
.accordion-header.active:hover { &.active:hover {
background-color: #666; background-color: #666;
} }
.accordion-header:after { &:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
color: #fff; float: right;
float: right; color: transparent;
margin-left: 5px; text-shadow: 0 0 0 #fff;
} position: absolute;
bottom: 5px;
right: 6px;
}
.accordion-header.active:after { &.active:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125; }
color: #fff;
float: right;
margin-left: 5px;
} }
.accordion-panel { .accordion-panel {
@ -551,13 +595,10 @@ a:visited {
background-color: #555; background-color: #555;
overflow-y: auto; overflow-y: auto;
overflow-x: none; overflow-x: none;
}
.accordion-panel div, div, ul, p, ul > li {
.accordion-panel ul, background-color: #555;
.accordion-panel p, }
.accordion-panel ul > li {
background-color: #555;
} }
/* override the global <span> styling */ /* override the global <span> styling */
@ -567,3 +608,34 @@ a:visited {
margin: 0; margin: 0;
padding: 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; overflow-y: scroll;
background-color: var(--my-background-color); background-color: var(--my-background-color);
table-layout: fixed; table-layout: fixed;
.prompt {
color: var(--my-prompt-color);
margin: 0;
padding: 0;
}
} }
#terminal-input { #terminal-input {
@ -50,6 +56,12 @@
display: flex; display: flex;
} }
#terminal-input-td textarea {
overflow: hidden;
resize: none;
height: auto;
}
#terminal-input-header { #terminal-input-header {
white-space: pre; 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-size: 16px;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; } font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; }
@ -13,7 +17,8 @@
:root { :root {
--my-font-color: #6f3; --my-font-color: #6f3;
--my-background-color: #000; --my-background-color: #000;
--my-highlight-color: #fff; } --my-highlight-color: #fff;
--my-prompt-color: #f92672; }
body { body {
background-color: var(--my-background-color); } background-color: var(--my-background-color); }
@ -81,7 +86,7 @@ tr:focus {
display: block; display: block;
color: #e6e6e6; color: #e6e6e6;
background-color: #555; background-color: #555;
padding: 16px; padding: 12px 8px;
text-decoration: none; } text-decoration: none; }
/* Hovering makes them lighter */ /* Hovering makes them lighter */
@ -99,19 +104,31 @@ tr:focus {
.mainmenu > li a.active:hover { .mainmenu > li a.active:hover {
background-color: #aaa; } 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 */ /* Plus and minus signs */
.mainmenu-accordion-header:after { .mainmenu-accordion-header:after {
content: '\2795'; content: '\2795';
font-size: 13px;
color: #fff;
float: right; float: right;
margin-left: 5px; } font-size: 13px;
position: absolute;
.mainmenu-accordion-header.opened:after { bottom: 25%;
content: "\2796"; } right: 3px;
color: transparent;
text-shadow: 0 0 0 #fff; }
.mainmenu-accordion-header.opened { .mainmenu-accordion-header.opened {
background-color: #222; } background-color: #222; }
.mainmenu-accordion-header.opened:after {
content: "\2796"; }
/* Slide down transition */ /* Slide down transition */
.mainmenu-accordion-panel { .mainmenu-accordion-panel {
@ -119,10 +136,6 @@ tr:focus {
opacity: 1; opacity: 1;
transition: max-height 0.2s ease-out; } 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 */ /* Make html links ("a" elements) nice looking buttons with this class */
a:link, a:link,
a:visited { a:visited {
@ -132,57 +145,51 @@ a:visited {
text-decoration: none; text-decoration: none;
background-color: #555; background-color: #555;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #333; border: 1px solid #333;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
-webkit-user-select: none; } -webkit-user-select: none; }
.a-link-button:hover {
.a-link-button:hover { background-color: #666; }
background-color: #666; } .a-link-button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
.a-link-button:active { -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); 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) */ /* Make anchor tags ("a" elements) inactive (not clickable) */
.a-link-button-inactive { .a-link-button-inactive {
text-decoration: none; text-decoration: none;
background-color: #333; background-color: #333;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #333; border: 1px solid #333;
cursor: default; } cursor: default; }
.a-link-button-inactive:hover .tooltiptext,
.a-link-button-inactive:hover .tooltiptext, .a-link-button-inactive:hover .tooltiptexthigh,
.a-link-button-inactive:hover .tooltiptexthigh, .a-link-button-inactive:hover .tooltiptextleft {
.a-link-button-inactive:hover .tooltiptextleft { visibility: visible; }
visibility: visible; } .a-link-button-inactive:active {
pointer-events: none; }
.a-link-button-inactive:active {
pointer-events: none; }
/* Make anchor tags ("a" elements) for activated actions */ /* Make anchor tags ("a" elements) for activated actions */
.a-link-button-bought { .a-link-button-bought {
text-decoration: none; text-decoration: none;
background-color: #0a0; background-color: #0a0;
color: #fff; color: #fff;
padding: 5px; padding: 3px 5px;
margin: 5px; margin: 5px;
border: 1px solid #0a0; border: 1px solid #0a0;
cursor: default; } cursor: default; }
.a-link-button-bought:hover .tooltiptext,
.a-link-button-bought:hover .tooltiptext, .a-link-button-bought:hover .tooltiptexthigh,
.a-link-button-bought:hover .tooltiptexthigh, .a-link-button-bought:hover .tooltiptextleft {
.a-link-button-bought:hover .tooltiptextleft { visibility: visible; }
visibility: visible; } .a-link-button-bought:active {
pointer-events: none; }
.a-link-button-bought:active {
pointer-events: none; }
.dropdown { .dropdown {
color: #fff; color: #fff;
@ -217,20 +224,20 @@ a:visited {
/* Tool tips (when hovering over an element */ /* Tool tips (when hovering over an element */
.tooltip { .tooltip {
position: relative; display: inline-block;
display: inline-block; } position: relative; }
.tooltip .tooltiptext {
.tooltip .tooltiptext { visibility: hidden;
visibility: hidden; width: 300px;
width: 300px; background-color: var(--my-background-color);
background-color: var(--my-background-color); border: 2px solid var(--my-highlight-color);
border: 2px solid var(--my-highlight-color); color: #fff;
color: #fff; text-align: center;
text-align: center; padding: 4px;
padding: 4px; left: 101%;
left: 101%; pointer-events: none;
position: absolute; position: absolute;
z-index: 99; } z-index: 99; }
/* Same thing as a normal tooltip except its a bit higher */ /* Same thing as a normal tooltip except its a bit higher */
.tooltip .tooltiptexthigh { .tooltip .tooltiptexthigh {
@ -246,7 +253,7 @@ a:visited {
position: absolute; position: absolute;
z-index: 99; } 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 */ rather than the right to avoid exceeding the elements normal width */
.tooltip .tooltiptextleft { .tooltip .tooltiptextleft {
visibility: hidden; visibility: hidden;
@ -436,18 +443,24 @@ a:visited {
top: 0; top: 0;
height: auto; height: auto;
/* Full height */ /* Full height */
padding: 8px; padding: 10px 2px;
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
width: 19%; width: auto;
max-width: 280px;
overflow: auto; overflow: auto;
/* Enable scroll if needed */ /* Enable scroll if needed */
background-color: #444; background-color: rgba(57, 54, 54, 0.9);
/* Fallback color */ /* Fallback color */
z-index: 1; } z-index: 1; }
#character-overview-text { #character-overview-text {
color: #fff; color: #faffdf; }
background-color: #444; } #character-overview-text table {
border-collapse: collapse;
margin: auto; }
#character-overview-text td {
padding: 2px;
vertical-align: middle; }
.character-stat-text { .character-stat-text {
color: #fff; color: #fff;
@ -456,6 +469,30 @@ a:visited {
.character-stat-cell { .character-stat-cell {
text-align: right; } 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-save-button,
#character-overview-options-button { #character-overview-options-button {
-webkit-border-radius: 12px; -webkit-border-radius: 12px;
@ -464,14 +501,17 @@ a:visited {
-webkit-box-shadow: 1px 1px 3px #000; -webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000; -moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000; box-shadow: 1px 1px 3px #000;
color: #aaa; color: #cecece;
display: inline-block;
font-size: 14px; font-size: 14px;
font-weight: bold; font-weight: bold;
height: 22px; height: 25px;
background-color: #000; } background-color: #000;
padding: 5px 8px; }
.character-quick-options { .character-quick-options {
padding-top: 5px; } margin-top: 10px;
text-align: center; }
#character-overview-save-button:hover, #character-overview-save-button:hover,
#character-overview-save-button:focus, #character-overview-save-button:focus,
@ -481,17 +521,13 @@ a:visited {
text-decoration: none; text-decoration: none;
cursor: pointer; } cursor: pointer; }
#character-overview-options-button {
display: inline; }
/* Scan analyze links from AutoLink */ /* Scan analyze links from AutoLink */
.scan-analyze-link { .scan-analyze-link {
cursor: pointer; cursor: pointer;
color: #fff; color: #fff;
text-decoration: underline; } text-decoration: underline; }
.scan-analyze-link:hover {
.scan-analyze-link:hover { text-decoration: none; }
text-decoration: none; }
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
.accordion-header { .accordion-header {
@ -499,35 +535,30 @@ a:visited {
font-size: 20px; font-size: 20px;
color: #fff; color: #fff;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 6px; padding: 4px 6px;
cursor: pointer; cursor: pointer;
width: 80%; width: 80%;
text-align: left; text-align: left;
border: none; border: none;
outline: none; } outline: none;
position: relative; }
.accordion-header.active, .accordion-header.active, .accordion-header:hover {
.accordion-header:hover { background-color: #555; }
background-color: #555; } .accordion-header.active:hover {
background-color: #666; }
.accordion-header.active:hover { .accordion-header:after {
background-color: #666; } content: '\2795';
/* "plus" sign (+) */
.accordion-header:after { font-size: 13px;
content: '\2795'; float: right;
/* "plus" sign (+) */ color: transparent;
font-size: 13px; text-shadow: 0 0 0 #fff;
color: #fff; position: absolute;
float: right; bottom: 5px;
margin-left: 5px; } right: 6px; }
.accordion-header.active:after {
.accordion-header.active:after { content: "\2796";
content: "\2796"; /* "minus" sign (-) */ }
/* "minus" sign (-) */
font-size: 13px;
color: #fff;
float: right;
margin-left: 5px; }
.accordion-panel { .accordion-panel {
margin: 0 6px 6px 6px; margin: 0 6px 6px 6px;
@ -538,12 +569,8 @@ a:visited {
background-color: #555; background-color: #555;
overflow-y: auto; overflow-y: auto;
overflow-x: none; } overflow-x: none; }
.accordion-panel div, .accordion-panel ul, .accordion-panel p, .accordion-panel ul > li {
.accordion-panel div, background-color: #555; }
.accordion-panel ul,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555; }
/* override the global <span> styling */ /* override the global <span> styling */
#active-scripts-total-production-active, #active-scripts-total-production-active,
@ -552,6 +579,32 @@ a:visited {
margin: 0; margin: 0;
padding: 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 { #terminal-container {
position: fixed; position: fixed;
margin-left: 10%; margin-left: 10%;
@ -570,6 +623,10 @@ a:visited {
overflow-y: scroll; overflow-y: scroll;
background-color: var(--my-background-color); background-color: var(--my-background-color);
table-layout: fixed; } table-layout: fixed; }
#terminal .prompt {
color: var(--my-prompt-color);
margin: 0;
padding: 0; }
#terminal-input { #terminal-input {
background-color: var(--my-background-color); background-color: var(--my-background-color);
@ -596,12 +653,19 @@ a:visited {
#terminal-input-td { #terminal-input-td {
display: flex; } display: flex; }
#terminal-input-td textarea {
overflow: hidden;
resize: none;
height: auto; }
#terminal-input-header { #terminal-input-header {
white-space: pre; } white-space: pre; }
#terminal-input-text-box { #terminal-input-text-box {
flex: 1 1 auto; } flex: 1 1 auto; }
/* COLORS */
/* Attributes */
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding /* CSS for different main menu pages, such as character info, script editor, etc (but excluding
terminal which has its own page) */ terminal which has its own page) */
.generic-menupage-container { .generic-menupage-container {
@ -776,36 +840,30 @@ a:visited {
.active-scripts-script-header { .active-scripts-script-header {
background-color: #555; background-color: #555;
color: var(--my-font-color); color: var(--my-font-color);
padding: 4px; padding: 4px 25px 4px 10px;
padding-left: 10px;
cursor: pointer; cursor: pointer;
width: auto; width: auto;
text-align: left; text-align: left;
border: none; border: none;
outline: none; } outline: none;
position: relative; }
.active-scripts-script-header:hover, .active-scripts-script-header:after {
.active-scripts-script-header.active:hover { content: '\2795';
background-color: #666; } /* "plus" sign (+) */
font-size: 13px;
.active-scripts-script-header.active { float: right;
background-color: #555; } margin-left: 5px;
color: transparent;
.active-scripts-script-header:after { text-shadow: 0 0 0 var(--my-font-color);
content: '\2795'; position: absolute;
/* "plus" sign (+) */ bottom: 4px; }
font-size: 13px; .active-scripts-script-header.active:after {
color: var(--my-font-color); content: "\2796";
float: right; /* "minus" sign (-) */ }
margin-left: 5px; } .active-scripts-script-header:hover, .active-scripts-script-header.active:hover {
background-color: #666; }
.active-scripts-script-header.active:after { .active-scripts-script-header.active {
content: "\2796"; background-color: #555; }
/* "minus" sign (-) */
font-size: 13px;
color: var(--my-font-color);
float: right;
margin-left: 5px; }
.active-scripts-script-panel { .active-scripts-script-panel {
padding: 0 18px; padding: 0 18px;
@ -813,15 +871,11 @@ a:visited {
width: auto; width: auto;
display: none; display: none;
margin-bottom: 6px; } margin-bottom: 6px; }
.active-scripts-script-panel p, .active-scripts-script-panel h2, .active-scripts-script-panel ul, .active-scripts-script-panel li {
.active-scripts-script-panel p, background-color: #555;
.active-scripts-script-panel h2, width: auto;
.active-scripts-script-panel ul, color: #fff;
.active-scripts-script-panel li { margin-left: 5%; }
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%; }
.active-scripts-button { .active-scripts-button {
-webkit-border-radius: 12px; -webkit-border-radius: 12px;
@ -836,12 +890,10 @@ a:visited {
margin: 4px; margin: 4px;
padding: 4px; padding: 4px;
background-color: #000; } background-color: #000; }
.active-scripts-button:hover, .active-scripts-button:focus {
.active-scripts-button:hover, color: #fff;
.active-scripts-button:focus { text-decoration: none;
color: #fff; cursor: pointer; }
text-decoration: none;
cursor: pointer; }
/* Hacknet Nodes */ /* Hacknet Nodes */
#hacknet-nodes-container { #hacknet-nodes-container {
@ -857,6 +909,14 @@ a:visited {
float: left; float: left;
overflow: hidden; overflow: hidden;
white-space: nowrap; } 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 { #hacknet-nodes-list {
list-style: none; list-style: none;
@ -876,31 +936,19 @@ a:visited {
#hacknet-nodes-purchase-button { #hacknet-nodes-purchase-button {
display: inline-block; } 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 { .hacknet-node-container {
display: inline-table; } display: inline-table; }
.hacknet-node-container .row {
.hacknet-node-container .row { display: table-row;
display: table-row; height: 30px; }
height: 30px; } .hacknet-node-container .row p {
display: table-cell; }
.hacknet-node-container .row p { .hacknet-node-container .upgradable-info {
display: table-cell; } display: inline-block;
margin: 0 4px;
.hacknet-node-container .upgradable-info { /* Don't want the vertical margin/padding, just left & right */
display: inline-block; padding: 0 4px;
margin: 0 4px; width: 64px; }
/* Don't want the vertical margin/padding, just left & right */
padding: 0 4px;
width: 64px; }
.menu-page-text { .menu-page-text {
width: 70vw; } width: 70vw; }
@ -957,8 +1005,8 @@ a:visited {
#faction-container p, #faction-container p,
#faction-container pre { #faction-container pre {
padding: 6px; padding: 4px 6px;
margin: 6px; } margin: 4px 6px; }
#faction-container pre { #faction-container pre {
width: 70%; width: 70%;
@ -977,13 +1025,9 @@ a:visited {
#faction-augmentations-container { #faction-augmentations-container {
position: fixed; position: fixed;
padding-top: 10px; } padding-top: 10px; }
#faction-augmentations-container p, #faction-augmentations-container a, #faction-augmentations-container ul, #faction-augmentations-container h1 {
#faction-augmentations-container p, margin: 8px;
#faction-augmentations-container a, padding: 4px; }
#faction-augmentations-container ul,
#faction-augmentations-container h1 {
margin: 8px;
padding: 4px; }
/* World */ /* World */
#world-container li { #world-container li {
@ -998,9 +1042,14 @@ a:visited {
.augmentations-list button, .augmentations-list button,
.augmentations-list div { .augmentations-list div {
color: var(--my-font-color); color: var(--my-font-color);
padding: 8px;
text-decoration: none; } text-decoration: none; }
.augmentations-list button {
padding: 2px 5px; }
.augmentations-list div {
padding: 6px; }
/* Tutorial */ /* Tutorial */
#tutorial-container { #tutorial-container {
position: fixed; position: fixed;
@ -1043,16 +1092,20 @@ a:visited {
margin: 10px; margin: 10px;
padding: 6px; } padding: 6px; }
#location-container * { #location-container > * {
margin: 10px 5px 10px 5px; } margin: 10px 5px 10px 5px; }
#location-job-reputation, #location-company-favor { #location-job-reputation,
#location-company-favor {
display: inline; } display: inline; }
/* Infiltration */ /* Infiltration */
#infiltration-container { #infiltration-container {
position: fixed; position: fixed;
padding: 6px; } padding: 6px; }
#infiltration-container span {
margin: 0;
padding: 0; }
#infiltration-left-panel, #infiltration-left-panel,
#infiltration-right-panel { #infiltration-right-panel {
@ -1071,6 +1124,9 @@ a:visited {
#infiltration-right-panel p { #infiltration-right-panel p {
margin: 4px; } margin: 4px; }
#infiltration-buttons {
margin-top: 20px; }
#infiltration-buttons .a-link-button { #infiltration-buttons .a-link-button {
display: inline; display: inline;
width: 25%; } width: 25%; }
@ -1139,6 +1195,8 @@ a:visited {
float: left; float: left;
background-color: #555; } background-color: #555; }
/* COLORS */
/* Attributes */
/* Both Work in progress and BitNode stuff */ /* Both Work in progress and BitNode stuff */
.generic-fullscreen-container { .generic-fullscreen-container {
color: var(--my-font-color); color: var(--my-font-color);
@ -1195,6 +1253,8 @@ a:visited {
.bitnode-destroyed:hover { .bitnode-destroyed:hover {
color: #fff; } color: #fff; }
/* COLORS */
/* Attributes */
/* Pop-up boxes */ /* Pop-up boxes */
.popup-box-container { .popup-box-container {
display: none; display: none;
@ -1277,6 +1337,10 @@ a:visited {
z-index: 2; z-index: 2;
background-color: var(--my-background-color); background-color: var(--my-background-color);
padding: 10px; } padding: 10px; }
.dialog-box-content p span,
#log-box-content p span {
padding: 0;
margin: 0; }
.dialog-box-close-button { .dialog-box-close-button {
-webkit-border-radius: 12px; -webkit-border-radius: 12px;
@ -1323,6 +1387,10 @@ a:visited {
padding: 8px; padding: 8px;
margin: 8px; } margin: 8px; }
#infiltration-box-content span {
padding: 0;
margin: 0; }
#infiltration-faction-select { #infiltration-faction-select {
background-color: #000; } background-color: #000; }
@ -1375,6 +1443,8 @@ a:visited {
#import-game-file-selector { #import-game-file-selector {
display: none; } display: none; }
/* COLORS */
/* Attributes */
/* interactivetutorial.css */ /* interactivetutorial.css */
#interactive-tutorial-wrapper { #interactive-tutorial-wrapper {
position: relative; } position: relative; }
@ -1385,13 +1455,11 @@ a:visited {
/* Stay in place */ /* Stay in place */
right: 0; right: 0;
top: 0; top: 0;
height: 400px; height: 450px;
/* Full height */
padding: 10px; padding: 10px;
border: 5px solid #fff; border: 5px solid #fff;
width: 20%; width: 23%;
overflow: auto; overflow: hidden;
/* Enable scroll if needed */
background-color: #444; background-color: #444;
/* Fallback color */ /* Fallback color */
color: #fff; } color: #fff; }
@ -1402,7 +1470,10 @@ a:visited {
padding: 4px; padding: 4px;
margin: 4px; margin: 4px;
color: #fff; color: #fff;
background-color: #444; } background-color: #444;
font-size: 14px;
max-height: 350px;
overflow-y: auto; }
#interactive-tutorial-exit, #interactive-tutorial-exit,
#interactive-tutorial-next, #interactive-tutorial-next,
@ -1414,7 +1485,7 @@ a:visited {
-moz-box-shadow: 1px 1px 3px #000; -moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000; box-shadow: 1px 1px 3px #000;
color: #aaa; color: #aaa;
font-size: 20px; font-size: 18px;
font-weight: bold; font-weight: bold;
background-color: #000; } background-color: #000; }
#interactive-tutorial-exit:hover, #interactive-tutorial-exit:focus, #interactive-tutorial-exit:hover, #interactive-tutorial-exit:focus,
@ -1426,16 +1497,29 @@ a:visited {
text-decoration: none; text-decoration: none;
cursor: pointer; } cursor: pointer; }
/*
#interactive-tutorial-exit { #interactive-tutorial-exit {
float: left; } float: left;
}
#interactive-tutorial-back { #interactive-tutorial-back {
margin-right: 20%; margin-right: 20%;
float: right; } float: right;
}
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0; }
#interactive-tutorial-back {
float: left; }
#interactive-tutorial-next { #interactive-tutorial-next {
float: right; } float: right; }
/* COLORS */
/* Attributes */
* { * {
font-size: 16px; font-size: 16px;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; } 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; box-sizing: border-box;
vertical-align: top; } vertical-align: top; }
/* COLORS */
/* Attributes */
@-webkit-keyframes LOADERSPINNER { @-webkit-keyframes LOADERSPINNER {
0% { 0% {
-webkit-transform: translate(-50%, -50%) rotate(0deg); } -webkit-transform: translate(-50%, -50%) rotate(0deg); }
@ -1604,6 +1690,24 @@ a:visited {
top: 50%; top: 50%;
left: 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 */ /* css for Missions */
/* Hacking missions */ /* Hacking missions */
#mission-container { #mission-container {
@ -1720,6 +1824,8 @@ a:visited {
.hack-mission-action-buttons-container { .hack-mission-action-buttons-container {
border: 2px solid #fff; } border: 2px solid #fff; }
/* COLORS */
/* Attributes */
#cmpy-mgmt-container p, #cmpy-mgmt-container p,
#cmpy-mgmt-container a, #cmpy-mgmt-container a,
#cmpy-mgmt-container div { #cmpy-mgmt-container div {
@ -1830,6 +1936,8 @@ a:visited {
.cmpy-mgmt-advertising-info { .cmpy-mgmt-advertising-info {
font-size: 12px; } font-size: 12px; }
/* COLORS */
/* Attributes */
#bladeburner-container a, #bladeburner-container a,
#bladeburner-container div, #bladeburner-container div,
#bladeburner-container p, #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 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 v0.40.2 - 8/27/2018
------------------- -------------------
* Bladeburner Changes: * Bladeburner Changes:
@ -17,9 +42,9 @@ v0.40.2 - 8/27/2018
* There are now, on average, more Synthoid communities in a city * 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 * 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: * Stock Market Changes:
*Added a watchlist filter feature to the UI that allows you to specify which stocks to show * 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 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 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 * 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: 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-) * 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. Netscript is the programming language used in the world of Bitburner.
When you write scripts in Bitburner, they are written in the Netscript language. 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 Netscript is simply a subset of `JavaScript <https://developer.mozilla.org/en-US/docs/Web/JavaScript>`_,.
syntax is almost idental to Javascript's, but it does not implement many of the This means that Netscript's syntax is
features that Javascript has. 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 If you have any requests or suggestions to improve the Netscript language, feel free
to reach out to the developer! to reach out to the developer!

@ -2,14 +2,18 @@
Netscript 1.0 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>`_. `JS-Interpreter <https://github.com/NeilFraser/JS-Interpreter>`_.
This interpreter was created for ES5, which means that the code written This is an ES5 JavaScript interpreter. This means that (almost) any JavaScript feature
for Netscript 1.0 must be compliant for that version. However, some additional that is available in ES5 is also available in Netscript 1.0. However, this also means
ES6+ features are implemented through polyfills. 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? Which ES6+ features are supported?
---------------------------------- ----------------------------------

@ -43,3 +43,19 @@ getBitNodeMultipliers
mults = getBitNodeMultipliers(); mults = getBitNodeMultipliers();
print(mults.ServerMaxMoney); print(mults.ServerMaxMoney);
print(mults.HackExpGain); 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 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') 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 scan
^^^^ ^^^^
@ -768,10 +790,22 @@ write
when writing to a port. 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 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 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. 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 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. 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 rm
^^ ^^
@ -879,30 +924,42 @@ getScriptRam
getHackTime 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 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. 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 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 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. 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 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 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. 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 getScriptIncome
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

@ -1,14 +1,18 @@
.. _netscript_misc:
Netscript Miscellaneous Netscript Miscellaneous
======================= =======================
.. _netscript_ports:
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 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, 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 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. Netscript functions can be used to interact with ports.
Right now, there are only 20 ports for Netscript, denoted by the number 1 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** **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. 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 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) .. js:method:: NetscriptPort.write(data)
@ -205,36 +211,12 @@ to specify a namespace for the import::
Note that exporting functions is not required. Note that exporting functions is not required.
Standard, Built-In JavaScript Objects
Javascript Math Module -------------------------------------
---------------------- Standard built-in JavaScript objects such as
`Math <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math>`_,
The `Javascript Math Module <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math>`_ is `Date <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date>`_,
supported in Netscript and is used in the same way:: `Number <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number>`_,
and others are supported as expected based on which version
numThreads = Math.floor(getServerRam("foodnstuff")[1] / 3.4); 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).
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

@ -410,6 +410,19 @@ getFactionFavorGain
This function returns the amount of favor you will gain for the specified faction when you reset by installing Augmentations. 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 createProgram
------------- -------------

@ -412,3 +412,18 @@ Then it could be removed using::
$ unalias "r" $ unalias "r"
It is not necessary to differentiate between global and non-global aliases when using 'unalias' 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="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
<link rel="manifest" href="dist/site.webmanifest"> <link rel="manifest" href="dist/site.webmanifest">
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000"> <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="apple-mobile-web-app-title" content="Bitburner">
<meta name="application-name" content="Bitburner"> <meta name="application-name" content="Bitburner">
<meta name="msapplication-TileColor" content="#000000"> <meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="dist/browserconfig.xml"> <meta name="msapplication-config" content="dist/browserconfig.xml">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="stylesheet" type="text/css" href="dist/engine.css" />
<!-- Google Analytics --> <!-- Google Analytics -->
<script> <script>
@ -22,11 +20,12 @@
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), (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) 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'); })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
</script>
<script>
ga('create', 'UA-100157497-1', 'auto'); ga('create', 'UA-100157497-1', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
</head> <link rel="shortcut icon" href="favicon.ico"><link href="dist/engine.css" rel="stylesheet"></head>
<body> <body>
<div id="entire-game-container" style="visibility:hidden;"> <div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container"> <div id="mainmenu-container">
@ -169,11 +168,11 @@
<!-- Terminal page --> <!-- Terminal page -->
<div id="terminal-container"> <div id="terminal-container">
<table id="terminal"> <table id="terminal">
<tr id="terminal-input"> <tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$ <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;"/> <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
@ -188,9 +187,9 @@
provides information about each script's production. The scripts are categorized by the hostname of the servers on which provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p> they are running. </p>
<p id="active-scripts-total-prod">Total online production of <p id="active-scripts-total-prod">Total online production of
Active scripts: <span id="active-scripts-total-production-active">$0.000</span> / sec<br /> 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">$0.000</span> Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span id="active-scripts-total-prod-aug-avg">$0.000</span> / sec)</p> (<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 class="active-scripts-list" id="active-scripts-list" style="list-style: none;">
</ul> </ul>
</div> </div>
@ -202,20 +201,20 @@
The Hacknet is a global, decentralized network of machines. It is used by hackers all around 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 the world to anonymously share computing power and perform distributed cyberattacks without the
fear of being traced. fear of being traced.
<br><br> <br/><br/>
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its 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 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. 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 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. in order to increase its computing power and thereby increase the profit you earn from it.
</p> </p>
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a> <a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
<br> <br/>
<div id="hacknet-nodes-money-multipliers-div"> <div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money"> <p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money"></span><br/> <span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br/>
<span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span> <span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
</p> </p>
<span id="hacknet-nodes-multipliers"> <span id="hacknet-nodes-multipliers">
<a id="hacknet-nodes-1x-multiplier" class="a-link-button-inactive"> x1 </a> <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"> <div id="create-program-container" class="generic-menupage-container">
<p id="create-program-page-text"> <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 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. at any time. Your progress will be saved and you can continue later.
</p> </p>
@ -472,8 +471,7 @@
<!-- Tutorial content --> <!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container"> <div id="tutorial-container" class="generic-menupage-container">
<a id="tutorial-getting-started-link" class="a-link-button" <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>
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-networking-link" class="a-link-button"> Servers & Networking </a>
<a id="tutorial-hacking-link" class="a-link-button"> Hacking </a> <a id="tutorial-hacking-link" class="a-link-button"> Hacking </a>
<a id="tutorial-scripts-link" class="a-link-button"> Scripts </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-jobs-link" class="a-link-button"> Companies and Infiltration </a>
<a id="tutorial-factions-link" class="a-link-button"> Factions </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-augmentations-link" class="a-link-button"> Augmentations </a>
<a id="tutorial-shortcuts-link" class="a-link-button" <a id="tutorial-shortcuts-link" class="a-link-button" href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-back-button" class="a-link-button"> Back </a> <a id="tutorial-back-button" class="a-link-button"> Back </a>
<p id="tutorial-text"> </p> <p id="tutorial-text"> </p>
@ -625,9 +622,9 @@
<!-- Slums --> <!-- Slums -->
<p id="location-slums-description"> <p id="location-slums-description">
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and 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> 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 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. successful. Your chance at successfully committing a crime is determined by your stats.
</p> </p>
<a class="a-link-button tooltip" id="location-slums-shoplift"> Shoplift </a> <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"> <div id="stock-market-container" class="generic-menupage-container">
<p> <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 To begin trading, you must first purchase an account. WSE accounts will persist
after you 'reset' by installing Augmentations. after you 'reset' by installing Augmentations.
@ -686,7 +683,7 @@
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE. 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 Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies. trading strategies.
<br><br> <br/><br/>
If you purchase access to the TIX API, you will retain that access even after If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations. you 'reset' by installing Augmentations.
</p> </p>
@ -696,7 +693,7 @@
<p> <p>
Four Sigma's (4S) Market Data Feed provides information about stocks Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies. 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 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. retain that access even after you 'reset' by installing Augmentations.
</p> </p>
@ -714,8 +711,8 @@
<a id="stock-market-mode" class="a-link-button tooltip"></a> <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-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a> <a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br><br> <br/><br/>
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"> </input> <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> <a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;"> <ul id="stock-market-list" style="list-style:none;">
</ul> </ul>
@ -756,7 +753,7 @@
<p id="faction-invitation-box-text"> </p> <p id="faction-invitation-box-text"> </p>
<p id="faction-invitation-box-message"> </p> <p id="faction-invitation-box-message"> </p>
<p id="faction-invitation-box-warning"> <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! Warning: Joining this faction may prevent you from joining other factions during this run!
</p> </p>
<span id="faction-invitation-box-yes" class="popup-box-button"> Yes </span> <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"> <div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p> <p id="infiltration-box-text"> </p>
<span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br><br> <span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br/><br/>
<select id="infiltration-faction-select"> </select> <br> <select id="infiltration-faction-select"> </select> <br/>
<span id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </span> <span id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </span>
</div> </div>
@ -840,8 +837,8 @@
</table> </table>
</div> </div>
<div class="character-quick-options"> <div class="character-quick-options">
<span id="character-overview-save-button"> Save Game </span> <span id="character-overview-save-button">Save Game</span>
<span id="character-overview-options-button"> Options </span> <span id="character-overview-options-button">Options</span>
</div> </div>
</div> </div>
</div> </div>
@ -856,7 +853,7 @@
<div id="game-options-content" class="game-options-box"> <div id="game-options-content" class="game-options-box">
<span id="game-options-close-button">&times;</span> <span id="game-options-close-button">&times;</span>
<h1> Game Options </h1> <h1> Game Options </h1>
<br> <br/>
<div id="game-options-left-panel"> <div id="game-options-left-panel">
<!-- Netscript execution time --> <!-- Netscript execution time -->
<fieldset> <fieldset>
@ -868,7 +865,7 @@
</span> </span>
</label> </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> <em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -927,7 +924,7 @@
<!-- Suppress faction invites --> <!-- Suppress faction invites -->
<fieldset> <fieldset>
<label for="settingsSuppressFactionInvites" class="tooltip">Suppress Faction Invites: <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 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. on the screen. Your outstanding faction invites can be viewed in the 'Factions' page.
</span> </span>
@ -938,7 +935,7 @@
<!-- Suppress travel confirmation --> <!-- Suppress travel confirmation -->
<fieldset> <fieldset>
<label for="settingsSuppressTravelConfirmation" class="tooltip">Suppress Travel Confirmation: <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. 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> </span>
</label> </label>
@ -949,7 +946,7 @@
<!-- Suppress buy aug confirmation --> <!-- Suppress buy aug confirmation -->
<fieldset> <fieldset>
<label for="settingsSuppressBuyAugmentationConfirmation" class="tooltip">Suppress buy augmentation confirmation: <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. If this is set, the confirmation message before buying augmentation will not show up.
</span> </span>
</label> </label>
@ -959,7 +956,7 @@
<!-- Hospitalization Popup --> <!-- Hospitalization Popup -->
<fieldset> <fieldset>
<label for="settingsSuppressHospitalizationPopup" class="tooltip">Suppress Hospitalization popup: <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. If this is set, a popup message will no longer be shown when you are hospitalized after taking too much damage.
</span> </span>
</label> </label>
@ -969,7 +966,7 @@
<!-- Disable Terminal and Navigation Shortcuts --> <!-- Disable Terminal and Navigation Shortcuts -->
<fieldset> <fieldset>
<label for="settingsDisableHotkeys" class="tooltip">Disable Hotkeys: <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. 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, 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. and the "Save and Close (Ctrl + b)" hotkey in the Text Editor.
@ -978,6 +975,32 @@
<input type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys"> <input type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</fieldset> </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 --> <!-- Donate button -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick"> <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="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%;"> <a id="debug-delete-scripts-link" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Delete Active Scripts (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 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. using this, save the game and then reload the page.
</span> </span>
</a> </a>
<a id="debug-soft-reset" class="a-link-button tooltip" style="display:block;width:46%;"> <a id="debug-soft-reset" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Soft Reset (DEBUG) Soft Reset
<span class="tooltiptext"> <span class="tooltiptextleft">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span> </span>
</a> </a>
</div> </div>
@ -1018,8 +1041,15 @@
<div id="loader" class="loaderoverlay"> <div id="loader" class="loaderoverlay">
<div class="loaderspinner"></div> <div class="loaderspinner"></div>
<div class="loaderlabel">Loading Bitburner...</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> </div>
<script src="dist/vendor.bundle.js"></script> <script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/engine.bundle.js"></script></body>
<script src="dist/engine.bundle.js"></script>
</body>
</html> </html>

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

321
package-lock.json generated

@ -608,6 +608,11 @@
"postcss-value-parser": "3.3.0" "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": { "aws-sign2": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
@ -800,6 +805,12 @@
"multicast-dns-service-types": "1.1.0" "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": { "boom": {
"version": "2.10.1", "version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
@ -1080,6 +1091,16 @@
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
"dev": true "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": { "camelcase": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", "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": { "cli-cursor": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@ -2181,6 +2219,30 @@
"source-list-map": "2.0.0" "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": { "css-selector-tokenizer": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
@ -2192,6 +2254,12 @@
"regexpu-core": "1.0.0" "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": { "cssesc": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
@ -2524,6 +2592,23 @@
"esutils": "2.0.2" "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": { "dom-serializer": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
@ -4790,12 +4875,86 @@
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
"dev": true "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": { "html-tags": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz",
"integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
"dev": true "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": { "htmlparser2": {
"version": "3.9.2", "version": "3.9.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
@ -6380,6 +6539,12 @@
"signal-exit": "3.0.2" "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": { "lru-cache": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
@ -6996,6 +7161,15 @@
"integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==",
"dev": true "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": { "node-forge": {
"version": "0.7.5", "version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", "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": { "num2fraction": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@ -7726,6 +7909,16 @@
"object-keys": "1.0.11" "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": { "object.omit": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
@ -7926,6 +8119,15 @@
"readable-stream": "2.3.4" "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": { "parse-asn1": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
@ -8979,6 +9181,16 @@
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
"dev": true "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": { "process": {
"version": "0.11.10", "version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -9355,6 +9567,12 @@
"jsesc": "0.5.0" "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": { "remark": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz",
@ -9417,6 +9635,81 @@
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"dev": true "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": { "repeat-element": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "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": { "tough-cookie": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
@ -12227,6 +12526,12 @@
"integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==", "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==",
"dev": true "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": { "uri-js": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "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", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" "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": { "utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

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

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

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

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

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

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

@ -83,9 +83,9 @@ const Programs = {
time: CONSTANTS.MillisecondsPerQuarterHour, time: CONSTANTS.MillisecondsPerQuarterHour,
}), }),
BitFlume: new Program("b1t_flum3.exe", { 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)", 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, time: CONSTANTS.MillisecondsPerFiveMinutes / 5,
}), }),
// special because you can't create it. // special because you can't create it.

@ -10,13 +10,13 @@ import {Player} from "./Player";
import {Settings} from "./Settings"; import {Settings} from "./Settings";
import {Page, routing} from "./ui/navigationTracking"; import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox"; import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement"; import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoBoxGetYesButton, import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox"; yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox";
@ -319,7 +319,7 @@ function displayFactionContent(factionName) {
if (isNaN(amt)) { if (isNaN(amt)) {
donateRepGain.innerText = "Invalid donate amount entered!"; donateRepGain.innerText = "Invalid donate amount entered!";
} else { } 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 " + donateRepGain.innerText = "This donation will result in " +
formatNumber(repGain, 3) + " reputation gain"; formatNumber(repGain, 3) + " reputation gain";
} }
@ -335,9 +335,9 @@ function displayFactionContent(factionName) {
dialogBoxCreate("You cannot afford to donate this much money!"); dialogBoxCreate("You cannot afford to donate this much money!");
} else { } else {
Player.loseMoney(amt); Player.loseMoney(amt);
var repGain = amt / 1e6 * Player.faction_rep_mult; var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain; 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"); faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
displayFactionContent(factionName); displayFactionContent(factionName);
} }
@ -603,10 +603,10 @@ function createFactionAugmentationDisplayElements(augmentationsList, augs, facti
pElem.innerHTML = "ALREADY OWNED"; pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) { } else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button"); 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 { } else {
aElem.setAttribute("class", "a-link-button-inactive"); 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"; pElem.style.color = "red";
} }
aDiv.appendChild(aElem); aDiv.appendChild(aElem);

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

@ -1,8 +1,9 @@
import {parse, Node} from "../utils/acorn"; import {parse, Node} from "../utils/acorn";
var FconfSettings = { var FconfSettings = {
ENABLE_BASH_HOTKEYS: false, ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false, ENABLE_TIMESTAMPS: false,
WRAP_INPUT: false,
} }
var FconfComments = { var FconfComments = {
@ -15,6 +16,11 @@ var FconfComments = {
"http://bitburner.readthedocs.io/en/latest/shortcuts.html", "http://bitburner.readthedocs.io/en/latest/shortcuts.html",
ENABLE_TIMESTAMPS: "Terminal commands and log entries will be timestamped. The timestamp\n" + ENABLE_TIMESTAMPS: "Terminal commands and log entries will be timestamped. The timestamp\n" +
"will have the format: M/D h:m", "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 //Parse Fconf settings from the config text
@ -74,6 +80,7 @@ function parseFconfSetting(setting, value) {
switch(setting) { switch(setting) {
case "ENABLE_BASH_HOTKEYS": case "ENABLE_BASH_HOTKEYS":
case "ENABLE_TIMESTAMPS": case "ENABLE_TIMESTAMPS":
case "WRAP_INPUT":
var value = value.toLowerCase(); var value = value.toLowerCase();
if (value === "1" || value === "true" || value === "y") { if (value === "1" || value === "true" || value === "y") {
value = true; value = true;

@ -3,6 +3,7 @@ import {Engine} from "./engine";
import {Faction, Factions, import {Faction, Factions,
displayFactionContent} from "./Faction"; displayFactionContent} from "./Faction";
import {Player} from "./Player"; import {Player} from "./Player";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
@ -10,7 +11,6 @@ import {createAccordionElement} from "../utils/uiHelpers/createA
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup"; import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking"; import {Page, routing} from "./ui/navigationTracking";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../utils/StringHelperFunctions";
import {getRandomInt} from "../utils/helpers/getRandomInt"; import {getRandomInt} from "../utils/helpers/getRandomInt";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement"; import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
@ -892,7 +892,7 @@ function createGangMemberUpgradePanel(memberObj) {
var upg = upgradeArray[j]; var upg = upgradeArray[j];
(function (upg, div, memberObj) { (function (upg, div, memberObj) {
div.appendChild(createElement("a", { 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", class:"a-link-button", margin:"2px", padding:"2px", display:"block",
fontSize:"12px", fontSize:"12px",
tooltip:upg.desc, tooltip:upg.desc,
@ -1411,12 +1411,12 @@ function updateGangMemberDisplayElement(memberObj) {
var stats = document.getElementById(name + "gang-member-stats-text"); var stats = document.getElementById(name + "gang-member-stats-text");
if (stats) { if (stats) {
stats.innerHTML = stats.innerHTML =
"Hacking: " + formatNumber(memberObj.hack, 0) + " (" + numeral(memberObj.hack_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) + " (" + numeral(memberObj.str_exp).format('(0.00a)') + " exp)<br>" + "Strength: " + formatNumber(memberObj.str, 0) + " (" + numeralWrapper.format(memberObj.str_exp, '(0.00a)') + " exp)<br>" +
"Defense: " + formatNumber(memberObj.def, 0) + " (" + numeral(memberObj.def_exp).format('(0.00a)') + " exp)<br>" + "Defense: " + formatNumber(memberObj.def, 0) + " (" + numeralWrapper.format(memberObj.def_exp, '(0.00a)') + " exp)<br>" +
"Dexterity: " + formatNumber(memberObj.dex, 0) + " (" + numeral(memberObj.dex_exp).format('(0.00a)') + " exp)<br>" + "Dexterity: " + formatNumber(memberObj.dex, 0) + " (" + numeralWrapper.format(memberObj.dex_exp, '(0.00a)') + " exp)<br>" +
"Agility: " + formatNumber(memberObj.agi, 0) + " (" + numeral(memberObj.agi_exp).format('(0.00a)') + " exp)<br>" + "Agility: " + formatNumber(memberObj.agi, 0) + " (" + numeralWrapper.format(memberObj.agi_exp, '(0.00a)') + " exp)<br>" +
"Charisma: " + formatNumber(memberObj.cha, 0) + " (" + numeral(memberObj.cha_exp).format('(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"); 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 {CONSTANTS} from "./Constants";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {iTutorialSteps, iTutorialNextStep, import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial"; ITutorial} from "./InteractiveTutorial";
import {Player} from "./Player"; import {Player} from "./Player";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners"; import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
@ -245,8 +245,8 @@ Reviver.constructors.HacknetNode = HacknetNode;
function purchaseHacknet() { function purchaseHacknet() {
/* INTERACTIVE TUTORIAL */ /* INTERACTIVE TUTORIAL */
if (iTutorialIsRunning) { if (ITutorial.isRunning) {
if (currITutorialStep == iTutorialSteps.HacknetNodesIntroduction) { if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
return; return;
@ -446,7 +446,7 @@ function updateHacknetNodesContent() {
//Update player's money //Update player's money
updateText("hacknet-nodes-player-money", "$" + formatNumber(Player.money.toNumber(), 2)); 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 //Update information in each owned hacknet node
for (var i = 0; i < Player.hacknetNodes.length; ++i) { 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-name-" + nodeName, nodeName);
updateText("hacknet-node-total-production-" + nodeName, "$" + formatNumber(nodeObj.totalMoneyGenerated, 2)); 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-level-" + nodeName, nodeObj.level);
updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB"); updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB");
updateText("hacknet-node-cores-" + nodeName, nodeObj.cores); 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>" + "tail [script] [args...] Displays dynamic logs for the specified script<br>" +
"theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" + "theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" +
"top Displays all running scripts and their RAM usage<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> { interface IMap<T> {
[key: string]: T; [key: string]: T;
@ -214,5 +215,11 @@ export const HelpTexts: IMap<string> = {
"Then it could be removed using:<br><br>" + "Then it could be removed using:<br><br>" +
'unalias "r"<br><br>' + 'unalias "r"<br><br>' +
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'", "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;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKill(inst); var res = attemptInfiltrationKill(inst);
if (res[0]) { 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 " + "rest of the facility's security. The facility's security " +
"level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%"); "level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
Player.karma -= 1; Player.karma -= 1;
@ -170,7 +170,7 @@ function nextInfiltrationLevel(inst) {
return false; return false;
} else { } else {
var dmgTaken = Math.max(1, Math.round(1.5 * inst.securityLevel / Player.defense)); 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 " + "and raise the alarm! You take " + dmgTaken + " damage and " +
"the facility's security level increases by " + "the facility's security level increases by " +
formatNumber((res[1]*100)-100, 2).toString() + "%"); formatNumber((res[1]*100)-100, 2).toString() + "%");
@ -186,12 +186,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationAssassinate(inst); var res = attemptInfiltrationAssassinate(inst);
if (res[0]) { 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; Player.karma -= 1;
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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 " + "you but are now more alert for an intruder. The facility's security level " +
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%"); "has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
@ -209,7 +209,7 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKill(inst); var res = attemptInfiltrationKill(inst);
if (res[0]) { 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 " + "rest of the facility's security. The facility's security " +
"level has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%"); "level has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
Player.karma -= 3; Player.karma -= 3;
@ -218,7 +218,7 @@ function nextInfiltrationLevel(inst) {
return false; return false;
} else { } else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense)); 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 " + "and raises the alarm! You take " + dmgTaken + " damage and " +
"the facility's security level has increased by " + "the facility's security level has increased by " +
formatNumber((res[1]*100)-100, 2).toString() + "%"); formatNumber((res[1]*100)-100, 2).toString() + "%");
@ -236,13 +236,13 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationAssassinate(inst); var res = attemptInfiltrationAssassinate(inst);
if (res[0]) { 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.karma -= 3;
++Player.numPeopleKilled; ++Player.numPeopleKilled;
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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 " + "you but is now more alert for an intruder. The facility's security level " +
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%"); "has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
@ -259,14 +259,14 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationKnockout(inst); var res = attemptInfiltrationKnockout(inst);
if (res[0]) { 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."); "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() + "%"); writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense)); 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 " + "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() + "%"); "the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(dmgTaken)) { if (Player.takeDamage(dmgTaken)) {
@ -282,13 +282,13 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationStealthKnockout(inst); var res = attemptInfiltrationStealthKnockout(inst);
if (res[0]) { 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!"); "any noise!");
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } else {
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense)); 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 " + "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() + "%"); "the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(dmgTaken)) { if (Player.takeDamage(dmgTaken)) {
@ -304,12 +304,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationHack(inst); var res = attemptInfiltrationHack(inst);
if (res[0]) { 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() + "%"); writeInfiltrationStatusText("The facility's security level increased by " + ((res[1]*100) - 100).toString() + "%");
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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() + "%"); "security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
updateInfiltrationButtons(inst, scenario); updateInfiltrationButtons(inst, scenario);
@ -321,12 +321,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationDestroySecurity(inst); var res = attemptInfiltrationDestroySecurity(inst);
if (res[0]) { 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() + "%"); writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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() + "%"); "security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
updateInfiltrationButtons(inst, scenario); updateInfiltrationButtons(inst, scenario);
@ -338,11 +338,11 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationSneak(inst); var res = attemptInfiltrationSneak(inst);
if (res[0]) { 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); endInfiltrationLevel(inst);
return false; return false;
} else { } 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() + "%"); "security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
updateInfiltrationButtons(inst, scenario); updateInfiltrationButtons(inst, scenario);
@ -354,12 +354,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationPickLockedDoor(inst); var res = attemptInfiltrationPickLockedDoor(inst);
if (res[0]) { 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() + "%"); writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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() + "%"); "increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
updateInfiltrationButtons(inst, scenario); updateInfiltrationButtons(inst, scenario);
@ -377,13 +377,13 @@ function nextInfiltrationLevel(inst) {
} }
var res = attemptInfiltrationBribe(inst); var res = attemptInfiltrationBribe(inst);
if (res[0]) { 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); "to the next clearance level for $" + bribeAmt);
Player.loseMoney(bribeAmt); Player.loseMoney(bribeAmt);
endInfiltrationLevel(inst); endInfiltrationLevel(inst);
return false; return false;
} else { } 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 " + "other security guards about your presence! The facility's " +
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%"); "security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
} }
@ -396,12 +396,12 @@ function nextInfiltrationLevel(inst) {
if (!e.isTrusted) {return false;} if (!e.isTrusted) {return false;}
var res = attemptInfiltrationEscape(inst); var res = attemptInfiltrationEscape(inst);
if (res[0]) { 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!"); "documents and company secrets!");
endInfiltration(inst, true); endInfiltration(inst, true);
return false; return false;
} else { } 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() + "%"); "security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
if (Player.takeDamage(1)) { if (Player.takeDamage(1)) {
endInfiltration(inst, false); endInfiltration(inst, false);
@ -429,9 +429,9 @@ function endInfiltrationLevel(inst) {
BitNodeMultipliers.InfiltrationMoney; BitNodeMultipliers.InfiltrationMoney;
inst.secretsStolen.push(baseSecretValue); inst.secretsStolen.push(baseSecretValue);
dialogBoxCreate("You found and stole a set of classified documents from the company. " + dialogBoxCreate("You found and stole a set of classified documents from the company. " +
"These classified secrets could probably be sold for money ($" + "These classified secrets could probably be sold for money (<span class='money-gold'>$" +
formatNumber(secretMoneyValue, 2) + "), or they " + formatNumber(secretMoneyValue, 2) + "</span>), or they " +
"could be given to factions for reputation (" + formatNumber(secretValue, 3) + " rep)"); "could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
} }
//Increase security level based on difficulty //Increase security level based on difficulty
@ -468,18 +468,22 @@ function updateInfiltrationLevelText(inst) {
} }
var expMultiplier = 2 * inst.clearanceLevel / inst.maxClearanceLevel; 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 = document.getElementById("infiltration-level-text").innerHTML =
"Facility name: " + inst.companyName + "<br>" + "Facility name:    " + inst.companyName + "<br>" +
"Clearance Level: " + inst.clearanceLevel + "<br>" + "Clearance Level:  " + inst.clearanceLevel + "<br>" +
"Security Level: " + formatNumber(inst.securityLevel, 3) + "<br><br>" + "Security Level:   " + formatNumber(inst.securityLevel, 3) + "<br><br>" +
"Total reputation value of secrets stolen: " + formatNumber(totalValue, 3) + "<br>" + "Total value of stolen secrets<br>" +
"Total monetary value of secrets stolen: $" + formatNumber(totalMoneyValue, 2) + "<br><br>" + "Reputation:       <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
"Hack exp gained: " + formatNumber(inst.hackingExpGained * expMultiplier, 3) + "<br>" + "Money:           <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
"Str exp gained: " + formatNumber(inst.strExpGained * expMultiplier, 3) + "<br>" + "Hack exp gained:  " + formatNumber(inst.hackingExpGained * expMultiplier, 3) + "<br>" +
"Def exp gained: " + formatNumber(inst.defExpGained * expMultiplier, 3) + "<br>" + "Str exp gained:   " + formatNumber(inst.strExpGained * expMultiplier, 3) + "<br>" +
"Dex exp gained: " + formatNumber(inst.dexExpGained * expMultiplier, 3) + "<br>" + "Def exp gained:   " + formatNumber(inst.defExpGained * expMultiplier, 3) + "<br>" +
"Agi exp gained: " + formatNumber(inst.agiExpGained * expMultiplier, 3) + "<br>" + "Dex exp gained:   " + formatNumber(inst.dexExpGained * expMultiplier, 3) + "<br>" +
"Cha exp gained: " + formatNumber(inst.chaExpGained * expMultiplier, 3); "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) { function updateInfiltrationButtons(inst, scenario) {

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

@ -23,7 +23,11 @@ function initLiterature() {
var title, fn, txt; var title, fn, txt;
title = "The Beginner's Guide to Hacking"; title = "The Beginner's Guide to Hacking";
fn = "hackers-starting-handbook.lit"; 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>" + "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 " + "-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>" + "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 {Settings} from "./Settings";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners"; import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createRandomIp} from "../utils/IPAddress"; import {createRandomIp} from "../utils/IPAddress";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate, import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxGetYesButton, yesNoBoxGetNoButton,
@ -1001,7 +1002,7 @@ function displayLocationContent() {
slumsDealDrugs.innerHTML = "Deal Drugs (" + (drugsChance*100).toFixed(3) + "% chance of success)"; slumsDealDrugs.innerHTML = "Deal Drugs (" + (drugsChance*100).toFixed(3) + "% chance of success)";
slumsDealDrugs.innerHTML += '<span class="tooltiptext"> Attempt to deal drugs </span>'; slumsDealDrugs.innerHTML += '<span class="tooltiptext"> Attempt to deal drugs </span>';
slumsBondForgery.style.display = "block"; 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>"; slumsBondForgery.innerHTML += "<span class='tooltiptext'> Attempt to forge corporate bonds</span>";
slumsTrafficArms.style.display = "block"; slumsTrafficArms.style.display = "block";
slumsTrafficArms.innerHTML = "Traffick Illegal Arms (" + (armsChance*100).toFixed(3) + "% chance of success)"; 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>" + 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 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) { 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 " + 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>" + "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 " + "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) { travelToAevum.addEventListener("click", function(e) {

@ -911,61 +911,5 @@ function isScriptErrorMessage(msg) {
return true; return true;
} }
//The same as Player's calculateHackingChance() function but takes in the server as an argument export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript, evaluate,
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,
isScriptErrorMessage, killNetscriptDelay, evaluateImport}; isScriptErrorMessage, killNetscriptDelay, evaluateImport};

@ -13,6 +13,12 @@ import {Companies, Company, CompanyPosition,
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram"; import {Programs} from "./CreateProgram";
import {DarkWebItems} from "./DarkWeb"; import {DarkWebItems} from "./DarkWeb";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import {AllGangs} from "./Gang"; import {AllGangs} from "./Gang";
import {Factions, Faction, joinFaction, import {Factions, Faction, joinFaction,
factionExists, purchaseAugmentation} from "./Faction"; factionExists, purchaseAugmentation} from "./Faction";
@ -28,11 +34,12 @@ import {Server, getServer, AddToAllServers,
GetServerByHostname} from "./Server"; GetServerByHostname} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings";
import {SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerIps} from "./SpecialServerIps";
import {Stock} from "./Stock";
import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols, import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock, initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
sellStock, updateStockPrices, displayStockMarketContent, sellStock, updateStockPrices, displayStockMarketContent,
updateStockTicker, updateStockPlayerPosition, updateStockTicker, updateStockPlayerPosition,
Stock, shortStock, sellShort, OrderTypes, shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket"; PositionTypes, placeOrder, cancelOrder} from "./StockMarket";
import {post} from "./ui/postToTerminal"; import {post} from "./ui/postToTerminal";
import {TextFile, getTextFile, createTextFile} from "./TextFile"; import {TextFile, getTextFile, createTextFile} from "./TextFile";
@ -42,10 +49,8 @@ import {unknownBladeburnerActionErrorMessage,
checkBladeburnerAccess} from "./NetscriptBladeburner.js"; checkBladeburnerAccess} from "./NetscriptBladeburner.js";
import {WorkerScript, workerScripts, import {WorkerScript, workerScripts,
killWorkerScript, NetscriptPorts} from "./NetscriptWorker"; killWorkerScript, NetscriptPorts} from "./NetscriptWorker";
import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript, import {makeRuntimeRejectMsg, netscriptDelay,
scriptCalculateHackingChance, scriptCalculateHackingTime, runScriptFromScript} from "./NetscriptEvaluator";
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
scriptCalculateGrowTime, scriptCalculateWeakenTime} from "./NetscriptEvaluator";
import {NetscriptPort} from "./NetscriptPort"; import {NetscriptPort} from "./NetscriptPort";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
@ -116,6 +121,7 @@ var possibleLogs = {
applyToCompany: true, applyToCompany: true,
joinFaction: true, joinFaction: true,
workForFaction: true, workForFaction: true,
donateToFaction: true,
createProgram: true, createProgram: true,
commitCrime: true, commitCrime: true,
shortStock: true, shortStock: true,
@ -292,7 +298,7 @@ function NetscriptFunctions(workerScript) {
} }
//Calculate the hacking time //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 //No root access or skill level too low
if (server.hasAdminRights == false) { if (server.hasAdminRights == false) {
@ -308,14 +314,14 @@ function NetscriptFunctions(workerScript) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.hack == null) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.hack == null) {
workerScript.scriptRef.log("Attempting to hack " + ip + " in " + hackingTime.toFixed(3) + " seconds (t=" + threads + ")"); 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);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = scriptCalculateHackingChance(server); var hackChance = calculateHackingChance(server);
var rand = Math.random(); var rand = Math.random();
var expGainedOnSuccess = scriptCalculateExpGain(server) * threads; var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4); var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success! if (rand < hackChance) { //Success!
const percentHacked = scriptCalculatePercentMoneyHacked(server); const percentHacked = calculatePercentMoneyHacked(server);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax)); let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) { if (isNaN(maxThreadNeeded)) {
//Server has a 'max money' of 0 (probably). //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"); 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) { 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);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable; const moneyBefore = server.moneyAvailable;
server.moneyAvailable += (1 * threads); //It can be grown even if it has no money server.moneyAvailable += (1 * threads); //It can be grown even if it has no money
var growthPercentage = processSingleServerGrowth(server, 450 * threads); var growthPercentage = processSingleServerGrowth(server, 450 * threads);
const moneyAfter = server.moneyAvailable; const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads); workerScript.scriptRef.recordGrow(server.ip, threads);
var expGain = scriptCalculateExpGain(server) * threads; var expGain = calculateHackingExpGain(server) * threads;
if (growthPercentage == 1) { if (growthPercentage == 1) {
expGain = 0; 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"); 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) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.weaken == null) {
workerScript.scriptRef.log("Executing weaken() on server " + server.hostname + " in " + 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);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
server.weaken(CONSTANTS.ServerWeakenAmount * threads); server.weaken(CONSTANTS.ServerWeakenAmount * threads);
workerScript.scriptRef.recordWeaken(server.ip, 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) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.weaken == null) {
workerScript.scriptRef.log("Server security level on " + server.hostname + " weakened to " + server.hackDifficulty + workerScript.scriptRef.log("Server security level on " + server.hostname + " weakened to " + server.hackDifficulty +
". Gained " + formatNumber(expGain, 4) + " hacking exp (t=" + threads + ")"); ". Gained " + formatNumber(expGain, 4) + " hacking exp (t=" + threads + ")");
@ -502,6 +508,17 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("Enabled logging for " + fn); 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){ nuke : function(ip){
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("nuke", CONSTANTS.ScriptPortProgramRamCost); return updateStaticRam("nuke", CONSTANTS.ScriptPortProgramRamCost);
@ -1843,6 +1860,25 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for write: " + port); 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) { read : function(port) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("read", CONSTANTS.ScriptReadWriteRamCost); return updateStaticRam("read", CONSTANTS.ScriptReadWriteRamCost);
@ -2047,7 +2083,7 @@ function NetscriptFunctions(workerScript) {
} }
return 0; return 0;
}, },
getHackTime : function(ip) { getHackTime : function(ip, hack, int) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("getHackTime", CONSTANTS.ScriptGetHackTimeRamCost); return updateStaticRam("getHackTime", CONSTANTS.ScriptGetHackTimeRamCost);
} }
@ -2057,9 +2093,9 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getHackTime() failed. Invalid IP or hostname passed in: " + ip); workerScript.scriptRef.log("getHackTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "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) { if (workerScript.checkingRam) {
return updateStaticRam("getGrowTime", CONSTANTS.ScriptGetHackTimeRamCost); return updateStaticRam("getGrowTime", CONSTANTS.ScriptGetHackTimeRamCost);
} }
@ -2069,9 +2105,9 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getGrowTime() failed. Invalid IP or hostname passed in: " + ip); workerScript.scriptRef.log("getGrowTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "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) { if (workerScript.checkingRam) {
return updateStaticRam("getWeakenTime", CONSTANTS.ScriptGetHackTimeRamCost); return updateStaticRam("getWeakenTime", CONSTANTS.ScriptGetHackTimeRamCost);
} }
@ -2081,7 +2117,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getWeakenTime() failed. Invalid IP or hostname passed in: " + ip); workerScript.scriptRef.log("getWeakenTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "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) { getScriptIncome : function(scriptname, ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
@ -3032,7 +3068,7 @@ function NetscriptFunctions(workerScript) {
return Factions[name].favor; return Factions[name].favor;
}, },
getFactionFavorGain: function(name){ getFactionFavorGain: function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
@ -3053,6 +3089,45 @@ function NetscriptFunctions(workerScript) {
return Factions[name].getFavorGain()[0]; 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) { createProgram : function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;} if (Player.bitNodeN !== 4) {ramCost *= 8;}

@ -21,15 +21,18 @@ import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile"; import {SourceFiles, applySourceFile} from "./SourceFile";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners"; import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createRandomIp} from "../utils/IPAddress"; import {createRandomIp} from "../utils/IPAddress";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
import numeral from "numeral/min/numeral.min";
import {formatNumber, import {formatNumber,
convertTimeMsToTimeElapsedString} from "../utils/StringHelperFunctions"; convertTimeMsToTimeElapsedString} from "../utils/StringHelperFunctions";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
function PlayerObject() { function PlayerObject() {
//Skills and stats //Skills and stats
this.hacking_skill = 1; this.hacking_skill = 1;
@ -119,11 +122,6 @@ function PlayerObject() {
this.crime_money_mult = 1; this.crime_money_mult = 1;
this.crime_success_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) //Flags/variables for working (Company, Faction, Creating Program, Taking Class)
this.isWorking = false; this.isWorking = false;
this.workType = ""; this.workType = "";
@ -265,9 +263,6 @@ PlayerObject.prototype.prestigeAugmentation = function() {
this.queuedAugmentations = []; this.queuedAugmentations = [];
this.startAction = false;
this.actionTime = 0;
this.isWorking = false; this.isWorking = false;
this.currentWorkFactionName = ""; this.currentWorkFactionName = "";
this.currentWorkFactionDescription = ""; this.currentWorkFactionDescription = "";
@ -349,9 +344,6 @@ PlayerObject.prototype.prestigeSourceFile = function() {
this.queuedAugmentations = []; this.queuedAugmentations = [];
this.augmentations = []; this.augmentations = [];
this.startAction = false;
this.actionTime = 0;
this.isWorking = false; this.isWorking = false;
this.currentWorkFactionName = ""; this.currentWorkFactionName = "";
this.currentWorkFactionDescription = ""; this.currentWorkFactionDescription = "";
@ -498,74 +490,10 @@ PlayerObject.prototype.resetMultipliers = function() {
this.bladeburner_success_chance_mult = 1; 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) { PlayerObject.prototype.hasProgram = function(programName) {
var home = Player.getHomeComputer(); var home = Player.getHomeComputer();
if (home == null) {return false;}
for (var i = 0; i < home.programs.length; ++i) { for (var i = 0; i < home.programs.length; ++i) {
if (programName.toLowerCase() == home.programs[i].toLowerCase()) {return true;} if (programName.toLowerCase() == home.programs[i].toLowerCase()) {return true;}
} }
@ -688,6 +616,7 @@ PlayerObject.prototype.resetWorkStatus = function() {
this.workChaExpGainRate = 0; this.workChaExpGainRate = 0;
this.workRepGainRate = 0; this.workRepGainRate = 0;
this.workMoneyGainRate = 0; this.workMoneyGainRate = 0;
this.workMoneyLossRate = 0;
this.workHackExpGained = 0; this.workHackExpGained = 0;
this.workStrExpGained = 0; this.workStrExpGained = 0;
@ -709,24 +638,109 @@ PlayerObject.prototype.resetWorkStatus = function() {
document.getElementById("work-in-progress-text").innerHTML = ""; document.getElementById("work-in-progress-text").innerHTML = "";
} }
PlayerObject.prototype.gainWorkExp = function() { PlayerObject.prototype.processWorkEarnings = function(numCycles=1) {
this.gainHackingExp(this.workHackExpGained); var hackExpGain = this.workHackExpGainRate * numCycles;
this.gainStrengthExp(this.workStrExpGained); var strExpGain = this.workStrExpGainRate * numCycles;
this.gainDefenseExp(this.workDefExpGained); var defExpGain = this.workDefExpGainRate * numCycles;
this.gainDexterityExp(this.workDexExpGained); var dexExpGain = this.workDexExpGainRate * numCycles;
this.gainAgilityExp(this.workAgiExpGained); var agiExpGain = this.workAgiExpGainRate * numCycles;
this.gainCharismaExp(this.workChaExpGained); 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 */ /* 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) { PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
//Since the work was cancelled early, player only gains half of what they've earned so far //Since the work was cancelled early, player only gains half of what they've earned so far
if (cancelled) { if (cancelled) {
this.workRepGained /= 2; this.workRepGained /= 2;
} }
this.gainWorkExp();
var company = Companies[this.companyName]; var company = Companies[this.companyName];
company.playerReputation += (this.workRepGained); company.playerReputation += (this.workRepGained);
@ -773,91 +787,6 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
this.resetWorkStatus(); 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() { PlayerObject.prototype.startWorkPartTime = function() {
this.resetWorkStatus(); this.resetWorkStatus();
this.isWorking = true; this.isWorking = true;
@ -886,57 +815,50 @@ PlayerObject.prototype.startWorkPartTime = function() {
} }
PlayerObject.prototype.workPartTime = function(numCycles) { PlayerObject.prototype.workPartTime = function(numCycles) {
this.workRepGainRate = this.getWorkRepGain(); //Cap the number of cycles being processed to whatever would put you at the
//work time limit (8 hours)
this.workHackExpGained += this.workHackExpGainRate * numCycles; var overMax = false;
this.workStrExpGained += this.workStrExpGainRate * numCycles; if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer8Hours) {
this.workDefExpGained += this.workDefExpGainRate * numCycles; overMax = true;
this.workDexExpGained += this.workDexExpGainRate * numCycles; numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / Engine._idleSpeed);
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; 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 timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
if (this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) { if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
var maxCycles = CONSTANTS.GameCyclesPer8Hours; return this.finishWorkPartTime();
this.workHackExpGained = this.workHackExpGainRate * maxCycles; }
this.workStrExpGained = this.workStrExpGainRate * maxCycles;
this.workDefExpGained = this.workDefExpGainRate * maxCycles; var comp = Companies[this.companyName], companyRep = "0";
this.workDexExpGained = this.workDexExpGainRate * maxCycles; if (comp == null || !(comp instanceof Company)) {
this.workAgiExpGained = this.workAgiExpGainRate * maxCycles; console.log("ERROR: Could not find Company: " + this.companyName);
this.workChaExpGained = this.workChaExpGainRate * maxCycles; } else {
this.workRepGained = this.workRepGainRate * maxCycles; companyRep = comp.playerReputation;
this.workMoneyGained = this.workMoneyGainRate * maxCycles;
this.finishWorkPartTime();
return;
} }
var txt = document.getElementById("work-in-progress-text"); var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName + 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 been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" + "You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" + "$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * cyclesPerSec, 4) + " / sec) reputation for this company <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 * cyclesPerSec, 4) + " / sec) hacking exp <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 * cyclesPerSec, 4) + " / sec) strength exp <br>" + formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" + formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" + formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br><br> " + formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma 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>" + "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."; "and there will be no penalty because this is a part-time job.";
} }
PlayerObject.prototype.finishWorkPartTime = function(sing=false) { PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
this.gainWorkExp();
var company = Companies[this.companyName]; var company = Companies[this.companyName];
company.playerReputation += (this.workRepGained); company.playerReputation += (this.workRepGained);
@ -978,51 +900,6 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
} }
/* Working for Faction */ /* 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) { PlayerObject.prototype.startFactionWork = function(faction) {
//Update reputation gain rate to account for faction favor //Update reputation gain rate to account for faction favor
var favorMult = 1 + (faction.favor / 100); var favorMult = 1 + (faction.favor / 100);
@ -1117,52 +994,81 @@ PlayerObject.prototype.workForFaction = function(numCycles) {
this.workRepGainRate *= favorMult; this.workRepGainRate *= favorMult;
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain; this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
this.workHackExpGained += this.workHackExpGainRate * numCycles; //Cap the number of cycles being processed to whatever would put you at limit (20 hours)
this.workStrExpGained += this.workStrExpGainRate * numCycles; var overMax = false;
this.workDefExpGained += this.workDefExpGainRate * numCycles; if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer20Hours) {
this.workDexExpGained += this.workDexExpGainRate * numCycles; overMax = true;
this.workAgiExpGained += this.workAgiExpGainRate * numCycles; numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / Engine._idleSpeed);
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; 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 timeWorked == 20 hours, then finish. You can only work for the faction for 20 hours
if (this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) { if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
var maxCycles = CONSTANTS.GameCyclesPer20Hours; return this.finishWork(false);
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);
} }
var txt = document.getElementById("work-in-progress-text"); var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently " + this.currentWorkFactionDescription + " for your faction " + faction.name + 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 been doing this for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"You have earned: <br><br>" + "You have earned: <br><br>" +
"$" + formatNumber(this.workMoneyGained, 2) + " (" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" + "$" + formatNumber(this.workMoneyGained, 2) + " (" + formatNumber(this.workMoneyGainRate * CYCLES_PER_SEC, 2) + " / sec) <br><br>" +
formatNumber(this.workRepGained, 4) + " (" + formatNumber(this.workRepGainRate * cyclesPerSec, 4) + " / sec) reputation for this faction <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 * cyclesPerSec, 4) + " / sec) hacking exp <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 * cyclesPerSec, 4) + " / sec) strength exp <br>" + formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" + formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" + formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br><br> " + formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br><br> " +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma 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>" + "You will automatically finish after working for 20 hours. You can cancel earlier if you wish.<br>" +
"There is no penalty for cancelling earlier."; "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 //Money gained per game cycle
PlayerObject.prototype.getWorkMoneyGain = function() { PlayerObject.prototype.getWorkMoneyGain = function() {
@ -1438,36 +1344,25 @@ PlayerObject.prototype.takeClass = function(numCycles) {
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += Engine._idleSpeed * numCycles;
var className = this.className; var className = this.className;
this.workHackExpGained += this.workHackExpGainRate * numCycles; this.processWorkEarnings(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;
var txt = document.getElementById("work-in-progress-text"); var txt = document.getElementById("work-in-progress-text");
txt.innerHTML = "You have been " + className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + txt.innerHTML = "You have been " + className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
"This has cost you: <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>" + "You have gained: <br>" +
formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * cyclesPerSec, 4) + " / sec) hacking exp <br>" + formatNumber(this.workHackExpGained, 4) + " (" + formatNumber(this.workHackExpGainRate * CYCLES_PER_SEC, 4) + " / sec) hacking exp <br>" +
formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * cyclesPerSec, 4) + " / sec) strength exp <br>" + formatNumber(this.workStrExpGained, 4) + " (" + formatNumber(this.workStrExpGainRate * CYCLES_PER_SEC, 4) + " / sec) strength exp <br>" +
formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * cyclesPerSec, 4) + " / sec) defense exp <br>" + formatNumber(this.workDefExpGained, 4) + " (" + formatNumber(this.workDefExpGainRate * CYCLES_PER_SEC, 4) + " / sec) defense exp <br>" +
formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * cyclesPerSec, 4) + " / sec) dexterity exp <br>" + formatNumber(this.workDexExpGained, 4) + " (" + formatNumber(this.workDexExpGainRate * CYCLES_PER_SEC, 4) + " / sec) dexterity exp <br>" +
formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * cyclesPerSec, 4) + " / sec) agility exp <br>" + formatNumber(this.workAgiExpGained, 4) + " (" + formatNumber(this.workAgiExpGainRate * CYCLES_PER_SEC, 4) + " / sec) agility exp <br>" +
formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * cyclesPerSec, 4) + " / sec) charisma exp <br>" + formatNumber(this.workChaExpGained, 4) + " (" + formatNumber(this.workChaExpGainRate * CYCLES_PER_SEC, 4) + " / sec) charisma exp <br>" +
"You may cancel at any time"; "You may cancel at any time";
} }
//The 'sing' argument defines whether or not this function was called //The 'sing' argument defines whether or not this function was called
//through a Singularity Netscript function //through a Singularity Netscript function
PlayerObject.prototype.finishClass = function(sing=false) { PlayerObject.prototype.finishClass = function(sing=false) {
this.gainWorkExp();
this.gainIntelligenceExp(CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.timeWorked / 1000)); this.gainIntelligenceExp(CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.timeWorked / 1000));
if (this.workMoneyGained > 0) { if (this.workMoneyGained > 0) {
@ -1592,7 +1487,7 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
if (this.committingCrimeThruSingFn) { if (this.committingCrimeThruSingFn) {
if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) { if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime successful! Gained " + 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.workHackExpGained, 3) + " hack exp, " +
formatNumber(this.workStrExpGained, 3) + " str exp, " + formatNumber(this.workStrExpGained, 3) + " str exp, " +
formatNumber(this.workDefExpGained, 3) + " def 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.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null; this.singFnCrimeWorkerScript = null;
@ -1701,7 +1601,7 @@ PlayerObject.prototype.hospitalize = function() {
dialogBoxCreate( dialogBoxCreate(
"You were in critical condition! You were taken to the hospital where " + "You were in critical condition! You were taken to the hospital where " +
"luckily they were able to save your life. You were charged " + "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 //those requirements and will return an array of all factions that the Player should
//receive an invitation to //receive an invitation to
PlayerObject.prototype.checkForFactionInvitations = function() { 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; var numAugmentations = this.augmentations.length;

@ -451,7 +451,7 @@ function loadImportedGame(saveObj, saveString) {
var popupId = "import-game-restart-game-notice"; var popupId = "import-game-restart-game-notice";
var txt = createElement("p", { 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" "to make sure everything runs smoothly"
}); });
var gotitBtn = createElement("a", { var gotitBtn = createElement("a", {
@ -516,9 +516,9 @@ function loadImportedGame(saveObj, saveString) {
Player.lastUpdate = Engine._lastUpdate; Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop Engine.start(); //Run main game loop and Scripts loop
dialogBoxCreate("While you were offline, your scripts generated $" + dialogBoxCreate("While you were offline, your scripts generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromScripts, 2) + " and your Hacknet Nodes generated $" + formatNumber(offlineProductionFromScripts, 2) + "</span> and your Hacknet Nodes generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromHacknetNodes, 2)); formatNumber(offlineProductionFromHacknetNodes, 2) + "</span>");
return true; return true;
} }

32
src/Script.js Normal file → Executable file

@ -21,7 +21,7 @@ import {CONSTANTS} from "./Constants";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings} from "./Fconf"; import {FconfSettings, parseFconfSettings} from "./Fconf";
import {iTutorialSteps, iTutorialNextStep, import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial"; ITutorial} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator"; import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions"; import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript, import {addWorkerScript,
@ -31,15 +31,14 @@ import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings";
import {post} from "./ui/postToTerminal"; import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile"; import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn"; import {parse, Node} from "../utils/acorn";
import {Page, routing} from "./ui/navigationTracking"; import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
import {compareArrays} from "../utils/helpers/compareArrays"; import {compareArrays} from "../utils/helpers/compareArrays";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getTimestamp} from "../utils/helpers/getTimestamp"; import {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo"; import {roundToTwo} from "../utils/helpers/roundToTwo";
@ -252,7 +251,7 @@ function updateScriptEditorContent() {
var codeCopy = code.repeat(1); var codeCopy = code.repeat(1);
var ramUsage = calculateRamUsage(codeCopy); var ramUsage = calculateRamUsage(codeCopy);
if (ramUsage !== -1) { if (ramUsage !== -1) {
scriptEditorRamText.innerText = "RAM: " + formatNumber(ramUsage, 2).toString() + "GB"; scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB";
} else { } else {
scriptEditorRamText.innerText = "RAM: Syntax Error"; scriptEditorRamText.innerText = "RAM: Syntax Error";
} }
@ -281,8 +280,9 @@ function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value; var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor'); var editor = ace.edit('javascript-editor');
var code = editor.getValue(); var code = editor.getValue();
if (iTutorialIsRunning && currITutorialStep == iTutorialSteps.TerminalTypeScript) { if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
if (filename != "foodnstuff.script") { //Make sure filename + code properly follow tutorial
if (filename !== "foodnstuff.script") {
dialogBoxCreate("Leave the script name as 'foodnstuff'!"); dialogBoxCreate("Leave the script name as 'foodnstuff'!");
return; return;
} }
@ -291,7 +291,23 @@ function saveAndCloseScriptEditor() {
dialogBoxCreate("Please copy and paste the code from the tutorial!"); dialogBoxCreate("Please copy and paste the code from the tutorial!");
return; 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 == "") { if (filename == "") {
@ -858,7 +874,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
console.log(runningScriptObj.filename + " called grow() on " + serv.hostname + " " + timesGrown + " times while offline"); console.log(runningScriptObj.filename + " called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
runningScriptObj.log("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); 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 {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram"; import {Programs} from "./CreateProgram";
import {Player} from "./Player"; import {Player} from "./Player";
import {RunningScript, Script} from "./Script"; import {SpecialServerIps} from "./SpecialServerIps";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
import {getRandomInt} from "../utils/helpers/getRandomInt"; import {getRandomInt} from "../utils/helpers/getRandomInt";
import {createRandomIp, ipExists} from "../utils/IPAddress"; import {createRandomIp, ipExists} from "../utils/IPAddress";
import {serverMetadata} from "./data/servers";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress"; import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
@ -125,652 +125,83 @@ Server.fromJSON = function(value) {
Reviver.constructors.Server = Server; Reviver.constructors.Server = Server;
function initForeignServers() { 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 */ /* Create a randomized network for all the foreign servers */
//Groupings for creating a randomized network //Groupings for creating a randomized network
var NetworkGroup1 = [IronGymServer, FoodNStuffServer, SigmaCosmeticsServer, JoesGunsServer, HongFangTeaHouseServer, HaraKiriSushiBarServer]; const networkLayers = [];
var NetworkGroup2 = [MaxHardwareServer, NectarNightclubServer, Zer0NightclubServer, CyberSecServer]; for (let i = 0; i < 15; i++) {
var NetworkGroup3 = [OmegaSoftwareServer, PhantasyServer, SilverHelixServer, NeoNightclubServer]; networkLayers.push([]);
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);
} }
for (var i = 0; i < NetworkGroup3.length; i++) { // Essentially any property that is of type 'number | IMinMaxRange'
var randomServerFromPrevGroup = NetworkGroup2[Math.floor(Math.random() * NetworkGroup2.length)]; const propertiesToPatternMatch = [
NetworkGroup3[i].serversOnNetwork.push(randomServerFromPrevGroup.ip); "hackDifficulty",
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup3[i].ip); "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++) { for (const metadata of serverMetadata) {
var randomServerFromPrevGroup = NetworkGroup3[Math.floor(Math.random() * NetworkGroup3.length)]; const serverParams = {
NetworkGroup4[i].serversOnNetwork.push(randomServerFromPrevGroup.ip); hostname: metadata.hostname,
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup4[i].ip); 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++) { /* Create a randomized network for all the foreign servers */
var randomServerFromPrevGroup = NetworkGroup4[Math.floor(Math.random() * NetworkGroup4.length)]; const linkComputers = (server1, server2) => {
NetworkGroup5[i].serversOnNetwork.push(randomServerFromPrevGroup.ip); server1.serversOnNetwork.push(server2.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup5[i].ip); server2.serversOnNetwork.push(server1.ip);
} };
for (var i = 0; i < NetworkGroup6.length; i++) { const getRandomArrayItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
var randomServerFromPrevGroup = NetworkGroup5[Math.floor(Math.random() * NetworkGroup5.length)];
NetworkGroup6[i].serversOnNetwork.push(randomServerFromPrevGroup.ip);
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup6[i].ip);
}
for (var i = 0; i < NetworkGroup7.length; i++) { const linkNetworkLayers = (network1, selectServer) => {
var randomServerFromPrevGroup = NetworkGroup6[Math.floor(Math.random() * NetworkGroup6.length)]; for (const server of network1) {
NetworkGroup7[i].serversOnNetwork.push(randomServerFromPrevGroup.ip); linkComputers(server, selectServer());
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup7[i].ip); }
} };
for (var i = 0; i < NetworkGroup8.length; i++) { // Connect the first tier of servers to the player's home computer
var randomServerFromPrevGroup = NetworkGroup7[Math.floor(Math.random() * NetworkGroup7.length)]; linkNetworkLayers(networkLayers[0], () => Player.getHomeComputer());
NetworkGroup8[i].serversOnNetwork.push(randomServerFromPrevGroup.ip); for (let i = 1; i < networkLayers.length; i++) {
randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup8[i].ip); linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1]));
}
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);
} }
} }

@ -19,6 +19,11 @@ interface IDefaultSettings {
*/ */
DisableHotkeys: boolean; DisableHotkeys: boolean;
/**
* Locale used for display numbers
*/
Locale: string;
/** /**
* Limit the number of log entries for each script being executed on each server. * Limit the number of log entries for each script being executed on each server.
*/ */
@ -39,6 +44,11 @@ interface IDefaultSettings {
*/ */
SuppressFactionInvites: boolean; 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. * 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. * Whether the user should be asked to confirm travelling between cities.
*/ */
SuppressTravelConfirmation: boolean; 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, AutosaveInterval: 60,
CodeInstructionRunTime: 50, CodeInstructionRunTime: 50,
DisableHotkeys: false, DisableHotkeys: false,
Locale: "en",
MaxLogCapacity: 50, MaxLogCapacity: 50,
MaxPortCapacity: 50, MaxPortCapacity: 50,
SuppressBuyAugmentationConfirmation: false, SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false, SuppressFactionInvites: false,
SuppressHospitalizationPopup: false,
SuppressMessages: false, SuppressMessages: false,
SuppressTravelConfirmation: false, SuppressTravelConfirmation: false,
SuppressHospitalizationPopup: false,
}; };
/** /**
@ -110,13 +116,14 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
DisableHotkeys: defaultSettings.DisableHotkeys, DisableHotkeys: defaultSettings.DisableHotkeys,
EditorKeybinding: "ace", EditorKeybinding: "ace",
EditorTheme: "Monokai", EditorTheme: "Monokai",
Locale: "en",
MaxLogCapacity: defaultSettings.MaxLogCapacity, MaxLogCapacity: defaultSettings.MaxLogCapacity,
MaxPortCapacity: defaultSettings.MaxPortCapacity, MaxPortCapacity: defaultSettings.MaxPortCapacity,
SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation, SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation,
SuppressFactionInvites: defaultSettings.SuppressFactionInvites, SuppressFactionInvites: defaultSettings.SuppressFactionInvites,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
SuppressMessages: defaultSettings.SuppressMessages, SuppressMessages: defaultSettings.SuppressMessages,
SuppressTravelConfirmation: defaultSettings.SuppressTravelConfirmation, SuppressTravelConfirmation: defaultSettings.SuppressTravelConfirmation,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
ThemeBackgroundColor: "#000000", ThemeBackgroundColor: "#000000",
ThemeFontColor: "#66ff33", ThemeFontColor: "#66ff33",
ThemeHighlightColor: "#ffffff", 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 {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions";
import {WorkerScript} from "./NetscriptWorker"; import {WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import {Stock} from "./Stock";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners"; import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../utils/JSONReviver";
import {Page, routing} from "./ui/navigationTracking"; 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 {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getRandomInt} from "../utils/helpers/getRandomInt"; import {getRandomInt} from "../utils/helpers/getRandomInt";
import {KEY} from "../utils/helpers/keyCodes"; 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 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 = { var OrderTypes = {
LimitBuy: "Limit Buy Order", LimitBuy: "Limit Buy Order",
LimitSell: "Limit Sell 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 //Order properties are passed in. Need to look for the order
var stockOrders = StockMarket["Orders"][params.stock.symbol]; var stockOrders = StockMarket["Orders"][params.stock.symbol];
var orderTxt = params.stock.symbol + " - " + params.shares + " @ " + 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) { for (var i = 0; i < stockOrders.length; ++i) {
var order = stockOrders[i]; var order = stockOrders[i];
if (params.shares === order.shares && if (params.shares === order.shares &&
@ -253,135 +228,135 @@ function initStockMarket() {
} }
var ecorp = Locations.AevumECorp; 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; StockMarket[ecorp] = ecorpStk;
var megacorp = Locations.Sector12MegaCorp; 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; StockMarket[megacorp] = megacorpStk;
var blade = Locations.Sector12BladeIndustries; 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; StockMarket[blade] = bladeStk;
var clarke = Locations.AevumClarkeIncorporated; 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; StockMarket[clarke] = clarkeStk;
var omnitek = Locations.VolhavenOmniTekIncorporated; 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; StockMarket[omnitek] = omnitekStk;
var foursigma = Locations.Sector12FourSigma; 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; StockMarket[foursigma] = foursigmaStk;
var kuaigong = Locations.ChongqingKuaiGongInternational; 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; StockMarket[kuaigong] = kuaigongStk;
var fulcrum = Locations.AevumFulcrumTechnologies; 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; StockMarket[fulcrum] = fulcrumStk;
var storm = Locations.IshimaStormTechnologies; 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; StockMarket[storm] = stormStk;
var defcomm = Locations.NewTokyoDefComm; 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; StockMarket[defcomm] = defcommStk;
var helios = Locations.VolhavenHeliosLabs; 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; StockMarket[helios] = heliosStk;
var vitalife = Locations.NewTokyoVitaLife; 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; StockMarket[vitalife] = vitalifeStk;
var icarus = Locations.Sector12IcarusMicrosystems; 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; StockMarket[icarus] = icarusStk;
var universalenergy = Locations.Sector12UniversalEnergy; 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; StockMarket[universalenergy] = universalenergyStk;
var aerocorp = Locations.AevumAeroCorp; 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; StockMarket[aerocorp] = aerocorpStk;
var omnia = Locations.VolhavenOmniaCybersystems; 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; StockMarket[omnia] = omniaStk;
var solaris = Locations.ChongqingSolarisSpaceSystems; 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; StockMarket[solaris] = solarisStk;
var globalpharm = Locations.NewTokyoGlobalPharmaceuticals; 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; StockMarket[globalpharm] = globalpharmStk;
var nova = Locations.IshimaNovaMedical; 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; StockMarket[nova] = novaStk;
var watchdog = Locations.AevumWatchdogSecurity; 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; StockMarket[watchdog] = watchdogStk;
var lexocorp = Locations.VolhavenLexoCorp; 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; StockMarket[lexocorp] = lexocorpStk;
var rho = Locations.AevumRhoConstruction; 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; StockMarket[rho] = rhoStk;
var alpha = Locations.Sector12AlphaEnterprises; 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; StockMarket[alpha] = alphaStk;
var syscore = Locations.VolhavenSysCoreSecurities; 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; StockMarket[syscore] = syscoreStk;
var computek = Locations.VolhavenCompuTek; 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; StockMarket[computek] = computekStk;
var netlink = Locations.AevumNetLinkTechnologies; 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; StockMarket[netlink] = netlinkStk;
var omega = Locations.IshimaOmegaSoftware; 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; StockMarket[omega] = omegaStk;
var fns = Locations.Sector12FoodNStuff; 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; StockMarket[fns] = fnsStk;
var sigmacosm = "Sigma Cosmetics"; 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; StockMarket[sigmacosm] = sigmacosmStk;
var joesguns = "Joes Guns"; 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; StockMarket[joesguns] = joesgunsStk;
var catalyst = "Catalyst Ventures"; 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; StockMarket[catalyst] = catalystStk;
var microdyne = "Microdyne Technologies"; 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; StockMarket[microdyne] = microdyneStk;
var titanlabs = "Titan Laboratories"; 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; StockMarket[titanlabs] = titanlabsStk;
var orders = {}; var orders = {};
@ -435,7 +410,7 @@ function buyStock(stock, shares) {
var totalPrice = stock.price * shares; var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) { if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
dialogBoxCreate("You do not have enough money to purchase this. You need " + 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; return false;
} }
@ -445,9 +420,9 @@ function buyStock(stock, shares) {
stock.playerShares += shares; stock.playerShares += shares;
stock.playerAvgPx = newTotal / stock.playerShares; stock.playerAvgPx = newTotal / stock.playerShares;
updateStockPlayerPosition(stock); updateStockPlayerPosition(stock);
dialogBoxCreate("Bought " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " + dialogBoxCreate("Bought " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees."); numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
return true; return true;
} }
@ -468,9 +443,9 @@ function sellStock(stock, shares) {
stock.playerAvgPx = 0; stock.playerAvgPx = 0;
} }
updateStockPlayerPosition(stock); updateStockPlayerPosition(stock);
dialogBoxCreate("Sold " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " + dialogBoxCreate("Sold " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(gains).format('($0.000a)') + "."); "a total of " + numeralWrapper.format(gains, '($0.000a)') + ".");
return true; return true;
} }
@ -494,10 +469,10 @@ function shortStock(stock, shares, workerScript=null) {
if (tixApi) { if (tixApi) {
workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have enough " + workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have enough " +
"money to purchase this short position. You need " + "money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + "."); numeralWrapper.format(totalPrice + CONSTANTS.StockMarketCommission, '($0.000a)') + ".");
} else { } else {
dialogBoxCreate("You do not have enough money to purchase this short position. You need " + 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; return false;
@ -511,14 +486,14 @@ function shortStock(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock); updateStockPlayerPosition(stock);
if (tixApi) { if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.shortStock == null) { 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 " + workerScript.scriptRef.log("Bought a short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees."); numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
} }
} else { } else {
dialogBoxCreate("Bought a short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " + dialogBoxCreate("Bought a short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees."); numeralWrapper.format(CONSTANTS.StockMarketCommission, '($0.000a)') + " in commission fees.");
} }
return true; return true;
} }
@ -555,14 +530,14 @@ function sellShort(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock); updateStockPlayerPosition(stock);
if (tixApi) { if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.sellShort == null) { 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 " + workerScript.scriptRef.log("Sold your short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)') + "."); "a total of " + numeralWrapper.format(origCost + profit, '($0.000a)') + ".");
} }
} else { } else {
dialogBoxCreate("Sold your short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " + dialogBoxCreate("Sold your short position of " + numeralWrapper.format(shares, '0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " + numeralWrapper.format(stock.price, '($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)') + "."); "a total of " + numeralWrapper.format(origCost + profit, '($0.000a)') + ".");
} }
return true; return true;
@ -721,7 +696,7 @@ function displayStockMarketContent() {
//Purchase WSE Account button //Purchase WSE Account button
var wseAccountButton = clearEventListeners("stock-market-buy-account"); var wseAccountButton = clearEventListeners("stock-market-buy-account");
stylePurchaseButton(wseAccountButton, CONSTANTS.WSEAccountCost, Player.hasWseAccount, 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"); "WSE Account - Purchased");
wseAccountButton.addEventListener("click", function() { wseAccountButton.addEventListener("click", function() {
Player.hasWseAccount = true; Player.hasWseAccount = true;
@ -735,7 +710,7 @@ function displayStockMarketContent() {
//Purchase TIX API Access account //Purchase TIX API Access account
var tixApiAccessButton = clearEventListeners("stock-market-buy-tix-api"); var tixApiAccessButton = clearEventListeners("stock-market-buy-tix-api");
stylePurchaseButton(tixApiAccessButton, CONSTANTS.TIXAPICost, Player.hasTixApiAccess, 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"); "TIX API Access - Purchased");
tixApiAccessButton.addEventListener("click", function() { tixApiAccessButton.addEventListener("click", function() {
Player.hasTixApiAccess = true; Player.hasTixApiAccess = true;
@ -747,7 +722,7 @@ function displayStockMarketContent() {
//Purchase Four Sigma Market Data Feed //Purchase Four Sigma Market Data Feed
var marketDataButton = clearEventListeners("stock-market-buy-4s-data"); var marketDataButton = clearEventListeners("stock-market-buy-4s-data");
stylePurchaseButton(marketDataButton, CONSTANTS.MarketData4SCost, Player.has4SData, 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"); "4S Market Data - Purchased");
marketDataButton.addEventListener("click", function() { marketDataButton.addEventListener("click", function() {
Player.has4SData = true; Player.has4SData = true;
@ -785,7 +760,7 @@ function displayStockMarketContent() {
//Purchase Four Sigma Market Data TIX API (Requires TIX API Access) //Purchase Four Sigma Market Data TIX API (Requires TIX API Access)
var marketDataTixButton = clearEventListeners("stock-market-buy-4s-tix-api"); var marketDataTixButton = clearEventListeners("stock-market-buy-4s-tix-api");
stylePurchaseButton(marketDataTixButton, CONSTANTS.MarketDataTixApi4SCost, Player.has4SDataTixApi, 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"); "4S Market Data TIX API - Purchased");
if (Player.hasTixApiAccess) { if (Player.hasTixApiAccess) {
marketDataTixButton.addEventListener("click", function() { marketDataTixButton.addEventListener("click", function() {
@ -848,7 +823,7 @@ function displayStockMarketContent() {
console.log("Creating Stock Market UI"); console.log("Creating Stock Market UI");
commissionText.innerHTML = commissionText.innerHTML =
"Commission Fees: Every transaction you make has a " + "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. " + "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 " + "This means all your positions are lost, so make sure to sell your stocks before installing " +
"Augmentations!"; "Augmentations!";
@ -1045,7 +1020,7 @@ function createStockTicker(stock) {
var li = document.createElement("li"), hdr = document.createElement("button"); var li = document.createElement("li"), hdr = document.createElement("button");
hdr.classList.add("accordion-header"); hdr.classList.add("accordion-header");
hdr.setAttribute("id", tickerId + "-hdr"); 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 //Div for entire panel
var stockDiv = document.createElement("div"); var stockDiv = document.createElement("div");
@ -1322,9 +1297,9 @@ function updateStockTicker(stock, increase) {
} }
return; 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) { if (Player.has4SData) {
hdrText += " - Volatility: " + numeral(stock.mv).format('0,0.00') + "%" + hdrText += " - Volatility: " + numeralWrapper.format(stock.mv, '0,0.00') + "%" +
" - Price Forecast: "; " - Price Forecast: ";
if (stock.b) { if (stock.b) {
hdrText += "+".repeat(Math.floor(stock.otlkMag/10) + 1); hdrText += "+".repeat(Math.floor(stock.otlkMag/10) + 1);
@ -1388,21 +1363,21 @@ function updateStockPlayerPosition(stock) {
"<h1 class='tooltip stock-market-position-text'>Long Position: " + "<h1 class='tooltip stock-market-position-text'>Long Position: " +
"<span class='tooltiptext'>Shares in the long position will increase " + "<span class='tooltiptext'>Shares in the long position will increase " +
"in value if the price of the corresponding stock increases</span></h1>" + "in value if the price of the corresponding stock increases</span></h1>" +
"<br>Shares: " + numeral(stock.playerShares).format('0,0') + "<br>Shares: " + numeralWrapper.format(stock.playerShares, '0,0') +
"<br>Average Price: " + numeral(stock.playerAvgPx).format('$0.000a') + "<br>Average Price: " + numeralWrapper.format(stock.playerAvgPx, '$0.000a') +
" (Total Cost: " + numeral(totalCost).format('$0.000a') + ")" + " (Total Cost: " + numeralWrapper.format(totalCost, '$0.000a') + ")" +
"<br>Profit: " + numeral(gains).format('$0.000a') + "<br>Profit: " + numeralWrapper.format(gains, '$0.000a') +
" (" + numeral(percentageGains).format('0.00%') + ")<br><br>"; " (" + numeralWrapper.format(percentageGains, '0.00%') + ")<br><br>";
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) { if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
stock.posTxtEl.innerHTML += stock.posTxtEl.innerHTML +=
"<h1 class='tooltip stock-market-position-text'>Short Position: " + "<h1 class='tooltip stock-market-position-text'>Short Position: " +
"<span class='tooltiptext'>Shares in short position will increase " + "<span class='tooltiptext'>Shares in short position will increase " +
"in value if the price of the corresponding stock decreases</span></h1>" + "in value if the price of the corresponding stock decreases</span></h1>" +
"<br>Shares: " + numeral(stock.playerShortShares).format('0,0') + "<br>Shares: " + numeralWrapper.format(stock.playerShortShares, '0,0') +
"<br>Average Price: " + numeral(stock.playerAvgShortPx).format('$0.000a') + "<br>Average Price: " + numeralWrapper.format(stock.playerAvgShortPx, '$0.000a') +
" (Total Cost: " + numeral(shortTotalCost).format('$0.000a') + ")" + " (Total Cost: " + numeralWrapper.format(shortTotalCost, '$0.000a') + ")" +
"<br>Profit: " + numeral(shortGains).format('$0.000a') + "<br>Profit: " + numeralWrapper.format(shortGains, '$0.000a') +
" (" + numeral(shortPercentageGains).format('0.00%') + ")" + " (" + numeralWrapper.format(shortPercentageGains, '0.00%') + ")" +
"<br><br><h1 class='stock-market-position-text'>Orders: </h1>"; "<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"); var posText = (order.pos === PositionTypes.Long ? "Long Position" : "Short Position");
li.style.color = "white"; li.style.color = "white";
li.innerText = order.type + " - " + posText + " - " + 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"); var cancelButton = document.createElement("span");
cancelButton.classList.add("stock-market-order-cancel-btn"); cancelButton.classList.add("stock-market-order-cancel-btn");

@ -10,17 +10,18 @@ import {executeDarkwebTerminalCommand,
import {Engine} from "./engine"; import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings, import {FconfSettings, parseFconfSettings,
createFconf} from "./Fconf"; createFconf} from "./Fconf";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import {TerminalHelpText, HelpTexts} from "./HelpText"; import {TerminalHelpText, HelpTexts} from "./HelpText";
import {iTutorialNextStep, iTutorialSteps, import {iTutorialNextStep, iTutorialSteps,
iTutorialIsRunning, ITutorial} from "./InteractiveTutorial";
currITutorialStep} from "./InteractiveTutorial";
import {showLiterature} from "./Literature"; import {showLiterature} from "./Literature";
import {showMessage, Message} from "./Message"; import {showMessage, Message} from "./Message";
import {scriptCalculateHackingTime,
scriptCalculateGrowTime,
scriptCalculateWeakenTime} from "./NetscriptEvaluator";
import {killWorkerScript, addWorkerScript} from "./NetscriptWorker"; import {killWorkerScript, addWorkerScript} from "./NetscriptWorker";
import numeral from "numeral/min/numeral.min";
import {Player} from "./Player"; import {Player} from "./Player";
import {hackWorldDaemon} from "./RedPill"; import {hackWorldDaemon} from "./RedPill";
import {findRunningScript, RunningScript, import {findRunningScript, RunningScript,
@ -32,10 +33,10 @@ import {Settings} from "./Settings";
import {SpecialServerIps, import {SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps"; SpecialServerNames} from "./SpecialServerIps";
import {TextFile, getTextFile} from "./TextFile"; import {TextFile, getTextFile} from "./TextFile";
import {containsAllStrings,
import {containsAllStrings, longestCommonStart, longestCommonStart} from "../utils/StringHelperFunctions";
formatNumber} from "../utils/StringHelperFunctions";
import {Page, routing} from "./ui/navigationTracking"; import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {KEY} from "../utils/helpers/keyCodes"; import {KEY} from "../utils/helpers/keyCodes";
import {addOffset} from "../utils/helpers/addOffset"; import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString"; import {isString} from "../utils/helpers/isString";
@ -48,6 +49,7 @@ import {yesNoBoxCreate,
import {post, hackProgressBarPost, import {post, hackProgressBarPost,
hackProgressPost} from "./ui/postToTerminal"; hackProgressPost} from "./ui/postToTerminal";
import autosize from 'autosize';
import * as JSZip from 'jszip'; import * as JSZip from 'jszip';
import * as FileSaver from 'file-saver'; import * as FileSaver from 'file-saver';
@ -64,12 +66,12 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) { if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val(); var command = terminalInput.value;
post( post(
"[" + "<span class='prompt'>[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") + (FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname + Player.getCurrentServer().hostname +
" ~]> " + command " ~]&gt;</span> " + command
); );
if (command.length > 0) { if (command.length > 0) {
@ -513,16 +515,27 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
let Terminal = { let Terminal = {
//Flags to determine whether the player is currently running a hack or an analyze //Flags to determine whether the player is currently running a hack or an analyze
hackFlag: false, hackFlag: false,
analyzeFlag: false, analyzeFlag: false,
actionStarted: false,
actionTime: 0,
commandHistory: [], commandHistory: [],
commandHistoryIndex: 0, commandHistoryIndex: 0,
resetTerminalInput: function() { resetTerminalInput: function() {
document.getElementById("terminal-input-td").innerHTML = if (FconfSettings.WRAP_INPUT) {
"<div id='terminal-input-header'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" + document.getElementById("terminal-input-td").innerHTML =
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>'; "<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"); var hdr = document.getElementById("terminal-input-header");
hdr.style.display = "inline"; 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) { finishAction: function(cancelled = false) {
if (Terminal.hackFlag) { if (Terminal.hackFlag) {
Terminal.finishHack(cancelled); Terminal.finishHack(cancelled);
@ -634,10 +673,10 @@ let Terminal = {
var server = Player.getCurrentServer(); var server = Player.getCurrentServer();
//Calculate whether hack was successful //Calculate whether hack was successful
var hackChance = Player.calculateHackingChance(); var hackChance = calculateHackingChance(server);
var rand = Math.random(); var rand = Math.random();
console.log("Hack success chance: " + hackChance + ", rand: " + rand); console.log("Hack success chance: " + hackChance + ", rand: " + rand);
var expGainedOnSuccess = Player.calculateExpGain(); var expGainedOnSuccess = calculateHackingExpGain(server);
var expGainedOnFailure = (expGainedOnSuccess / 4); var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success! if (rand < hackChance) { //Success!
if (SpecialServerIps[SpecialServerNames.WorldDaemon] && if (SpecialServerIps[SpecialServerNames.WorldDaemon] &&
@ -649,7 +688,7 @@ let Terminal = {
return; return;
} }
server.manuallyHacked = true; server.manuallyHacked = true;
var moneyGained = Player.calculatePercentMoneyHacked(); var moneyGained = calculatePercentMoneyHacked(server);
moneyGained = Math.floor(server.moneyAvailable * moneyGained); moneyGained = Math.floor(server.moneyAvailable * moneyGained);
if (moneyGained <= 0) {moneyGained = 0;} //Safety check if (moneyGained <= 0) {moneyGained = 0;} //Safety check
@ -661,11 +700,11 @@ let Terminal = {
server.fortify(CONSTANTS.ServerFortifyAmount); 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 } else { //Failure
//Player only gains 25% exp for failure? TODO Can change this later to balance //Player only gains 25% exp for failure? TODO Can change this later to balance
Player.gainHackingExp(expGainedOnFailure) 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) { finishAnalyze: function(cancelled = false) {
if (cancelled == false) { if (cancelled == false) {
post(Player.getCurrentServer().hostname + ": "); let currServ = Player.getCurrentServer();
post("Organization name: " + Player.getCurrentServer().organizationName); post(currServ.hostname + ": ");
post("Organization name: " + currServ.organizationName);
var rootAccess = ""; var rootAccess = "";
if (Player.getCurrentServer().hasAdminRights) {rootAccess = "YES";} if (currServ.hasAdminRights) {rootAccess = "YES";}
else {rootAccess = "NO";} else {rootAccess = "NO";}
post("Root Access: " + rootAccess); post("Root Access: " + rootAccess);
post("Required hacking skill: " + Player.getCurrentServer().requiredHackingSkill); post("Required hacking skill: " + currServ.requiredHackingSkill);
post("Estimated server security level: " + formatNumber(addOffset(Player.getCurrentServer().hackDifficulty, 5), 3)); post("Server security level: " + numeralWrapper.format(currServ.hackDifficulty, '0.000a'));
post("Estimated chance to hack: " + formatNumber(addOffset(Player.calculateHackingChance() * 100, 5), 2) + "%"); post("Chance to hack: " + numeralWrapper.format(calculateHackingChance(currServ) * 100, '0.00%'));
post("Estimated time to hack: " + formatNumber(addOffset(Player.calculateHackingTime(), 5), 3) + " seconds"); post("Time to hack: " + numeralWrapper.format(calculateHackingTime(currServ), '0.000') + " seconds");
post("Estimated total money available on server: $" + formatNumber(addOffset(Player.getCurrentServer().moneyAvailable, 5), 2)); post("Total money available on server: $" + numeralWrapper.format(currServ.moneyAvailable, '$0,0.00'));
post("Required number of open ports for NUKE: " + Player.getCurrentServer().numOpenPortsRequired); post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired);
if (Player.getCurrentServer().sshPortOpen) {
if (currServ.sshPortOpen) {
post("SSH port: Open") post("SSH port: Open")
} else { } else {
post("SSH port: Closed") post("SSH port: Closed")
} }
if (Player.getCurrentServer().ftpPortOpen) { if (currServ.ftpPortOpen) {
post("FTP port: Open") post("FTP port: Open")
} else { } else {
post("FTP port: Closed") post("FTP port: Closed")
} }
if (Player.getCurrentServer().smtpPortOpen) { if (currServ.smtpPortOpen) {
post("SMTP port: Open") post("SMTP port: Open")
} else { } else {
post("SMTP port: Closed") post("SMTP port: Closed")
} }
if (Player.getCurrentServer().httpPortOpen) { if (currServ.httpPortOpen) {
post("HTTP port: Open") post("HTTP port: Open")
} else { } else {
post("HTTP port: Closed") post("HTTP port: Closed")
} }
if (Player.getCurrentServer().sqlPortOpen) { if (currServ.sqlPortOpen) {
post("SQL port: Open") post("SQL port: Open")
} else { } else {
post("SQL port: Closed") post("SQL port: Closed")
@ -731,7 +772,56 @@ let Terminal = {
$('input[class=terminal-input]').prop('disabled', false); $('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(); command = command.trim();
//Replace all extra whitespace in command with a single space //Replace all extra whitespace in command with a single space
command = command.replace(/\s\s+/g, ' '); command = command.replace(/\s\s+/g, ' ');
@ -762,11 +852,11 @@ let Terminal = {
if (commandArray.length == 0) {return;} if (commandArray.length == 0) {return;}
/****************** Interactive Tutorial Terminal Commands ******************/ /****************** Interactive Tutorial Terminal Commands ******************/
if (iTutorialIsRunning) { if (ITutorial.isRunning) {
var foodnstuffServ = GetServerByHostname("foodnstuff"); var foodnstuffServ = GetServerByHostname("foodnstuff");
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;} if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
switch(currITutorialStep) { switch(ITutorial.currStep) {
case iTutorialSteps.TerminalHelp: case iTutorialSteps.TerminalHelp:
if (commandArray[0] == "help") { if (commandArray[0] == "help") {
post(TerminalHelpText); post(TerminalHelpText);
@ -816,17 +906,7 @@ let Terminal = {
if (commandArray.length != 1) { if (commandArray.length != 1) {
post("Incorrect usage of analyze command. Usage: analyze"); return; post("Incorrect usage of analyze command. Usage: analyze"); return;
} }
//Analyze the current server for information Terminal.startAnalyze();
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);
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
post("Bad command. Please follow the tutorial"); post("Bad command. Please follow the tutorial");
@ -842,15 +922,7 @@ let Terminal = {
break; break;
case iTutorialSteps.TerminalManualHack: case iTutorialSteps.TerminalManualHack:
if (commandArray.length == 1 && commandArray[0] == "hack") { if (commandArray.length == 1 && commandArray[0] == "hack") {
Terminal.hackFlag = true; Terminal.startHack();
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);
iTutorialNextStep(); iTutorialNextStep();
} else {post("Bad command. Please follow the tutorial");} } else {post("Bad command. Please follow the tutorial");}
break; break;
@ -921,17 +993,7 @@ let Terminal = {
if (commandArray.length != 1) { if (commandArray.length != 1) {
post("Incorrect usage of analyze command. Usage: analyze"); return; post("Incorrect usage of analyze command. Usage: analyze"); return;
} }
//Analyze the current server for information Terminal.startAnalyze();
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);
break; break;
case "buy": case "buy":
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) { if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
@ -1084,15 +1146,7 @@ let Terminal = {
} else if (Player.getCurrentServer().requiredHackingSkill > Player.hacking_skill) { } 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"); post("Your hacking skill is not high enough to attempt hacking this machine. Try analyzing the machine to determine the required hacking skill");
} else { } else {
Terminal.hackFlag = true; Terminal.startHack();
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);
} }
break; break;
case "help": case "help":
@ -1191,7 +1245,7 @@ let Terminal = {
var scriptBaseRamUsage = currServ.scripts[i].ramUsage; var scriptBaseRamUsage = currServ.scripts[i].ramUsage;
var ramUsage = scriptBaseRamUsage * numThreads; 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; return;
} }
} }
@ -1346,6 +1400,7 @@ let Terminal = {
post("Incorrect usage of scan-analyze command. usage: scan-analyze [depth]"); post("Incorrect usage of scan-analyze command. usage: scan-analyze [depth]");
} }
break; break;
/* eslint-disable no-case-declarations */
case "scp": case "scp":
if (commandArray.length != 2) { if (commandArray.length != 2) {
post("Incorrect usage of scp command. Usage: scp [file] [destination hostname/ip]"); post("Incorrect usage of scp command. Usage: scp [file] [destination hostname/ip]");
@ -1389,8 +1444,7 @@ let Terminal = {
} }
} }
destServer.messages.push(scriptname); destServer.messages.push(scriptname);
post(scriptname + " copied over to " + destServer.hostname); return post(scriptname + " copied over to " + destServer.hostname);
return;
} }
//Scp for txt files //Scp for txt files
@ -1406,18 +1460,15 @@ let Terminal = {
if (!found) {return post("Error: no such file exists!");} if (!found) {return post("Error: no such file exists!");}
for (var i = 0; i < destServer.textFiles.length; ++i) { let tRes = Terminal.writeToTextFile(destServer, txtFile.fn, txtFile.text);
if (destServer.textFiles[i].fn === scriptname) { if (!tRes.success) {
//Overwrite return post("Error: scp failed");
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);
}
} }
var newFile = new TextFile(txtFile.fn, txtFile.text); if (tRes.overwritten) {
destServer.textFiles.push(newFile); post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwriten`);
return post(scriptname + " copied over to " + destServer.hostname); return post(`${scriptname} overwritten on ${destServer.hostname}`);
}
return post(`${scriptname} copied over to ${destServer.hostname}`);
} }
//Get the current script //Get the current script
@ -1433,27 +1484,17 @@ let Terminal = {
return; return;
} }
//Overwrite script if it exists let sRes = Terminal.writeToScriptFile(destServer, scriptname, sourceScript.code);
for (var i = 0; i < destServer.scripts.length; ++i) { if (!sRes.success) {
if (scriptname == destServer.scripts[i].filename) { return post(`Error: scp failed`);
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;
}
} }
if (sRes.overwritten) {
var newScript = new Script(); post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwritten`);
newScript.filename = scriptname; return post(`${scriptname} overwritten on ${destServer.hostname}`);
newScript.code = sourceScript.code; }
newScript.ramUsage = sourceScript.ramUsage; post(`${scriptname} copied over to ${destServer.hostname}`);
newScript.destServer = ip; break;
destServer.scripts.push(newScript); /* eslint-enable no-case-declarations */
post(scriptname + " copied over to " + destServer.hostname);
break;
case "sudov": case "sudov":
if (commandArray.length != 1) { if (commandArray.length != 1) {
post("Incorrect number of arguments. Usage: sudov"); return; post("Incorrect number of arguments. Usage: sudov"); return;
@ -1556,7 +1597,7 @@ let Terminal = {
var spacesThread = Array(numSpacesThread+1).join(" "); var spacesThread = Array(numSpacesThread+1).join(" ");
//Calculate and transform RAM usage //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]; var entry = [script.filename, spacesScript, script.threads, spacesThread, ramUsage];
post(entry.join("")); post(entry.join(""));
@ -1577,6 +1618,40 @@ let Terminal = {
} }
} }
break; 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: default:
post("Command not found"); post("Command not found");
} }
@ -1769,9 +1844,9 @@ let Terminal = {
if (commandArray.length != 1) { if (commandArray.length != 1) {
post("Incorrect usage of free command. Usage: free"); return; post("Incorrect usage of free command. Usage: free"); return;
} }
post("Total: " + formatNumber(Player.getCurrentServer().maxRam, 2) + " GB"); post("Total: " + numeralWrapper.format(Player.getCurrentServer().maxRam, '0.00') + " GB");
post("Used: " + formatNumber(Player.getCurrentServer().ramUsed, 2) + " GB"); post("Used: " + numeralWrapper.format(Player.getCurrentServer().ramUsed, '0.00') + " GB");
post("Available: " + formatNumber(Player.getCurrentServer().maxRam - Player.getCurrentServer().ramUsed, 2) + " 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 //First called when the "run [program]" command is called. Checks to see if you
@ -1892,9 +1967,9 @@ let Terminal = {
post("Server base security level: " + targetServer.baseDifficulty); post("Server base security level: " + targetServer.baseDifficulty);
post("Server current security level: " + targetServer.hackDifficulty); post("Server current security level: " + targetServer.hackDifficulty);
post("Server growth rate: " + targetServer.serverGrowth); post("Server growth rate: " + targetServer.serverGrowth);
post("Netscript hack() execution time: " + formatNumber(scriptCalculateHackingTime(targetServer), 1) + "s"); post("Netscript hack() execution time: " + numeralWrapper.format(scriptCalculateHackingTime(targetServer), '0.0') + "s");
post("Netscript grow() execution time: " + formatNumber(scriptCalculateGrowTime(targetServer)/1000, 1) + "s"); post("Netscript grow() execution time: " + numeralWrapper.format(scriptCalculateGrowTime(targetServer), '0.0') + "s");
post("Netscript weaken() execution time: " + formatNumber(scriptCalculateWeakenTime(targetServer)/1000, 1) + "s"); post("Netscript weaken() execution time: " + numeralWrapper.format(scriptCalculateWeakenTime(targetServer), '0.0') + "s");
}; };
programHandlers[Programs.AutoLink.name] = () => { programHandlers[Programs.AutoLink.name] = () => {
post("This executable cannot be run."); post("This executable cannot be run.");
@ -1920,7 +1995,7 @@ let Terminal = {
if(!fulfilled) { if(!fulfilled) {
post("Augmentations: " + Player.augmentations.length + " / 30"); 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("One path below must be fulfilled...");
post("----------HACKING PATH----------"); post("----------HACKING PATH----------");
post("Hacking skill: " + Player.hacking_skill + " / 2500"); post("Hacking skill: " + Player.hacking_skill + " / 2500");
@ -2053,6 +2128,7 @@ let Terminal = {
} }
} }
post("ERROR: No such script"); 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 {createElement} from "../utils/uiHelpers/createElement";
import {exceptionAlert} from "../utils/helpers/exceptionAlert"; import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {removeLoadingScreen} from "../utils/uiHelpers/removeLoadingScreen"; import {removeLoadingScreen} from "../utils/uiHelpers/removeLoadingScreen";
import numeral from "numeral/min/numeral.min";
import {numeralWrapper} from "./ui/numeralFormat";
import {formatNumber, import {formatNumber,
convertTimeMsToTimeElapsedString, convertTimeMsToTimeElapsedString,
replaceAt} from "../utils/StringHelperFunctions"; replaceAt} from "../utils/StringHelperFunctions";
import {loxBoxCreate, logBoxUpdateText, import {loxBoxCreate, logBoxUpdateText,
logBoxOpened} from "../utils/LogBox"; logBoxOpened} from "../utils/LogBox";
import {updateActiveScriptsItems} from "./ActiveScriptsUI"; import {updateActiveScriptsItems} from "./ActiveScriptsUI";
import {Augmentations, installAugmentations, import {Augmentations, installAugmentations,
initAugmentations, AugmentationNames, initAugmentations, AugmentationNames,
@ -150,7 +151,7 @@ $(document).keydown(function(e) {
} }
}); });
let Engine = { const Engine = {
version: "", version: "",
Debug: true, Debug: true,
overview: new CharacterOverview(), overview: new CharacterOverview(),
@ -570,7 +571,7 @@ let Engine = {
var intText = ""; var intText = "";
if (Player.intelligence > 0) { if (Player.intelligence > 0) {
intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + "<br><br><br>"; intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + '<br>';
} }
let bitNodeTimeText = ""; let bitNodeTimeText = "";
@ -584,21 +585,21 @@ let Engine = {
'Current City: ' + Player.city + '<br><br>' + 'Current City: ' + Player.city + '<br><br>' +
'Employer: ' + Player.companyName + '<br>' + 'Employer: ' + Player.companyName + '<br>' +
'Job Title: ' + companyPosition + '<br><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>' + '<b>Stats</b><br><br>' +
'Hacking Level: ' + (Player.hacking_skill).toLocaleString() + '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() + '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() + '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() + '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() + '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() + 'Charisma: ' + (Player.charisma).toLocaleString() +
" (" + numeral(Player.charisma_exp).format('(0.000a)') + ' experience)<br>' + ' (' + numeralWrapper.format(Player.charisma_exp, '(0.000a)') + ' experience)<br>' +
intText + intText + '<br><br>' +
'<b>Multipliers</b><br><br>' + '<b>Multipliers</b><br><br>' +
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' + 'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' + 'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
@ -923,14 +924,14 @@ let Engine = {
Player.playtimeSinceLastBitnode += time; Player.playtimeSinceLastBitnode += time;
//Start Manual hack //Start Manual hack
if (Player.startAction == true) { if (Terminal.actionStarted === true) {
Engine._totalActionTime = Player.actionTime; Engine._totalActionTime = Terminal.actionTime;
Engine._actionTimeLeft = Player.actionTime; Engine._actionTimeLeft = Terminal.actionTime;
Engine._actionInProgress = true; Engine._actionInProgress = true;
Engine._actionProgressBarCount = 1; Engine._actionProgressBarCount = 1;
Engine._actionProgressStr = "[ ]"; Engine._actionProgressStr = "[ ]";
Engine._actionTimeStr = "Time left: "; Engine._actionTimeStr = "Time left: ";
Player.startAction = false; Terminal.actionStarted = false;
} }
//Working //Working
@ -1324,9 +1325,9 @@ let Engine = {
Player.lastUpdate = Engine._lastUpdate; Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop Engine.start(); //Run main game loop and Scripts loop
removeLoadingScreen(); removeLoadingScreen();
dialogBoxCreate("While you were offline, your scripts generated $" + dialogBoxCreate("While you were offline, your scripts generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromScripts, 2) + " and your Hacknet Nodes generated $" + formatNumber(offlineProductionFromScripts, 2) + "</span> and your Hacknet Nodes generated <span class='money-gold'>$" +
formatNumber(offlineProductionFromHacknetNodes, 2)); formatNumber(offlineProductionFromHacknetNodes, 2) + "</span>");
//Close main menu accordions for loaded game //Close main menu accordions for loaded game
var visibleMenuTabs = [terminal, createScript, activeScripts, stats, var visibleMenuTabs = [terminal, createScript, activeScripts, stats,
hacknetnodes, city, tutorial, options, dev]; 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 {Engine} from "../engine";
import {Settings} from "../Settings"; import {Settings} from "../Settings";
import {numeralWrapper} from "./NumeralFormat";
function setSettingsLabels() { function setSettingsLabels() {
var nsExecTime = document.getElementById("settingsNSExecTimeRangeValLabel"); var nsExecTime = document.getElementById("settingsNSExecTimeRangeValLabel");
var nsLogLimit = document.getElementById("settingsNSLogRangeValLabel"); var nsLogLimit = document.getElementById("settingsNSLogRangeValLabel");
@ -12,6 +15,7 @@ function setSettingsLabels() {
var suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup"); var suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup");
var autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel"); var autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel");
var disableHotkeys = document.getElementById("settingsDisableHotkeys"); var disableHotkeys = document.getElementById("settingsDisableHotkeys");
var locale = document.getElementById("settingsLocale");
//Initialize values on labels //Initialize values on labels
nsExecTime.innerHTML = Settings.CodeInstructionRunTime + "ms"; nsExecTime.innerHTML = Settings.CodeInstructionRunTime + "ms";
@ -24,8 +28,10 @@ function setSettingsLabels() {
suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup; suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup;
autosaveInterval.innerHTML = Settings.AutosaveInterval; autosaveInterval.innerHTML = Settings.AutosaveInterval;
disableHotkeys.checked = Settings.DisableHotkeys; 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 nsExecTimeInput = document.getElementById("settingsNSExecTimeRangeVal");
var nsLogRangeInput = document.getElementById("settingsNSLogRangeVal"); var nsLogRangeInput = document.getElementById("settingsNSLogRangeVal");
var nsPortRangeInput = document.getElementById("settingsNSPortRangeVal"); var nsPortRangeInput = document.getElementById("settingsNSPortRangeVal");
@ -60,6 +66,7 @@ function setSettingsLabels() {
} }
}; };
//Set handlers for when settings change on checkboxes
suppressMsgs.onclick = function() { suppressMsgs.onclick = function() {
Settings.SuppressMessages = this.checked; Settings.SuppressMessages = this.checked;
}; };
@ -84,6 +91,19 @@ function setSettingsLabels() {
Settings.DisableHotkeys = this.checked; 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 //Theme
if (Settings.ThemeHighlightColor == null || Settings.ThemeFontColor == null || Settings.ThemeBackgroundColor == null) { if (Settings.ThemeHighlightColor == null || Settings.ThemeFontColor == null || Settings.ThemeBackgroundColor == null) {
console.log("ERROR: Cannot find Theme Settings"); console.log("ERROR: Cannot find Theme Settings");

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

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

@ -34,11 +34,14 @@ export function createProgressBarText(params: IProgressBarConfiguration) {
}; };
// tslint:disable-next-line:prefer-object-spread // 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)); // This way there is always at least one bar filled in...
const dashes: number = derivedParams.totalTicks - bars; 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 // 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() { export function getTimestamp() {
const d: Date = new Date(); const d: Date = new Date();
// A negative slice value takes from the end of the string rather than the beginning. // A negative slice value takes from the end of the string rather than the beginning.

@ -1,6 +1,7 @@
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var MiniCssExtractPlugin = require('mini-css-extract-plugin'); var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env, argv) => ({ module.exports = (env, argv) => ({
plugins: [ plugins: [
@ -16,6 +17,45 @@ module.exports = (env, argv) => ({
jQuery: "jquery", jQuery: "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({ new MiniCssExtractPlugin({
filename: "[name].css", filename: "[name].css",
chunkFilename: "[id].css" chunkFilename: "[id].css"