Merge pull request #5 from danielyxie/dev

Merge upstream
This commit is contained in:
Matthew Goff
2018-09-01 08:15:42 -05:00
committed by GitHub
70 changed files with 2790 additions and 1557 deletions

13
.editorconfig Normal file
View 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

View File

@ -1,2 +1,12 @@
$fontFamily: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
$defaultFontSize: 16px;
/* COLORS */
$hacky-green: #adff2f;
/* Attributes */
$my-stat-hp-color: #dd3434;
$my-stat-money-color: #ffd700;
$my-stat-hack-color: $hacky-green;
$my-stat-cha-color: #a671d1;
$my-stat-int-color: #6495ed;

View File

@ -27,14 +27,14 @@
}
/* Action & Skills panel navigation button */
%bladburner-nav-button {
%bladeburner-nav-button {
border: 1px solid #fff;
margin: 2px;
padding: 2px;
}
.bladeburner-nav-button {
@extend %bladburner-nav-button;
@extend %bladeburner-nav-button;
color: #fff;
@ -44,7 +44,7 @@
}
.bladeburner-nav-button-inactive {
@extend %bladburner-nav-button;
@extend %bladeburner-nav-button;
text-decoration: none;
background-color: #555;

View File

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

View File

@ -22,17 +22,15 @@
#script-editor-container {
background-color: transparent;
}
#javascript-editor {
margin: 10px;
height: 80%;
width: 100%;
margin-left: 6px;
padding-left: 6px;
padding-top: 6px;
padding-bottom: 6px;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
@ -101,9 +99,7 @@
resize: none;
color: #fff;
margin: 4px;
padding: 2px;
border: 2px solid var(--my-highlight-color);
}
@ -225,15 +221,15 @@
.active-scripts-script-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
}
.active-scripts-script-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
}
@ -244,16 +240,13 @@
width: auto;
display: none;
margin-bottom: 6px;
}
.active-scripts-script-panel p,
.active-scripts-script-panel h2,
.active-scripts-script-panel ul,
.active-scripts-script-panel li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
p, h2, ul, li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}
.active-scripts-button {
@ -266,13 +259,13 @@
margin: 4px;
padding: 4px;
background-color: #000;
}
.active-scripts-button:hover,
.active-scripts-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
}
/* Hacknet Nodes */
@ -291,6 +284,16 @@
float: left;
overflow: hidden;
white-space: nowrap;
&.hacknet-node {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
margin: 6px;
padding: 7px;
width: 35vw;
border: 2px solid var(--my-highlight-color);
}
}
#hacknet-nodes-list {
@ -316,34 +319,24 @@
display: inline-block;
}
.hacknet-node {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
margin: 6px;
padding: 6px;
width: 34vw;
border: 2px solid var(--my-highlight-color);
}
.hacknet-node-container {
display: inline-table;
}
.hacknet-node-container .row {
display: table-row;
height: 30px;
}
.row {
display: table-row;
height: 30px;
.hacknet-node-container .row p {
display: table-cell;
}
p {
display: table-cell;
}
}
.hacknet-node-container .upgradable-info {
display: inline-block;
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
padding: 0 4px;
width: $defaultFontSize * 4;
.upgradable-info {
display: inline-block;
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
padding: 0 4px;
width: $defaultFontSize * 4;
}
}
.menu-page-text {
@ -426,17 +419,14 @@
}
/* Faction Augmentations */
#faction-augmentations-container{
#faction-augmentations-container {
position: fixed;
padding-top: 10px;
}
#faction-augmentations-container p,
#faction-augmentations-container a,
#faction-augmentations-container ul,
#faction-augmentations-container h1{
margin: 8px;
padding: 4px;
p, a, ul, h1 {
margin: 8px;
padding: 4px;
}
}
/* World */
@ -451,11 +441,20 @@
padding-top: 10px;
}
.augmentations-list button,
.augmentations-list div {
color: var(--my-font-color);
padding: 8px;
text-decoration: none;
.augmentations-list {
button,
div {
color: var(--my-font-color);
text-decoration: none;
}
button {
padding: 2px 5px;
}
div {
padding: 6px;
}
}
/* Tutorial */
@ -510,11 +509,12 @@
padding: 6px;
}
#location-container * {
#location-container > * {
margin: 10px 5px 10px 5px;
}
#location-job-reputation, #location-company-favor {
#location-job-reputation,
#location-company-favor {
display: inline;
}
@ -523,6 +523,7 @@
position: fixed;
padding: 6px;
}
#infiltration-left-panel,
#infiltration-right-panel {
display: inline-block;
@ -552,6 +553,25 @@
#stock-market-container {
position: fixed;
padding: 6px;
p {
font-size: $defaultFontSize * 0.8125;
}
a {
font-size: $defaultFontSize * 0.875;
}
h2 {
margin-top: 10px;
margin-left: 10px;
display: block;
}
}
/* Change font size of Stock TIcker headers */
#stock-market-list li {
button {
font-size: $defaultFontSize;
}
}
#stock-market-container p {
@ -564,6 +584,11 @@
margin: 10px;
}
#stock-market-watchlist-filter {
width: 50%;
margin-left: 10px;
}
.stock-market-input {
display: inline-block;
padding: 4px;

View File

@ -8,6 +8,8 @@
z-index: 10; /* Sit on top */
left: 0;
top: 0;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: auto;
@ -16,10 +18,21 @@
.popup-box-content {
background-color: var(--my-background-color);
margin: 15% auto;
padding: 12px;
border: 5px solid var(--my-highlight-color);
width: 70%;
max-height: 80%;
/*
margin: auto;
height:auto;
max-height:80%;
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
*/
overflow-y: auto;
color: var(--my-font-color);
}
@ -141,11 +154,12 @@
#game-options-content {
background-color: var(--my-background-color);
margin: 15% auto;
padding: 10px;
border: 5px solid var(--my-highlight-color);
width: 60%;
color: var(--my-font-color);
width: 80%;
max-height: 80%;
overflow-y: auto;
}
#game-options-left-panel,

View File

@ -3,10 +3,11 @@
@import "reset";
:root{
:root {
--my-font-color: #6f3;
--my-background-color: #000;
--my-highlight-color: #fff;
--my-prompt-color: #f92672;
}
body {
@ -83,7 +84,7 @@ tr:focus {
display: block;
color: #e6e6e6;
background-color: #555;
padding: 16px;
padding: 12px 8px;
text-decoration: none;
}
@ -105,21 +106,36 @@ tr:focus {
background-color: #aaa;
}
#hacking-menu-header-li,
#character-menu-header-li,
#world-menu-header-li,
#help-menu-header-li {
position: relative;
}
/* Accordion Outline */
.mainmenu-accordion-header {
outline: 2px solid #fff;
}
/* Plus and minus signs */
.mainmenu-accordion-header:after {
content: '\02795';
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
.mainmenu-accordion-header.opened:after {
content: "\2796";
font-size: $defaultFontSize * 0.8125;
position: absolute;
bottom: 25%;
right: 3px;
color: transparent;
text-shadow: 0 0 0 #fff;
}
.mainmenu-accordion-header.opened {
background-color: #222;
&:after {
content: "\2796";
}
}
/* Slide down transition */
@ -129,11 +145,6 @@ tr:focus {
transition: max-height 0.2s ease-out;
}
/* Borders */
.mainmenu-accordion-header {
border: 2px solid #fff;
}
/* Make html links ("a" elements) nice looking buttons with this class */
a:link,
a:visited {
@ -144,7 +155,7 @@ a:visited {
text-decoration: none;
background-color: #555;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
@ -167,7 +178,7 @@ a:visited {
text-decoration: none;
background-color: #333;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #333;
cursor: default;
@ -188,7 +199,7 @@ a:visited {
text-decoration: none;
background-color: #0a0;
color: #fff;
padding: 5px;
padding: 3px 5px;
margin: 5px;
border: 1px solid #0a0;
cursor: default;
@ -218,9 +229,9 @@ a:visited {
#create-program-tab {
position: relative;
}
#create-program-notification {
font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */
top: 0;
right: 0;
@ -310,11 +321,23 @@ a:visited {
display: inline-block;
}
.help-tip:hover {
.help-tip-big {
content: '?';
padding: 3px;
margin-left: 3px;
color: #fff;
border: 1px solid #fff;
border-radius: 8px;
display: inline-block;
}
.help-tip:hover,
.help-tip-big:hover {
background-color: #888;
}
.help-tip:active {
.help-tip:active,
.help-tip-big:active {
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
}
@ -352,7 +375,7 @@ a:visited {
/* Blinking Cursor */
/* ----- blinking cursor animation ----- */
.typed-cursor{
.typed-cursor {
opacity: 1;
-webkit-animation: blink 0.95s infinite;
-moz-animation: blink 0.95s infinite;
@ -388,24 +411,21 @@ a:visited {
}
/* Status text */
@-webkit-keyframes status-text{
from{
@-webkit-keyframes status-text {
from {
opacity: 1;
top: 0;
}
to{
to {
opacity: 0;
top: 0;
}
}
.status-text{
display: inline;
position: fixed;
top: 0;
-webkit-animation: status-text 3s 1;
background-color: transparent;
.status-text {
display: inline-block;
height: 15%;
position: fixed;
z-index: 2;
-webkit-animation: status-text 3s 1;
}
#status-text-container {
@ -413,14 +433,14 @@ a:visited {
}
#status-text {
font-size: $defaultFontSize * 1.25;
color: #fff;
right: 0;
bottom: 0;
padding: 4px;
margin-right: 14px;
background-color: transparent;
z-index: 2;
font-size: $defaultFontSize * 1.25;
bottom: 0;
color: #fff;
margin-right: 14px;
padding: 4px;
right: 0;
top: 0;
width: auto;
}
@ -428,47 +448,78 @@ a:visited {
#character-overview-wrapper {
position: relative;
}
#character-overview-container {
display: none;
position: absolute; /* Stay in place */
right: 0;
top: 0;
height: auto; /* Full height */
padding: 8px;
padding: 10px 2px;
border: 2px solid var(--my-highlight-color);
width: 19%;
width: auto;
max-width: 280px;
overflow: auto; /* Enable scroll if needed */
background-color: #444; /* Fallback color */
background-color: #393636; /* Fallback color */
z-index: 1;
}
#character-overview-text {
color: #fff;
background-color: #444;
color: #faffdf;
table {
border-collapse: collapse;
margin: auto;
}
td {
padding: 2px;
vertical-align: middle;
}
}
.character-stat-text {
color: #fff;
background-color: #444;
}
.character-stat-cell {
.character-stat-cell {
text-align: right;
}
#character-hack-wrapper td,
#character-agi-wrapper td {
border-bottom: 1px #aaa solid;
padding-bottom: 10px;
}
#character-str-wrapper td,
#character-cha-wrapper td {
padding-top: 10px;
}
#character-hp-wrapper { color: $my-stat-hp-color; }
#character-money-wrapper { color: $my-stat-money-color; }
#character-hack-wrapper { color: $my-stat-hack-color; }
#character-cha-wrapper { color: $my-stat-cha-color; }
#character-int-wrapper { color: $my-stat-int-color; }
#character-overview-save-button,
#character-overview-options-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
color: #cecece;
display: inline-block;
font-size: $defaultFontSize * 0.875;
font-weight: bold;
height: 22px;
height: 25px;
background-color: #000;
padding: 5px 8px;
}
.character-quick-options {
padding-top: 5px;
margin-top: 10px;
text-align: center;
}
#character-overview-save-button:hover,
@ -480,16 +531,13 @@ a:visited {
cursor: pointer;
}
#character-overview-options-button {
display: inline;
}
/* Scan analyze links from AutoLink */
.scan-analyze-link {
cursor: pointer;
color: #fff;
text-decoration: underline;
}
.scan-analyze-link:hover {
text-decoration: none;
}
@ -500,37 +548,37 @@ a:visited {
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
padding: 4px 6px;
cursor: pointer;
width: 80%;
text-align: left;
border: none;
outline: none;
}
position: relative;
.accordion-header.active,
.accordion-header:hover {
background-color: #555;
}
&.active,
&:hover {
background-color: #555;
}
.accordion-header.active:hover {
background-color: #666;
}
&.active:hover {
background-color: #666;
}
.accordion-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&:after {
content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
color: transparent;
text-shadow: 0 0 0 #fff;
position: absolute;
bottom: 3px;
right: 6px;
}
.accordion-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
}
.accordion-panel {
@ -542,13 +590,10 @@ a:visited {
background-color: #555;
overflow-y: auto;
overflow-x: none;
}
.accordion-panel div,
.accordion-panel ul,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555;
div, ul, p, ul > li {
background-color: #555;
}
}
/* override the global <span> styling */

View File

@ -51,6 +51,7 @@
}
#terminal-input-header {
color: var(--my-prompt-color);
white-space: pre;
}

View File

@ -6,6 +6,14 @@
color: var(--my-font-color);
width: 99%;
height: 100%;
overflow-y: hidden;
}
.generic-fullscreen-container-scroll {
height: 100%;
width: 100%;
overflow: auto;
padding-right: 20px;
}
#work-in-progress-container {

File diff suppressed because one or more lines are too long

91
dist/engine.css vendored
View File

@ -276,10 +276,21 @@ a:visited {
border-radius: 5px;
display: inline-block; }
.help-tip:hover {
.help-tip-big {
content: '?';
padding: 3px;
margin-left: 3px;
color: #fff;
border: 1px solid #fff;
border-radius: 8px;
display: inline-block; }
.help-tip:hover,
.help-tip-big:hover {
background-color: #888; }
.help-tip:active {
.help-tip:active,
.help-tip-big:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); }
@ -388,32 +399,29 @@ a:visited {
/* Status text */
@-webkit-keyframes status-text {
from {
opacity: 1;
top: 0; }
opacity: 1; }
to {
opacity: 0;
top: 0; } }
opacity: 0; } }
.status-text {
display: inline;
display: inline-block;
height: 15%;
position: fixed;
top: 0;
-webkit-animation: status-text 3s 1;
background-color: transparent;
height: 15%; }
z-index: 2;
-webkit-animation: status-text 3s 1; }
#status-text-container {
background-color: transparent; }
#status-text {
font-size: 20px;
color: #fff;
right: 0;
bottom: 0;
padding: 4px;
margin-right: 14px;
background-color: transparent;
z-index: 2;
font-size: 20px;
bottom: 0;
color: #fff;
margin-right: 14px;
padding: 4px;
right: 0;
top: 0;
width: auto; }
/* Character Overview */
@ -1071,6 +1079,18 @@ a:visited {
#stock-market-container {
position: fixed;
padding: 6px; }
#stock-market-container p {
font-size: 13px; }
#stock-market-container a {
font-size: 14px; }
#stock-market-container h2 {
margin-top: 10px;
margin-left: 10px;
display: block; }
/* Change font size of Stock TIcker headers */
#stock-market-list li button {
font-size: 16px; }
#stock-market-container p {
padding: 10px;
@ -1080,6 +1100,10 @@ a:visited {
#stock-market-container a {
margin: 10px; }
#stock-market-watchlist-filter {
width: 50%;
margin-left: 10px; }
.stock-market-input {
display: inline-block;
padding: 4px;
@ -1119,7 +1143,14 @@ a:visited {
.generic-fullscreen-container {
color: var(--my-font-color);
width: 99%;
height: 100%; }
height: 100%;
overflow-y: hidden; }
.generic-fullscreen-container-scroll {
height: 100%;
width: 100%;
overflow: auto;
padding-right: 20px; }
#work-in-progress-container {
position: fixed; }
@ -1174,6 +1205,8 @@ a:visited {
/* Sit on top */
left: 0;
top: 0;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: auto;
@ -1181,10 +1214,21 @@ a:visited {
.popup-box-content {
background-color: var(--my-background-color);
margin: 15% auto;
padding: 12px;
border: 5px solid var(--my-highlight-color);
width: 70%;
max-height: 80%;
/*
margin: auto;
height:auto;
max-height:80%;
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
*/
overflow-y: auto;
color: var(--my-font-color); }
.popup-box-button,
@ -1292,11 +1336,12 @@ a:visited {
#game-options-content {
background-color: var(--my-background-color);
margin: 15% auto;
padding: 10px;
border: 5px solid var(--my-highlight-color);
width: 60%;
color: var(--my-font-color); }
color: var(--my-font-color);
width: 80%;
max-height: 80%;
overflow-y: auto; }
#game-options-left-panel,
#game-options-right-panel {

112
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,48 @@
Changelog
=========
v0.40.2 - 8/27/2018
-------------------
* Bladeburner Changes:
* Added getBonusTime(), getSkillUpgradeCost(), and getCity() Netscript functions to the API
* Buffed the effects of many Bladeburner Augmentations
* The Blade's Simulacrum Augmentation requires significantly less reputation but slightly more money
* Slightly increased the amount of successes needed for a Contract/Operation in order to increase its max level
* Increased the amount of money gained from Contracts by ~25%
* Increased the base amount of rank gained from Operations by 10%
* Significantly increased the 'randomness' in determining a Contract/Operation's initial count and rate of count increase
* The number (count) of Operations should now increase significantly faster
* There are now, on average, more Synthoid communities in a city
* If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation
* Stock Market Changes:
*Added a watchlist filter feature to the UI that allows you to specify which stocks to show
*Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks
*Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript
* There is now a setting for enabling/disabling the popup that appears when you are hospitalized
* Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-)
* Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-)
* Bug Fix: Bladeburner skill cost multiplier should now properly increase in BitNode-12 (by hydroflame)
* Bug Fix: 'document', 'hacknet', and 'window' keywords should no longer be counted multiple times in RAM calculations
* Bug Fix: Joining factions through Singularity functions should now prevent you from joining opposing factions
* Bug Fix: Four Sigma should no longer have two 'Speech Enhancement' Augmentations (by Kline-)
v0.40.1 - 8/5/2018 - Community Update
-------------------------------------
* Added getPurchasedServerCost() Netscript function (by kopelli)
* Added getFavorToDonate() Netscript function (by hydroflame)
* Added getFactionFavorGain() and getCompanyFavorGain() Singularity functions (by hydroflame)
* Accumulated 'bonus' time in Bladeburner is now displayed in the UI (by hydroflame)
* The Red Pill can now be purchased with negative money (since its supposed to be free) (by hydroflame)
* Cranial Signal Processor Augmentations now have the previous generation as a prerequisite. i.e. Cranial Signal Processor - Gen II requires Gen I (by Kline-)
* Terminal now supports semicolon usage (end of command). This allows chaining multiple Terminal commands (by hydroflame)
* Bladeburner Raid operations can no longer be performed if your estimate of Synthoid communities is zero (by hydroflame)
* The difficulty of BN-12 now scales faster (by hydroflame)
* Active Scripts UI now shows a RAM Usage bar for each server (by kopelli)
* Bug Fix: Corrected terminal timestamp format (by kopelli)
* Bug Fix: NetscriptJS scripts should now die properly if they don't have a 'main' function (by hydroflame)
* Bug Fix: write(), read(), and tryWrite() Netscript functions should now work properly for writing Arrays/objects to Netscript Ports
* Various minor UI/QOL fixes by hydroflame, kopelli, and Kline-
v0.40.0 - 7/28/2018
-------------------
* **WARNING: This update makes some significant changes to Netscript and therefore you may need to make some changes to your scripts. See** `this post <https://www.reddit.com/r/Bitburner/comments/9252j4/psa_netscript_10_changes_in_next_version_v0400/>`_ **this post for details**

