Merged from dev. Fixed merge conflicts. Updated terminal documentation for wget

This commit is contained in:
danielyxie 2018-09-07 21:21:23 -05:00
commit b36855fe52
44 changed files with 2995 additions and 1344 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;

@ -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 {
background-color: #666;
}
.active-scripts-script-header.active {
background-color: #555;
}
.active-scripts-script-header:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right; float: right;
margin-left: 5px; margin-left: 5px;
} color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
.active-scripts-script-header.active:after { &.active:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125; }
color: var(--my-font-color);
float: right; &:hover,
margin-left: 5px; &.active:hover {
background-color: #666;
}
&.active {
background-color: #555;
}
} }
.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,
.active-scripts-script-panel ul,
.active-scripts-script-panel li {
background-color: #555; background-color: #555;
width: auto; width: auto;
color: #fff; color: #fff;
margin-left: 5%; 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,
#faction-augmentations-container ul,
#faction-augmentations-container h1{
margin: 8px; margin: 8px;
padding: 4px; padding: 4px;
}
} }
/* World */ /* World */
@ -451,11 +440,20 @@
padding-top: 10px; padding-top: 10px;
} }
.augmentations-list button, .augmentations-list {
.augmentations-list div { button,
div {
color: var(--my-font-color); color: var(--my-font-color);
padding: 8px;
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,
.tooltiptextleft {
visibility: visible; 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,
.tooltiptextleft {
visibility: visible; 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,11 +257,10 @@ 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);
@ -256,8 +270,10 @@ a:visited {
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 */
@ -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;
margin-left: 5px; color: transparent;
} 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,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555; 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 {

@ -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?
---------------------------------- ----------------------------------

@ -772,6 +772,18 @@ write
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 +825,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
^^ ^^

@ -1,9 +1,13 @@
.. _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.
@ -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)

@ -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

@ -2,19 +2,17 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Bitburner</title> <title>Bitburner - development</title>
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png">
<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">
@ -110,7 +109,7 @@
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div> </div>
<div id="javascript-editor"></div> <div id="javascript-editor"></div>
@ -160,7 +159,7 @@
<fieldset> <fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/> <input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" />
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset> </fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
@ -171,7 +170,7 @@
<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>
@ -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>
@ -625,9 +624,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 +672,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 +685,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 +695,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 +713,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>
@ -744,7 +743,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container"> <div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content"> <div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p> <p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"/> <input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span> <span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span> <span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
</div> </div>
@ -756,7 +755,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 +768,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 +839,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 +855,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 +867,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>
@ -882,7 +881,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50"/> <input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" />
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -896,7 +895,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50"/> <input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" />
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -908,7 +907,7 @@
</span> </span>
</label> </label>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60"/> <input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" />
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em> <em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -994,7 +993,7 @@
<a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a> <a id="save-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Save Game </a>
<a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a> <a id="delete-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Delete Game </a>
<a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a> <a id="export-game-link" class="a-link-button" style="display:inline-block;width:46%;"> Export Game </a>
<input type="file" id="import-game-file-selector" name="file"/> <input type="file" id="import-game-file-selector" name="file" />
<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
@ -1006,7 +1005,7 @@
<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="tooltiptext">
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>
@ -1019,7 +1018,5 @@
<div class="loaderspinner"></div> <div class="loaderspinner"></div>
<div class="loaderlabel">Loading Bitburner...</div> <div class="loaderlabel">Loading Bitburner...</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>

316
package-lock.json generated

@ -805,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",
@ -1085,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",
@ -1684,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",
@ -2186,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",
@ -2197,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",
@ -2529,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",
@ -4795,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",
@ -6385,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",
@ -7001,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",
@ -7608,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",
@ -7731,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",
@ -7931,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",
@ -8984,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",
@ -9360,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",
@ -9422,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",
@ -11693,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",
@ -12232,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",
@ -12377,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",

@ -46,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",

@ -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);
@ -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({

@ -6,6 +6,9 @@ let CONSTANTS = {
//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,
@ -493,9 +496,24 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.40.3<br>" + `v0.40.3<br>
"* Added a setting in .fconf for enabling line-wrap in the Terminal input<br>" -----------------------------------------------<br>
* Bladeburner Changes:<br>
*** Increased the effect that agi and dexterity have on action time<br>
*** Starting number of contracts/operations available will be slightly lower<br>
*** Random events will now happen slightly more often<br>
*** Slightly increased the rate at which the Overclock skill point cost increases<br>
-----------------------------------------------<br>
* The maximum volatility of stocks is now randomized (randomly generated within a certain range every time the game resets)<br>
* Increased the range of possible values for initial stock prices<br>
* b1t_flum3.exe program can now be created immediately at Hacking level 1 (rather than hacking level 5)<br>
* UI improvements for the character overview panel and the left-hand menu (by mat-jaworski)<br>
* Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0<br>
* Added tryWrite() Netscript function<br>
* When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished<br>
* Added a setting in .fconf for enabling line-wrap in the Terminal input<br>
* Added 'wget' Terminal command<br>
* Improved the introductory tutorial`
} }
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.

@ -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),
}; };

90
src/Hacking.js Normal file

@ -0,0 +1,90 @@
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) {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500;
const baseSkill = 50;
const diffFactor = 2.5;
const intFactor = 0.1;
var skillFactor = (diffFactor * difficultyMult + baseDiff);
// tslint:disable-next-line
skillFactor /= (Player.hacking_skill + baseSkill + (intFactor * Player.intelligence));
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) {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server);
}
/**
* Returns time it takes to complete a weaken operation on a server, in seconds
*/
export function calculateWeakenTime(server) {
const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server);
}

@ -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);

@ -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
@ -469,17 +469,18 @@ function updateInfiltrationLevelText(inst) {
var expMultiplier = 2 * inst.clearanceLevel / inst.maxClearanceLevel; var expMultiplier = 2 * inst.clearanceLevel / inst.maxClearanceLevel;
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);
} }
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>" +
//Create a popup with final introductory stuff
var popupId = "interactive-tutorial-ending-popup";
var txt = createElement("p", {
innerHTML:
"If you are new to the game, the following links may be useful for you!<br><br>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner' target='_blank'>Getting Started Guide</a>" + "<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner' target='_blank'>Getting Started Guide</a>" +
"<a class='a-link-button' href='http://bitburner.wikia.com/wiki/Bitburner_Wiki' target='_blank'>Wiki</a><br><br>" + "<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. " + "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"); "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>" +

@ -1001,7 +1001,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)";

@ -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";
@ -292,7 +297,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 +313,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 +395,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 +442,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 + ")");
@ -1843,6 +1848,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);
@ -2057,7 +2081,7 @@ 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); //Returns seconds
}, },
getGrowTime : function(ip) { getGrowTime : function(ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
@ -2069,7 +2093,7 @@ 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); //Returns seconds
}, },
getWeakenTime : function(ip) { getWeakenTime : function(ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
@ -2081,7 +2105,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); //Returns seconds
}, },
getScriptIncome : function(scriptname, ip) { getScriptIncome : function(scriptname, ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {

@ -30,6 +30,8 @@ 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 +121,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 +262,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 +343,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 +489,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 +615,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 +637,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 +786,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 +814,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 +899,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 +993,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 +1343,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) {
@ -1642,7 +1536,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;
@ -2049,7 +1948,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;

@ -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;
} }

@ -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,
@ -281,8 +281,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 +292,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 == "") {

@ -39,6 +39,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 +53,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;
} }
/** /**
@ -95,9 +95,9 @@ const defaultSettings: IDefaultSettings = {
MaxPortCapacity: 50, MaxPortCapacity: 50,
SuppressBuyAugmentationConfirmation: false, SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false, SuppressFactionInvites: false,
SuppressHospitalizationPopup: false,
SuppressMessages: false, SuppressMessages: false,
SuppressTravelConfirmation: false, SuppressTravelConfirmation: false,
SuppressHospitalizationPopup: false,
}; };
/** /**
@ -114,9 +114,9 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
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",

95
src/Stock.ts Normal file

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

@ -10,15 +10,17 @@ 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 numeral from "numeral/min/numeral.min";
import {Player} from "./Player"; import {Player} from "./Player";
@ -70,7 +72,7 @@ $(document).keydown(function(event) {
"[" + "[" +
(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) {
@ -516,6 +518,8 @@ 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,
@ -630,6 +634,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);
@ -644,10 +674,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] &&
@ -659,7 +689,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
@ -690,43 +720,44 @@ 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: " + formatNumber(currServ.hackDifficulty, 3));
post("Estimated chance to hack: " + formatNumber(addOffset(Player.calculateHackingChance() * 100, 5), 2) + "%"); post("Chance to hack: " + formatNumber(calculateHackingChance(currServ) * 100, 2) + "%");
post("Estimated time to hack: " + formatNumber(addOffset(Player.calculateHackingTime(), 5), 3) + " seconds"); post("Time to hack: " + formatNumber(calculateHackingTime(currServ), 3) + " seconds");
post("Estimated total money available on server: $" + formatNumber(addOffset(Player.getCurrentServer().moneyAvailable, 5), 2)); post("Total money available on server: $" + formatNumber(currServ.moneyAvailable, 2));
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")
@ -821,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);
@ -875,16 +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
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");
@ -900,14 +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
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;
@ -978,16 +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
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")) {
@ -1140,14 +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
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":
@ -1964,9 +1963,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: " + formatNumber(calculateHackingTime(targetServer), 1) + "s");
post("Netscript grow() execution time: " + formatNumber(scriptCalculateGrowTime(targetServer)/1000, 1) + "s"); post("Netscript grow() execution time: " + formatNumber(calculateGrowTime(targetServer), 1) + "s");
post("Netscript weaken() execution time: " + formatNumber(scriptCalculateWeakenTime(targetServer)/1000, 1) + "s"); post("Netscript weaken() execution time: " + formatNumber(calculateWeakenTime(targetServer), 1) + "s");
}; };
programHandlers[Programs.AutoLink.name] = () => { programHandlers[Programs.AutoLink.name] = () => {
post("This executable cannot be run."); post("This executable cannot be run.");
@ -2125,6 +2124,7 @@ let Terminal = {
} }
} }
post("ERROR: No such script"); post("ERROR: No such script");
} }
}; };

@ -150,7 +150,7 @@ $(document).keydown(function(e) {
} }
}); });
let Engine = { const Engine = {
version: "", version: "",
Debug: true, Debug: true,
overview: new CharacterOverview(), overview: new CharacterOverview(),
@ -570,7 +570,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 +584,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>' + ' (' + numeral(Player.hacking_exp).format('(0.000a)') + ' experience)<br>' +
'Strength: ' + (Player.strength).toLocaleString() + 'Strength: ' + (Player.strength).toLocaleString() +
" (" + numeral(Player.strength_exp).format('(0.000a)') + ' experience)<br>' + ' (' + numeral(Player.strength_exp).format('(0.000a)') + ' experience)<br>' +
'Defense: ' + (Player.defense).toLocaleString() + 'Defense: ' + (Player.defense).toLocaleString() +
" (" + numeral(Player.defense_exp).format('(0.000a)')+ ' experience)<br>' + ' (' + numeral(Player.defense_exp).format('(0.000a)')+ ' experience)<br>' +
'Dexterity: ' + (Player.dexterity).toLocaleString() + 'Dexterity: ' + (Player.dexterity).toLocaleString() +
" (" + numeral(Player.dexterity_exp).format('(0.000a)') + ' experience)<br>' + ' (' + numeral(Player.dexterity_exp).format('(0.000a)') + ' experience)<br>' +
'Agility: ' + (Player.agility).toLocaleString() + 'Agility: ' + (Player.agility).toLocaleString() +
" (" + numeral(Player.agility_exp).format('(0.000a)') + ' experience)<br>' + ' (' + numeral(Player.agility_exp).format('(0.000a)') + ' experience)<br>' +
'Charisma: ' + (Player.charisma).toLocaleString() + 'Charisma: ' + (Player.charisma).toLocaleString() +
" (" + numeral(Player.charisma_exp).format('(0.000a)') + ' experience)<br>' + ' (' + numeral(Player.charisma_exp).format('(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 +923,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 +1324,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];

1024
src/index.html Normal file

File diff suppressed because it is too large Load Diff

@ -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"