View File

@ -143,7 +143,9 @@ getActionEstimatedSuccessChance
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
Returns the estimated success chance for the specified action
Returns the estimated success chance for the specified action. This chance
is returned as a decimal value, NOT a percentage (e.g. if you have an estimated
success chance of 80%, then this function will return 0.80, NOT 80).
getActionCountRemaining
-----------------------
@ -235,18 +237,30 @@ getSkillLevel
.. js:function:: getSkillLevel(skillName="")
:param string skillName: Name of skill
:param string skillName: Name of skill. Case-sensitive and must be an exact match
This function returns your level in the specified skill.
The function returns -1 if an invalid skill name is passed in
getSkillUpgradeCost
-------------------
.. js:function:: getSkillUpgradeCost(skillName="")
:param string skillName: Name of skill. Case-sensitive and must be an exact match
This function returns the number of skill points needed to upgrade the
specified skill.
The function returns -1 if an invalid skill name is passed in.
upgradeSkill
------------
.. js:function:: upgradeSkill(skillName)
:param string skillName: Name of Skill to be upgraded. Must be an exact match
:param string skillName: Name of Skill to be upgraded. Case-sensitive and must be an exact match
Attempts to upgrade the specified Bladeburner skill. Returns true if the
skill is successfully upgraded, and false otherwise
@ -358,6 +372,21 @@ joinBladeburnerDivision
Returns false otherwise
getBonusTime
------------
.. js:function:: getBonusTime()
Returns the amount of accumulated "bonus time" (seconds) for the Bladeburner mechanic.
"Bonus time" is accumulated when the game is offline or if the game is
inactive in the browser.
"Bonus time" makes the game progress faster, up to 5x the normal speed.
For example, if an action takes 30 seconds to complete but you've accumulated
over 30 seconds in bonus time, then the action will only take 6 seconds
in real life to complete.
Examples
--------

View File

@ -659,16 +659,14 @@ getNextHacknetNodeCost
.. js:function:: getNextHacknetNodeCost()
Returns the cost of purchasing a new Hacknet Node
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
purchaseHacknetNode
^^^^^^^^^^^^^^^^^^^
.. js:function:: purchaseHacknetNode()
Purchases a new Hacknet Node. Returns a number with the index of the Hacknet Node. This index is equivalent to the number at the
end of the Hacknet Node's name (e.g The Hacknet Node named 'hacknet-node-4' will have an index of 4). If the player cannot afford
to purchase a new Hacknet Node then the function will return false.
Deprecated (no longer usable). See :doc:`netscripthacknetnodeapi`
getPurchasedServerCost
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,3 +1,5 @@
.. _netscripthacknetnodeapi:
Netscript Hacknet Node API
==========================

View File

@ -159,7 +159,7 @@ cancelOrder
:param number price: Execution price for the order
:param string type: Type of order. It must specify "limit" or "stop", and must also specify "buy" or "sell". This is NOT
case-sensitive. Here are four examples that will work:
* limitbuy
* limitsell
* stopbuy
@ -172,3 +172,38 @@ cancelOrder
Cancels an oustanding Limit or Stop order on the stock market.
The ability to use limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
getStockVolatility
------------------
.. js:function:: getStockVolatility(sym)
:param string sym: Symbol of stock
Returns the volatility of the specified stock.
Volatility represents the maximum percentage by which a stock's price can
change every tick. The volatility is returned as a decimal value, NOT
a percentage (e.g. if a stock has a volatility of 3%, then this function will
return 0.03, NOT 3).
In order to use this function, you must first purchase access to the Four Sigma (4S)
Market Data TIX API.
getStockForecast
----------------
.. js:function:: getStockForecast(sym)
:param string sym: Symbol of stock
Returns the probability that the specified stock's price will increase
(as opposed to decrease) during the next tick.
The probability is returned as a decimal value, NOT a percentage (e.g. if a
stock has a 60% chance of increasing, then this function will return 0.6,
NOT 60).
In other words, if this function returned 0.30 for a stock, then this means
that the stock's price has a 30% chance of increasing and a 70% chance of
decreasing during the next tick.

View File

@ -311,6 +311,18 @@ getCompanyFavor
This function will return the amount of favor you have at the specified company.
If the company passed in as an argument is invalid, -1 will be returned.
getCompanyFavorGain
-------------------
.. js:function:: getCompanyFavorGain(companyName)
:param string companyName: Name of the company. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function will return the amount of favor you will gain for the specified company
when you reset by installing Augmentations.
checkFactionInvitations
-----------------------
@ -386,6 +398,17 @@ getFactionFavor
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function returns the amount of favor you have for the specified faction.
getFactionFavorGain
-------------------
.. js:function:: getFactionFavorGain(factionName)
:param string factionName: Name of faction. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function returns the amount of favor you will gain for the specified faction when you reset by installing Augmentations.
createProgram
-------------

View File

@ -110,7 +110,7 @@
<div id="script-editor-filename-wrapper">
<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 id="javascript-editor"></div>
@ -160,7 +160,7 @@
<fieldset>
<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>
</fieldset>
</div> <!-- End script editor options panel -->
@ -169,11 +169,11 @@
<!-- Terminal page -->
<div id="terminal-container">
<table id="terminal">
<tr id="terminal-input">
<tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;" />
</td>
</tr>
</tr>
</table>
</div>
@ -215,7 +215,7 @@
<div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money"></span><br/>
<span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span>
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production"></span>
</p>
<span id="hacknet-nodes-multipliers">
<a id="hacknet-nodes-1x-multiplier" class="a-link-button-inactive"> x1 </a>
@ -452,7 +452,7 @@
<div id="create-program-container" class="generic-menupage-container">
<p id="create-program-page-text">
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating on a program you can cancel
can vary based on how complex the program is. If you are working on creating a program you can cancel
at any time. Your progress will be saved and you can continue later.
</p>
@ -625,9 +625,9 @@
<!-- Slums -->
<p id="location-slums-description">
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
other shadowy entities. The city's government and police have neglected this area for years... <br><br><br>
In the Slums you can commit crimes to earn money and experience. Crime attempts are not always
other shadowy entities. The city's government and police have neglected this area for years...
<br><br><br>
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
successful. Your chance at successfully committing a crime is determined by your stats.
</p>
<a class="a-link-button tooltip" id="location-slums-shoplift"> Shoplift </a>
@ -679,22 +679,44 @@
after you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-account" class="a-link-button-inactive"> Buy WSE Account </a>
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
<h2> Trade Information eXchange (TIX) API </h2>
<p>
You can also purchase access to the World Stock Exchange's TIX API! TIX, short for
Trade Information eXchange, is the communications protocol supported by the WSE.
<br><br>
Gaining access to the TIX API lets you write code to build automated trading
systems. In other words, you can create your own algorithmic trading strategies!
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies.
<br><br>
If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
<h2> Four Sigma (4S) Market Data Feed </h2>
<p>
Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies.
<br><br>
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
retain that access even after you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-4s-data" class="a-link-button-inactive tooltip">
Buy 4S Market Data Feed
</a>
<div class="help-tip-big" id="stock-market-4s-data-help-tip">?</div>
<a id="stock-market-buy-4s-tix-api" class="a-link-button-inactive tooltip">
Buy 4S Market Data TIX API Access
</a>
<p id="stock-market-commission"> </p>
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br><br>
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"> </input>
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;">
</ul>
</div>
@ -722,7 +744,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content">
<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-no" class="popup-box-button"> No </span>
</div>
@ -766,7 +788,9 @@
</div>
<!-- Red Pill Container -->
<div id="red-pill-container" class="generic-fullscreen-container"></div>
<div id="red-pill-container" class="generic-fullscreen-container">
<div id="red-pill-content" class="generic-fullscreen-container-scroll"></div>
</div>
<!-- Cinematic Text Container -->
<div id="cinematic-text-container" class="generic-fullscreen-container"></div>
@ -813,11 +837,11 @@
<tr id="character-int-wrapper">
<td>Int:&nbsp;</td><td id="character-int-text" class="character-stat-cell"></td>
</tr>
<table>
</table>
</div>
<div class="character-quick-options">
<span id="character-overview-save-button"> Save Game </span>
<span id="character-overview-options-button"> Options </span>
<span id="character-overview-save-button">Save Game</span>
<span id="character-overview-options-button">Options</span>
</div>
</div>
</div>
@ -844,7 +868,7 @@
</span>
</label>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25" />
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -858,7 +882,7 @@
</span>
</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>
</fieldset>
@ -872,7 +896,7 @@
</span>
</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>
</fieldset>
@ -884,7 +908,7 @@
</span>
</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>
</fieldset>
@ -932,6 +956,16 @@
<input type="checkbox" name="settingsSuppressBuyAugmentationConfirmation" id="settingsSuppressBuyAugmentationConfirmation">
</fieldset>
<!-- Hospitalization Popup -->
<fieldset>
<label for="settingsSuppressHospitalizationPopup" class="tooltip">Suppress Hospitalization popup:
<span class="tooltiptext">
If this is set, a popup message will no longer be shown when you are hospitalized after taking too much damage.
</span>
</label>
<input type="checkbox" name="settingsSuppressHospitalizationPopup" id="settingsSuppressHospitalizationPopup">
</fieldset>
<!-- Disable Terminal and Navigation Shortcuts -->
<fieldset>
<label for="settingsDisableHotkeys" class="tooltip">Disable Hotkeys:
@ -960,7 +994,7 @@
<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="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="debug-delete-scripts-link" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Delete Active Scripts
@ -972,7 +1006,7 @@
<a id="debug-soft-reset" class="a-link-button tooltip" style="display:block;width:46%;">
(DEBUG) Soft Reset
<span class="tooltiptext">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>
</a>
</div>

View File

@ -81,6 +81,7 @@ let NetscriptFunctions =
"gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" +
"getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" +
"getCompanyFavor|stopAction|getFactionFavor|" +
"getFavorToDonate|getFactionFavorGain|getCompanyFavorGain|" +
"checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" +
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" +
@ -99,9 +100,10 @@ let NetscriptFunctions =
"getActionTime|getActionEstimatedSuccessChance|getActionCountRemaining|" +
"getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" +
"setActionAutolevel|setActionLevel|" +
"getRank|getSkillPoints|getSkillLevel|upgradeSkill|getTeamSize|" +
"getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" +
"upgradeSkill|getTeamSize|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction";
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime";
var NetscriptHighlightRules = function(options) {
var keywordMapper = this.createKeywordMapper({

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "bitburner",
"version": "0.35.1",
"version": "0.40.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -101,5 +101,5 @@
"watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development"
},
"version": "0.35.1"
"version": "0.40.2"
}

View File

@ -1,4 +1,3 @@
import {Engine} from "./engine";
import {workerScripts,
killWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
@ -16,6 +15,7 @@ import {formatNumber} from "../utils/StringHelperFunctions";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElement} from "../utils/uiHelpers/removeElement";
import {roundToTwo} from "../utils/helpers/roundToTwo";
import {Page, routing} from "./ui/navigationTracking";
/* {
* serverName: {
@ -232,7 +232,7 @@ function updateActiveScriptsItems(maxTasks=150) {
}
}
if (Engine.currentPage !== Engine.Page.ActiveScripts) {return;}
if (!routing.isOn(Page.ActiveScripts)) {return;}
var total = 0;
for (var i = 0; i < workerScripts.length; ++i) {
try {

View File

@ -1,4 +1,4 @@
import {post} from "./Terminal";
import {post} from "./ui/postToTerminal";
let Aliases = {};
let GlobalAliases = {};

View File

@ -1,4 +1,4 @@
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {Factions, getNextNeurofluxLevel,
@ -746,7 +746,7 @@ function initAugmentations() {
"Increases the player's hacking skill by 7%.",
prereqs:[AugmentationNames.CranialSignalProcessorsG1]
});
CranialSignalProcessorsG2.addToFactions(["NiteSec"]);
CranialSignalProcessorsG2.addToFactions(["CyberSec", "NiteSec"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG2)) {
delete Augmentations[AugmentationNames.CranialSignalProcessorsG2];
}
@ -764,7 +764,7 @@ function initAugmentations() {
"Increases the player's hacking skill by 9%.",
prereqs:[AugmentationNames.CranialSignalProcessorsG2]
});
CranialSignalProcessorsG3.addToFactions(["NiteSec", "The Black Hand"]);
CranialSignalProcessorsG3.addToFactions(["NiteSec", "The Black Hand", "BitRunners"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG3)) {
delete Augmentations[AugmentationNames.CranialSignalProcessorsG3];
}
@ -782,7 +782,7 @@ function initAugmentations() {
"Increases the amount of money the player can inject into servers using grow() by 25%.",
prereqs:[AugmentationNames.CranialSignalProcessorsG3]
});
CranialSignalProcessorsG4.addToFactions(["The Black Hand"]);
CranialSignalProcessorsG4.addToFactions(["The Black Hand", "BitRunners"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG4)) {
delete Augmentations[AugmentationNames.CranialSignalProcessorsG4];
}
@ -848,7 +848,7 @@ function initAugmentations() {
"Increases the amount of reputation the player gains when working for a company by 10%."
});
SpeechEnhancement.addToFactions(["Tian Di Hui", "Speakers for the Dead", "Four Sigma", "KuaiGong International",
"Clarke Incorporated", "Four Sigma", "Bachman & Associates"]);
"Clarke Incorporated", "Bachman & Associates"]);
if (augmentationExists(AugmentationNames.SpeechEnhancement)) {
delete Augmentations[AugmentationNames.SpeechEnhancement];
}
@ -1575,7 +1575,7 @@ function initAugmentations() {
"AR HUD and assist the user in field missions.<br><br>" +
"This augmentation:<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 3%.<br>" +
"Increases the player's dexterity by 3%."
"Increases the player's dexterity by 5%."
});
EsperEyewear.addToFactions([BladeburnersFactionName]);
resetAugmentation(EsperEyewear);
@ -1589,7 +1589,7 @@ function initAugmentations() {
"This augmentation:<br>" +
"Increases the player's sucess chance in Bladeburner contracts/operations by 3%.<br>" +
"Increases the player's effectiveness in Bladeburner Field Analysis by 5%.<br>" +
"Increases the player's Bladeburner stamina gain rate by 1%."
"Increases the player's Bladeburner stamina gain rate by 2%."
});
EMS4Recombination.addToFactions([BladeburnersFactionName]);
resetAugmentation(EMS4Recombination);
@ -1602,7 +1602,7 @@ function initAugmentations() {
"crystallized graphene plating.<br><br>" +
"This augmentation:<br>" +
"Increases the player's defense by 5%.<br>" +
"Increases the player's strength and dexterity by 3%.<br>" +
"Increases the player's strength and dexterity by 5%.<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 4%."
});
OrionShoulder.addToFactions([BladeburnersFactionName]);
@ -1617,7 +1617,7 @@ function initAugmentations() {
"it can also be effective against non-augmented enemies due to its high temperature " +
"and concussive force.<br><br>" +
"This augmentation:<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 5%."
"Increases the player's success chance in Bladeburner contracts/operations by 6%."
});
HyperionV1.addToFactions([BladeburnersFactionName]);
resetAugmentation(HyperionV1);
@ -1629,7 +1629,7 @@ function initAugmentations() {
"more power-efficiency, more accurate, and can fire plasma bolts at a much " +
"higher velocity than the V1 model.<br><br>" +
"This augmentation:<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 7%.",
"Increases the player's success chance in Bladeburner contracts/operations by 8%.",
prereqs:[AugmentationNames.HyperionV1]
});
HyperionV2.addToFactions([BladeburnersFactionName]);
@ -1642,7 +1642,7 @@ function initAugmentations() {
"serum was originally developed by the Chinese military in an attempt to " +
"create super soldiers.<br><br>" +
"This augmentation:<br>" +
"Increases all of the player's combat stats by 5%.<br>" +
"Increases all of the player's combat stats by 7%.<br>" +
"Increases the player's Bladeburner stamina gain rate by 5%.<br>"
});
GolemSerum.addToFactions([BladeburnersFactionName]);
@ -1655,7 +1655,7 @@ function initAugmentations() {
"This augmentation:<br>" +
"Increases the player's effectiveness in Bladeburner Field Analysis by 10%.<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 4%.<br>" +
"Increases the player's dexterity experience gain rate by 5%."
"Increases the player's dexterity experience gain rate by 10%."
});
VangelisVirus.addToFactions([BladeburnersFactionName]);
resetAugmentation(VangelisVirus);
@ -1668,7 +1668,7 @@ function initAugmentations() {
"agility/reflexes.<br><br>" +
"This augmentation:<br>" +
"Increases the player's effectiveness in Bladeburner Field Analysis by 15%.<br>" +
"Increases the player's defense and dexterity experience gain rate by 5%.<br>" +
"Increases the player's defense and dexterity experience gain rate by 10%.<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 5%.",
prereqs:[AugmentationNames.VangelisVirus]
});
@ -1682,7 +1682,7 @@ function initAugmentations() {
"structurally support the body and grants heightened strength and " +
"durability.<br><br>" +
"This augmentation:<br>" +
"Increases the player's experience gain rate for all combat stats by 4%.<br>" +
"Increases the player's experience gain rate for all combat stats by 5%.<br>" +
"Increases the player's Bladeburner max stamina by 10%."
});
INTERLINKED.addToFactions([BladeburnersFactionName]);
@ -1709,7 +1709,7 @@ function initAugmentations() {
"concussive, thermal, chemical, and electric trauma. It also enhances the user's " +
"strength and agility.<br><br>" +
"This augmentation:<br>" +
"Increases all of the player's combat stats by 2%.<br>" +
"Increases all of the player's combat stats by 4%.<br>" +
"Increases the player's Bladeburner stamina gain rate by 2%.<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 3%.",
});
@ -1780,7 +1780,7 @@ function initAugmentations() {
resetAugmentation(BladeArmorIPU);
var BladesSimulacrum = new Augmentation({
name:AugmentationNames.BladesSimulacrum, repCost:6e3, moneyCost:75e9,
name:AugmentationNames.BladesSimulacrum, repCost:3e3, moneyCost:80e9,
info:"A highly-advanced matter phase-shifter module that is embedded " +
"in the brainstem and cerebellum. This augmentation allows " +
"the user to project and control a holographic simulacrum within an " +
@ -2297,48 +2297,48 @@ function applyAugmentation(aug, reapply=false) {
//Bladeburner augmentations
case AugmentationNames.EsperEyewear:
Player.bladeburner_success_chance_mult *= 1.03;
Player.dexterity_mult *= 1.03;
Player.dexterity_mult *= 1.05;
break;
case AugmentationNames.EMS4Recombination:
Player.bladeburner_success_chance_mult *= 1.03;
Player.bladeburner_analysis_mult *= 1.05;
Player.bladeburner_stamina_gain_mult *= 1.01;
Player.bladeburner_stamina_gain_mult *= 1.02;
break;
case AugmentationNames.OrionShoulder:
Player.defense_mult *= 1.05;
Player.strength_mult *= 1.03;
Player.dexterity_mult *= 1.03;
Player.strength_mult *= 1.05;
Player.dexterity_mult *= 1.05;
Player.bladeburner_success_chance_mult *= 1.04;
break;
case AugmentationNames.HyperionV1:
Player.bladeburner_success_chance_mult *= 1.05;
Player.bladeburner_success_chance_mult *= 1.06;
break;
case AugmentationNames.HyperionV2:
Player.bladeburner_success_chance_mult *= 1.07;
Player.bladeburner_success_chance_mult *= 1.08;
break;
case AugmentationNames.GolemSerum:
Player.strength_mult *= 1.05;
Player.defense_mult *= 1.05;
Player.dexterity_mult *= 1.05;
Player.agility_mult *= 1.05;
Player.strength_mult *= 1.07;
Player.defense_mult *= 1.07;
Player.dexterity_mult *= 1.07;
Player.agility_mult *= 1.07;
Player.bladeburner_stamina_gain_mult *= 1.05;
break;
case AugmentationNames.VangelisVirus:
Player.dexterity_exp_mult *= 1.05;
Player.dexterity_exp_mult *= 1.1;
Player.bladeburner_analysis_mult *= 1.1;
Player.bladeburner_success_chance_mult *= 1.04;
break;
case AugmentationNames.VangelisVirus3:
Player.defense_exp_mult *= 1.05;
Player.dexterity_exp_mult *= 1.05;
Player.defense_exp_mult *= 1.1;
Player.dexterity_exp_mult *= 1.1;
Player.bladeburner_analysis_mult *= 1.15;
Player.bladeburner_success_chance_mult *= 1.05;
break;
case AugmentationNames.INTERLINKED:
Player.strength_exp_mult *= 1.04;
Player.defense_exp_mult *= 1.04;
Player.dexterity_exp_mult *= 1.04;
Player.agility_exp_mult *= 1.04;
Player.strength_exp_mult *= 1.05;
Player.defense_exp_mult *= 1.05;
Player.dexterity_exp_mult *= 1.05;
Player.agility_exp_mult *= 1.05;
Player.bladeburner_max_stamina_mult *= 1.1;
break;
case AugmentationNames.BladeRunner:
@ -2347,10 +2347,10 @@ function applyAugmentation(aug, reapply=false) {
Player.bladeburner_stamina_gain_mult *= 1.05;
break;
case AugmentationNames.BladeArmor:
Player.strength_mult *= 1.02;
Player.defense_mult *= 1.02;
Player.dexterity_mult *= 1.02;
Player.agility_mult *= 1.02;
Player.strength_mult *= 1.04;
Player.defense_mult *= 1.04;
Player.dexterity_mult *= 1.04;
Player.agility_mult *= 1.04;
Player.bladeburner_stamina_gain_mult *= 1.02;
Player.bladeburner_success_chance_mult *= 1.03;
break;

View File

@ -1,4 +1,5 @@
import {Player} from "./Player";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {Player} from "./Player";
function BitNode(n, name, desc="", info="") {
this.number = n;
@ -141,7 +142,7 @@ function initBitNodes() {
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
"In this BitNode:<br><br>" +
"You start with $100 million<br>" +
"You start with $250 million<br>" +
"The only way to earn money is by trading on the stock market<br>" +
"You start with a WSE membership and access to the TIX API<br>" +
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
@ -199,43 +200,6 @@ function initBitNodes() {
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
}
let BitNodeMultipliers = {
HackingLevelMultiplier: 1,
ServerMaxMoney: 1,
ServerStartingMoney: 1,
ServerGrowthRate: 1,
ServerWeakenRate: 1,
ServerStartingSecurity: 1,
ManualHackMoney: 1,
ScriptHackMoney: 1,
CompanyWorkMoney: 1,
CrimeMoney: 1,
HacknetNodeMoney: 1,
CompanyWorkExpGain: 1,
ClassGymExpGain: 1,
FactionWorkExpGain: 1,
HackExpGain: 1,
CrimeExpGain: 1,
FactionWorkRepGain: 1,
FactionPassiveRepGain: 1,
RepToDonateToFaction: 1,
AugmentationRepCost: 1,
AugmentationMoneyCost: 1,
InfiltrationMoney: 1,
InfiltrationRep: 1,
CorporationValuation: 1,
BladeburnerRank: 1,
BladeburnerSkillCost: 1,
}
function initBitNodeMultipliers() {
if (Player.bitNodeN == null) {
Player.bitNodeN = 1;
@ -394,7 +358,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.CorporationValuation = dec;
BitNodeMultipliers.BladeburnerRank = dec;
BitNodeMultipliers.BladeburnerSkillCost = dec;
BitNodeMultipliers.BladeburnerSkillCost = inc;
break;
default:
console.log("WARNING: Player.bitNodeN invalid");
@ -403,7 +367,5 @@ function initBitNodeMultipliers() {
}
export {initBitNodes,
BitNode,
BitNodes,
BitNodeMultipliers,
initBitNodeMultipliers};

178
src/BitNodeMultipliers.ts Normal file
View File

@ -0,0 +1,178 @@
/**
* Bitnode multipliers influence the difficulty of different aspects of the game.
* Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the
* player toward the intended strategy. Unless they really want to play the long, slow game of waiting...
*/
interface IBitNodeMultipliers {
/**
* Influences the base cost to purchase an augmentation.
*/
AugmentationMoneyCost: number;
/**
* Influences the base rep the player must have with a faction to purchase an augmentation.
*/
AugmentationRepCost: number;
/**
* Influences how quickly the player can gain rank within Bladeburner.
*/
BladeburnerRank: number;
/**
* Influences the cost of skill levels from Bladeburner.
*/
BladeburnerSkillCost: number;
/**
* Influences the experience gained for each ability when a player completes a class.
*/
ClassGymExpGain: number;
/**
* Influences the experience gained for each ability when the player completes working their job.
*/
CompanyWorkExpGain: number;
/**
* Influences how much money the player earns when completing working their job.
*/
CompanyWorkMoney: number;
/**
* Influences the valuation of corporations created by the player.
*/
CorporationValuation: number;
/**
* Influences the base experience gained for each ability when the player commits a crime.
*/
CrimeExpGain: number;
/**
* Influences the base money gained when the player commits a crime.
*/
CrimeMoney: number;
/**
* Influences how much rep the player gains in each faction simply by being a member.
*/
FactionPassiveRepGain: number;
/**
* Influences the experience gained for each ability when the player completes work for a Faction.
*/
FactionWorkExpGain: number;
/**
* Influences how much rep the player gains when performing work for a faction.
*/
FactionWorkRepGain: number;
/**
* Influences the experienced gained when hacking a server.
*/
HackExpGain: number;
/**
* Influences how quickly the player's hacking level (not experience) scales
*/
HackingLevelMultiplier: number;
/**
* Influences how much money each Hacknet node can generate.
*/
HacknetNodeMoney: number;
/**
* Influences how much money is gained when the player infiltrates a company.
*/
InfiltrationMoney: number;
/**
* Influences how much rep the player can gain from factions when selling stolen documents and secrets
*/
InfiltrationRep: number;
/**
* Influences how much money can be stolen from a server when the player performs a hack against it through
* the Terminal.
*/
ManualHackMoney: number;
/**
* Influences the minimum favor the player must have with a faction before they can donate to gain rep.
*/
RepToDonateToFaction: number;
/**
* Influences how much money can be stolen from a server when a script performs a hack against it.
*/
ScriptHackMoney: number;
/**
* Influences the growth percentage per cycle against a server.
*/
ServerGrowthRate: number;
/**
* Influences the maxmimum money that a server can grow to.
*/
ServerMaxMoney: number;
/**
* Influences the initial money that a server starts with.
*/
ServerStartingMoney: number;
/**
* Influences the initial security level (hackDifficulty) of a server.
*/
ServerStartingSecurity: number;
/**
* Influences the weaken amount per invocation against a server.
*/
ServerWeakenRate: number;
}
/**
* The multipliers that are influenced by current Bitnode progression.
*/
// tslint:disable-next-line:variable-name
export const BitNodeMultipliers: IBitNodeMultipliers = {
HackingLevelMultiplier: 1,
ServerGrowthRate: 1,
ServerMaxMoney: 1,
ServerStartingMoney: 1,
ServerStartingSecurity: 1,
ServerWeakenRate: 1,
CompanyWorkMoney: 1,
CrimeMoney: 1,
HacknetNodeMoney: 1,
ManualHackMoney: 1,
ScriptHackMoney: 1,
ClassGymExpGain: 1,
CompanyWorkExpGain: 1,
CrimeExpGain: 1,
FactionWorkExpGain: 1,
HackExpGain: 1,
FactionPassiveRepGain: 1,
FactionWorkRepGain: 1,
RepToDonateToFaction: 1,
AugmentationMoneyCost: 1,
AugmentationRepCost: 1,
InfiltrationMoney: 1,
InfiltrationRep: 1,
CorporationValuation: 1,
BladeburnerRank: 1,
BladeburnerSkillCost: 1,
};

View File

@ -1,13 +1,13 @@
import {Augmentations, AugmentationNames} from "./Augmentations";
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {Faction, Factions, factionExists,
joinFaction, displayFactionContent} from "./Faction";
import {Locations} from "./Location";
import {Locations} from "./Locations";
import {Player} from "./Player";
import {hackWorldDaemon, redPillFlag} from "./RedPill";
import {KEY} from "./Terminal";
import {KEY} from "../utils/helpers/keyCodes";
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
import {dialogBoxCreate} from "../utils/DialogBox";
@ -20,6 +20,7 @@ import {appendLineBreaks} from "../utils/uiHelpers/app
import {clearObject} from "../utils/helpers/clearObject";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getRandomInt} from "../utils/helpers/getRandomInt";
@ -61,12 +62,12 @@ var ActionCountGrowthPeriod = 300; //Time (s) it takes for action count to g
var RankToFactionRepFactor = 2; //Delta Faction Rep = this * Delta Rank
var RankNeededForFaction = 25;
var ContractSuccessesPerLevel = 3; //How many successes you need to level up a contract
var OperationSuccessesPerLevel = 2.5; //How many successes you need to level up an op
var ContractSuccessesPerLevel = 3.5; //How many successes you need to level up a contract
var OperationSuccessesPerLevel = 3; //How many successes you need to level up an op
var RanksPerSkillPoint = 4; //How many ranks needed to get 1 Skill Point
var ContractBaseMoneyGain = 40e3; //Base Money Gained per contract
var ContractBaseMoneyGain = 50e3; //Base Money Gained per contract
//DOM related variables
var ActiveActionCssClass = "bladeburner-active-action";
@ -145,7 +146,7 @@ var consoleHelpText = {
//Keypresses for Console
$(document).keydown(function(event) {
if (Engine.currentPage === Engine.Page.Bladeburner) {
if (routing.isOn(Page.Bladeburner)) {
//if (DomElems.consoleInput && !event.ctrlKey && !event.shiftKey && !event.altKey) {
// DomElems.consoleInput.focus();
//}
@ -213,8 +214,8 @@ function City(params={}) {
this.popEst = this.pop * (Math.random() + 0.5);
//Number of Synthoid communities population and estimate
this.comms = params.comms ? params.comms : getRandomInt(1, 40);
this.commsEst = this.comms + getRandomInt(-2, 2);
this.comms = params.comms ? params.comms : getRandomInt(5, 100);
this.commsEst = this.comms + getRandomInt(-5, 5);
if (this.commsEst < 0) {this.commsEst = 0;}
this.chaos = 0;
}
@ -733,7 +734,7 @@ Bladeburner.prototype.create = function() {
"whatever city you are currently in.",
baseDifficulty:125,difficultyFac:1.02,rewardFac:1.041,
rankGain:0.3, hpLoss:0.5,
count:getRandomInt(300, 800), countGrowth:getRandomInt(1, 5),
count:getRandomInt(100, 500), countGrowth:getRandomInt(5, 75)/10,
weights:{hack:0,str:0.05,def:0.05,dex:0.35,agi:0.35,cha:0.1, int:0.05},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.9, int:1},
isStealth:true
@ -745,7 +746,7 @@ Bladeburner.prototype.create = function() {
"current city, and will also increase its chaos level.",
baseDifficulty:250, difficultyFac:1.04,rewardFac:1.085,
rankGain:0.9, hpLoss:1,
count:getRandomInt(200, 750), countGrowth:getRandomInt(1, 3),
count:getRandomInt(25, 750), countGrowth:getRandomInt(5, 75)/10,
weights:{hack:0,str:0.15,def:0.15,dex:0.25,agi:0.25,cha:0.1, int:0.1},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.8, int:0.9},
isKill:true
@ -757,7 +758,7 @@ Bladeburner.prototype.create = function() {
"city, and will also increase its chaos level.",
baseDifficulty:200, difficultyFac:1.03, rewardFac:1.065,
rankGain:0.6, hpLoss:1,
count:getRandomInt(300, 900), countGrowth:getRandomInt(1,4),
count:getRandomInt(50, 1000), countGrowth:getRandomInt(5,75)/10,
weights:{hack:0,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0.1, int:0.1},
decays:{hack:0,str:0.91,def:0.91,dex:0.91,agi:0.91,cha:0.8, int:0.9},
isKill:true
@ -771,8 +772,8 @@ Bladeburner.prototype.create = function() {
"synthoid data.<br><br>" +
"You will NOT lose HP from failed Investigation ops.",
baseDifficulty:400, difficultyFac:1.03,rewardFac:1.07,reqdRank:25,
rankGain:2, rankLoss:0.2,
count:getRandomInt(50, 400), countGrowth:1,
rankGain:2.2, rankLoss:0.2,
count:getRandomInt(50, 200), countGrowth:getRandomInt(10, 40)/10,
weights:{hack:0.25,str:0.05,def:0.05,dex:0.2,agi:0.1,cha:0.25, int:0.1},
decays:{hack:0.85,str:0.9,def:0.9,dex:0.9,agi:0.9,cha:0.7, int:0.9},
isStealth:true
@ -784,8 +785,8 @@ Bladeburner.prototype.create = function() {
"Successful Undercover ops will increase the accuracy of your synthoid " +
"data.",
baseDifficulty:500, difficultyFac:1.04, rewardFac:1.09, reqdRank:100,
rankGain:4, rankLoss:0.4, hpLoss:2,
count:getRandomInt(50, 300), countGrowth:1,
rankGain:4.4, rankLoss:0.4, hpLoss:2,
count:getRandomInt(25, 300), countGrowth:getRandomInt(10, 40)/10,
weights:{hack:0.2,str:0.05,def:0.05,dex:0.2,agi:0.2,cha:0.2, int:0.1},
decays:{hack:0.8,str:0.9,def:0.9,dex:0.9,agi:0.9,cha:0.7, int:0.9},
isStealth:true
@ -795,8 +796,8 @@ Bladeburner.prototype.create = function() {
desc:"Conduct a sting operation to bait and capture particularly " +
"notorious Synthoid criminals.",
baseDifficulty:650, difficultyFac:1.04, rewardFac:1.095, reqdRank:500,
rankGain:5, rankLoss:0.5, hpLoss:2.5,
count:getRandomInt(25,400), countGrowth:0.75,
rankGain:5.5, rankLoss:0.5, hpLoss:2.5,
count:getRandomInt(25,400), countGrowth:getRandomInt(3, 40)/10,
weights:{hack:0.25,str:0.05,def:0.05,dex:0.25,agi:0.1,cha:0.2, int:0.1},
decays:{hack:0.8,str:0.85,def:0.85,dex:0.85,agi:0.85,cha:0.7, int:0.9},
isStealth:true
@ -807,8 +808,8 @@ Bladeburner.prototype.create = function() {
"there must be an existing Synthoid community in your current city " +
"in order for this Operation to be successful",
baseDifficulty:800, difficultyFac:1.045, rewardFac:1.1, reqdRank:3000,
rankGain:50,rankLoss:2.5,hpLoss:50,
count:getRandomInt(25, 150), countGrowth:0.2,
rankGain:55,rankLoss:2.5,hpLoss:50,
count:getRandomInt(25, 150), countGrowth:getRandomInt(2, 40)/10,
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
decays:{hack:0.7,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.9},
isKill:true
@ -819,8 +820,8 @@ Bladeburner.prototype.create = function() {
"objective is to complete the task without " +
"drawing any attention. Stealth and discretion are key.",
baseDifficulty:1000, difficultyFac:1.05, rewardFac:1.11, reqdRank:20e3,
rankGain:20, rankLoss:2, hpLoss:10,
count:getRandomInt(25, 250), countGrowth:0.1,
rankGain:22, rankLoss:2, hpLoss:10,
count:getRandomInt(25, 250), countGrowth:getRandomInt(1, 20)/10,
weights:{hack:0.1,str:0.1,def:0.1,dex:0.3,agi:0.3,cha:0, int:0.1},
decays:{hack:0.7,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.9},
isStealth:true, isKill:true
@ -831,8 +832,8 @@ Bladeburner.prototype.create = function() {
"important, high-profile social and political leaders " +
"in the Synthoid communities.",
baseDifficulty:1500, difficultyFac:1.06, rewardFac:1.14, reqdRank:50e3,
rankGain:40, rankLoss:4, hpLoss:5,
count:getRandomInt(25, 200), countGrowth:0.1,
rankGain:44, rankLoss:4, hpLoss:5,
count:getRandomInt(25, 200), countGrowth:getRandomInt(1, 20)/10,
weights:{hack:0.1,str:0.1,def:0.1,dex:0.3,agi:0.3,cha:0, int:0.1},
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.8},
isStealth:true, isKill:true
@ -852,8 +853,12 @@ Bladeburner.prototype.process = function() {
//If the Player starts doing some other actions, set action to idle and alert
if (Augmentations[AugmentationNames.BladesSimulacrum].owned === false && Player.isWorking) {
if (this.action.type !== ActionTypes["Idle"]) {
dialogBoxCreate("Your Bladeburner action was cancelled because you started " +
"doing something else");
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) {
msg += `<br><br>Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`
this.automateEnabled = false;
}
dialogBoxCreate(msg);
}
this.resetAction();
}
@ -915,7 +920,7 @@ Bladeburner.prototype.process = function() {
}
}
if (Engine.currentPage === Engine.Page.Bladeburner) {
if (routing.isOn(Page.Bladeburner)) {
this.updateContent();
}
}
@ -1257,7 +1262,7 @@ Bladeburner.prototype.completeAction = function() {
return hackWorldDaemon(Player.bitNodeN);
}
if (Engine.currentPage === Engine.Page.Bladeburner) {
if (routing.isOn(Page.Bladeburner)) {
this.createActionAndSkillsContent();
}
@ -1338,7 +1343,7 @@ Bladeburner.prototype.completeAction = function() {
Player.gainHackingExp(hackingExpGain);
Player.gainIntelligenceExp(BaseIntGain);
Player.gainCharismaExp(charismaExpGain);
this.changeRank(0.1);
this.changeRank(0.1 * BitNodeMultipliers.BladeburnerRank);
console.log("DEBUG: Field Analysis effectiveness is " + (eff * this.skillMultipliers.successChanceEstimate));
this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate);
if (this.logging.general) {
@ -1527,7 +1532,7 @@ Bladeburner.prototype.randomEvent = function() {
var destCity = this.cities[destCityName];
if (!(sourceCity instanceof City) || !(destCity instanceof City)) {
throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()");
throw new Error("sourceCity/destCity was not a City object in Bladeburner.randomEvent()");
}
if (chance <= 0.05) {
@ -1824,8 +1829,8 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewBonusTime = createElement("p", {
innerText: "Bonus time: ",
display: "inline-block",
tooltip: "You gain bonus time while offline or when you're not performing any action. " +
"Bonus time makes the game progress faster."
tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser). " +
"Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed."
});
DomElems.overviewSkillPoints = createElement("p", {display:"block"});
@ -2194,7 +2199,7 @@ Bladeburner.prototype.updateContent = function() {
}
Bladeburner.prototype.updateOverviewContent = function() {
if (Engine.currentPage !== Engine.Page.Bladeburner) {return;}
if (!routing.isOn(Page.Bladeburner)) {return;}
DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2);
DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3);
DomElems.overviewGen1.innerHTML =
@ -3465,6 +3470,27 @@ Bladeburner.prototype.getSkillLevelNetscriptFn = function(skillName, workerScrip
}
}
Bladeburner.prototype.getSkillUpgradeCostNetscriptFn = function(skillName, workerScript) {
var errorLogText = "ERROR: bladeburner.getSkillUpgradeCostNetscriptFn() failed due to an invalid skill specified: " +
skillName + ". Note that the name of the skill is case-sensitive";
if (skillName === "") {
return -1;
}
if (!Skills.hasOwnProperty(skillName)) {
workerScript.log(errorLogText);
return -1;
}
var skill = Skills[skillName];
if (this.skills[skillName] == null) {
return skill.calculateCost(0);
} else {
return skill.calculateCost(this.skills[skillName]);
}
}
Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript) {
var errorLogText = "ERROR: bladeburner.upgradeSkill() failed due to an invalid skill specified: " +
skillName + ". Note that the name of the skill is case-sensitive";
@ -3498,7 +3524,7 @@ Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript
this.skillPoints -= cost;
this.upgradeSkill(skill);
if (Engine.currentPage === Engine.Page.Bladeburner && DomElems.currentTab.toLowerCase() === "skills") {
if (routing.isOn(Page.Bladeburner) && DomElems.currentTab.toLowerCase() === "skills") {
this.createActionAndSkillsContent();
}
if (workerScript.shouldLog("upgradeSkill")) {
@ -3620,7 +3646,7 @@ Bladeburner.prototype.joinBladeburnerFactionNetscriptFn = function(workerScript)
if (workerScript.shouldLog("joinBladeburnerFaction")) {
workerScript.log("Joined Bladeburners Faction");
}
if (Engine.currentPage === Engine.Page.Bladeburner) {
if (routing.isOn(Page.Bladeburner)) {
removeChildrenFromElement(DomElems.overviewDiv);
this.createOverviewContent();
}

View File

@ -1,6 +1,5 @@
import {CONSTANTS} from "./Constants";
import {Locations} from "./Location";
import {Player} from "./Player";
import {Locations} from "./Locations";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";

View File

@ -1,8 +1,7 @@
import {BitNodeMultipliers} from "./BitNode";
import {Engine} from "./engine";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {Factions} from "./Faction";
import {showLiterature} from "./Literature";
import {Locations} from "./Location";
import {Locations} from "./Locations";
import {Player} from "./Player";
import Decimal from "decimal.js";
@ -13,6 +12,7 @@ import {Reviver, Generic_toJSON,
import numeral from "numeral/min/numeral.min";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking";
import {formatNumber, generateRandomString} from "../utils/StringHelperFunctions";
import {getRandomInt} from "../utils/helpers/getRandomInt";
import {isString} from "../utils/helpers/isString";
@ -3118,7 +3118,7 @@ Corporation.prototype.process = function() {
this.state.nextState();
if (Engine.currentPage === Engine.Page.Corporation) {this.updateUIContent();}
if (routing.isOn(Page.Corporation)) {this.updateUIContent();}
}
}

View File

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.40.0",
Version: "0.40.2",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -115,6 +115,8 @@ let CONSTANTS = {
//Stock market constants
WSEAccountCost: 200e6,
TIXAPICost: 5e9,
MarketData4SCost: 1e9,
MarketDataTixApi4SCost: 20e9,
StockMarketCommission: 100e3,
//Hospital/Health
@ -491,19 +493,32 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>",
LatestUpdate:
"v0.40.1 - Community Update<br>" +
"* Added getPurchasedServerCost() Netscript function (by kopelli)<br>" +
"* Added getFavorToDonate() Netscript function (by hydroflame)<br>" +
"* Added getFactionFavorGain() and getCompanyFavorGain() Singularity functions (by hydroflame)<br>" +
"* Accumulated 'bonus' time in Bladeburner is now displayed in the UI (by hydroflame)<br>" +
"* The Red Pill can now be purchased with negative money (since its supposed to be free) (by hydroflame)<br>" +
"* Cranial Signal Processor Augmentations now have the previous generation as a prerequisite. i.e. Cranial Signal Processor - Gen II requires Gen I (by Kline-)<br>" +
"* Bladeburner Raid operations can no longer be performed if your estimate of Synthoid communities is zero (by hydroflame)<br>" +
"* The difficulty of BN-12 now scales faster (by hydroflame)<br>" +
"* Active Scripts UI now shows a RAM Usage bar for each server (by kopelli)<br>" +
"* Bug Fix: Corrected terminal timestamp format (by kopelli)<br>" +
"* Bug Fix: NetscriptJS scripts should now die properly if they don't have a 'main' function (by hydroflame)<br>" +
"* Various minor UI/QOL fixes by hydroflame, kopelli, and Kline-"
"v0.40.2<br>" +
"------------------------------<br>" +
"* Bladeburner Changes:<br>" +
"*** Added getBonusTime(), getSkillUpgradeCost(), and getCity() Netscript functions to the API<br>" +
"*** Buffed the effects of many Bladeburner Augmentations<br>" +
"*** The Blade's Simulacrum Augmentation requires significantly less reputation but slightly more money<br>" +
"*** Slightly increased the amount of successes needed for a Contract/Operation in order to increase its max level<br>" +
"*** Increased the amount of money gained from Contracts by ~25%<br>" +
"*** Increased the base amount of rank gained from Operations by 10%<br>" +
"*** Significantly increased the 'randomness' in determining a Contract/Operation's initial count and rate of count increase<br>" +
"*** The number (count) of Operations should now increase significantly faster<br>" +
"*** There are now, on average, more Synthoid communities in a city<br>" +
"*** If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation<br>" +
"------------------------------<br>" +
"* Stock Market Changes:<br>" +
"***Added a watchlist filter feature to the UI that allows you to specify which stocks to show<br>" +
"***Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks<br>" +
"***Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript<br>" +
"------------------------------<br>" +
"* There is now a setting for enabling/disabling the popup that appears when you are hospitalized<br>" +
"* Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-)<br>" +
"* Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-)<br>" +
"* Bug Fix: Bladeburner skill cost multiplier should now properly increase in BitNode-12 (by hydroflame)<br>" +
"* Bug Fix: 'document', 'hacknet', and 'window' keywords should no longer be counted multiple times in RAM calculations<br>" +
"* Bug Fix: Joining factions through Singularity functions should now prevent you from joining opposing factions<br>" +
"* Bug Fix: Four Sigma should no longer have two 'Speech Enhancement' Augmentations (by Kline-)<br>"
}

View File

@ -1,7 +1,7 @@
import {Programs} from "./CreateProgram";
import {Player} from "./Player";
import {SpecialServerIps} from "./SpecialServerIps";
import {post} from "./Terminal";
import {post} from "./ui/postToTerminal";
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
import {formatNumber} from "../utils/StringHelperFunctions";

View File

@ -1,6 +1,6 @@
import {Augmentations, AugmentationNames,
PlayerOwnedAugmentation} from "./Augmentations";
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {FactionInfos} from "./FactionInfo";
@ -9,6 +9,7 @@ import {HackingMission, setInMission} from "./Missions";
import {Player} from "./Player";
import {Settings} from "./Settings";
import {Page, routing} from "./ui/navigationTracking";
import {dialogBoxCreate} from "../utils/DialogBox";
import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
@ -137,7 +138,7 @@ function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (Engine.currentPage === Engine.Page.Factions) {
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
} else {

View File

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

View File

@ -9,6 +9,7 @@ import {Reviver, Generic_toJSON,
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getRandomInt} from "../utils/helpers/getRandomInt";
@ -24,7 +25,7 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
/* Gang.js */
//Switch between territory and management screen with 1 and 2
$(document).keydown(function(event) {
if (Engine.currentPage == Engine.Page.Gang && !yesNoBoxOpen) {
if (routing.isOn(Page.Gang) && !yesNoBoxOpen) {
if (gangMemberFilter != null && gangMemberFilter === document.activeElement) {return;}
if (event.keyCode === 49) {
if(gangTerritorySubpage.style.display === "block") {

View File

@ -1,14 +1,15 @@
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {Player} from "./Player";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {createElement} from "../utils/uiHelpers/createElement";
import {Page, routing} from "./ui/navigationTracking";
import {formatNumber} from "../utils/StringHelperFunctions";
import {getElementById} from "../utils/uiHelpers/getElementById";
@ -244,8 +245,8 @@ Reviver.constructors.HacknetNode = HacknetNode;
function purchaseHacknet() {
/* INTERACTIVE TUTORIAL */
if (iTutorialIsRunning) {
if (currITutorialStep == iTutorialSteps.HacknetNodesIntroduction) {
if (ITutorial.isRunning) {
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
iTutorialNextStep();
} else {
return;
@ -273,7 +274,7 @@ function purchaseHacknet() {
Player.loseMoney(cost);
Player.hacknetNodes.push(node);
if (Engine.currentPage === Engine.Page.HacknetNodes) {
if (routing.isOn(Page.HacknetNodes)) {
displayHacknetNodesContent();
}
updateTotalHacknetProduction();
@ -445,7 +446,7 @@ function updateHacknetNodesContent() {
//Update player's money
updateText("hacknet-nodes-player-money", "$" + formatNumber(Player.money.toNumber(), 2));
updateText("hacknet-nodes-total-production", "$" + formatNumber(Player.totalHacknetNodeProduction, 2) + " / second");
updateText("hacknet-nodes-total-production", "$" + formatNumber(Player.totalHacknetNodeProduction, 2) + " / sec");
//Update information in each owned hacknet node
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
@ -547,7 +548,7 @@ function updateHacknetNodeDomElement(nodeObj) {
updateText("hacknet-node-name-" + nodeName, nodeName);
updateText("hacknet-node-total-production-" + nodeName, "$" + formatNumber(nodeObj.totalMoneyGenerated, 2));
updateText("hacknet-node-production-rate-" + nodeName, "($" + formatNumber(nodeObj.moneyGainRatePerSecond, 2) + " / second)");
updateText("hacknet-node-production-rate-" + nodeName, "($" + formatNumber(nodeObj.moneyGainRatePerSecond, 2) + " / sec)");
updateText("hacknet-node-level-" + nodeName, nodeObj.level);
updateText("hacknet-node-ram-" + nodeName, nodeObj.ram + "GB");
updateText("hacknet-node-cores-" + nodeName, nodeObj.cores);

View File

@ -1,4 +1,4 @@
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {Player} from "./Player";

View File

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

View File

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

File diff suppressed because it is too large Load Diff

90
src/Locations.ts Normal file
View File

@ -0,0 +1,90 @@
import { IMap } from "./types";
/**
* Display Location Content when visiting somewhere in the World
*/
// tslint:disable-next-line:variable-name
export const Locations: IMap<string> = {
// Cities
Aevum: "Aevum",
Chongqing: "Chongqing",
Ishima: "Ishima",
NewTokyo: "New Tokyo",
Sector12: "Sector-12",
Volhaven: "Volhaven",
// Aevum Locations
AevumAeroCorp: "AeroCorp",
AevumBachmanAndAssociates: "Bachman & Associates",
AevumClarkeIncorporated: "Clarke Incorporated",
AevumCrushFitnessGym: "Crush Fitness Gym",
AevumECorp: "ECorp",
AevumFulcrumTechnologies: "Fulcrum Technologies",
AevumGalacticCybersystems: "Galactic Cybersystems",
AevumNetLinkTechnologies: "NetLink Technologies",
AevumPolice: "Aevum Police Headquarters",
AevumRhoConstruction: "Rho Construction",
AevumSlums: "Aevum Slums",
AevumSnapFitnessGym: "Snap Fitness Gym",
AevumSummitUniversity: "Summit University",
AevumTravelAgency: "Aevum Travel Agency",
AevumWatchdogSecurity: "Watchdog Security",
// Chongqing locations
ChongqingKuaiGongInternational: "KuaiGong International",
ChongqingSlums: "Chongqing Slums",
ChongqingSolarisSpaceSystems: "Solaris Space Systems",
ChongqingTravelAgency: "Chongqing Travel Agency",
// Sector 12
Sector12AlphaEnterprises: "Alpha Enterprises",
Sector12BladeIndustries: "Blade Industries",
Sector12CIA: "Central Intelligence Agency",
Sector12CarmichaelSecurity: "Carmichael Security",
Sector12CityHall: "Sector-12 City Hall",
Sector12DeltaOne: "DeltaOne",
Sector12FoodNStuff: "FoodNStuff",
Sector12FourSigma: "Four Sigma",
Sector12IcarusMicrosystems: "Icarus Microsystems",
Sector12IronGym: "Iron Gym",
Sector12JoesGuns: "Joe's Guns",
Sector12MegaCorp: "MegaCorp",
Sector12NSA: "National Security Agency",
Sector12PowerhouseGym: "Powerhouse Gym",
Sector12RothmanUniversity: "Rothman University",
Sector12Slums: "Sector-12 Slums",
Sector12TravelAgency: "Sector-12 Travel Agency",
Sector12UniversalEnergy: "Universal Energy",
// New Tokyo
NewTokyoDefComm: "DefComm",
NewTokyoGlobalPharmaceuticals: "Global Pharmaceuticals",
NewTokyoNoodleBar: "Noodle Bar",
NewTokyoSlums: "New Tokyo Slums",
NewTokyoTravelAgency: "New Tokyo Travel Agency",
NewTokyoVitaLife: "VitaLife",
// Ishima
IshimaNovaMedical: "Nova Medical",
IshimaOmegaSoftware: "Omega Software",
IshimaSlums: "Ishima Slums",
IshimaStormTechnologies: "Storm Technologies",
IshimaTravelAgency: "Ishima Travel Agency",
// Volhaven
VolhavenCompuTek: "CompuTek",
VolhavenHeliosLabs: "Helios Labs",
VolhavenLexoCorp: "LexoCorp",
VolhavenMilleniumFitnessGym: "Millenium Fitness Gym",
VolhavenNWO: "NWO",
VolhavenOmniTekIncorporated: "OmniTek Incorporated",
VolhavenOmniaCybersystems: "Omnia Cybersystems",
VolhavenSlums: "Volhaven Slums",
VolhavenSysCoreSecurities: "SysCore Securities",
VolhavenTravelAgency: "Volhaven Travel Agency",
VolhavenZBInstituteOfTechnology: "ZB Institute of Technology",
// Generic locations
Hospital: "Hospital",
WorldStockExchange: "World Stock Exchange",
};

View File

@ -1,4 +1,4 @@
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Player} from "./Player";
import {Environment} from "./NetscriptEnvironment";

View File

@ -5,7 +5,7 @@ import {updateActiveScriptsItems} from "./ActiveScriptsUI";
import {Augmentations, Augmentation,
augmentationExists, installAugmentations,
AugmentationNames} from "./Augmentations";
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {determineCrimeSuccess, findCrime} from "./Crimes";
import {Bladeburner} from "./Bladeburner";
import {Companies, Company, CompanyPosition,
@ -13,12 +13,11 @@ import {Companies, Company, CompanyPosition,
import {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram";
import {DarkWebItems} from "./DarkWeb";
import {Engine} from "./engine";
import {AllGangs} from "./Gang";
import {Factions, Faction, joinFaction,
factionExists, purchaseAugmentation} from "./Faction";
import {getCostOfNextHacknetNode, purchaseHacknet} from "./HacknetNode";
import {Locations} from "./Location";
import {Locations} from "./Locations";
import {Message, Messages} from "./Message";
import {inMission} from "./Missions";
import {Player} from "./Player";
@ -35,7 +34,7 @@ import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
updateStockTicker, updateStockPlayerPosition,
Stock, shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket";
import {post} from "./Terminal";
import {post} from "./ui/postToTerminal";
import {TextFile, getTextFile, createTextFile} from "./TextFile";
import {unknownBladeburnerActionErrorMessage,
@ -50,6 +49,7 @@ import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
import {NetscriptPort} from "./NetscriptPort";
import Decimal from "decimal.js";
import {Page, routing} from "./ui/navigationTracking";
import {dialogBoxCreate} from "../utils/DialogBox";
import {isPowerOfTwo} from "../utils/helpers/isPowerOfTwo";
import {arrayToString} from "../utils/helpers/arrayToString";
@ -183,11 +183,11 @@ function NetscriptFunctions(workerScript) {
/**
* @param {number} ram The amount of server RAM to calculate cost of.
* @exception {Error} If the value passed in is not numeric, out of range, or too large of a value.
* @returns {number} The cost of
* @returns {number} The cost of
*/
const getPurchaseServerRamCostGuard = (ram) => {
const guardedRam = Math.round(ram);
if (isNaN(guardedRam) || !powerOfTwo(guardedRam)) {
if (isNaN(guardedRam) || !isPowerOfTwo(guardedRam)) {
throw Error("failed due to invalid ram argument. Must be numeric and a power of 2");
}
@ -1415,7 +1415,7 @@ function NetscriptFunctions(workerScript) {
var newTotal = origTotal + totalPrice;
stock.playerShares += shares;
stock.playerAvgPx = newTotal / stock.playerShares;
if (Engine.currentPage == Engine.Page.StockMarket) {
if (routing.isOn(Page.StockMarket)) {
updateStockPlayerPosition(stock);
}
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.buyStock == null) {
@ -1456,7 +1456,7 @@ function NetscriptFunctions(workerScript) {
if (stock.playerShares == 0) {
stock.playerAvgPx = 0;
}
if (Engine.currentPage == Engine.Page.StockMarket) {
if (routing.isOn(Page.StockMarket)) {
updateStockPlayerPosition(stock);
}
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.sellStock == null) {
@ -1599,6 +1599,36 @@ function NetscriptFunctions(workerScript) {
};
return cancelOrder(params, workerScript);
},
getStockVolatility : function(symbol) {
if (workerScript.checkingRam) {
return updateStaticRam("getStockVolatility", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("getStockVolatility", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.has4SDataTixApi) {
throw makeRuntimeRejectMsg(workerScript, "You don't have 4S Market Data TIX API Access! Cannot use getStockVolatility()");
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into getStockVolatility()");
}
return stock.mv / 100; //Convert from percentage to decimal
},
getStockForecast : function(symbol) {
if (workerScript.checkingRam) {
return updateStaticRam("getStockForecast", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("getStockForecast", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.has4SDataTixApi) {
throw makeRuntimeRejectMsg(workerScript, "You don't have 4S Market Data TIX API Access! Cannot use getStockForecast()");
}
var stock = SymbolToStockMap[symbol];
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into getStockForecast()");
}
var forecast = 50;
stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag;
return forecast / 100; //Convert from percentage to decimal
},
getPurchasedServerLimit : function() {
if (workerScript.checkingRam) {
return updateStaticRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit);
@ -2844,16 +2874,16 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("ERROR: Cannot join " + name + " Faction because you have not been invited. joinFaction() failed");
return false;
}
var index = Player.factionInvitations.indexOf(name);
if (index === -1) {
//Redundant and should never happen...
workerScript.scriptRef.log("ERROR: Cannot join " + name + " Faction because you have not been invited. joinFaction() failed");
return false;
}
Player.factionInvitations.splice(index, 1);
var fac = Factions[name];
joinFaction(fac);
//Update Faction Invitation list to account for joined + banned factions
for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == name || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
i--;
}
}
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.joinFaction == null) {
workerScript.scriptRef.log("Joined the " + name + " faction.");
@ -3424,12 +3454,7 @@ function NetscriptFunctions(workerScript) {
}
updateDynamicRam("getCurrentAction", CONSTANTS.ScriptBladeburnerApiBaseRamCost / 4);
if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) {
let res = Player.bladeburner.getTypeAndNameFromActionId(Player.bladeburner.action);
if (res.type === "Idle" && res.name === "Idle") {
return null;
} else {
return res;
}
return Player.bladeburner.getTypeAndNameFromActionId(Player.bladeburner.action);
}
throw makeRuntimeRejectMsg(workerScript, "getCurrentAction() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
@ -3644,6 +3669,21 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "getSkillLevel() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
},
getSkillUpgradeCost : function(skillName="") {
if (workerScript.checkingRam) {
return updateStaticRam("getSkillUpgradeCost", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
}
updateDynamicRam("getSkillUpgradeCost", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) {
try {
return Player.bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
} catch(e) {
throw makeRuntimeRejectMsg(workerScript, "Bladeburner.getSkillUpgradeCost() failed with exception: " + e);
}
}
throw makeRuntimeRejectMsg(workerScript, "getSkillUpgradeCost() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
},
upgradeSkill : function(skillName) {
if (workerScript.checkingRam) {
return updateStaticRam("upgradeSkill", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
@ -3734,6 +3774,21 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "getCityChaos() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
},
getCity : function() {
if (workerScript.checkingRam) {
return updateStaticRam("getCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
}
updateDynamicRam("getCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
if (Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || hasBladeburner2079SF)) {
try {
return Player.bladeburner.city;
} catch(e) {
throw makeRuntimeRejectMsg(workerScript, "Bladeburner.getCity() failed with exception: " + e);
}
}
throw makeRuntimeRejectMsg(workerScript, "getCity() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
},
switchCity : function(cityName) {
if (workerScript.checkingRam) {
return updateStaticRam("switchCity", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
@ -3791,6 +3846,14 @@ function NetscriptFunctions(workerScript) {
}
throw makeRuntimeRejectMsg(workerScript, "joinBladeburnerDivision() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
},
getBonusTime : function() {
if (workerScript.checkingRam) {return 0;}
if ((Player.bitNodeN === 7 || hasBladeburner2079SF)) {
return Math.round(Player.bladeburner.storedCycles / 5);
}
throw makeRuntimeRejectMsg(workerScript, "getBonusTime() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7");
}
}
} //End return

View File

@ -199,7 +199,8 @@ function startNetscript1Script(workerScript) {
});
}
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
} else if (name === "sprintf" || name === "vsprintf" || name === "scp") {
} else if (name === "sprintf" || name === "vsprintf" || name === "scp" ||
name == "write" || name === "read" || name === "tryWrite") {
let tempWrapper = function() {
let fnArgs = [];

View File

@ -1,7 +1,7 @@
import {Augmentations, applyAugmentation,
AugmentationNames,
PlayerOwnedAugmentation} from "./Augmentations";
import {BitNodes, BitNode, BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {Company, Companies, getNextCompanyPosition,
getJobRequirementText, CompanyPosition,
CompanyPositions} from "./Company";
@ -13,9 +13,10 @@ import {Engine} from "./engine";
import {Factions, Faction,
displayFactionContent} from "./Faction";
import {Gang, resetGangs} from "./Gang";
import {Locations} from "./Location";
import {Locations} from "./Locations";
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
import {AllServers, Server, AddToAllServers} from "./Server";
import {Settings} from "./Settings";
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile";
@ -172,6 +173,8 @@ function PlayerObject() {
//Stock Market
this.hasWseAccount = false;
this.hasTixApiAccess = false;
this.has4SData = false;
this.has4SDataTixApi = false;
//Gang
this.gang = 0;
@ -388,6 +391,8 @@ PlayerObject.prototype.prestigeSourceFile = function() {
//Reset Stock market
this.hasWseAccount = false;
this.hasTixApiAccess = false;
this.has4SData = false;
this.has4SDataTixApi = false;
//BitNode 3: Corporatocracy
if (this.bitNodeN === 3) {this.money = new Decimal(150e9);}
@ -561,6 +566,8 @@ PlayerObject.prototype.analyze = function() {
PlayerObject.prototype.hasProgram = function(programName) {
var home = Player.getHomeComputer();
if (home == null) {return false;}
for (var i = 0; i < home.programs.length; ++i) {
if (programName.toLowerCase() == home.programs[i].toLowerCase()) {return true;}
}
@ -1692,9 +1699,14 @@ PlayerObject.prototype.takeDamage = function(amt) {
}
PlayerObject.prototype.hospitalize = function() {
dialogBoxCreate("You were in critical condition! You were taken to the hospital where " +
"luckily they were able to save your life. You were charged $" +
formatNumber(this.max_hp * CONSTANTS.HospitalCostPerHp, 2));
if (Settings.SuppressHospitalizationPopup === false) {
dialogBoxCreate(
"You were in critical condition! You were taken to the hospital where " +
"luckily they were able to save your life. You were charged " +
numeral(this.max_hp * CONSTANTS.HospitalCostPerHp).format('$0.000a')
);
}
this.loseMoney(this.max_hp * CONSTANTS.HospitalCostPerHp);
this.hp = this.max_hp;
}
@ -2039,7 +2051,7 @@ PlayerObject.prototype.reapplyAllSourceFiles = function() {
//those requirements and will return an array of all factions that the Player should
//receive an invitation to
PlayerObject.prototype.checkForFactionInvitations = function() {
let invitedFactions = []; //Array which will hold all Factions th eplayer should be invited to
let invitedFactions = []; //Array which will hold all Factions the player should be invited to
var numAugmentations = this.augmentations.length;

57
src/Prestige.js Normal file → Executable file
View File

@ -109,17 +109,6 @@ function prestigeAugmentation() {
//Messages
initMessages();
//Reset Stock market
if (Player.hasWseAccount) {
initStockMarket();
initSymbolToStockMap();
}
setStockMarketContentCreated(false);
var stockMarketList = document.getElementById("stock-market-list");
while(stockMarketList.firstChild) {
stockMarketList.removeChild(stockMarketList.firstChild);
}
//Gang, in BitNode 2
if (Player.bitNodeN == 2 && Player.inGang()) {
var faction = Factions[Player.gang.facName];
@ -140,6 +129,20 @@ function prestigeAugmentation() {
Player.hasTixApiAccess = true;
}
//Reset Stock market
if (Player.hasWseAccount) {
initStockMarket();
initSymbolToStockMap();
}
setStockMarketContentCreated(false);
var stockMarketList = document.getElementById("stock-market-list");
while(stockMarketList.firstChild) {
stockMarketList.removeChild(stockMarketList.firstChild);
}
var watchlist = document.getElementById("stock-market-watchlist-filter");
watchlist.value = ""; //Reset watchlist filter
//Load Terminal Screen
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
Terminal.resetTerminalInput();
@ -241,22 +244,6 @@ function prestigeSourceFile() {
//Reinitialize Bit Node flags
initSingularitySFFlags();
//Reset Stock market, gang, and corporation
if (Player.hasWseAccount) {
initStockMarket();
initSymbolToStockMap();
}
setStockMarketContentCreated(false);
var stockMarketList = document.getElementById("stock-market-list");
while(stockMarketList.firstChild) {
stockMarketList.removeChild(stockMarketList.firstChild);
}
Player.gang = null;
deleteGangDisplayContent();
Player.corporation = null;
Player.bladeburner = null;
//BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) {
Player.money = new Decimal(150e9);
@ -316,6 +303,22 @@ function prestigeSourceFile() {
Player.hasTixApiAccess = true;
}
//Reset Stock market, gang, and corporation
if (Player.hasWseAccount) {
initStockMarket();
initSymbolToStockMap();
}
setStockMarketContentCreated(false);
var stockMarketList = document.getElementById("stock-market-list");
while(stockMarketList.firstChild) {
stockMarketList.removeChild(stockMarketList.firstChild);
}
Player.gang = null;
deleteGangDisplayContent();
Player.corporation = null;
Player.bladeburner = null;
//Gain int exp
Player.gainIntelligenceExp(5);
}

View File

@ -1,4 +1,4 @@
import {BitNode, BitNodes} from "./BitNode";
import {BitNodes} from "./BitNode";
import {Engine} from "./engine";
import {Player} from "./Player";
import {prestigeSourceFile} from "./Prestige";
@ -19,7 +19,7 @@ import {yesNoBoxCreate, yesNoBoxGetYesButton,
function writeRedPillLine(line) {
return new Promise(function(resolve, reject) {
var container = document.getElementById("red-pill-container");
var container = document.getElementById("red-pill-content");
var pElem = document.createElement("p");
container.appendChild(pElem);
@ -134,7 +134,7 @@ function giveSourceFile(bitNodeNumber) {
function loadBitVerse(destroyedBitNodeNum, flume=false) {
//Clear the screen
var container = document.getElementById("red-pill-container");
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
//Create the Bit Verse

View File

@ -13,7 +13,7 @@ import {loadMessages, initMessages, Messages} from "./Message";
import {Player, loadPlayer} from "./Player";
import {loadAllRunningScripts} from "./Script";
import {AllServers, loadAllServers} from "./Server";
import {loadSettings, initSettings, Settings} from "./Settings";
import {Settings} from "./Settings";
import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps";
import {loadStockMarket, StockMarket} from "./StockMarket";
import {dialogBoxCreate} from "../utils/DialogBox";
@ -23,6 +23,7 @@ import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {createStatusText} from "./ui/createStatusText";
import {formatNumber} from "../utils/StringHelperFunctions";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
@ -97,7 +98,7 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
//console.log("Saved game to LocalStorage!");
} catch(e) {
if (e.code == 22) {
Engine.createStatusText("Save failed for localStorage! Check console(F12)");
createStatusText("Save failed for localStorage! Check console(F12)");
console.log("Failed to save game to localStorage because the size of the save file " +
"is too large. However, the game will still be saved to IndexedDb if your browser " +
"supports it. If you would like to save to localStorage as well, then " +
@ -106,7 +107,7 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
}
}
Engine.createStatusText("Game saved!");
createStatusText("Game saved!");
}
function loadGame(saveString) {
@ -168,13 +169,13 @@ function loadGame(saveString) {
}
if (saveObj.hasOwnProperty("SettingsSave")) {
try {
loadSettings(saveObj.SettingsSave);
Settings.load(saveObj.SettingsSave);
} catch(e) {
console.log("ERROR: Failed to parse Settings. Re-initing default values");
initSettings();
Settings.init();
}
} else {
initSettings();
Settings.init();
}
if (saveObj.hasOwnProperty("FconfSettingsSave")) {
try {
@ -387,12 +388,12 @@ function loadImportedGame(saveObj, saveString) {
}
if (saveObj.hasOwnProperty("SettingsSave")) {
try {
loadSettings(saveObj.SettingsSave);
Settings.load(saveObj.SettingsSave);
} catch(e) {
initSettings();
Settings.init();
}
} else {
initSettings();
Settings.init();
}
if (saveObj.hasOwnProperty("FconfSettingsSave")) {
try {
@ -581,7 +582,7 @@ BitburnerSaveObject.prototype.deleteGame = function(db) {
request.onerror = function(e) {
console.log("Failed to delete save from indexedDb: " + e);
}
Engine.createStatusText("Game deleted!");
createStatusText("Game deleted!");
}
function createNewUpdateText() {

View File

@ -21,7 +21,7 @@ import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings} from "./Fconf";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript,
@ -29,10 +29,11 @@ import {addWorkerScript,
import {Player} from "./Player";
import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings";
import {post} from "./Terminal";
import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn";
import {Page, routing} from "./ui/navigationTracking";
import {dialogBoxCreate} from "../utils/DialogBox";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
@ -260,7 +261,7 @@ function updateScriptEditorContent() {
//Define key commands in script editor (ctrl o to save + close, etc.)
$(document).keydown(function(e) {
if (Settings.DisableHotkeys === true) {return;}
if (Engine.currentPage == Engine.Page.ScriptEditor) {
if (routing.isOn(Page.ScriptEditor)) {
//Ctrl + b
if (e.keyCode == 66 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
@ -280,8 +281,9 @@ function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
if (iTutorialIsRunning && currITutorialStep == iTutorialSteps.TerminalTypeScript) {
if (filename != "foodnstuff.script") {
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "foodnstuff.script") {
dialogBoxCreate("Leave the script name as 'foodnstuff'!");
return;
}
@ -290,7 +292,23 @@ function saveAndCloseScriptEditor() {
dialogBoxCreate("Please copy and paste the code from the tutorial!");
return;
}
iTutorialNextStep();
//Save the script
let s = Player.getCurrentServer();
for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript();
Engine.loadTerminalContent();
return iTutorialNextStep();
}
}
//If the current script does NOT exist, create a new one
let script = new Script();
script.saveScript();
s.scripts.push(script);
return iTutorialNextStep();
}
if (filename == "") {
@ -364,7 +382,7 @@ function Script() {
//Get the script data from the Script Editor and save it to the object
Script.prototype.saveScript = function() {
if (Engine.currentPage == Engine.Page.ScriptEditor) {
if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
@ -461,6 +479,18 @@ function parseOnlyRamCalculate(server, code, workerScript) {
const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) {
const ref = unresolvedRefs.shift();
// Check if this is one of the special keys, and add the appropriate ram cost if so.
if (ref === "hacknet" && !resolvedRefs.has("hacknet")) {
ram += CONSTANTS.ScriptHacknetNodesRamCost;
}
if (ref === "document" && !resolvedRefs.has("document")) {
ram += CONSTANTS.ScriptDomRamCost;
}
if (ref === "window" && !resolvedRefs.has("window")) {
ram += CONSTANTS.ScriptDomRamCost;
}
resolvedRefs.add(ref);
if (ref.endsWith(".*")) {
@ -478,13 +508,6 @@ function parseOnlyRamCalculate(server, code, workerScript) {
}
}
// Check if this is one of the special keys, and add the appropriate ram cost if so.
if (ref == specialReferenceIF) ram += CONSTANTS.ScriptIfRamCost;
if (ref == specialReferenceFOR) ram += CONSTANTS.ScriptForRamCost;
if (ref == specialReferenceWHILE) ram += CONSTANTS.ScriptWhileRamCost;
if (ref == "hacknet") ram += CONSTANTS.ScriptHacknetNodesRamCost;
if (ref == "document" || ref == "window") ram += CONSTANTS.ScriptDomRamCost;
// Check if this ident is a function in the workerscript env. If it is, then we need to
// get its RAM cost. We do this by calling it, which works because the running script
// is in checkingRam mode.

View File

@ -1,4 +1,4 @@
import {BitNodeMultipliers} from "./BitNode";
import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Programs} from "./CreateProgram";
import {Player} from "./Player";

129
src/Settings.ts Normal file
View File

@ -0,0 +1,129 @@
import { ISelfInitializer, ISelfLoading } from "./types";
/**
* Represents the default settings the player could customize.
*/
interface IDefaultSettings {
/**
* How often the game should autosave the player's progress, in seconds.
*/
AutosaveInterval: number;
/**
* How many milliseconds between execution points for Netscript 1 statements.
*/
CodeInstructionRunTime: number;
/**
* Whether global keyboard shortcuts should be recognized throughout the game.
*/
DisableHotkeys: boolean;
/**
* Limit the number of log entries for each script being executed on each server.
*/
MaxLogCapacity: number;
/**
* Limit how many entries can be written to a Netscript Port before entries start to get pushed out.
*/
MaxPortCapacity: number;
/**
* Whether the player should be asked to confirm purchasing each and every augmentation.
*/
SuppressBuyAugmentationConfirmation: boolean;
/**
* Whether the user should be prompted to join each faction via a dialog box.
*/
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.
*/
SuppressMessages: boolean;
/**
* Whether the user should be asked to confirm travelling between cities.
*/
SuppressTravelConfirmation: boolean;
}
/**
* Represents all possible settings the player wants to customize to their play style.
*/
interface ISettings extends IDefaultSettings {
/**
* The keybinding to use in the script editor.
* TODO: This should really be an enum of allowed values.
*/
EditorKeybinding: string;
/**
* The theme used in the script editor.
* TODO: This should really be an enum of allowed values.
*/
EditorTheme: string;
/**
* The CSS background theme color to apply across the game.
*/
ThemeBackgroundColor: string;
/**
* The CSS text theme color to apply across the game.
*/
ThemeFontColor: string;
/**
* The CSS foreground theme color to apply across the game.
*/
ThemeHighlightColor: string;
}
const defaultSettings: IDefaultSettings = {
AutosaveInterval: 60,
CodeInstructionRunTime: 50,
DisableHotkeys: false,
MaxLogCapacity: 50,
MaxPortCapacity: 50,
SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false,
SuppressHospitalizationPopup: false,
SuppressMessages: false,
SuppressTravelConfirmation: false,
};
/**
* The current options the player has customized to their play style.
*/
// tslint:disable-next-line:variable-name
export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
AutosaveInterval: defaultSettings.AutosaveInterval,
CodeInstructionRunTime: 25,
DisableHotkeys: defaultSettings.DisableHotkeys,
EditorKeybinding: "ace",
EditorTheme: "Monokai",
MaxLogCapacity: defaultSettings.MaxLogCapacity,
MaxPortCapacity: defaultSettings.MaxPortCapacity,
SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation,
SuppressFactionInvites: defaultSettings.SuppressFactionInvites,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
SuppressMessages: defaultSettings.SuppressMessages,
SuppressTravelConfirmation: defaultSettings.SuppressTravelConfirmation,
ThemeBackgroundColor: "#000000",
ThemeFontColor: "#66ff33",
ThemeHighlightColor: "#ffffff",
init() {
Object.assign(Settings, defaultSettings);
},
load(saveString: string) {
Object.assign(Settings, JSON.parse(saveString));
},
};

View File

@ -1,5 +1,5 @@
import {Player} from "./Player";
import {BitNode, BitNodes} from "./BitNode";
import {BitNodes} from "./BitNode";
/* SourceFile.js */
//Each SourceFile corresponds to a BitNode with the same number

342
src/StockMarket.js Normal file → Executable file
View File

@ -1,6 +1,5 @@
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {Locations} from "./Location";
import {Locations} from "./Locations";
import {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions";
import {WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
@ -9,9 +8,13 @@ import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {Page, routing} from "./ui/navigationTracking";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getRandomInt} from "../utils/helpers/getRandomInt";
import {KEY} from "../utils/helpers/keyCodes";
import {createElement} from "../utils/uiHelpers/createElement";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton,
@ -410,6 +413,7 @@ function stockMarketCycle() {
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;}
var thresh = 0.6;
if (stock.b) {thresh = 0.4;}
if (Math.random() < thresh) {
@ -430,8 +434,8 @@ function buyStock(stock, shares) {
var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
dialogBoxCreate("You do not have enough money to purchase this. You need $" +
formatNumber(totalPrice + CONSTANTS.StockMarketCommission, 2).toString() + ".");
dialogBoxCreate("You do not have enough money to purchase this. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
return false;
}
@ -441,9 +445,9 @@ function buyStock(stock, shares) {
stock.playerShares += shares;
stock.playerAvgPx = newTotal / stock.playerShares;
updateStockPlayerPosition(stock);
dialogBoxCreate("Bought " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. You also paid $" +
formatNumber(CONSTANTS.StockMarketCommission, 2) + " in commission fees.");
dialogBoxCreate("Bought " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
return true;
}
@ -464,9 +468,9 @@ function sellStock(stock, shares) {
stock.playerAvgPx = 0;
}
updateStockPlayerPosition(stock);
dialogBoxCreate("Sold " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. After commissions, you gained " +
"a total of $" + formatNumber(gains, 2));
dialogBoxCreate("Sold " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(gains).format('($0.000a)') + ".");
return true;
}
@ -488,12 +492,12 @@ function shortStock(stock, shares, workerScript=null) {
var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have " +
workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have enough " +
"money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)'));
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
} else {
dialogBoxCreate("You do not have enough money to purchase this short position. You need $" +
formatNumber(totalPrice + CONSTANTS.StockMarketCommission, 2) + ".");
dialogBoxCreate("You do not have enough money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)') + ".");
}
return false;
@ -507,14 +511,14 @@ function shortStock(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock);
if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.shortStock == null) {
workerScript.scriptRef.log("Bought a short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at " +
workerScript.scriptRef.log("Bought a short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
}
} else {
dialogBoxCreate("Bought a short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. You also paid $" +
formatNumber(CONSTANTS.StockMarketCommission, 2) + " in commission fees.");
dialogBoxCreate("Bought a short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
}
return true;
}
@ -551,14 +555,14 @@ function sellShort(stock, shares, workerScript=null) {
updateStockPlayerPosition(stock);
if (tixApi) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.sellShort == null) {
workerScript.scriptRef.log("Sold your short position of " + shares + " shares of " + stock.symbol + " at " +
workerScript.scriptRef.log("Sold your short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)'));
"a total of " + numeral(origCost + profit).format('($0.000a)') + ".");
}
} else {
dialogBoxCreate("Sold your short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. After commissions, you gained " +
"a total of $" + formatNumber(origCost + profit, 2));
dialogBoxCreate("Sold your short position of " + numeral(shares).format('0,0') + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)') + ".");
}
return true;
@ -593,7 +597,7 @@ function updateStockPrices() {
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long);
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long);
processOrders(stock, OrderTypes.StopSell, PositionTypes.Short);
if (Engine.currentPage == Engine.Page.StockMarket) {
if (routing.isOn(Page.StockMarket)) {
updateStockTicker(stock, true);
}
} else {
@ -602,7 +606,7 @@ function updateStockPrices() {
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short);
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short);
processOrders(stock, OrderTypes.StopSell, PositionTypes.Long);
if (Engine.currentPage == Engine.Page.StockMarket) {
if (routing.isOn(Page.StockMarket)) {
updateStockTicker(stock, false);
}
}
@ -694,20 +698,31 @@ var stockMarketContentCreated = false;
var stockMarketPortfolioMode = false;
var COMM = CONSTANTS.StockMarketCommission;
function displayStockMarketContent() {
if (Player.hasWseAccount == null) {Player.hasWseAccount = false;}
if (Player.hasWseAccount == null) {Player.hasWseAccount = false;}
if (Player.hasTixApiAccess == null) {Player.hasTixApiAccess = false;}
if (Player.has4SData == null) {Player.has4SData = false;}
if (Player.has4SDataTixApi == null) {Player.has4SDataTixApi = false;}
function stylePurchaseButton(btn, cost, flag, initMsg, purchasedMsg) {
btn.innerText = initMsg;
btn.classList.remove("a-link-button");
btn.classList.remove("a-link-button-bought");
btn.classList.remove("a-link-button-inactive");
if (!flag && Player.money.gte(cost)) {
btn.classList.add("a-link-button");
} else if (flag) {
btn.innerText = purchasedMsg;
btn.classList.add("a-link-button-bought");
} else {
btn.classList.add("a-link-button-inactive");
}
}
//Purchase WSE Account button
var wseAccountButton = clearEventListeners("stock-market-buy-account");
wseAccountButton.innerText = "Buy WSE Account - $" + formatNumber(CONSTANTS.WSEAccountCost, 2).toString();
if (!Player.hasWseAccount && Player.money.gte(CONSTANTS.WSEAccountCost)) {
wseAccountButton.setAttribute("class", "a-link-button");
} else if (Player.hasWseAccount){
wseAccountButton.innerText = "WSE Account - Purchased";
wseAccountButton.setAttribute("class", "a-link-button-bought");
} else {
wseAccountButton.setAttribute("class", "a-link-button-inactive");
}
stylePurchaseButton(wseAccountButton, CONSTANTS.WSEAccountCost, Player.hasWseAccount,
"Buy WSE Account - " + numeral(CONSTANTS.WSEAccountCost).format('($0.000a)'),
"WSE Account - Purchased");
wseAccountButton.addEventListener("click", function() {
Player.hasWseAccount = true;
initStockMarket();
@ -719,16 +734,9 @@ function displayStockMarketContent() {
//Purchase TIX API Access account
var tixApiAccessButton = clearEventListeners("stock-market-buy-tix-api");
tixApiAccessButton.innerText = "Buy Trade Information eXchange (TIX) API Access - $" +
formatNumber(CONSTANTS.TIXAPICost, 2).toString();
if (!Player.hasTixApiAccess && Player.money.gte(CONSTANTS.TIXAPICost)) {
tixApiAccessButton.setAttribute("class", "a-link-button");
} else if(Player.hasTixApiAccess) {
tixApiAccessButton.innerText = "Trade Information eXchange (TIX) API Access - Purchased"
tixApiAccessButton.setAttribute("class", "a-link-button-bought");
} else {
tixApiAccessButton.setAttribute("class", "a-link-button-inactive");
}
stylePurchaseButton(tixApiAccessButton, CONSTANTS.TIXAPICost, Player.hasTixApiAccess,
"Buy Trade Information eXchange (TIX) API Access - " + numeral(CONSTANTS.TIXAPICost).format('($0.000a)'),
"TIX API Access - Purchased");
tixApiAccessButton.addEventListener("click", function() {
Player.hasTixApiAccess = true;
Player.loseMoney(CONSTANTS.TIXAPICost);
@ -736,23 +744,111 @@ function displayStockMarketContent() {
return false;
});
//Purchase Four Sigma Market Data Feed
var marketDataButton = clearEventListeners("stock-market-buy-4s-data");
stylePurchaseButton(marketDataButton, CONSTANTS.MarketData4SCost, Player.has4SData,
"Buy 4S Market Data Access - " + numeral(CONSTANTS.MarketData4SCost).format('($0.000a)'),
"4S Market Data - Purchased");
marketDataButton.addEventListener("click", function() {
Player.has4SData = true;
Player.loseMoney(CONSTANTS.MarketData4SCost);
displayStockMarketContent();
return false;
});
marketDataButton.appendChild(createElement("span", {
class:"tooltiptext",
innerText:"Lets you view additional pricing and volatility information about stocks"
}));
marketDataButton.style.marginRight = "2px"; //Adjusts following help tip to be slightly closer
//4S Market Data Help Tip
var marketDataHelpTip = clearEventListeners("stock-market-4s-data-help-tip");
marketDataHelpTip.style.marginTop = "10px";
marketDataHelpTip.addEventListener("click", ()=>{
dialogBoxCreate("Access to the 4S Market Data feed will display two additional pieces " +
"of information about each stock: Price Forecast & Volatility<br><br>" +
"Price Forecast indicates the probability the stock has of increasing or " +
"decreasing. A '+' forecast means the stock has a higher chance of increasing " +
"than decreasing, and a '-' means the opposite. The number of '+/-' symbols " +
"is used to illustrate the magnitude of these probabilities. For example, " +
"'+++' means that the stock has a significantly higher chance of increasing " +
"than decreasing, while '+' means that the stock only has a slightly higher chance " +
"of increasing than decreasing.<br><br>" +
"Volatility represents the maximum percentage by which a stock's price " +
"can change every tick (a tick occurs every few seconds while the game " +
"is running).<br><br>" +
"A stock's price forecast can change over time. This is also affected by volatility. " +
"The more volatile a stock is, the more its price forecast will change.");
return false;
});
//Purchase Four Sigma Market Data TIX API (Requires TIX API Access)
var marketDataTixButton = clearEventListeners("stock-market-buy-4s-tix-api");
stylePurchaseButton(marketDataTixButton, CONSTANTS.MarketDataTixApi4SCost, Player.has4SDataTixApi,
"Buy 4S Market Data TIX API Access - " + numeral(CONSTANTS.MarketDataTixApi4SCost).format('($0.000a)'),
"4S Market Data TIX API - Purchased");
if (Player.hasTixApiAccess) {
marketDataTixButton.addEventListener("click", function() {
Player.has4SDataTixApi = true;
Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost);
displayStockMarketContent();
return false;
});
marketDataTixButton.appendChild(createElement("span", {
class:"tooltiptext",
innerText:"Lets you access 4S Market Data through Netscript"
}));
} else {
marketDataTixButton.classList.remove("a-link-button");
marketDataTixButton.classList.remove("a-link-button-bought");
marketDataTixButton.classList.remove("a-link-button-inactive");
marketDataTixButton.classList.add("a-link-button-inactive");
marketDataTixButton.appendChild(createElement("span", {
class:"tooltiptext",
innerText:"Requires TIX API Access"
}));
}
var stockList = document.getElementById("stock-market-list");
if (stockList == null) {return;}
//UI Elements that should only appear if you have WSE account access
var commissionText = document.getElementById("stock-market-commission");
var modeBtn = document.getElementById("stock-market-mode");
var expandBtn = document.getElementById("stock-market-expand-tickers");
var collapseBtn = document.getElementById("stock-market-collapse-tickers");
var watchlistFilter = document.getElementById("stock-market-watchlist-filter");
var watchlistUpdateBtn = document.getElementById("stock-market-watchlist-filter-update");
//If Player doesn't have account, clear stocks UI and return
if (!Player.hasWseAccount) {
stockMarketContentCreated = false;
while (stockList.firstChild) {
stockList.removeChild(stockList.firstChild);
}
commissionText.style.visibility = "hidden";
modeBtn.style.visibility = "hidden";
expandBtn.style.visibility = "hidden";
collapseBtn.style.visibility = "hidden";
watchlistFilter.style.visibility = "hidden";
watchlistUpdateBtn.style.visibility = "hidden";
return;
} else {
commissionText.style.visibility = "visible";
modeBtn.style.visibility = "visible";
expandBtn.style.visibility = "visible";
collapseBtn.style.visibility = "visible";
watchlistFilter.style.visibility = "visible";
watchlistUpdateBtn.style.visibility = "visible";
}
//Create stock market content if you have an account
if (!stockMarketContentCreated && Player.hasWseAccount) {
console.log("Creating Stock Market UI");
document.getElementById("stock-market-commission").innerHTML =
"Commission Fees: Every transaction you make has a $" +
formatNumber(CONSTANTS.StockMarketCommission, 2) + " commission fee.<br><br>" +
commissionText.innerHTML =
"Commission Fees: Every transaction you make has a " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " commission fee.<br><br>" +
"WARNING: When you reset after installing Augmentations, the Stock Market is reset. " +
"This means all your positions are lost, so make sure to sell your stocks before installing " +
"Augmentations!";
@ -803,7 +899,6 @@ function displayStockMarketContent() {
});
//Switch to Portfolio Mode Button
var modeBtn = clearEventListeners("stock-market-mode");
if (modeBtn) {
modeBtn.innerHTML = "Switch to 'Portfolio' Mode" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
@ -811,9 +906,7 @@ function displayStockMarketContent() {
}
//Expand/Collapse tickers buttons
var expandBtn = clearEventListeners("stock-market-expand-tickers"),
collapseBtn = clearEventListeners("stock-market-collapse-tickers"),
stockList = document.getElementById("stock-market-list");
var stockList = document.getElementById("stock-market-list");
if (expandBtn) {
expandBtn.addEventListener("click", ()=>{
var tickerHdrs = stockList.getElementsByClassName("accordion-header");
@ -835,14 +928,30 @@ function displayStockMarketContent() {
});
}
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;} //orders property is an array
createStockTicker(stock);
//Watchlish filter
if (watchlistFilter && watchlistUpdateBtn) {
//Initialize value in watchlist
if (StockMarket.watchlistFilter) {
watchlistFilter.value = StockMarket.watchlistFilter; //Remove whitespace
}
watchlistUpdateBtn.addEventListener("click", ()=> {
let filterValue = watchlistFilter.value.toString();
StockMarket.watchlistFilter = filterValue.replace(/\s/g, '');
if (stockMarketPortfolioMode) {
switchToPortfolioMode();
} else {
switchToDisplayAllMode();
}
});
watchlistFilter.addEventListener("keyup", (e)=>{
e.preventDefault();
if (e.keyCode === KEY.ENTER) {watchlistUpdateBtn.click();}
})
} else {
console.warn("Stock Market Watchlist DOM elements could not be found");
}
setStockTickerClickHandlers(); //Clicking headers opens/closes panels
createAllStockTickers();
stockMarketContentCreated = true;
}
@ -850,8 +959,10 @@ function displayStockMarketContent() {
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
updateStockTicker(stock, null);
updateStockOrderList(stock);
if (stock instanceof Stock) {
updateStockTicker(stock, null);
updateStockOrderList(stock);
}
}
}
}
@ -860,17 +971,34 @@ function displayStockMarketContent() {
//Displays only stocks you have position/order in
function switchToPortfolioMode() {
stockMarketPortfolioMode = true;
var stockList = document.getElementById("stock-market-list");
if (stockList == null) {return;}
var modeBtn = clearEventListeners("stock-market-mode");
if (modeBtn) {
modeBtn.innerHTML = "Switch to 'All stocks' Mode" +
"<span class='tooltiptext'>Displays all stocks on the WSE</span>";
modeBtn.addEventListener("click", switchToDisplayAllMode);
}
while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);}
createAllStockTickers();
}
//Displays all stocks
function switchToDisplayAllMode() {
stockMarketPortfolioMode = false;
var modeBtn = clearEventListeners("stock-market-mode");
if (modeBtn) {
modeBtn.innerHTML = "Switch to 'Portfolio' Mode" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
modeBtn.addEventListener("click", switchToPortfolioMode);
}
createAllStockTickers();
}
function createAllStockTickers() {
var stockList = document.getElementById("stock-market-list");
if (stockList == null) {
exceptionAlert("Error creating Stock Tickers UI. DOM element with ID 'stock-market-list' could not be found");
}
removeChildrenFromElement(stockList);
//Get Order book (create it if it hasn't been created)
var orderBook = StockMarket["Orders"];
if (orderBook == null) {
var orders = {};
@ -882,41 +1010,30 @@ function switchToPortfolioMode() {
}
}
StockMarket["Orders"] = orders;
orderBook = StockMarket["Orders"];
}
let watchlist = null;
if (StockMarket.watchlistFilter != null && StockMarket.watchlistFilter !== "") {
let filter = StockMarket.watchlistFilter.replace(/\s/g, '');
watchlist = filter.split(",");
}
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;} //orders property is an array
var stockOrders = orderBook[stock.symbol];
if (stock.playerShares === 0 && stock.playerShortShares === 0 &&
stockOrders.length === 0) {continue;}
createStockTicker(stock);
}
}
setStockTickerClickHandlers();
}
if (watchlist && !watchlist.includes(stock.symbol)) {continue;} //Watchlist filtering
//Displays all stocks
function switchToDisplayAllMode() {
stockMarketPortfolioMode = false;
var stockList = document.getElementById("stock-market-list");
if (stockList == null) {return;}
var modeBtn = clearEventListeners("stock-market-mode");
if (modeBtn) {
modeBtn.innerHTML = "Switch to 'Portfolio' Mode" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
modeBtn.addEventListener("click", switchToPortfolioMode);
}
while(stockList.firstChild) {stockList.removeChild(stockList.firstChild);}
for (var name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
var stock = StockMarket[name];
if (!(stock instanceof Stock)) {continue;} //orders property is an array
let stockOrders = orderBook[stock.symbol];
if (stockMarketPortfolioMode) {
if (stock.playerShares === 0 && stock.playerShortShares === 0 &&
stockOrders.length === 0) {continue;}
}
createStockTicker(stock);
}
}
setStockTickerClickHandlers();
setStockTickerClickHandlers(); //Clicking headers opens/closes panels
}
function createStockTicker(stock) {
@ -928,7 +1045,7 @@ function createStockTicker(stock) {
var li = document.createElement("li"), hdr = document.createElement("button");
hdr.classList.add("accordion-header");
hdr.setAttribute("id", tickerId + "-hdr");
hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + stock.price;
hdr.innerHTML = stock.name + " - " + stock.symbol + " - " + numeral(stock.price).format('($0.000a)');
//Div for entire panel
var stockDiv = document.createElement("div");
@ -1182,7 +1299,7 @@ function setStockTickerClickHandlers() {
//'increase' argument is a boolean indicating whether the price increased or decreased
function updateStockTicker(stock, increase) {
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
if (!routing.isOn(Page.StockMarket)) {return;}
if (!(stock instanceof Stock)) {
console.log("Invalid stock in updateStockTicker():");
console.log(stock);
@ -1197,17 +1314,32 @@ function updateStockTicker(stock, increase) {
var hdr = document.getElementById(tickerId + "-hdr");
if (hdr == null) {
if (!stockMarketPortfolioMode) {console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol);}
if (!stockMarketPortfolioMode) {
let watchlist = StockMarket.watchlistFilter;
if (watchlist !== "" && watchlist.includes(stock.symbol)) {
console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol);
}
}
return;
}
hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + formatNumber(stock.price, 2);
let hdrText = stock.name + " (" + stock.symbol + ") - " + numeral(stock.price).format('($0.000a)');
if (Player.has4SData) {
hdrText += " - Volatility: " + numeral(stock.mv).format('0,0.00') + "%" +
" - Price Forecast: ";
if (stock.b) {
hdrText += "+".repeat(Math.floor(stock.otlkMag/10) + 1);
} else {
hdrText += "-".repeat(Math.floor(stock.otlkMag/10) + 1);
}
}
hdr.innerText = hdrText;
if (increase != null) {
increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red";
}
}
function updateStockPlayerPosition(stock) {
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
if (!routing.isOn(Page.StockMarket)) {return;}
if (!(stock instanceof Stock)) {
console.log("Invalid stock in updateStockPlayerPosition():");
console.log(stock);
@ -1256,32 +1388,38 @@ function updateStockPlayerPosition(stock) {
"<h1 class='tooltip stock-market-position-text'>Long Position: " +
"<span class='tooltiptext'>Shares in the long position will increase " +
"in value if the price of the corresponding stock increases</span></h1>" +
"<br>Shares: " + formatNumber(stock.playerShares, 0) +
"<br>Shares: " + numeral(stock.playerShares).format('0,0') +
"<br>Average Price: " + numeral(stock.playerAvgPx).format('$0.000a') +
" (Total Cost: " + numeral(totalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(gains).format('$0.000a') +
" (" + formatNumber(percentageGains*100, 2) + "%)<br><br>";
" (" + numeral(percentageGains).format('0.00%') + ")<br><br>";
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
stock.posTxtEl.innerHTML +=
"<h1 class='tooltip stock-market-position-text'>Short Position: " +
"<span class='tooltiptext'>Shares in short position will increase " +
"in value if the price of the corresponding stock decreases</span></h1>" +
"<br>Shares: " + formatNumber(stock.playerShortShares, 0) +
"<br>Shares: " + numeral(stock.playerShortShares).format('0,0') +
"<br>Average Price: " + numeral(stock.playerAvgShortPx).format('$0.000a') +
" (Total Cost: " + numeral(shortTotalCost).format('$0.000a') + ")" +
"<br>Profit: " + numeral(shortGains).format('$0.000a') +
" (" + formatNumber(shortPercentageGains*100, 2) + "%)" +
" (" + numeral(shortPercentageGains).format('0.00%') + ")" +
"<br><br><h1 class='stock-market-position-text'>Orders: </h1>";
}
}
function updateStockOrderList(stock) {
if (Engine.currentPage !== Engine.Page.StockMarket) {return;}
if (!routing.isOn(Page.StockMarket)) {return;}
var tickerId = "stock-market-ticker-" + stock.symbol;
var orderList = document.getElementById(tickerId + "-order-list");
if (orderList == null) {
if (!stockMarketPortfolioMode) {console.log("ERROR: Could not find order list for " + stock.symbol);}
//Log only if its a valid error
if (!stockMarketPortfolioMode) {
let watchlist = StockMarket.watchlistFilter;
if (watchlist !== "" && watchlist.includes(stock.symbol)) {
console.log("ERROR: Could not find order list for " + stock.symbol);
}
}
return;
}
@ -1327,7 +1465,7 @@ function updateStockOrderList(stock) {
var posText = (order.pos === PositionTypes.Long ? "Long Position" : "Short Position");
li.style.color = "white";
li.innerText = order.type + " - " + posText + " - " +
order.shares + " @ $" + formatNumber(order.price, 2);
order.shares + " @ " + numeral(order.price).format('($0.000a)');
var cancelButton = document.createElement("span");
cancelButton.classList.add("stock-market-order-cancel-btn");

View File

@ -12,8 +12,7 @@ import {FconfSettings, parseFconfSettings,
createFconf} from "./Fconf";
import {TerminalHelpText, HelpTexts} from "./HelpText";
import {iTutorialNextStep, iTutorialSteps,
iTutorialIsRunning,
currITutorialStep} from "./InteractiveTutorial";
ITutorial} from "./InteractiveTutorial";
import {showLiterature} from "./Literature";
import {showMessage, Message} from "./Message";
import {scriptCalculateHackingTime,
@ -35,6 +34,8 @@ import {TextFile, getTextFile} from "./TextFile";
import {containsAllStrings, longestCommonStart,
formatNumber} from "../utils/StringHelperFunctions";
import {Page, routing} from "./ui/navigationTracking";
import {KEY} from "../utils/helpers/keyCodes";
import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString";
import {arrayToString} from "../utils/helpers/arrayToString";
@ -43,86 +44,40 @@ import {logBoxCreate} from "../utils/LogBox";
import {yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox";
import {post, hackProgressBarPost,
hackProgressPost} from "./ui/postToTerminal";
import * as JSZip from 'jszip';
import * as FileSaver from 'file-saver';
/* Write text to terminal */
//If replace is true then spaces are replaced with "&nbsp;"
function post(input) {
$("#terminal-input").before('<tr class="posted"><td class="terminal-line" style="color: var(--my-font-color); background-color: var(--my-background-color); white-space:pre-wrap;">' + input + '</td></tr>');
updateTerminalScroll();
}
//Same thing as post but the td cells have ids so they can be animated for the hack progress bar
function hackProgressBarPost(input) {
$("#terminal-input").before('<tr class="posted"><td id="hack-progress-bar" style="color: var(--my-font-color); background-color: var(--my-background-color);">' + input + '</td></tr>');
updateTerminalScroll();
}
function hackProgressPost(input) {
$("#terminal-input").before('<tr class="posted"><td id="hack-progress" style="color: var(--my-font-color); background-color: var(--my-background-color);">' + input + '</td></tr>');
updateTerminalScroll();
}
//Scroll to the bottom of the terminal's 'text area'
function updateTerminalScroll() {
var element = document.getElementById("terminal-container");
element.scrollTop = element.scrollHeight;
}
function postNetburnerText() {
post("Bitburner v" + CONSTANTS.Version);
}
//Key Codes
var KEY = {
TAB: 9,
ENTER: 13,
CTRL: 17,
UPARROW: 38,
DOWNARROW: 40,
A: 65,
B: 66,
C: 67,
D: 68,
E: 69,
F: 70,
H: 72,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
R: 82,
S: 83,
U: 85,
W: 87,
}
//Defines key commands in terminal
$(document).keydown(function(event) {
//Terminal
if (Engine.currentPage == Engine.Page.Terminal) {
if (routing.isOn(Page.Terminal)) {
var terminalInput = document.getElementById("terminal-input-text-box");
if (terminalInput != null && !event.ctrlKey && !event.shiftKey) {terminalInput.focus();}
if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val();
if (command.length > 0) {
post(
"[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
);
post(
"[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
);
if (command.length > 0) {
Terminal.resetTerminalInput(); //Clear input first
Terminal.executeCommand(command);
const commands = command.split(";");
for(let i = 0; i < commands.length; i++) {
if(commands[i].match(/^\s*$/)) { continue; }
Terminal.executeCommand(commands[i]);
}
}
}
@ -198,6 +153,12 @@ $(document).keydown(function(event) {
if (terminalInput == null) {return;}
var input = terminalInput.value;
if (input == "") {return;}
const semiColonIndex = input.lastIndexOf(";");
if(semiColonIndex !== -1) {
input = input.slice(semiColonIndex+1);
}
input = input.trim();
input = input.replace(/\s\s+/g, ' ');
@ -277,13 +238,13 @@ $(document).keydown(function(event) {
//Keep terminal in focus
let terminalCtrlPressed = false, shiftKeyPressed = false;
$(document).ready(function() {
if (Engine.currentPage === Engine.Page.Terminal) {
if (routing.isOn(Page.Terminal)) {
$('.terminal-input').focus();
}
});
$(document).keydown(function(e) {
if (Engine.currentPage == Engine.Page.Terminal) {
if (e.which == 17) {
if (routing.isOn(Page.Terminal)) {
if (e.which == KEY.CTRL) {
terminalCtrlPressed = true;
} else if (e.shiftKey) {
shiftKeyPressed = true;
@ -299,8 +260,8 @@ $(document).keydown(function(e) {
}
})
$(document).keyup(function(e) {
if (Engine.currentPage == Engine.Page.Terminal) {
if (e.which == 17) {
if (routing.isOn(Page.Terminal)) {
if (e.which == KEY.CTRL) {
terminalCtrlPressed = false;
}
if (e.shiftKey) {
@ -350,7 +311,18 @@ function tabCompletion(command, arg, allPossibilities, index=0) {
} else {
val = command + " " + allPossibilities[0];
}
document.getElementById("terminal-input-text-box").value = val;
const textBox = document.getElementById("terminal-input-text-box");
const oldValue = textBox.value;
const semiColonIndex = oldValue.lastIndexOf(";");
if(semiColonIndex === -1) {
// no ; replace the whole thing.
textBox.value = val;
} else {
// replace just after the last semicolon
textBox.value = textBox.value.slice(0, semiColonIndex+1)+" "+val;
}
document.getElementById("terminal-input-text-box").focus();
} else {
var longestStartSubstr = longestCommonStart(allPossibilities);
@ -789,11 +761,11 @@ let Terminal = {
if (commandArray.length == 0) {return;}
/****************** Interactive Tutorial Terminal Commands ******************/
if (iTutorialIsRunning) {
if (ITutorial.isRunning) {
var foodnstuffServ = GetServerByHostname("foodnstuff");
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
switch(currITutorialStep) {
switch(ITutorial.currStep) {
case iTutorialSteps.TerminalHelp:
if (commandArray[0] == "help") {
post(TerminalHelpText);
@ -1836,7 +1808,7 @@ let Terminal = {
* @returns {void}
*/
/**
* @type {Object.<string, ProgramHandler}
* @type {Object.<string, ProgramHandler}
*/
const programHandlers = {};
programHandlers[Programs.NukeProgram.name] = (server) => {
@ -1885,7 +1857,7 @@ let Terminal = {
server.openPortCount++;
};
programHandlers[Programs.HTTPWormProgram.name] = (server) => {
if (serv.httpPortOpen) {
if (server.httpPortOpen) {
post("HTTP Port (80) is already open!");
return;
}
@ -2084,4 +2056,4 @@ let Terminal = {
}
};
export {postNetburnerText, post, Terminal, KEY};
export {postNetburnerText, Terminal};

View File

@ -33,8 +33,9 @@ import {displayFactionContent, joinFaction,
processPassiveFactionRepGain, Factions,
inviteToFaction, initFactions} from "./Faction";
import {FconfSettings} from "./Fconf";
import {Locations, displayLocationContent,
import {displayLocationContent,
initLocationButtons} from "./Location";
import {Locations} from "./Locations";
import {displayGangContent, updateGangContent,
Gang} from "./Gang";
import {displayHacknetNodesContent, processAllHacknetNodeEarnings,
@ -55,7 +56,8 @@ import {saveObject, loadGame} from "./SaveObject";
import {loadAllRunningScripts, scriptEditorInit,
updateScriptEditorContent} from "./Script";
import {AllServers, Server, initForeignServers} from "./Server";
import {Settings, setSettingsLabels} from "./Settings";
import {Settings} from "./Settings";
import {setSettingsLabels} from "./ui/setSettingsLabels";
import {initSourceFiles, SourceFiles,
PlayerOwnedSourceFile} from "./SourceFile";
import {SpecialServerIps, initSpecialServerIps} from "./SpecialServerIps";
@ -64,7 +66,9 @@ import {StockMarket, StockSymbols,
initSymbolToStockMap, stockMarketCycle,
updateStockPrices,
displayStockMarketContent} from "./StockMarket";
import {Terminal, postNetburnerText, post, KEY} from "./Terminal";
import {Terminal, postNetburnerText} from "./Terminal";
import {KEY} from "../utils/helpers/keyCodes";
import {Page, routing} from "./ui/navigationTracking";
// These should really be imported with the module that is presenting that UI, but because they very much depend on the
// cascade order, we'll pull them all in here.
@ -126,7 +130,7 @@ $(document).keydown(function(e) {
Engine.loadCreateProgramContent();
} else if (e.keyCode === KEY.F && e.altKey) {
//Overriden by Fconf
if (Engine.currentPage === Engine.Page.Terminal && FconfSettings.ENABLE_BASH_HOTKEYS) {
if (routing.isOn(Page.Terminal) && FconfSettings.ENABLE_BASH_HOTKEYS) {
return;
}
e.preventDefault();
@ -149,6 +153,7 @@ $(document).keydown(function(e) {
let Engine = {
version: "",
Debug: true,
overview: new CharacterOverview(),
//Clickable objects
Clickables: {
@ -255,45 +260,15 @@ let Engine = {
characterInfo: null,
},
//Current page status
Page: {
Terminal: "Terminal",
CharacterInfo: "CharacterInfo",
ScriptEditor: "ScriptEditor",
ActiveScripts: "ActiveScripts",
HacknetNodes: "HacknetNodes",
World: "World",
CreateProgram: "CreateProgram",
Factions: "Factions",
Faction: "Faction",
Augmentations: "Augmentations",
Tutorial: "Tutorial",
DevMenu: "Dev Menu",
Location: "Location",
workInProgress: "WorkInProgress",
RedPill: "RedPill",
CinematicText: "CinematicText",
Infiltration: "Infiltration",
StockMarket: "StockMarket",
Gang: "Gang",
Mission: "Mission",
Corporation: "Corporation",
Bladeburner: "Bladeburner",
},
currentPage: null,
overview: new CharacterOverview(),
//Time variables (milliseconds unix epoch time)
_lastUpdate: new Date().getTime(),
_idleSpeed: 200, //Speed (in ms) at which the main loop is updated
/* Load content when a main menu button is clicked */
loadTerminalContent: function() {
Engine.hideAllContent();
Engine.Display.terminalContent.style.display = "block";
Engine.currentPage = Engine.Page.Terminal;
routing.navigateTo(Page.Terminal);
document.getElementById("terminal-menu-link").classList.add("active");
},
@ -301,7 +276,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.characterContent.style.display = "block";
Engine.displayCharacterInfo();
Engine.currentPage = Engine.Page.CharacterInfo;
routing.navigateTo(Page.CharacterInfo);
document.getElementById("stats-menu-link").classList.add("active");
},
@ -315,7 +290,7 @@ let Engine = {
}
editor.focus();
updateScriptEditorContent();
Engine.currentPage = Engine.Page.ScriptEditor;
routing.navigateTo(Page.ScriptEditor);
document.getElementById("create-script-menu-link").classList.add("active");
},
@ -323,7 +298,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.activeScriptsContent.style.display = "block";
updateActiveScriptsItems();
Engine.currentPage = Engine.Page.ActiveScripts;
routing.navigateTo(Page.ActiveScripts);
document.getElementById("active-scripts-menu-link").classList.add("active");
},
@ -331,7 +306,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.hacknetNodesContent.style.display = "block";
displayHacknetNodesContent();
Engine.currentPage = Engine.Page.HacknetNodes;
routing.navigateTo(Page.HacknetNodes);
document.getElementById("hacknet-nodes-menu-link").classList.add("active");
},
@ -339,7 +314,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.worldContent.style.display = "block";
Engine.displayWorldInfo();
Engine.currentPage = Engine.Page.World;
routing.navigateTo(Page.World);
document.getElementById("city-menu-link").classList.add("active");
},
@ -347,7 +322,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.createProgramContent.style.display = "block";
displayCreateProgramContent();
Engine.currentPage = Engine.Page.CreateProgram;
routing.navigateTo(Page.CreateProgram);
document.getElementById("create-program-menu-link").classList.add("active");
},
@ -355,21 +330,21 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.factionsContent.style.display = "block";
Engine.displayFactionsInfo();
Engine.currentPage = Engine.Page.Factions;
routing.navigateTo(Page.Factions);
document.getElementById("factions-menu-link").classList.add("active");
},
loadFactionContent: function() {
Engine.hideAllContent();
Engine.Display.factionContent.style.display = "block";
Engine.currentPage = Engine.Page.Faction;
routing.navigateTo(Page.Faction);
},
loadAugmentationsContent: function() {
Engine.hideAllContent();
Engine.Display.augmentationsContent.style.display = "block";
displayAugmentationsContent();
Engine.currentPage = Engine.Page.Augmentations;
routing.navigateTo(Page.Augmentations);
document.getElementById("augmentations-menu-link").classList.add("active");
},
@ -377,7 +352,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.tutorialContent.style.display = "block";
Engine.displayTutorialContent();
Engine.currentPage = Engine.Page.Tutorial;
routing.navigateTo(Page.Tutorial);
document.getElementById("tutorial-menu-link").classList.add("active");
},
@ -385,7 +360,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.devMenuContent.style.display = "block";
Engine.displayDevMenuContent();
Engine.currentPage = Engine.Page.DevMenu;
routing.navigateTo(Page.DevMenu);
document.getElementById("dev-menu-link").classList.add("active");
},
@ -393,7 +368,7 @@ let Engine = {
Engine.hideAllContent();
Engine.Display.locationContent.style.display = "block";
displayLocationContent();
Engine.currentPage = Engine.Page.Location;
routing.navigateTo(Page.Location);
},
loadTravelContent: function() {
@ -439,7 +414,7 @@ let Engine = {
//mainMenu.style.visibility = "hidden";
mainMenu.style.visibility = "hidden";
Engine.Display.workInProgressContent.style.display = "block";
Engine.currentPage = Engine.Page.WorkInProgress;
routing.navigateTo(Page.WorkInProgress);
},
loadRedPillContent: function() {
@ -447,7 +422,7 @@ let Engine = {
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
Engine.Display.redPillContent.style.display = "block";
Engine.currentPage = Engine.Page.RedPill;
routing.navigateTo(Page.RedPill);
},
loadCinematicTextContent: function() {
@ -455,19 +430,19 @@ let Engine = {
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
Engine.Display.cinematicTextContent.style.display = "block";
Engine.currentPage = Engine.Page.CinematicText;
routing.navigateTo(Page.CinematicText);
},
loadInfiltrationContent: function() {
Engine.hideAllContent();
Engine.Display.infiltrationContent.style.display = "block";
Engine.currentPage = Engine.Page.Infiltration;
routing.navigateTo(Page.Infiltration);
},
loadStockMarketContent: function() {
Engine.hideAllContent();
Engine.Display.stockMarketContent.style.display = "block";
Engine.currentPage = Engine.Page.StockMarket;
routing.navigateTo(Page.StockMarket);
displayStockMarketContent();
},
@ -475,10 +450,10 @@ let Engine = {
Engine.hideAllContent();
if (document.getElementById("gang-container") || Player.inGang()) {
displayGangContent();
Engine.currentPage = Engine.Page.Gang;
routing.navigateTo(Page.Gang);
} else {
Engine.loadTerminalContent();
Engine.currentPage = Engine.Page.Terminal;
routing.navigateTo(Page.Terminal);
}
},
@ -487,7 +462,7 @@ let Engine = {
document.getElementById("mainmenu-container").style.visibility = "hidden";
document.getElementById("character-overview-wrapper").style.visibility = "hidden";
Engine.Display.missionContent.style.display = "block";
Engine.currentPage = Engine.Page.Mission;
routing.navigateTo(Page.Mission);
},
loadCorporationContent: function() {
@ -495,7 +470,7 @@ let Engine = {
Engine.hideAllContent();
document.getElementById("character-overview-wrapper").style.visibility = "hidden";
Player.corporation.createUI();
Engine.currentPage = Engine.Page.Corporation;
routing.navigateTo(Page.Corporation);
}
},
@ -503,7 +478,7 @@ let Engine = {
if (Player.bladeburner instanceof Bladeburner) {
try {
Engine.hideAllContent();
Engine.currentPage = Engine.Page.Bladeburner;
routing.navigateTo(Page.Bladeburner);
Player.bladeburner.createContent();
} catch(e) {
exceptionAlert(e);
@ -573,11 +548,11 @@ let Engine = {
displayCharacterOverviewInfo: function() {
Engine.overview.update();
const save = document.getElementById("character-overview-save-button");
const flashClass = "flashing-button";
if(!Settings.AutosaveInterval) {
if(!Settings.AutosaveInterval) {
save.classList.add(flashClass);
} else {
save.classList.remove(flashClass);
@ -595,7 +570,7 @@ let Engine = {
var intText = "";
if (Player.intelligence > 0) {
intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + "<br><br><br>";
intText = 'Intelligence: ' + (Player.intelligence).toLocaleString() + '<br>';
}
let bitNodeTimeText = "";
@ -609,21 +584,21 @@ let Engine = {
'Current City: ' + Player.city + '<br><br>' +
'Employer: ' + Player.companyName + '<br>' +
'Job Title: ' + companyPosition + '<br><br>' +
'Money: $' + formatNumber(Player.money.toNumber(), 2)+ '<br><br><br>' +
'Money: $' + formatNumber(Player.money.toNumber(), 2) + '<br><br><br>' +
'<b>Stats</b><br><br>' +
'Hacking Level: ' + (Player.hacking_skill).toLocaleString() +
" (" + numeral(Player.hacking_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeral(Player.hacking_exp).format('(0.000a)') + ' experience)<br>' +
'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() +
" (" + numeral(Player.defense_exp).format('(0.000a)')+ ' experience)<br>' +
' (' + numeral(Player.defense_exp).format('(0.000a)')+ ' experience)<br>' +
'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() +
" (" + numeral(Player.agility_exp).format('(0.000a)') + ' experience)<br>' +
' (' + numeral(Player.agility_exp).format('(0.000a)') + ' experience)<br>' +
'Charisma: ' + (Player.charisma).toLocaleString() +
" (" + numeral(Player.charisma_exp).format('(0.000a)') + ' experience)<br>' +
intText +
' (' + numeral(Player.charisma_exp).format('(0.000a)') + ' experience)<br>' +
intText + '<br><br>' +
'<b>Multipliers</b><br><br>' +
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
@ -1061,7 +1036,7 @@ let Engine = {
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
//Always update, but make the interval longer if the page isn't active
updateActiveScriptsItems();
if (Engine.currentPage === Engine.Page.ActiveScripts) {
if (routing.isOn(Page.ActiveScripts)) {
Engine.Counters.updateActiveScriptsDisplay = 5;
} else {
Engine.Counters.updateActiveScriptsDisplay = 10;
@ -1070,11 +1045,11 @@ let Engine = {
if (Engine.Counters.updateDisplays <= 0) {
Engine.displayCharacterOverviewInfo();
if (Engine.currentPage == Engine.Page.CharacterInfo) {
if (routing.isOn(Page.CharacterInfo)) {
Engine.displayCharacterInfo();
} else if (Engine.currentPage == Engine.Page.HacknetNodes) {
} else if (routing.isOn(Page.HacknetNodes)) {
updateHacknetNodesContent();
} else if (Engine.currentPage == Engine.Page.CreateProgram) {
} else if (routing.isOn(Page.CreateProgram)) {
displayCreateProgramContent();
}
@ -1086,16 +1061,16 @@ let Engine = {
}
if (Engine.Counters.updateDisplaysMed <= 0) {
if (Engine.currentPage === Engine.Page.Corporation) {
if (routing.isOn(Page.Corporation)) {
Player.corporation.updateUIContent();
}
Engine.Counters.updateDisplaysMed = 9;
}
if (Engine.Counters.updateDisplaysLong <= 0) {
if (Engine.currentPage === Engine.Page.Gang) {
if (routing.isOn(Page.Gang)) {
updateGangContent();
} else if (Engine.currentPage === Engine.Page.ScriptEditor) {
} else if (routing.isOn(Page.ScriptEditor)) {
updateScriptEditorContent();
}
Engine.Counters.updateDisplaysLong = 15;
@ -1210,23 +1185,6 @@ let Engine = {
}
},
_prevTimeout: null,
createStatusText: function(txt) {
if (Engine._prevTimeout != null) {
clearTimeout(Engine._prevTimeout);
Engine._prevTimeout = null;
}
var statusText = document.getElementById("status-text")
statusText.style.display = "inline-block";
statusText.setAttribute("class", "status-text");
statusText.innerHTML = txt;
Engine._prevTimeout = setTimeout(function() {
statusText.style.display = "none";
statusText.removeAttribute("class");
statusText.innerHTML = "";
}, 3000);
},
//Used when initializing a game
//elems should be an array of all DOM elements under the header
closeMainMenuHeader: function(elems) {
@ -1430,11 +1388,12 @@ let Engine = {
bladeburner.style.display = "none";
corp.style.display = "none";
gang.style.display = "none";
dev.style.display = "none";
Engine.openMainMenuHeader(
[terminal, createScript, activeScripts, stats,
hacknetnodes, city,
tutorial, options, dev]
tutorial, options]
);
//Start interactive tutorial
@ -1450,7 +1409,7 @@ let Engine = {
setDisplayElements: function() {
//Content elements
Engine.Display.terminalContent = document.getElementById("terminal-container");
Engine.currentPage = Engine.Page.Terminal;
routing.navigateTo(Page.Terminal);
Engine.Display.characterContent = document.getElementById("character-container");
Engine.Display.characterContent.style.display = "none";
@ -1965,7 +1924,9 @@ let Engine = {
Engine.Clickables.devMainMenuButton = clearEventListeners("dev-menu-link");
Engine.Clickables.devMainMenuButton.addEventListener("click", function() {
Engine.loadDevMenuContent();
if( process.env.NODE_ENV === "development") {
Engine.loadDevMenuContent();
}
return false;
});

View File

@ -9,3 +9,29 @@ export type EqualityFunc<T> = (a: T, b: T) => boolean;
export interface IMap<T> {
[key: string]: T;
}
/**
* Performs some action, with no returned value.
*/
export type Action = () => void;
/**
* Contains a method to initialize itself to a known state.
*/
export interface ISelfInitializer {
/**
* Initialize/reset the object to a known, default state.
*/
init(): void;
}
/**
* Contains a method to repopulate itself based on a JSON string.
*/
export interface ISelfLoading {
/**
* Loads the save state onto the current object.
* @param saveState JSON string representing the save state.
*/
load(saveState: string): void;
}

View File

@ -0,0 +1,27 @@
import { getElementById } from "../../utils/uiHelpers/getElementById";
import { Action } from "../types";
const threeSeconds: number = 3000;
let x: number | undefined;
/**
* Displays a status message to the player for approximately 3 seconds.
* @param text The status text to display
*/
export function createStatusText(text: string) {
if (x !== undefined) {
clearTimeout(x);
// Likely not needed due to clearTimeout, but just in case...
x = undefined;
}
const statusElement: HTMLElement = getElementById("status-text");
statusElement.classList.add("status-text");
statusElement.innerText = text;
const handler: Action = () => {
statusElement.classList.remove("status-text");
statusElement.innerText = "";
};
x = setTimeout(handler, threeSeconds);
}

View File

@ -0,0 +1,146 @@
/**
* The full-screen page the player is currently be on.
* These pages are mutually exclusive.
*/
export enum Page {
/**
* (Default) The terminal is where the player issues all commands, executes scripts, etc.
*/
Terminal = "Terminal",
/**
* Displays most of the statistics about the player.
*/
CharacterInfo = "CharacterInfo",
/**
* The console for editing Netscript files.
*/
ScriptEditor = "ScriptEditor",
/**
* Monitor the scripts currently executing across the servers.
*/
ActiveScripts = "ActiveScripts",
/**
* View, purchase, and upgrade Hacknet nodes.
*/
HacknetNodes = "HacknetNodes",
/**
* View the city the player is currently in.
*/
World = "World",
/**
* The list of programs the player could potentially build.
*/
CreateProgram = "CreateProgram",
/**
* The list of all factions, and invites, available to the player.
*/
Factions = "Factions",
/**
* Information about a specific faction.
*/
Faction = "Faction",
/**
* The list of installed, and yet-to-be installed, augmentations the player has purchased.
*/
Augmentations = "Augmentations",
/**
* A collection of in-game material to learn about the game.
*/
Tutorial = "Tutorial",
/**
* A collection of items to manipulate the state of the game. Useful for development.
*/
DevMenu = "Dev Menu",
/**
* Information about the specific location the player at (job, company, etc.);
*/
Location = "Location",
/**
* A blocking page to show the player they are currently doing some action (building a program, working, etc.).
*/
workInProgress = "WorkInProgress",
/**
* A special screen to show the player they've reached a certain point in the game.
*/
RedPill = "RedPill",
/**
* A special screen to show the player they've reached a certain point in the game.
*/
CinematicText = "CinematicText",
/**
* Mini-game to infiltrate a company, gaining experience from successful progress.
*/
Infiltration = "Infiltration",
/**
* View the in-game stock market.
*/
StockMarket = "StockMarket",
/**
* Manage gang actions and members.
*/
Gang = "Gang",
/**
* Perform missions for a Faction.
*/
Mission = "Mission",
/**
* Manage a corporation.
*/
Corporation = "Corporation",
/**
* Manage special Bladeburner activities.
*/
Bladeburner = "Bladeburner",
}
/**
* This class keeps track of player navigation/routing within the game.
*/
class Routing {
/**
* Tracking the what page the user is currently on.
*/
private currentPage: Page | null = null;
/**
* Determines if the player is currently on the specified page.
* @param page The page to compare against the current state.
*/
isOn(page: Page) {
return this.currentPage === page;
}
/**
* Routes the player to the appropriate page.
* @param page The page to navigate to.
*/
navigateTo(page: Page) {
this.currentPage = page;
}
}
/**
* The routing instance for tracking page navigation.
*/
export const routing: Routing = new Routing();

40
src/ui/postToTerminal.ts Normal file
View File

@ -0,0 +1,40 @@
import { getElementById } from "../../utils/uiHelpers/getElementById";
/**
* Adds some output to the terminal.
* @param input Text or HTML to output to the terminal
*/
export function post(input: string) {
postContent(input);
}
/**
* Adds some output to the terminal with an identifier of "hack-progress-bar"
* @param input Text or HTML to output to the terminal
*/
export function hackProgressBarPost(input: string) {
postContent(input, "hack-progress-bar");
}
/**
* Adds some output to the terminal with an identifier of "hack-progress"
* @param input Text or HTML to output to the terminal
*/
export function hackProgressPost(input: string) {
postContent(input, "hack-progress");
}
function postContent(input: string, id?: string) {
// tslint:disable-next-line:max-line-length
const style: string = `color: var(--my-font-color); background-color:var(--my-background-color);${id === undefined ? " white-space:pre-wrap;" : ""}`;
// tslint:disable-next-line:max-line-length
const content: string = `<tr class="posted"><td ${id === undefined ? 'class="terminal-line"' : `id="${id}"`} style="${style}">${input}</td></tr>`;
const inputElement: HTMLElement = getElementById("terminal-input");
inputElement.insertAdjacentHTML("beforebegin", content);
scrollTerminalToBottom();
}
function scrollTerminalToBottom() {
const container: HTMLElement = getElementById("terminal-container");
container.scrollTop = container.scrollHeight;
}

View File

@ -1,38 +1,5 @@
import {Engine} from "./engine";
/* Settings.js */
let Settings = {
CodeInstructionRunTime: 25,
MaxLogCapacity: 50,
MaxPortCapacity: 50,
SuppressMessages: false,
SuppressFactionInvites: false,
SuppressTravelConfirmation: false,
SuppressBuyAugmentationConfirmation: false,
AutosaveInterval: 60,
DisableHotkeys: false,
ThemeHighlightColor: "#ffffff",
ThemeFontColor: "#66ff33",
ThemeBackgroundColor: "#000000",
EditorTheme: "Monokai",
EditorKeybinding: "ace",
}
function loadSettings(saveString) {
Settings = JSON.parse(saveString);
}
function initSettings() {
Settings.CodeInstructionRunTime = 50;
Settings.MaxLogCapacity = 50;
Settings.MaxPortCapacity = 50;
Settings.SuppressMessages = false;
Settings.SuppressFactionInvites = false;
Settings.SuppressTravelConfirmation = false;
Settings.SuppressBuyAugmentationConfirmation = false;
Settings.AutosaveInterval = 60;
Settings.DisableHotkeys = false;
}
import {Engine} from "../engine";
import {Settings} from "../Settings";
function setSettingsLabels() {
var nsExecTime = document.getElementById("settingsNSExecTimeRangeValLabel");
@ -42,6 +9,7 @@ function setSettingsLabels() {
var suppressFactionInv = document.getElementById("settingsSuppressFactionInvites")
var suppressTravelConfirmation = document.getElementById("settingsSuppressTravelConfirmation");
var suppressBuyAugmentationConfirmation = document.getElementById("settingsSuppressBuyAugmentationConfirmation");
var suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup");
var autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel");
var disableHotkeys = document.getElementById("settingsDisableHotkeys");
@ -53,6 +21,7 @@ function setSettingsLabels() {
suppressFactionInv.checked = Settings.SuppressFactionInvites;
suppressTravelConfirmation.checked = Settings.SuppressTravelConfirmation;
suppressBuyAugmentationConfirmation.checked = Settings.SuppressBuyAugmentationConfirmation;
suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup;
autosaveInterval.innerHTML = Settings.AutosaveInterval;
disableHotkeys.checked = Settings.DisableHotkeys;
@ -105,8 +74,11 @@ function setSettingsLabels() {
suppressBuyAugmentationConfirmation.onclick = function() {
Settings.SuppressBuyAugmentationConfirmation = this.checked;
console.log('sup buy: '+Settings.SuppressBuyAugmentationConfirmation);
};
suppressHospitalizationPopup.onclick = function() {
Settings.SuppressHospitalizationPopup = this.checked;
}
disableHotkeys.onclick = function() {
Settings.DisableHotkeys = this.checked;
@ -126,4 +98,4 @@ function setSettingsLabels() {
}
}
export {Settings, initSettings, setSettingsLabels, loadSettings};
export { setSettingsLabels };

View File

@ -1,7 +1,8 @@
import {Faction, joinFaction} from "../src/Faction";
import {joinFaction} from "../src/Faction";
import {Engine} from "../src/engine";
import {Player} from "../src/Player";
import {clearEventListeners} from "./uiHelpers/clearEventListeners";
import {Page, routing} from "../src/ui/navigationTracking";
/* Faction Invitation Pop-up box */
function factionInvitationBoxClose() {
@ -11,7 +12,7 @@ function factionInvitationBoxClose() {
function factionInvitationBoxOpen() {
var factionInvitationBox = document.getElementById("faction-invitation-box-container");
factionInvitationBox.style.display = "block";
factionInvitationBox.style.display = "flex";
}
function factionInvitationSetText(txt) {
@ -30,7 +31,7 @@ function factionInvitationBoxCreate(faction) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (Engine.currentPage === Engine.Page.Factions) {
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
@ -45,7 +46,7 @@ function factionInvitationBoxCreate(faction) {
}
joinFaction(faction);
factionInvitationBoxClose();
if (Engine.currentPage === Engine.Page.Factions) {
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
return false;

View File

@ -35,7 +35,7 @@ function gameOptionsBoxClose() {
function gameOptionsBoxOpen() {
var box = document.getElementById("game-options-container");
box.style.display = "block";
box.style.display = "flex";
setTimeout(function() {
gameOptionsOpened = true;
}, 500);

View File

@ -1,4 +1,4 @@
import {BitNodeMultipliers} from "../src/BitNode";
import {BitNodeMultipliers} from "../src/BitNodeMultipliers";
import {CONSTANTS} from "../src/Constants";
import {Factions, Faction} from "../src/Faction";
import {Player} from "../src/Player";

View File

@ -32,7 +32,7 @@ function yesNoBoxCreate(txt) {
var c = document.getElementById("yes-no-box-container");
if (c) {
c.style.display = "block";
c.style.display = "flex";
} else {
console.log("ERROR: Container not found for YesNoBox");
}
@ -75,7 +75,7 @@ function yesNoTxtInpBoxCreate(txt) {
var c = document.getElementById("yes-no-text-input-box-container");
if (c) {
c.style.display = "block";
c.style.display = "flex";
} else {
console.log("ERROR: Container not found for YesNoTextInputBox");
}

View File

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

View File

@ -1,3 +1,6 @@
/**
* Formats the current time (to the minute).
*/
export function getTimestamp() {
const d: Date = new Date();
// A negative slice value takes from the end of the string rather than the beginning.

30
utils/helpers/keyCodes.ts Normal file
View File

@ -0,0 +1,30 @@
import { IMap } from "../../src/types";
/**
* Keyboard key codes
*/
export const KEY: IMap<number> = {
A: 65,
B: 66,
C: 67,
CTRL: 17,
D: 68,
DOWNARROW: 40,
E: 69,
ENTER: 13,
F: 70,
H: 72,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
R: 82,
S: 83,
TAB: 9,
U: 85,
UPARROW: 38,
W: 87,
};

View File

@ -4,5 +4,6 @@
*/
export function roundToTwo(decimal: number) {
const leftShift: number = Math.round(parseFloat(`${decimal}e+2`));
return +(`${leftShift}e-2`);
}

View File

@ -27,18 +27,18 @@ interface IAccordionConfigurationParameters {
export function createAccordionElement(params: IAccordionConfigurationParameters) {
const liElem: HTMLLIElement = createElement("li") as HTMLLIElement;
const header: HTMLButtonElement = createElement("button", {
class: "accordion-header",
clickListener() {
this.classList.toggle("active");
const pnl: CSSStyleDeclaration = (this.nextElementSibling as HTMLDivElement).style;
pnl.display = pnl.display === "block" ? "none" : "block";
},
id: params.id !== undefined ? `${params.id}-hdr` : undefined,
class:"accordion-header",
innerHTML: params.hdrText,
}) as HTMLButtonElement;
const panel: HTMLDivElement = createElement("div", {
class: "accordion-panel",
id: params.id !== undefined ? `${params.id}-panel` : undefined,
class:"accordion-panel",
innerHTML: params.panelText,
}) as HTMLDivElement;

View File

@ -9,8 +9,8 @@ import { getElementById } from "./getElementById";
export function createPopup(id: string, elems: HTMLElement[]) {
const container: HTMLDivElement = createElement("div", {
class: "popup-box-container",
display: "block",
id: id,
display: "flex",
id,
}) as HTMLDivElement;
const content: HTMLElement = createElement("div", {
class: "popup-box-content",