Merge pull request #426 from danielyxie/dev

v0.40.1
This commit is contained in:
danielyxie 2018-08-06 19:31:45 -04:00 committed by GitHub
commit ab51ea94fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 4781 additions and 1187 deletions

1
.gitignore vendored

@ -5,3 +5,4 @@ Netburner.txt
/dist/*.map /dist/*.map
/tests/*.map /tests/*.map
/tests/*.bundle.* /tests/*.bundle.*
/tests/*.css

61
css/_mixins.scss Normal file

@ -0,0 +1,61 @@
@mixin animation($property) {
-webkit-animation: $property;
-moz-animation: $property;
-ms-animation: $property;
-o-animation: $property;
animation: $property;
}
@mixin borderRadius($property) {
-webkit-border-radius: $property;
-moz-border-radius: $property;
border-radius: $property;
}
@mixin boxShadow($value) {
-webkit-box-shadow: $value;
-moz-box-shadow: $value;
box-shadow: $value;
}
@mixin keyframes($animationName) {
@-webkit-keyframes #{$animationName} {
$browser: '-webkit-' !global;
@content;
}
@-moz-keyframes #{$animationName} {
$browser: '-moz-' !global;
@content;
}
@-ms-keyframes #{$animationName} {
$browser: '-ms-' !global;
@content;
}
@-o-keyframes #{$animationName} {
$browser: '-o-' !global;
@content;
}
@keyframes #{$animationName} {
$browser: '' !global;
@content;
}
}
@mixin transform($property) {
-webkit-transform: $property;
-moz-transform: $property;
-ms-transform: $property;
-o-transform: $property;
transform: $property;
}
@mixin userSelect($value) {
-webkit-user-select: $value;
-moz-user-select: $value;
-ms-user-select: $value;
user-select: $value;
}

15
css/_reset.scss Normal file

@ -0,0 +1,15 @@
@import "theme";
* {
font-size: $defaultFontSize;
font-family: $fontFamily;
}
*,
*:before,
*:after {
margin: 0;
padding: 0;
box-sizing: border-box;
vertical-align: top;
}

2
css/_theme.scss Normal file

@ -0,0 +1,2 @@
$fontFamily: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
$defaultFontSize: 16px;

@ -1,9 +1,13 @@
#bladeburner-container p, @import "theme";
#bladeburner-container pre,
#bladeburner-container a, #bladeburner-container {
#bladeburner-container div, a,
#bladeburner-container td { div,
font-size: 13px; p,
pre,
td {
font-size: $defaultFontSize * 0.8125;
}
} }
.bladeburner-action { .bladeburner-action {
@ -11,10 +15,10 @@
margin: 7px; margin: 7px;
padding: 7px; padding: 7px;
white-space: pre-wrap; white-space: pre-wrap;
}
.bladeburner-action pre { pre {
white-space: pre-wrap; white-space: pre-wrap;
}
} }
/* Whatever action is currently active */ /* Whatever action is currently active */
@ -23,21 +27,25 @@
} }
/* Action & Skills panel navigation button */ /* Action & Skills panel navigation button */
.bladeburner-nav-button { %bladeburner-nav-button {
border: 1px solid #fff; border: 1px solid #fff;
color: #fff;
padding: 2px;
margin: 2px; margin: 2px;
padding: 2px;
} }
.bladeburner-nav-button:hover { .bladeburner-nav-button {
background-color: #3d4044; @extend %bladeburner-nav-button;
color: #fff;
&:hover {
background-color: #3d4044;
}
} }
.bladeburner-nav-button-inactive { .bladeburner-nav-button-inactive {
border: 1px solid #fff; @extend %bladeburner-nav-button;
padding: 2px;
margin: 2px;
text-decoration: none; text-decoration: none;
background-color: #555; background-color: #555;
cursor: default; cursor: default;
@ -76,7 +84,7 @@
margin: 0 !important; margin: 0 !important;
border: 0; border: 0;
background-color: var(--my-background-color); background-color: var(--my-background-color);
font-size: 13px; font-size: $defaultFontSize * 0.8125;
outline: none; outline: none;
color: var(--my-font-color); color: var(--my-font-color);
flex: 1 1 auto; flex: 1 1 auto;

@ -1,7 +1,10 @@
@import "mixins";
@import "theme";
#cmpy-mgmt-container p, #cmpy-mgmt-container p,
#cmpy-mgmt-container a, #cmpy-mgmt-container a,
#cmpy-mgmt-container div { #cmpy-mgmt-container div {
font-size: 13px; font-size: $defaultFontSize * 0.8125;
} }
/* Header tabs */ /* Header tabs */
@ -118,10 +121,14 @@
margin: 2px; margin: 2px;
padding: 6px; padding: 6px;
border-radius: 25px; border-radius: 25px;
font-size: "12px"; font-size: $defaultFontSize * 0.75;
color: var(--my-font-color); color: var(--my-font-color);
} }
.cmpy-mgmt-upgrade-div:hover { .cmpy-mgmt-upgrade-div:hover {
background-color: #333; background-color: #333;
} }
.cmpy-mgmt-advertising-info {
font-size: $defaultFontSize * 0.75;
}

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* interactivetutorial.css */ /* interactivetutorial.css */
#interactive-tutorial-wrapper { #interactive-tutorial-wrapper {
position: relative; position: relative;
@ -15,10 +18,10 @@
overflow: auto; /* Enable scroll if needed */ overflow: auto; /* Enable scroll if needed */
background-color: #444; /* Fallback color */ background-color: #444; /* Fallback color */
color: #fff; color: #fff;
}
#interactive-tutorial-container > strong { > strong {
background-color: #444; background-color: #444;
}
} }
#interactive-tutorial-text { #interactive-tutorial-text {
@ -31,16 +34,20 @@
#interactive-tutorial-exit, #interactive-tutorial-exit,
#interactive-tutorial-next, #interactive-tutorial-next,
#interactive-tutorial-back { #interactive-tutorial-back {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
font-size: 20px; font-size: $defaultFontSize * 1.25;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
background-color: #000; background-color: #000;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
} }
#interactive-tutorial-exit { #interactive-tutorial-exit {
@ -55,14 +62,3 @@
#interactive-tutorial-next { #interactive-tutorial-next {
float: right; float: right;
} }
#interactive-tutorial-exit:hover,
#interactive-tutorial-exit:focus,
#interactive-tutorial-next:hover,
#interactive-tutorial-next:focus,
#interactive-tutorial-back:hover,
#interactive-tutorial-back:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}

@ -1,148 +0,0 @@
@-webkit-keyframes LOADERSPINNER {
0% { -webkit-transform: translate(-50%, -50%) rotate(0deg); }
100% { -webkit-transform: translate(-50%, -50%) rotate(360deg); }
}
@-moz-keyframes LOADERSPINNER {
0% { -moz-transform: translate(-50%, -50%) rotate(0deg); }
100% { -moz-transform: translate(-50%, -50%) rotate(360deg); }
}
@-ms-keyframes LOADERSPINNER {
0% { -ms-transform: translate(-50%, -50%) rotate(0deg); }
100% { -ms-transform: translate(-50%, -50%) rotate(360deg); }
}
@-o-keyframes LOADERSPINNER {
0% { -o-transform: translate(-50%, -50%) rotate(0deg); }
100% { -o-transform: translate(-50%, -50%) rotate(360deg); }
}
@keyframes LOADERSPINNER {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
@-webkit-keyframes LOADERLABEL {
0% { opacity: 1.0; -webkit-transform: translate(-50%, -50%) scale(1.0); }
5% { opacity: 0.5; -webkit-transform: translate(-50%, -50%) scale(0.5); }
95% { opacity: 0.5; -webkit-transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1.0; -webkit-transform: translate(-50%, -50%) scale(1.0); }
}
@-moz-keyframes LOADERLABEL {
0% { opacity: 1.0; -moz-transform: translate(-50%, -50%) scale(1.0); }
5% { opacity: 0.5; -moz-transform: translate(-50%, -50%) scale(0.5); }
95% { opacity: 0.5; -moz-transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1.0; -moz-transform: translate(-50%, -50%) scale(1.0); }
}
@-ms-keyframes LOADERLABEL {
0% { opacity: 1.0; -ms-transform: translate(-50%, -50%) scale(1.0); }
5% { opacity: 0.5; -ms-transform: translate(-50%, -50%) scale(0.5); }
95% { opacity: 0.5; -ms-transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1.0; -ms-transform: translate(-50%, -50%) scale(1.0); }
}
@-o-keyframes LOADERLABEL {
0% { opacity: 1.0; -o-transform: translate(-50%, -50%) scale(1.0); }
5% { opacity: 0.5; -o-transform: translate(-50%, -50%) scale(0.5); }
95% { opacity: 0.5; -o-transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1.0; -o-transform: translate(-50%, -50%) scale(1.0); }
}
@keyframes LOADERLABEL {
0% { opacity: 1.0; transform: translate(-50%, -50%) scale(1.0); }
5% { opacity: 0.5; transform: translate(-50%, -50%) scale(0.5); }
95% { opacity: 0.5; transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1.0; transform: translate(-50%, -50%) scale(1.0); }
}
*, *:before, *:after {
margin: 0;
padding: 0;
box-sizing: border-box;
vertical-align: top;
}
.loaderoverlay {
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 1.0);
}
.loaderoverlay .loaderspinner,
.loaderoverlay .loaderspinner:before,
.loaderoverlay .loaderspinner:after {
border: 20px solid rgba(0, 0, 0, 0);
border-top: 20px solid #ccc;
border-bottom: 20px solid #ccc;
border-radius: 1000px;
position: absolute;
top: 50%;
left: 50%;
}
.loaderoverlay .loaderspinner:before,
.loaderoverlay .loaderspinner:after {
content: "";
}
.loaderoverlay .loaderspinner {
width: 200px;
height: 200px;
-webkit-animation: LOADERSPINNER 5s linear infinite;
-moz-animation: LOADERSPINNER 5s linear infinite;
-ms-animation: LOADERSPINNER 5s linear infinite;
-o-animation: LOADERSPINNER 5s linear infinite;
animation: LOADERSPINNER 5s linear infinite;
}
.loaderoverlay .loaderspinner:before {
width: 160px;
height: 160px;
-webkit-animation: LOADERSPINNER 10s linear infinite;
-moz-animation: LOADERSPINNER 10s linear infinite;
-ms-animation: LOADERSPINNER 10s linear infinite;
-o-animation: LOADERSPINNER 10s linear infinite;
animation: LOADERSPINNER 10s linear infinite;
}
.loaderoverlay .loaderspinner:after {
width: 120px;
height: 120px;
-webkit-animation: LOADERSPINNER 5s linear infinite;
-moz-animation: LOADERSPINNER 5s linear infinite;
-ms-animation: LOADERSPINNER 5s linear infinite;
-o-animation: LOADERSPINNER 5s linear infinite;
animation: LOADERSPINNER 5s linear infinite;
}
.loaderoverlay .loaderlabel {
color: #6f3;
text-transform: uppercase;
font-family: sans-serif;
font-size: 22px;
font-weight: 700;
letter-spacing: 2px;
position: absolute;
top: 50%;
left: 50%;
-webkit-animation: LOADERLABEL 5s linear infinite;
-moz-animation: LOADERLABEL 5s linear infinite;
-ms-animation: LOADERLABEL 5s linear infinite;
-o-animation: LOADERLABEL 5s linear infinite;
animation: LOADERLABEL 5s linear infinite;
}
button[type="button"] {
padding: 0.5rem 1rem;
position: absolute;
z-index: 1;
bottom: 10px;
left: 50%;
-webkit-transform: translateX(-50%);
-moz-transform: translateX(-50%);
-ms-transform: translateX(-50%);
-o-transform: translateX(-50%);
transform: translateX(-50%);
}
/* Customize */
.loaderoverlay {
background: #000;
}
.loaderoverlay .loaderspinner,
.loaderoverlay .loaderspinner:before,
.loaderoverlay .loaderspinner:after {
border-top-color: #6f3 !important;
border-bottom-color: #6f3 !important;
}
.loaderoverlay .loaderlabel {
color: #6f3;
}

94
css/loader.scss Normal file

@ -0,0 +1,94 @@
@import "mixins";
@import "reset";
@import "theme";
@include keyframes(LOADERSPINNER) {
0% {
#{$browser}transform: translate(-50%, -50%) rotate(0deg);
}
100% {
#{$browser}transform: translate(-50%, -50%) rotate(360deg);
}
}
@include keyframes(LOADERLABEL) {
0% {
opacity: 1.0;
#{$browser}transform: translate(-50%, -50%) scale(1.0);
}
5% {
opacity: 0.5;
#{$browser}transform: translate(-50%, -50%) scale(0.5);
}
95% {
opacity: 0.5;
#{$browser}transform: translate(-50%, -50%) scale(0.5);
}
100% {
opacity: 1.0;
#{$browser}transform: translate(-50%, -50%) scale(1.0);
}
}
.loaderoverlay {
$spinnerBoxSize: 200px;
$themeColor: #6f3;
position: absolute;
width: 100%;
height: 100%;
background: #000;
color: $themeColor;
%spinnerBox {
border: 20px solid rgba(0, 0, 0, 0);
border-top-color: $themeColor;
border-bottom-color: $themeColor;
border-radius: 1000px;
position: absolute;
top: 50%;
left: 50%;
}
.loaderspinner:before,
.loaderspinner:after {
content: "";
}
.loaderspinner {
@extend %spinnerBox;
@include animation(LOADERSPINNER 5s linear infinite);
width: $spinnerBoxSize;
height: $spinnerBoxSize;
}
.loaderspinner:before {
@extend %spinnerBox;
@include animation(LOADERSPINNER 10s linear infinite);
width: $spinnerBoxSize * 0.8;
height: $spinnerBoxSize * 0.8;
}
.loaderspinner:after {
@extend %spinnerBox;
@include animation(LOADERSPINNER 5s linear infinite);
width: $spinnerBoxSize * 0.6;
height: $spinnerBoxSize * 0.6;
}
.loaderlabel {
@include animation(LOADERLABEL 5s linear infinite);
text-transform: uppercase;
font-family: sans-serif;
font-size: $defaultFontSize * 1.375;
font-weight: 700;
letter-spacing: 2px;
position: absolute;
top: 50%;
left: 50%;
}
}

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding /* CSS for different main menu pages, such as character info, script editor, etc (but excluding
terminal which has its own page) */ terminal which has its own page) */
@ -32,7 +35,7 @@
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
z-index: 1; z-index: 1;
font-family: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman'; font-family: $fontFamily;
} }
.ace_line, .ace_line,
@ -43,7 +46,7 @@
} }
.ace_text-input { .ace_text-input {
font-size: 16px; font-size: $defaultFontSize;
background-color: transparent; background-color: transparent;
} }
@ -89,6 +92,9 @@
} }
#script-editor-filename { #script-editor-filename {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
background-color: #555; background-color: #555;
display: inline-block; display: inline-block;
float: center; float: center;
@ -99,15 +105,6 @@
padding: 2px; padding: 2px;
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
-webkit-box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
-moz-box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
} }
#script-editor-status { #script-editor-status {
@ -132,7 +129,7 @@
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
padding: 2px; padding: 2px;
font-size: 12px; font-size: $defaultFontSize * 0.75;
} }
/* Active scripts */ /* Active scripts */
@ -154,7 +151,7 @@
.active-scripts-server-header { .active-scripts-server-header {
background-color: #444; background-color: #444;
font-size: 20px; font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 6px; padding: 6px;
@ -176,7 +173,7 @@
.active-scripts-server-header:after { .active-scripts-server-header:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -184,7 +181,7 @@
.active-scripts-server-header.active:after { .active-scripts-server-header.active:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -227,7 +224,7 @@
.active-scripts-script-header:after { .active-scripts-script-header:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color); color: var(--my-font-color);
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -235,7 +232,7 @@
.active-scripts-script-header.active:after { .active-scripts-script-header.active:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color); color: var(--my-font-color);
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -260,15 +257,12 @@
} }
.active-scripts-button { .active-scripts-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
font-size: 16px; font-size: $defaultFontSize;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
margin: 4px; margin: 4px;
padding: 4px; padding: 4px;
background-color: #000; background-color: #000;
@ -323,19 +317,13 @@
} }
.hacknet-node { .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; margin: 6px;
padding: 6px; padding: 6px;
width: 34vw; width: 34vw;
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
-webkit-box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
-moz-box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
box-shadow:
inset 0 0 8px rgba(0, 0, 0, 0.1),
0 0 16px rgba(0, 0, 0, 0.1);
} }
.hacknet-node-container { .hacknet-node-container {
@ -355,7 +343,7 @@
display: inline-block; display: inline-block;
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */ margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
padding: 0 4px; padding: 0 4px;
width: 48px; /* Four times font-size */ width: $defaultFontSize * 4;
} }
.menu-page-text { .menu-page-text {

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* css for Missions */ /* css for Missions */
/* Hacking missions */ /* Hacking missions */
@ -15,10 +18,10 @@
width: 100%; width: 100%;
overflow-y: auto; overflow-y: auto;
padding-right: 10px; padding-right: 10px;
}
.hack-mission-grid::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
}
} }
.hack-mission-node { .hack-mission-node {
@ -27,17 +30,15 @@
align-self: center; align-self: center;
justify-self: center; justify-self: center;
display: inline-block; display: inline-block;
}
.hack-mission-node p { p {
margin-top: 8px; @include userSelect(none);
color: #fff;
font-size: 12px; margin-top: 8px;
text-align: center; color: #fff;
-webkit-user-select: none; font-size: $defaultFontSize * 0.75;
-moz-user-select: none; text-align: center;
-ms-user-select: none; }
user-select: none;
} }
.hack-mission-player-node { .hack-mission-player-node {
@ -56,11 +57,9 @@
} }
.hack-mission-cpu-node { .hack-mission-cpu-node {
@include borderRadius(50%);
width: 100%; width: 100%;
height: 100%; height: 100%;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
} }
.hack-mission-firewall-node { .hack-mission-firewall-node {
@ -69,47 +68,37 @@
} }
.hack-mission-database-node { .hack-mission-database-node {
@include transform(skew(20deg));
width: 100%; width: 100%;
height: 90%; height: 90%;
-webkit-transform: skew(20deg);
-moz-transform: skew(20deg);
-o-transform: skew(20deg);
}
.hack-mission-database-node p { p {
-webkit-transform: skew(-20deg); @include transform(skew(-20deg));
-moz-transform: skew(-20deg); @include userSelect(none);
-o-transform: skew(-20deg);
color: #fff; color: #fff;
font-size: 12px; font-size: $defaultFontSize * 0.75;
margin-top: 8px; margin-top: 8px;
text-align: center; text-align: center;
-webkit-user-select: none; }
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} }
.hack-mission-transfer-node { .hack-mission-transfer-node {
@include transform(skew(-20deg));
width: 100%; width: 100%;
height: 90%; height: 90%;
-webkit-transform: skew(-20deg);
-moz-transform: skew(-20deg);
-o-transform: skew(-20deg);
}
.hack-mission-transfer-node p { p {
-webkit-transform: skew(20deg); @include transform(skew(20deg));
-moz-transform: skew(20deg); @include userSelect(none);
-o-transform: skew(20deg);
color: #fff; color: #fff;
font-size: 12px; font-size: $defaultFontSize * 0.75;
margin-top: 8px; margin-top: 8px;
text-align: center; text-align: center;
-webkit-user-select: none; }
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} }
.hack-mission-spam-node, .hack-mission-spam-node,

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* Pop-up boxes */ /* Pop-up boxes */
.popup-box-container { .popup-box-container {
display: none; /* Hidden by default */ display: none; /* Hidden by default */
@ -24,7 +27,7 @@
.popup-box-button-inactive { .popup-box-button-inactive {
color: #aaa; color: #aaa;
float: right; float: right;
font-size: 16px; font-size: $defaultFontSize;
font-weight: bold; font-weight: bold;
padding: 2px; padding: 2px;
margin: 6px; margin: 6px;
@ -74,16 +77,13 @@
} }
.dialog-box-close-button { .dialog-box-close-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
float: right; float: right;
color: #aaa; color: #aaa;
font-size: 20px; font-size: $defaultFontSize * 1.25;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
} }
#log-box-close { #log-box-close {
@ -155,18 +155,15 @@
} }
#game-options-close-button { #game-options-close-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
float: right; float: right;
margin: 4px; margin: 4px;
padding: 4px; padding: 4px;
font-size: 20px; font-size: $defaultFontSize * 1.25;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px #fff;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
} }
#game-options-close-button:hover, #game-options-close-button:hover,

@ -1,16 +1,14 @@
@import "mixins";
@import "theme";
@import "reset";
:root{ :root{
--my-font-color: #6f3; --my-font-color: #6f3;
--my-background-color: #000; --my-background-color: #000;
--my-highlight-color: #fff; --my-highlight-color: #fff;
} }
* {
margin: 0;
padding: 0;
font-size: 16px;
font-family: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
}
body { body {
background-color: var(--my-background-color); background-color: var(--my-background-color);
} }
@ -23,7 +21,7 @@ h2,
} }
h1 { h1 {
font-size: 22px; font-size: $defaultFontSize * 1.375;
color: var(--my-font-color); color: var(--my-font-color);
} }
@ -41,6 +39,16 @@ span {
padding: 4px; padding: 4px;
} }
button[type="button"] {
@include transform(translateX(-50%));
padding: 0.5rem 1rem;
position: absolute;
z-index: 1;
bottom: 10px;
left: 50%;
}
#entire-game-container { #entire-game-container {
background-color: transparent; background-color: transparent;
} }
@ -100,7 +108,7 @@ tr:focus {
/* Plus and minus signs */ /* Plus and minus signs */
.mainmenu-accordion-header:after { .mainmenu-accordion-header:after {
content: '\02795'; content: '\02795';
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -127,7 +135,8 @@ tr:focus {
} }
/* Make html links ("a" elements) nice looking buttons with this class */ /* Make html links ("a" elements) nice looking buttons with this class */
a:link, a:visited { a:link,
a:visited {
color: #fff; color: #fff;
} }
@ -150,9 +159,7 @@ a:link, a:visited {
} }
.a-link-button:active { .a-link-button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); @include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
} }
/* Make anchor tags ("a" elements) inactive (not clickable) */ /* Make anchor tags ("a" elements) inactive (not clickable) */
@ -212,7 +219,7 @@ a:link, a:visited {
position: relative; position: relative;
} }
#create-program-notification { #create-program-notification {
font-size: 10px; font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */ position: absolute; /* Position the badge within the relatively positioned button */
top: 0; top: 0;
@ -308,9 +315,7 @@ a:link, a:visited {
} }
.help-tip:active { .help-tip:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); @include boxShadow(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);
} }
/* Flashing button (Red) */ /* Flashing button (Red) */
@ -408,7 +413,7 @@ a:link, a:visited {
} }
#status-text { #status-text {
font-size: 20px; font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
right: 0; right: 0;
bottom: 0; bottom: 0;
@ -428,8 +433,8 @@ a:link, a:visited {
position: absolute; /* Stay in place */ position: absolute; /* Stay in place */
right: 0; right: 0;
top: 0; top: 0;
height: 205px; /* Full height */ height: auto; /* Full height */
padding: 5px; padding: 8px;
border: 2px solid var(--my-highlight-color); border: 2px solid var(--my-highlight-color);
width: 19%; width: 19%;
overflow: auto; /* Enable scroll if needed */ overflow: auto; /* Enable scroll if needed */
@ -438,27 +443,34 @@ a:link, a:visited {
} }
#character-overview-text { #character-overview-text {
padding: 4px;
margin: 8px;
color: #fff; color: #fff;
background-color: #444; background-color: #444;
} }
.character-stat-text {
color: #fff;
background-color: #444;
}
.character-stat-cell {
text-align: right;
}
#character-overview-save-button, #character-overview-save-button,
#character-overview-options-button { #character-overview-options-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
font-size: 14px; font-size: $defaultFontSize * 0.875;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
height: 22px; height: 22px;
background-color: #000; background-color: #000;
} }
.character-quick-options {
padding-top: 5px;
}
#character-overview-save-button:hover, #character-overview-save-button:hover,
#character-overview-save-button:focus, #character-overview-save-button:focus,
#character-overview-options-button:hover, #character-overview-options-button:hover,
@ -485,7 +497,7 @@ a:link, a:visited {
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
.accordion-header { .accordion-header {
background-color: #444; background-color: #444;
font-size: 20px; font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 6px; padding: 6px;
@ -507,7 +519,7 @@ a:link, a:visited {
.accordion-header:after { .accordion-header:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -515,7 +527,7 @@ a:link, a:visited {
.accordion-header.active:after { .accordion-header.active:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: 13px; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -538,3 +550,11 @@ a:link, a:visited {
.accordion-panel ul > li { .accordion-panel ul > li {
background-color: #555; background-color: #555;
} }
/* override the global <span> styling */
#active-scripts-total-production-active,
#active-scripts-total-prod-aug-total,
#active-scripts-total-prod-aug-avg {
margin: 0;
padding: 0;
}

@ -1,3 +1,5 @@
@import "theme";
#terminal-container { #terminal-container {
position: fixed; position: fixed;
margin-left: 10%; margin-left: 10%;
@ -12,7 +14,7 @@
padding-left: 10px; padding-left: 10px;
height: auto; height: auto;
width: 70%; width: 70%;
font-size: 16px; font-size: $defaultFontSize;
overflow: auto; overflow: auto;
overflow-y: scroll; overflow-y: scroll;
background-color: var(--my-background-color); background-color: var(--my-background-color);
@ -31,7 +33,7 @@
margin: 0 !important; margin: 0 !important;
border: 0; border: 0;
background-color: var(--my-background-color); background-color: var(--my-background-color);
font-size: 16px; font-size: $defaultFontSize;
outline: none; outline: none;
color: var(--my-font-color); color: var(--my-font-color);
} }

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* Both Work in progress and BitNode stuff */ /* Both Work in progress and BitNode stuff */
.generic-fullscreen-container { .generic-fullscreen-container {
color: var(--my-font-color); color: var(--my-font-color);
@ -16,19 +19,16 @@
} }
#work-in-progress-cancel-button { #work-in-progress-cancel-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa; color: #aaa;
float: left; float: left;
font-size: 20px; font-size: $defaultFontSize * 1.25;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
margin: 10px; margin: 10px;
padding: 5px; padding: 5px;
border-radius: 12px;
border: 3px solid #fff; border: 3px solid #fff;
-moz-box-shadow: 1px 1px 3px #000;
-webkit-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
} }
#work-in-progress-cancel-button:hover, #work-in-progress-cancel-button:hover,

File diff suppressed because one or more lines are too long

1864
dist/engine.css vendored Normal file

File diff suppressed because it is too large Load Diff

100
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,23 @@
Changelog Changelog
========= =========
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 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** * **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**

@ -670,6 +670,21 @@ purchaseHacknetNode
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 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. to purchase a new Hacknet Node then the function will return false.
getPurchasedServerCost
^^^^^^^^^^^^^^^^^^^^^^
.. js:function:: getPurchasedServerCost(ram)
:param number ram: Amount of RAM of a potential purchased server. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20)
Returns the cost to purchase a server with the specified amount of *ram*.
Examples::
for (i = 1; i <= 20; i++) {
tprint(i + " -- " + getPurchasedServerCost(Math.pow(2, i)));
}
purchaseServer purchaseServer
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

@ -312,6 +312,18 @@ getCompanyFavor
This function will return the amount of favor you have at the specified company. 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. 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 checkFactionInvitations
----------------------- -----------------------
@ -387,6 +399,17 @@ getFactionFavor
This function returns the amount of favor you have for the specified faction. 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 createProgram
------------- -------------
@ -484,7 +507,7 @@ getOwnedAugmentations
getOwnedSourceFiles getOwnedSourceFiles
------------------- -------------------
..js:function:: getOwnedSourceFiles() .. js:function:: getOwnedSourceFiles()
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function. If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.

@ -14,16 +14,7 @@
<meta name="msapplication-TileColor" content="#000000"> <meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="dist/browserconfig.xml"> <meta name="msapplication-config" content="dist/browserconfig.xml">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="stylesheet" type="text/css" href="css/styles.css" /> <link rel="stylesheet" type="text/css" href="dist/engine.css" />
<link rel="stylesheet" type="text/css" href="css/terminal.css" />
<link rel="stylesheet" type="text/css" href="css/menupages.css" />
<link rel="stylesheet" type="text/css" href="css/workinprogress.css" />
<link rel="stylesheet" type="text/css" href="css/popupboxes.css" />
<link rel="stylesheet" type="text/css" href="css/interactivetutorial.css" />
<link rel="stylesheet" type="text/css" href="css/loader.css" />
<link rel="stylesheet" type="text/css" href="css/missions.css" />
<link rel="stylesheet" type="text/css" href="css/companymanagement.css" />
<link rel="stylesheet" type="text/css" href="css/bladeburner.css" />
<!-- Google Analytics --> <!-- Google Analytics -->
<script> <script>
@ -119,7 +110,7 @@
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" /> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
</div> </div>
<div id="javascript-editor"></div> <div id="javascript-editor"></div>
@ -169,7 +160,7 @@
<fieldset> <fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" /> <input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset> </fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
@ -196,7 +187,10 @@
<p id="active-scripts-text"> This page displays a list of all of your scripts that are currently running across every machine. It also <p id="active-scripts-text"> This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the servers on which provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p> they are running. </p>
<p id="active-scripts-total-prod"> Total online production rate: </p> <p id="active-scripts-total-prod">Total online production of
Active scripts: <span id="active-scripts-total-production-active">$0.000</span> / sec<br />
Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total">$0.000</span>
(<span id="active-scripts-total-prod-aug-avg">$0.000</span> / sec)</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;"> <ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;">
</ul> </ul>
</div> </div>
@ -220,7 +214,7 @@
<br> <br>
<div id="hacknet-nodes-money-multipliers-div"> <div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money"> <p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money" ></span><br /> <span>Money:</span><span id="hacknet-nodes-player-money"></span><br/>
<span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span> <span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span>
</p> </p>
<span id="hacknet-nodes-multipliers"> <span id="hacknet-nodes-multipliers">
@ -497,24 +491,24 @@
<!-- dev menu --> <!-- dev menu -->
<div id="dev-menu-container" class="generic-menupage-container"> <div id="dev-menu-container" class="generic-menupage-container">
<p id='dev-menu-text'>If you see this menu you can pretty much break the game. It's recommended that you use this menu only to setup a save file appropriate to test a new feature or bug fix.</p> <p id="dev-menu-text">If you see this menu you can pretty much break the game. It's recommended that you use this menu only to setup a save file appropriate to test a new feature or bug fix.</p>
<p id='dev-menu-text'>Generic</p> <p id="dev-menu-text">Generic</p>
<a id="dev-need-money" class="a-link-button">Add $1000t</a> <a id="dev-need-money" class="a-link-button">Add $1000t</a>
<a id="dev-need-ram" class="a-link-button">Double home RAM</a> <a id="dev-need-ram" class="a-link-button">Double home RAM</a>
<p id='dev-menu-text'>Augmentation related: </p> <p id="dev-menu-text">Augmentation related: </p>
<!-- gets populated with the list of all augments --> <!-- gets populated with the list of all augments -->
<select id="dev-menu-aug-dropdown" class="dropdown"></select> <select id="dev-menu-aug-dropdown" class="dropdown"></select>
<a id="dev-add-aug" class="a-link-button tooltip">Queue Augmentation<span class="tooltiptext">May require save + reload</span></a> <a id="dev-add-aug" class="a-link-button tooltip">Queue Augmentation<span class="tooltiptext">May require save + reload</span></a>
<input id="dev-sf-n" type="number" class="text-input" placeholder="SourceFile-N"><input id="dev-sf-lvl" type="number" class="text-input" placeholder="SourceFile-Lvl"><a id="dev-add-source-file" class="a-link-button tooltip"> Add/Remove source file <span class="tooltiptext">If Lvl == 0 the sf will be removed, calling it with another level will replace your current source file. You CAN set a source file higher than it's maximum level.</span></a> <input id="dev-sf-n" type="number" class="text-input" placeholder="SourceFile-N"><input id="dev-sf-lvl" type="number" class="text-input" placeholder="SourceFile-Lvl"><a id="dev-add-source-file" class="a-link-button tooltip"> Add/Remove source file <span class="tooltiptext">If Lvl == 0 the sf will be removed, calling it with another level will replace your current source file. You CAN set a source file higher than it's maximum level.</span></a>
<p id='dev-menu-text'>Faction related: </p> <p id="dev-menu-text">Faction related: </p>
<select id="dev-menu-faction-dropdown" class="dropdown"></select> <select id="dev-menu-faction-dropdown" class="dropdown"></select>
<a id="dev-add-faction" class="a-link-button tooltip">Receive invite<span class="tooltiptext">May require save + reload</span></a> <a id="dev-add-faction" class="a-link-button tooltip">Receive invite<span class="tooltiptext">May require save + reload</span></a>
<p id='dev-menu-text'>Program related: </p> <p id="dev-menu-text">Program related: </p>
<select id="dev-menu-connect-dropdown" class="dropdown"></select> <select id="dev-menu-connect-dropdown" class="dropdown"></select>
<a id="dev-connect" class="a-link-button tooltip">Connect<span class="tooltiptext">Connect to the target server.</span></a> <a id="dev-connect" class="a-link-button tooltip">Connect<span class="tooltiptext">Connect to the target server.</span></a>
@ -523,12 +517,12 @@
<a id="dev-bit-flume" class="a-link-button tooltip">Trigger BitFlume<span class="tooltiptext">Quick escape to change BN, does not give SFs</span></a> <a id="dev-bit-flume" class="a-link-button tooltip">Trigger BitFlume<span class="tooltiptext">Quick escape to change BN, does not give SFs</span></a>
<p id='dev-menu-text'>Server related: </p> <p id="dev-menu-text">Server related: </p>
<a id="dev-open-all" class="a-link-button tooltip">NUKE + ports all servers<span class="tooltiptext">Opens all ports, nukes all servers, gains root access to everything (still need the appropriate hacking level)</span></a> <a id="dev-open-all" class="a-link-button tooltip">NUKE + ports all servers<span class="tooltiptext">Opens all ports, nukes all servers, gains root access to everything (still need the appropriate hacking level)</span></a>
<a id="dev-min-security" class="a-link-button tooltip">minimize all servers security<span class="tooltiptext">All servers security will be set to their minimum security</span></a> <a id="dev-min-security" class="a-link-button tooltip">minimize all servers security<span class="tooltiptext">All servers security will be set to their minimum security</span></a>
<a id="dev-max-money" class="a-link-button tooltip">maximize all servers money<span class="tooltiptext">Set all servers available money to maximum for that server</span></a> <a id="dev-max-money" class="a-link-button tooltip">maximize all servers money<span class="tooltiptext">Set all servers available money to maximum for that server</span></a>
<p id='dev-menu-text'>Exp/stats related: </p> <p id="dev-menu-text">Exp/stats related: </p>
<input id="dev-hacking-exp" type="number" class="text-input" placeholder="+exp/-exp (int)"> <input id="dev-hacking-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
<a id="dev-add-hacking" class="a-link-button tooltip">add hacking exp<span class="tooltiptext">Add that many hacking experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a> <a id="dev-add-hacking" class="a-link-button tooltip">add hacking exp<span class="tooltiptext">Add that many hacking experience point, use negative numbers to remove, don't worry about going under 0 exp</span></a>
<input id="dev-strength-exp" type="number" class="text-input" placeholder="+exp/-exp (int)"> <input id="dev-strength-exp" type="number" class="text-input" placeholder="+exp/-exp (int)">
@ -650,7 +644,7 @@
<a id="location-slums-heist" class="a-link-button tooltip"> Heist </a> <a id="location-slums-heist" class="a-link-button tooltip"> Heist </a>
<!-- City Hall --> <!-- City Hall -->
<a id="location-cityhall-create-corporation" class='a-link-button'>Create a Corporation</a> <a id="location-cityhall-create-corporation" class="a-link-button">Create a Corporation</a>
<!-- Bladeburner@NSA --> <!-- Bladeburner@NSA -->
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a> <a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
@ -696,7 +690,7 @@
you 'reset' by installing Augmentations. you 'reset' by installing Augmentations.
</p> </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-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> <a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
<p id="stock-market-commission"> </p> <p id="stock-market-commission"> </p>
<a id="stock-market-mode" class="a-link-button tooltip"></a> <a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a> <a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
@ -728,7 +722,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container"> <div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content"> <div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p> <p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" /> <input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"/>
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span> <span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span> <span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
</div> </div>
@ -790,9 +784,41 @@
<!-- Character Overview Screen --> <!-- Character Overview Screen -->
<div id="character-overview-wrapper"> <div id="character-overview-wrapper">
<div id="character-overview-container"> <div id="character-overview-container">
<p id="character-overview-text"> </p> <div id="character-overview-text">
<span id="character-overview-save-button"> Save Game </span> <table>
<span id="character-overview-options-button"> Options </span> <tr id="character-hp-wrapper">
<td>Hp:</td><td id="character-hp-text" class="character-stat-cell"></td>
</tr>
<tr id="character-money-wrapper">
<td>Money:&nbsp;</td><td id="character-money-text" class="character-stat-cell"></td>
</tr>
<tr id="character-hack-wrapper">
<td>Hack:&nbsp;</td><td id="character-hack-text" class="character-stat-cell"></td>
</tr>
<tr id="character-str-wrapper">
<td>Str:&nbsp;</td><td id="character-str-text" class="character-stat-cell"></td>
</tr>
<tr id="character-def-wrapper">
<td>Def:&nbsp;</td><td id="character-def-text" class="character-stat-cell"></td>
</tr>
<tr id="character-dex-wrapper">
<td>Dex:&nbsp;</td><td id="character-dex-text" class="character-stat-cell"></td>
</tr>
<tr id="character-agi-wrapper">
<td>Agi:&nbsp;</td><td id="character-agi-text" class="character-stat-cell"></td>
</tr>
<tr id="character-cha-wrapper">
<td>Cha:&nbsp;</td><td id="character-cha-text" class="character-stat-cell"></td>
</tr>
<tr id="character-int-wrapper">
<td>Int:&nbsp;</td><td id="character-int-text" class="character-stat-cell"></td>
</tr>
<table>
</div>
<div class="character-quick-options">
<span id="character-overview-save-button"> Save Game </span>
<span id="character-overview-options-button"> Options </span>
</div>
</div> </div>
</div> </div>
@ -818,7 +844,7 @@
</span> </span>
</label> </label>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25" /> <input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -832,7 +858,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" /> <input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50"/>
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -846,7 +872,7 @@
</span> </span>
</label> </label>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" /> <input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50"/>
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em> <em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>
@ -858,7 +884,7 @@
</span> </span>
</label> </label>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" /> <input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60"/>
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em> <em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset> </fieldset>

@ -72,6 +72,7 @@ let NetscriptFunctions =
"serverExists|fileExists|isRunning|" + "serverExists|fileExists|isRunning|" +
"deleteServer|getPurchasedServers|" + "deleteServer|getPurchasedServers|" +
"getPurchasedServerLimit|getPurchasedServerMaxRam|" + "getPurchasedServerLimit|getPurchasedServerMaxRam|" +
"getPurchasedServerCost|" +
"purchaseServer|round|write|read|peek|clear|rm|getPortHandle|" + "purchaseServer|round|write|read|peek|clear|rm|getPortHandle|" +
"scriptRunning|scriptKill|getScriptName|getScriptRam|" + "scriptRunning|scriptKill|getScriptName|getScriptRam|" +
"getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" + "getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" +
@ -80,6 +81,7 @@ let NetscriptFunctions =
"gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" + "gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" +
"getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" + "getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" +
"getCompanyFavor|stopAction|getFactionFavor|" + "getCompanyFavor|stopAction|getFactionFavor|" +
"getFavorToDonate|getFactionFavorGain|getCompanyFavorGain|" +
"checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" + "checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" +
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" + "createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" + "getOwnedSourceFiles|getAugmentationsFromFaction|" +

1918
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -52,11 +52,14 @@
"less": "^3.0.4", "less": "^3.0.4",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"lodash": "^4.17.10", "lodash": "^4.17.10",
"mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.0.0", "mocha-lcov-reporter": "^1.0.0",
"node-sass": "^4.9.2",
"nsp": "^3.2.1", "nsp": "^3.2.1",
"raw-loader": "~0.5.0", "raw-loader": "~0.5.0",
"sass-loader": "^7.0.3",
"script-loader": "~0.7.0", "script-loader": "~0.7.0",
"should": "^11.1.1", "should": "^11.1.1",
"simple-git": "^1.96.0", "simple-git": "^1.96.0",

@ -1,6 +1,5 @@
import {Engine} from "./engine"; import {Engine} from "./engine";
import {workerScripts, import {workerScripts,
addWorkerScript,
killWorkerScript} from "./NetscriptWorker"; killWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import {getServer} from "./Server"; import {getServer} from "./Server";
@ -8,12 +7,15 @@ import {dialogBoxCreate} from "../utils/DialogBox";
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement"; import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
import {arrayToString} from "../utils/helpers/arrayToString"; import {arrayToString} from "../utils/helpers/arrayToString";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
import {exceptionAlert} from "../utils/helpers/exceptionAlert"; import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getElementById} from "../utils/uiHelpers/getElementById";
import {logBoxCreate} from "../utils/LogBox"; import {logBoxCreate} from "../utils/LogBox";
import numeral from "numeral/min/numeral.min"; import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../utils/StringHelperFunctions";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement"; import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElement} from "../utils/uiHelpers/removeElement"; import {removeElement} from "../utils/uiHelpers/removeElement";
import {roundToTwo} from "../utils/helpers/roundToTwo";
/* { /* {
* serverName: { * serverName: {
@ -28,12 +30,39 @@ import {removeElement} from "../utils/uiHelpers/removeElement";
let ActiveScriptsUI = {}; let ActiveScriptsUI = {};
let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements
const getHeaderHtml = (server) => {
// TODO: calculate the longest hostname length rather than hard coding it
const longestHostnameLength = 18;
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
const barOptions = {
progress: server.ramUsed / server.maxRam,
totalTicks: 30
};
return `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, '&nbsp;');
};
const updateHeaderHtml = (server) => {
const accordion = ActiveScriptsUI[server.hostname];
if (accordion === null || accordion === undefined) {
return;
}
// convert it to a string, as that's how it's stored it will come out of the data attributes
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
accordion.header.dataset.ramPercentage = ramPercentage;
accordion.header.innerHTML = getHeaderHtml(server);
}
}
function createActiveScriptsServerPanel(server) { function createActiveScriptsServerPanel(server) {
let hostname = server.hostname; let hostname = server.hostname;
var activeScriptsList = document.getElementById("active-scripts-list"); var activeScriptsList = document.getElementById("active-scripts-list");
let res = createAccordionElement({hdrText:hostname}); let res = createAccordionElement({
hdrText: getHeaderHtml(server)
});
let li = res[0]; let li = res[0];
var hdr = res[1]; var hdr = res[1];
let panel = res[2]; let panel = res[2];
@ -116,20 +145,27 @@ function addActiveScriptsItem(workerscript) {
"Args: " + arrayToString(workerscript.args) "Args: " + arrayToString(workerscript.args)
})); }));
var panelText = createElement("p", { var panelText = createElement("p", {
innerText:"Loading...", fontSize:"14px", innerText: "Loading...",
fontSize: "14px",
}); });
panel.appendChild(panelText); panel.appendChild(panelText);
panel.appendChild(createElement("br")); panel.appendChild(createElement("br"));
panel.appendChild(createElement("span", { panel.appendChild(createElement("span", {
innerText:"Log", class:"active-scripts-button", margin:"4px", padding:"4px", innerText: "Log",
clickListener:()=>{ class: "active-scripts-button",
margin: "4px",
padding: "4px",
clickListener: () => {
logBoxCreate(workerscript.scriptRef); logBoxCreate(workerscript.scriptRef);
return false; return false;
} }
})); }));
panel.appendChild(createElement("span", { panel.appendChild(createElement("span", {
innerText:"Kill Script", class:"active-scripts-button", margin:"4px", padding:"4px", innerText: "Kill Script",
clickListener:()=>{ class: "active-scripts-button",
margin: "4px",
padding: "4px",
clickListener: () => {
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.scriptRef.server); killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.scriptRef.server);
dialogBoxCreate("Killing script, may take a few minutes to complete..."); dialogBoxCreate("Killing script, may take a few minutes to complete...");
return false; return false;
@ -205,11 +241,10 @@ function updateActiveScriptsItems(maxTasks=150) {
exceptionAlert(e); exceptionAlert(e);
} }
} }
document.getElementById("active-scripts-total-prod").innerHTML =
"Total online production of Active Scripts: " + numeral(total).format('$0.000a') + " / sec<br>" + getElementById("active-scripts-total-production-active").innerText = numeral(total).format('$0.000a');
"Total online production since last Aug installation: " + getElementById("active-scripts-total-prod-aug-total").innerText = numeral(Player.scriptProdSinceLastAug).format('$0.000a');
numeral(Player.scriptProdSinceLastAug).format('$0.000a') + " (" + getElementById("active-scripts-total-prod-aug-avg").innerText = numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a');
numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a') + " / sec)";
return total; return total;
} }
@ -225,6 +260,8 @@ function updateActiveScriptsItemContent(workerscript) {
return; //Hasn't been created yet. We'll skip it return; //Hasn't been created yet. We'll skip it
} }
updateHeaderHtml(server);
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name]; var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) { for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i])); itemNameArray.push(String(workerscript.args[i]));
@ -252,6 +289,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
return; return;
} }
updateHeaderHtml(server);
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime; var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
//Only update if the item is visible //Only update if the item is visible

File diff suppressed because it is too large Load Diff

@ -357,8 +357,8 @@ function initBitNodeMultipliers() {
sf12Lvl = Player.sourceFiles[i].lvl; sf12Lvl = Player.sourceFiles[i].lvl;
} }
} }
var inc = Math.pow(1.01, sf12Lvl); var inc = Math.pow(1.02, sf12Lvl);
var dec = Math.pow(0.99, sf12Lvl); var dec = 1/inc;
BitNodeMultipliers.HackingLevelMultiplier = dec; BitNodeMultipliers.HackingLevelMultiplier = dec;
BitNodeMultipliers.ServerMaxMoney = dec; BitNodeMultipliers.ServerMaxMoney = dec;

@ -1094,6 +1094,7 @@ Bladeburner.prototype.startAction = function(actionId) {
throw new Error ("Failed to get Operation Object for: " + actionId.name); throw new Error ("Failed to get Operation Object for: " + actionId.name);
} }
if (action.count < 1) {return this.resetAction();} if (action.count < 1) {return this.resetAction();}
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) {return this.resetAction();}
this.actionTimeToComplete = action.getActionTime(this); this.actionTimeToComplete = action.getActionTime(this);
} catch(e) { } catch(e) {
exceptionAlert(e); exceptionAlert(e);
@ -1420,13 +1421,6 @@ Bladeburner.prototype.completeOperation = function(success) {
} }
var city = this.getCurrentCity(); var city = this.getCurrentCity();
if (this.logging.ops) {
if (success) {
this.log(action.name + " completed successfully! ")
} else {
}
}
switch (action.name) { switch (action.name) {
case "Investigation": case "Investigation":
if (success) { if (success) {
@ -1663,6 +1657,7 @@ Bladeburner.prototype.initializeDomElementRefs = function() {
overviewEstComms: null, overviewEstComms: null,
overviewChaos: null, overviewChaos: null,
overviewSkillPoints: null, overviewSkillPoints: null,
overviewBonusTime: null,
overviewAugSuccessMult: null, overviewAugSuccessMult: null,
overviewAugMaxStaminaMult: null, overviewAugMaxStaminaMult: null,
overviewAugStaminaGainMult: null, overviewAugStaminaGainMult: null,
@ -1826,8 +1821,15 @@ Bladeburner.prototype.createOverviewContent = function() {
"Having too high of a chaos level can make contracts and operations harder." "Having too high of a chaos level can make contracts and operations harder."
}); });
DomElems.overviewBonusTime = createElement("p", {
innerText: "Bonus time: ",
display: "inline-block",
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"}); DomElems.overviewSkillPoints = createElement("p", {display:"block"});
DomElems.overviewAugSuccessMult = createElement("p", {display:"block"}); DomElems.overviewAugSuccessMult = createElement("p", {display:"block"});
DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"}); DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"});
DomElems.overviewAugStaminaGainMult = createElement("p", {display:"block"}); DomElems.overviewAugStaminaGainMult = createElement("p", {display:"block"});
@ -1846,6 +1848,7 @@ Bladeburner.prototype.createOverviewContent = function() {
appendLineBreaks(DomElems.overviewDiv, 1); appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewChaos); DomElems.overviewDiv.appendChild(DomElems.overviewChaos);
appendLineBreaks(DomElems.overviewDiv, 2); appendLineBreaks(DomElems.overviewDiv, 2);
DomElems.overviewDiv.appendChild(DomElems.overviewBonusTime);
DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints); DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints);
appendLineBreaks(DomElems.overviewDiv, 1); appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewAugSuccessMult); DomElems.overviewDiv.appendChild(DomElems.overviewAugSuccessMult);
@ -2206,6 +2209,7 @@ Bladeburner.prototype.updateOverviewContent = function() {
DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0); DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0);
DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos); DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0); DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0);
DomElems.overviewBonusTime.childNodes[0].nodeValue = "Bonus time: " + this.storedCycles/CyclesPerSecond;
DomElems.overviewAugSuccessMult.innerText = "Aug. Success Chance Mult: " + formatNumber(Player.bladeburner_success_chance_mult*100, 1) + "%"; DomElems.overviewAugSuccessMult.innerText = "Aug. Success Chance Mult: " + formatNumber(Player.bladeburner_success_chance_mult*100, 1) + "%";
DomElems.overviewAugMaxStaminaMult.innerText = "Aug. Max Stamina Mult: " + formatNumber(Player.bladeburner_max_stamina_mult*100, 1) + "%"; DomElems.overviewAugMaxStaminaMult.innerText = "Aug. Max Stamina Mult: " + formatNumber(Player.bladeburner_max_stamina_mult*100, 1) + "%";
DomElems.overviewAugStaminaGainMult.innerText = "Aug. Stamina Gain Mult: " + formatNumber(Player.bladeburner_stamina_gain_mult*100, 1) + "%"; DomElems.overviewAugStaminaGainMult.innerText = "Aug. Stamina Gain Mult: " + formatNumber(Player.bladeburner_stamina_gain_mult*100, 1) + "%";

58
src/CharacterOverview.js Normal file

@ -0,0 +1,58 @@
import {Player} from "./Player";
import numeral from "numeral/min/numeral.min";
function CharacterOverview() {
this.hp = document.getElementById("character-hp-text");
this.money = document.getElementById("character-money-text");
this.hack = document.getElementById("character-hack-text");
this.str = document.getElementById("character-str-text");
this.def = document.getElementById("character-def-text");
this.dex = document.getElementById("character-dex-text");
this.agi = document.getElementById("character-agi-text");
this.cha = document.getElementById("character-cha-text");
this.int = document.getElementById("character-int-text");
this.intWrapper = document.getElementById("character-int-wrapper");
this.repaintElem = document.getElementById("character-overview-text");
}
CharacterOverview.prototype.repaint = function() {
// this is an arbitrary function we can call to trigger a repaint.
this.repaintElem.getClientRects();
}
CharacterOverview.prototype.update = function() {
if (Player.hp == null) {Player.hp = Player.max_hp;}
const replaceAndChanged = function(elem, text) {
if(elem.textContent === text) {
return false;
}
elem.textContent = text;
return true;
}
let changed = false;
changed = replaceAndChanged(this.hp, Player.hp + " / " + Player.max_hp) || changed;
changed = replaceAndChanged(this.money, numeral(Player.money.toNumber()).format('($0.000a)')) || changed;
changed = replaceAndChanged(this.hack, (Player.hacking_skill).toLocaleString()) || changed;
changed = replaceAndChanged(this.str, (Player.strength).toLocaleString()) || changed;
changed = replaceAndChanged(this.def, (Player.defense).toLocaleString()) || changed;
changed = replaceAndChanged(this.dex, (Player.dexterity).toLocaleString()) || changed;
changed = replaceAndChanged(this.agi, (Player.agility).toLocaleString()) || changed;
changed = replaceAndChanged(this.cha, (Player.charisma).toLocaleString()) || changed;
changed = replaceAndChanged(this.int, (Player.intelligence).toLocaleString()) || changed;
// handle int appearing
const int = this.intWrapper;
const old = int.style.display;
const now = Player.intelligence >= 1 ? "" : "none";
if(old !== now) {
int.style.display = now;
changed = true;
}
// recalculate box size if something changed
if(changed) this.repaint();
}
export {CharacterOverview};

@ -4736,7 +4736,7 @@ Corporation.prototype.updateDivisionContent = function(division) {
var totalAdvertisingFac = advertisingFactors[0]; var totalAdvertisingFac = advertisingFactors[0];
advertisingInfo = advertisingInfo =
"<p class='tooltip'>Advertising Multiplier: x" + formatNumber(totalAdvertisingFac, 3) + "<p class='tooltip'>Advertising Multiplier: x" + formatNumber(totalAdvertisingFac, 3) +
"<span class='tooltiptext' style='font-size:12px'>Total multiplier for this industry's sales due to its awareness and popularity<br>" + "<span class='tooltiptext cmpy-mgmt-advertising-info'>Total multiplier for this industry's sales due to its awareness and popularity<br>" +
"Awareness Bonus: x" + formatNumber(Math.pow(awarenessFac, 0.85), 3) + "<br>" + "Awareness Bonus: x" + formatNumber(Math.pow(awarenessFac, 0.85), 3) + "<br>" +
"Popularity Bonus: x" + formatNumber(Math.pow(popularityFac, 0.85), 3) + "<br>" + "Popularity Bonus: x" + formatNumber(Math.pow(popularityFac, 0.85), 3) + "<br>" +
"Ratio Multiplier: x" + formatNumber(Math.pow(ratioFac, 0.85), 3) + "</span></p><br>" "Ratio Multiplier: x" + formatNumber(Math.pow(ratioFac, 0.85), 3) + "</span></p><br>"

@ -1,5 +1,5 @@
let CONSTANTS = { let CONSTANTS = {
Version: "0.40.0", Version: "0.40.1",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience //Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then //and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -31,6 +31,7 @@ let CONSTANTS = {
HacknetNodeMaxCores: 16, HacknetNodeMaxCores: 16,
/* Faction and Company favor */ /* Faction and Company favor */
BaseFavorToDonate: 150,
FactionReputationToFavorBase: 500, FactionReputationToFavorBase: 500,
FactionReputationToFavorMult: 1.02, FactionReputationToFavorMult: 1.02,
CompanyReputationToFavorBase: 500, CompanyReputationToFavorBase: 500,
@ -70,6 +71,7 @@ let CONSTANTS = {
ScriptHNUpgCoreRamCost: 0.8, ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0, ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5, ScriptBuySellStockRamCost: 2.5,
ScriptGetPurchaseServerRamCost: 0.25,
ScriptPurchaseServerRamCost: 2.25, ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05, ScriptGetPurchasedServerLimit: 0.05,
ScriptGetPurchasedServerMaxRam: 0.05, ScriptGetPurchasedServerMaxRam: 0.05,
@ -78,6 +80,7 @@ let CONSTANTS = {
ScriptArbScriptRamCost: 1.0, //Functions that apply to all scripts regardless of args ScriptArbScriptRamCost: 1.0, //Functions that apply to all scripts regardless of args
ScriptGetScriptRamCost: 0.1, ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05, ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptSingularityFn1RamCost: 1, ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2, ScriptSingularityFn2RamCost: 2,
@ -488,33 +491,21 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.40.0<br>" + "v0.40.1 - Community Update<br>" +
"* <b>WARNING: This update makes some significant changes to Netscript and therefore you may need to " + "* Added getPurchasedServerCost() Netscript function (by kopelli)<br>" +
"make some changes to your scripts. See <a href='https://www.reddit.com/r/Bitburner/comments/9252j4/psa_netscript_10_changes_in_next_version_v0400/' target='_blank'> " + "* Added getFavorToDonate() Netscript function (by hydroflame)<br>" +
"this post for details</a></b><br>" + "* Added getFactionFavorGain() and getCompanyFavorGain() Singularity functions (by hydroflame)<br>" +
"* Netscript 1.0 (NS1) now uses a fully-fledged ES5 JavaScript Interpreter. This means many new features are now available in NS1, and this also fixes several bugs." + "* Accumulated 'bonus' time in Bladeburner is now displayed in the UI (by hydroflame)<br>" +
" However this also means any ES6+ features are no longer supported in NS1 <br>" + "* The Red Pill can now be purchased with negative money (since its supposed to be free) (by hydroflame)<br>" +
"* When a server is hacked with a very large number of threads and left with no money, the server's security level " + "* 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>" +
"now only increases by however many threads were needed to drain the server. For example, if you hack a server with " + "* Terminal now supports semicolon usage (end of command). This allows chaining multiple Terminal commands (by hydroflame)<br>" +
"5000 threads but it only needed 2000 threads to deplete the server's money, then the server's security will only increase " + "* Bladeburner Raid operations can no longer be performed if your estimate of Synthoid communities is zero (by hydroflame)<br>" +
"as if you had hacked it with 2000 threads (change by hydroflame)<br>" + "* The difficulty of BN-12 now scales faster (by hydroflame)<br>" +
"* Added getCurrentAction() to Bladeburner API<br>" + "* Active Scripts UI now shows a RAM Usage bar for each server (by kopelli)<br>" +
"* Added a variety of functions to Bladeburner API that deal with action levels (change by hydroflame)<br>" + "* Bug Fix: Corrected terminal timestamp format (by kopelli)<br>" +
"* Added getPurchasedServerLimit() and getPurchasedServerMaxRam() functions to Netscript (change by hydroflame & kopelli)<br>" + "* Bug Fix: NetscriptJS scripts should now die properly if they don't have a 'main' function (by hydroflame)<br>" +
"* Added getOwnedSourceFiles() Singularity function (by hydroflame)<br>" + "* Bug Fix: write(), read(), and tryWrite() Netscript functions should now work properly for writing Arrays/objects to Netscript Ports<br>" +
"* Completely re-designed the Hacknet Node API<br>" + "* Various minor UI/QOL fixes by hydroflame, kopelli, and Kline-"
"* getSkillLevel() in Bladeburner API now returns an error if no argument is passed in (as opposed to an object with all skill levels). This may break scripts<br>" +
"* Minimum Netscript execution time reduced from 15ms to 10ms (configurable in Options)<br>" +
"* Company reputation needed to get invited to Megacorporation factions decreased from 250k to 200k<br>" +
"* HP is now reset (restored) when Augmenting<br>" +
"* Source-File 6 now increases both the level and experience gain of all combat stats (it was only experience gain previously)<br>" +
"* Reverted a previous change for Source-File 12. It's benefits are now multiplicative rather than additive<br>" +
"* Starting Infiltration security level for almost every location decreased by ~10%<br>" +
"* Changed 'fl1ght.exe' message when its listed conditions are fulfilled (by hydroflame)<br>" +
"* The 'Save Game' button in the top-right overview panel now flashes red if autosave is disabled<br>" +
"* Bug Fix: Infiltration buttons can no longer be clicked through NetscriptJS<br>" +
"* Bug Fix: Bladeburner 'Overclock' skill can no longer be leveled above max level through the API (by hydroflame)<br>" +
"* Bug Fix: Healthcare division in Bladeburner should no longer cause game to crash"
} }

@ -55,7 +55,7 @@ Faction.prototype.gainFavor = function() {
this.rolloverRep = res[1]; this.rolloverRep = res[1];
} }
//Returns an array with [How much favor would be gained, how much favor would be left over] //Returns an array with [How much favor would be gained, how much rep would be left over]
Faction.prototype.getFavorGain = function() { Faction.prototype.getFavorGain = function() {
if (this.favor == null || this.favor == undefined) {this.favor = 0;} if (this.favor == null || this.favor == undefined) {this.favor = 0;}
if (this.rolloverRep == null || this.rolloverRep == undefined) {this.rolloverRep = 0;} if (this.rolloverRep == null || this.rolloverRep == undefined) {this.rolloverRep = 0;}
@ -432,7 +432,7 @@ function displayFactionContent(factionName) {
throw new Error("Not a member of this faction, cannot display faction information"); throw new Error("Not a member of this faction, cannot display faction information");
} }
donateDiv.style.display = faction.favor >= (150 * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none"; donateDiv.style.display = faction.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none";
hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none"; hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none";
hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none"; hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none";
@ -669,7 +669,7 @@ function purchaseAugmentation(aug, fac, sing=false) {
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " + var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
"purchase this one."; "purchase this one.";
if (sing) {return txt;} else {dialogBoxCreate(txt);} if (sing) {return txt;} else {dialogBoxCreate(txt);}
} else if (Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) { } else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let txt = "You don't have enough money to purchase " + aug.name; let txt = "You don't have enough money to purchase " + aug.name;
if (sing) {return txt;} if (sing) {return txt;}
dialogBoxCreate(txt); dialogBoxCreate(txt);
@ -677,7 +677,7 @@ function purchaseAugmentation(aug, fac, sing=false) {
let txt = "You don't have enough faction reputation to purchase " + aug.name; let txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {return txt;} if (sing) {return txt;}
dialogBoxCreate(txt); dialogBoxCreate(txt);
} else if (Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) { } else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
if (Player.firstAugPurchased === false) { if (Player.firstAugPurchased === false) {
Player.firstAugPurchased = true; Player.firstAugPurchased = true;
document.getElementById("augmentations-tab").style.display = "list-item"; document.getElementById("augmentations-tab").style.display = "list-item";

@ -178,7 +178,25 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Index specified for Hacknet Node is out-of-bounds: " + i); throw makeRuntimeRejectMsg(workerScript, "Index specified for Hacknet Node is out-of-bounds: " + i);
} }
return Player.hacknetNodes[i]; return Player.hacknetNodes[i];
} };
/**
* @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
*/
const getPurchaseServerRamCostGuard = (ram) => {
const guardedRam = Math.round(ram);
if (isNaN(guardedRam) || !isPowerOfTwo(guardedRam)) {
throw Error("failed due to invalid ram argument. Must be numeric and a power of 2");
}
if (guardedRam > CONSTANTS.PurchasedServerMaxRam) {
throw Error("failed because specified RAM was too high. Maximum RAM on a purchased server is " + CONSTANTS.PurchasedServerMaxRam + "GB");
}
return guardedRam * CONSTANTS.BaseCostFor1GBOfRamServer;
};
return { return {
hacknet : { hacknet : {
@ -1597,6 +1615,22 @@ function NetscriptFunctions(workerScript) {
return CONSTANTS.PurchasedServerMaxRam; return CONSTANTS.PurchasedServerMaxRam;
}, },
getPurchasedServerCost: function(ram) {
if (workerScript.checkingRam) {
return updateStaticRam("getPurchasedServerCost", CONSTANTS.ScriptGetPurchaseServerRamCost);
}
updateDynamicRam("getPurchasedServerCost", CONSTANTS.ScriptGetPurchaseServerRamCost);
let cost = 0;
try {
cost = getPurchaseServerRamCostGuard(ram);
} catch (e) {
workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' " + e.message);
return "";
}
return cost;
},
purchaseServer : function(hostname, ram) { purchaseServer : function(hostname, ram) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {
return updateStaticRam("purchaseServer", CONSTANTS.ScriptPurchaseServerRamCost); return updateStaticRam("purchaseServer", CONSTANTS.ScriptPurchaseServerRamCost);
@ -1615,18 +1649,14 @@ function NetscriptFunctions(workerScript) {
return ""; return "";
} }
ram = Math.round(ram); let cost = 0;
if (isNaN(ram) || !isPowerOfTwo(ram)) { try {
workerScript.scriptRef.log("ERROR: purchaseServer() failed due to invalid ram argument. Must be numeric and a power of 2"); cost = getPurchaseServerRamCostGuard(ram);
} catch (e) {
workerScript.scriptRef.log("ERROR: 'purchaseServer()' " + e.message);
return ""; return "";
} }
if (ram > CONSTANTS.PurchasedServerMaxRam) {
workerScript.scriptRef.log("ERROR: purchasedServer() failed because specified RAM was too high. Maximum RAM on a purchased server is " + CONSTANTS.PurchasedServerMaxRam + "GB");
return "";
}
var cost = ram * CONSTANTS.BaseCostFor1GBOfRamServer;
if (Player.money.lt(cost)) { if (Player.money.lt(cost)) {
workerScript.scriptRef.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2)); workerScript.scriptRef.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2));
return ""; return "";
@ -2112,6 +2142,13 @@ function NetscriptFunctions(workerScript) {
yesNoBoxCreate(txt); yesNoBoxCreate(txt);
}); });
}, },
getFavorToDonate: function() {
if (workerScript.checkingRam) {
return updateStaticRam("getFavorToDonate", CONSTANTS.ScriptGetFavorToDonate);
}
updateDynamicRam("getFavorToDonate", CONSTANTS.ScriptGetFavorToDonate);
return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
},
/* Singularity Functions */ /* Singularity Functions */
universityCourse : function(universityName, className) { universityCourse : function(universityName, className) {
@ -2747,6 +2784,27 @@ function NetscriptFunctions(workerScript) {
} }
return company.favor; return company.favor;
}, },
getCompanyFavorGain : function(companyName) {
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost / 4;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) {
return updateStaticRam("getCompanyFavorGain", ramCost);
}
updateDynamicRam("getCompanyFavorGain", ramCost);
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getCompanyFavorGain(). It is a Singularity Function and requires SourceFile-4 (level 2) to run.");
return -1;
}
}
var company = Companies[companyName];
if (company == null || !(company instanceof Company)) {
workerScript.scriptRef.log("ERROR: Invalid companyName passed into getCompanyFavorGain(): " + companyName);
return -1;
}
return company.getFavorGain()[0];
},
checkFactionInvitations : function() { checkFactionInvitations : function() {
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;} if (Player.bitNodeN !== 4) {ramCost *= 8;}
@ -2944,6 +3002,27 @@ function NetscriptFunctions(workerScript) {
return Factions[name].favor; return Factions[name].favor;
}, },
getFactionFavorGain: function(name){
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) {
return updateStaticRam("getFactionFavorGain", ramCost);
}
updateDynamicRam("getFactionFavorGain", ramCost);
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 2)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getFactionFavorGain(). It is a Singularity Function and requires SourceFile-4 (level 2) to run.");
return -1;
}
}
if (!factionExists(name)) {
workerScript.scriptRef.log("ERROR: Faction specified in getFactionFavorGain() does not exist.");
return -1;
}
return Factions[name].getFavorGain()[0];
},
createProgram : function(name) { createProgram : function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;} if (Player.bitNodeN !== 4) {ramCost *= 8;}

@ -38,8 +38,7 @@ export async function executeJSScript(scripts = [], workerScript) {
// TODO: putting await in a non-async function yields unhelpful // TODO: putting await in a non-async function yields unhelpful
// "SyntaxError: unexpected reserved word" with no line number information. // "SyntaxError: unexpected reserved word" with no line number information.
if (!loadedModule.main) { if (!loadedModule.main) {
throw makeRuntimeRejectMsg(script.filename + throw makeRuntimeRejectMsg(workerScript, `${script.filename} cannot be run because it does not have a main function.`);
" did not have a main function, cannot run it.");
} }
return loadedModule.main(ns); return loadedModule.main(ns);
} finally { } finally {

@ -199,7 +199,8 @@ function startNetscript1Script(workerScript) {
}); });
} }
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper)); 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 tempWrapper = function() {
let fnArgs = []; let fnArgs = [];

@ -435,7 +435,7 @@ PlayerObject.prototype.calculateSkill = function(exp, mult=1) {
} }
PlayerObject.prototype.updateSkillLevels = function() { PlayerObject.prototype.updateSkillLevels = function() {
this.hacking_skill = Math.max(1, Math.floor(this.calculateSkill(this.hacking_exp, this.hacking_mult) * BitNodeMultipliers.HackingLevelMultiplier)); this.hacking_skill = Math.max(1, Math.floor(this.calculateSkill(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier)));
this.strength = this.calculateSkill(this.strength_exp, this.strength_mult); this.strength = this.calculateSkill(this.strength_exp, this.strength_mult);
this.defense = this.calculateSkill(this.defense_exp, this.defense_mult); this.defense = this.calculateSkill(this.defense_exp, this.defense_mult);
this.dexterity = this.calculateSkill(this.dexterity_exp, this.dexterity_mult); this.dexterity = this.calculateSkill(this.dexterity_exp, this.dexterity_mult);

@ -24,12 +24,12 @@ import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial"; iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator"; import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions"; import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript, killWorkerScript, import {addWorkerScript,
WorkerScript} from "./NetscriptWorker"; WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import {AllServers, processSingleServerGrowth} from "./Server"; import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings";
import {post, Terminal} from "./Terminal"; import {post} from "./Terminal";
import {TextFile} from "./TextFile"; import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn"; import {parse, Node} from "../utils/acorn";
@ -39,6 +39,7 @@ import {Reviver, Generic_toJSON,
import {compareArrays} from "../utils/helpers/compareArrays"; import {compareArrays} from "../utils/helpers/compareArrays";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../utils/StringHelperFunctions";
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo"; import {roundToTwo} from "../utils/helpers/roundToTwo";
var keybindings = { var keybindings = {
@ -962,7 +963,7 @@ RunningScript.prototype.log = function(txt) {
} }
let logEntry = txt; let logEntry = txt;
if (FconfSettings.ENABLE_TIMESTAMPS) { if (FconfSettings.ENABLE_TIMESTAMPS) {
logEntry = "[" + Terminal.getTimestamp() + "] " + logEntry; logEntry = "[" + getTimestamp() + "] " + logEntry;
} }
this.logs.push(logEntry); this.logs.push(logEntry);
this.logUpd = true; this.logUpd = true;

@ -38,6 +38,7 @@ import {containsAllStrings, longestCommonStart,
import {addOffset} from "../utils/helpers/addOffset"; import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString"; import {isString} from "../utils/helpers/isString";
import {arrayToString} from "../utils/helpers/arrayToString"; import {arrayToString} from "../utils/helpers/arrayToString";
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {logBoxCreate} from "../utils/LogBox"; import {logBoxCreate} from "../utils/LogBox";
import {yesNoBoxCreate, import {yesNoBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetYesButton,
@ -112,16 +113,20 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) { if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val(); var command = $('input[class=terminal-input]').val();
if (command.length > 0) { post(
post( "[" +
"[" + (FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
(FconfSettings.ENABLE_TIMESTAMPS ? Terminal.getTimestamp() + " " : "") + Player.getCurrentServer().hostname +
Player.getCurrentServer().hostname + " ~]> " + command
" ~]> " + command );
);
if (command.length > 0) {
Terminal.resetTerminalInput(); //Clear input first 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]);
}
} }
} }
@ -197,6 +202,12 @@ $(document).keydown(function(event) {
if (terminalInput == null) {return;} if (terminalInput == null) {return;}
var input = terminalInput.value; var input = terminalInput.value;
if (input == "") {return;} if (input == "") {return;}
const semiColonIndex = input.lastIndexOf(";");
if(semiColonIndex !== -1) {
input = input.slice(semiColonIndex+1);
}
input = input.trim(); input = input.trim();
input = input.replace(/\s\s+/g, ' '); input = input.replace(/\s\s+/g, ' ');
@ -349,7 +360,18 @@ function tabCompletion(command, arg, allPossibilities, index=0) {
} else { } else {
val = command + " " + allPossibilities[0]; 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(); document.getElementById("terminal-input-text-box").focus();
} else { } else {
var longestStartSubstr = longestCommonStart(allPossibilities); var longestStartSubstr = longestCommonStart(allPossibilities);
@ -646,11 +668,6 @@ let Terminal = {
} }
}, },
getTimestamp: function() {
let d = new Date();
return (d.getMonth() + "/" + d.getDay() + " " + d.getHours() + ":" + d.getMinutes());
},
finishAction: function(cancelled = false) { finishAction: function(cancelled = false) {
if (Terminal.hackFlag) { if (Terminal.hackFlag) {
Terminal.finishHack(cancelled); Terminal.finishHack(cancelled);
@ -1831,142 +1848,164 @@ let Terminal = {
if (splitArgs.length > 1) { if (splitArgs.length > 1) {
programName = splitArgs[0]; programName = splitArgs[0];
} }
switch (programName) {
case Programs.NukeProgram.name:
if (s.hasAdminRights) {
post("You already have root access to this computer. There is no reason to run NUKE.exe");
} else {
if (s.openPortCount >= Player.getCurrentServer().numOpenPortsRequired) {
s.hasAdminRights = true;
post("NUKE successful! Gained root access to " + Player.getCurrentServer().hostname);
//TODO Make this take time rather than be instant
} else {
post("NUKE unsuccessful. Not enough ports have been opened");
}
}
break;
case Programs.BruteSSHProgram.name:
if (s.sshPortOpen) {
post("SSH Port (22) is already open!");
} else {
s.sshPortOpen = true;
post("Opened SSH Port(22)!")
++s.openPortCount;
}
break;
case Programs.FTPCrackProgram.name:
if (s.ftpPortOpen) {
post("FTP Port (21) is already open!");
} else {
s.ftpPortOpen = true;
post("Opened FTP Port (21)!");
++s.openPortCount;
}
break;
case Programs.RelaySMTPProgram.name:
if (s.smtpPortOpen) {
post("SMTP Port (25) is already open!");
} else {
s.smtpPortOpen = true;
post("Opened SMTP Port (25)!");
++s.openPortCount;
}
break;
case Programs.HTTPWormProgram.name:
if (s.httpPortOpen) {
post("HTTP Port (80) is already open!");
} else {
s.httpPortOpen = true;
post("Opened HTTP Port (80)!");
++s.openPortCount;
}
break;
case Programs.SQLInjectProgram.name:
if (s.sqlPortOpen) {
post("SQL Port (1433) is already open!");
} else {
s.sqlPortOpen = true;
post("Opened SQL Port (1433)!");
++s.openPortCount;
}
break;
case Programs.ServerProfiler.name:
if (splitArgs.length != 2) {
post("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
return;
}
var serv = getServer(splitArgs[1]);
if (serv == null) {
post("Invalid server IP/hostname");
return;
}
post(serv.hostname + ":");
post("Server base security level: " + serv.baseDifficulty);
post("Server current security level: " + serv.hackDifficulty);
post("Server growth rate: " + serv.serverGrowth);
post("Netscript hack() execution time: " + formatNumber(scriptCalculateHackingTime(serv), 1) + "s");
post("Netscript grow() execution time: " + formatNumber(scriptCalculateGrowTime(serv)/1000, 1) + "s");
post("Netscript weaken() execution time: " + formatNumber(scriptCalculateWeakenTime(serv)/1000, 1) + "s");
break;
case Programs.AutoLink.name:
post("This executable cannot be run.");
post("AutoLink.exe lets you automatically connect to other servers when using 'scan-analyze'.");
post("When using scan-analyze, click on a server's hostname to connect to it.");
break;
case Programs.DeepscanV1.name:
post("This executable cannot be run.");
post("DeepscanV1.exe lets you run 'scan-analyze' with a depth up to 5.");
break;
case Programs.DeepscanV2.name:
post("This executable cannot be run.");
post("DeepscanV2.exe lets you run 'scan-analyze' with a depth up to 10.");
break;
case Programs.Flight.name:
const fulfilled = Player.augmentations.length >= 30 &&
Player.money.gt(1e11) &&
((Player.hacking_skill >= 2500)||
(Player.strength >= 1500 &&
Player.defense >= 1500 &&
Player.dexterity >= 1500 &&
Player.agility >= 1500));
if(!fulfilled) {
post("Augmentations: " + Player.augmentations.length + " / 30");
post("Money: " + numeral(Player.money.toNumber()).format('($0.000a)') + " / " + numeral(1e11).format('($0.000a)')); // TODO: refactor this/these out of Terminal. This logic could reside closer to the Programs themselves.
post("One path below must be fulfilled..."); /**
post("----------HACKING PATH----------"); * @typedef {function (server=, args=)} ProgramHandler
post("Hacking skill: " + Player.hacking_skill + " / 2500"); * @param {Server} server The current server the program is being executed against
post("----------COMBAT PATH----------"); * @param {string[]} args The command line arguments passed in to the program
post("Strength: " + Player.strength + " / 1500"); * @returns {void}
post("Defense: " + Player.defense + " / 1500"); */
post("Dexterity: " + Player.dexterity + " / 1500"); /**
post("Agility: " + Player.agility + " / 1500"); * @type {Object.<string, ProgramHandler}
} else { */
post("We will contact you."); const programHandlers = {};
post("-- Daedalus --"); programHandlers[Programs.NukeProgram.name] = (server) => {
} if (server.hasAdminRights) {
break; post("You already have root access to this computer. There is no reason to run NUKE.exe");
case Programs.BitFlume.name: return;
var yesBtn = yesNoBoxGetYesButton(), }
noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Travel to BitNode Nexus";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function() {
hackWorldDaemon(Player.bitNodeN, true);
return yesNoBoxClose();
});
noBtn.addEventListener("click", function() {
return yesNoBoxClose();
});
yesNoBoxCreate("WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.<br><br>" +
"Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode " +
"and select a new one.");
break; if (server.openPortCount >= Player.getCurrentServer().numOpenPortsRequired) {
default: server.hasAdminRights = true;
post("Invalid executable. Cannot be run"); post("NUKE successful! Gained root access to " + Player.getCurrentServer().hostname);
return; // TODO: Make this take time rather than be instant
} return;
}
post("NUKE unsuccessful. Not enough ports have been opened");
};
programHandlers[Programs.BruteSSHProgram.name] = (server) => {
if (server.sshPortOpen) {
post("SSH Port (22) is already open!");
return;
}
server.sshPortOpen = true;
post("Opened SSH Port(22)!")
server.openPortCount++;
};
programHandlers[Programs.FTPCrackProgram.name] = (server) => {
if (server.ftpPortOpen) {
post("FTP Port (21) is already open!");
return;
}
server.ftpPortOpen = true;
post("Opened FTP Port (21)!");
server.openPortCount++;
};
programHandlers[Programs.RelaySMTPProgram.name] = (server) => {
if (server.smtpPortOpen) {
post("SMTP Port (25) is already open!");
return;
}
server.smtpPortOpen = true;
post("Opened SMTP Port (25)!");
server.openPortCount++;
};
programHandlers[Programs.HTTPWormProgram.name] = (server) => {
if (server.httpPortOpen) {
post("HTTP Port (80) is already open!");
return;
}
server.httpPortOpen = true;
post("Opened HTTP Port (80)!");
server.openPortCount++;
};
programHandlers[Programs.SQLInjectProgram.name] = (server) => {
if (server.sqlPortOpen) {
post("SQL Port (1433) is already open!");
return;
}
server.sqlPortOpen = true;
post("Opened SQL Port (1433)!");
server.openPortCount++;
};
programHandlers[Programs.ServerProfiler.name] = (server, args) => {
if (args.length !== 2) {
post("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
return;
}
const targetServer = getServer(args[1]);
if (targetServer == null) {
post("Invalid server IP/hostname");
return;
}
post(targetServer.hostname + ":");
post("Server base security level: " + targetServer.baseDifficulty);
post("Server current security level: " + targetServer.hackDifficulty);
post("Server growth rate: " + targetServer.serverGrowth);
post("Netscript hack() execution time: " + formatNumber(scriptCalculateHackingTime(targetServer), 1) + "s");
post("Netscript grow() execution time: " + formatNumber(scriptCalculateGrowTime(targetServer)/1000, 1) + "s");
post("Netscript weaken() execution time: " + formatNumber(scriptCalculateWeakenTime(targetServer)/1000, 1) + "s");
};
programHandlers[Programs.AutoLink.name] = () => {
post("This executable cannot be run.");
post("AutoLink.exe lets you automatically connect to other servers when using 'scan-analyze'.");
post("When using scan-analyze, click on a server's hostname to connect to it.");
};
programHandlers[Programs.DeepscanV1.name] = () => {
post("This executable cannot be run.");
post("DeepscanV1.exe lets you run 'scan-analyze' with a depth up to 5.");
};
programHandlers[Programs.DeepscanV2.name] = () => {
post("This executable cannot be run.");
post("DeepscanV2.exe lets you run 'scan-analyze' with a depth up to 10.");
};
programHandlers[Programs.Flight.name] = () => {
const fulfilled = Player.augmentations.length >= 30 &&
Player.money.gt(1e11) &&
((Player.hacking_skill >= 2500)||
(Player.strength >= 1500 &&
Player.defense >= 1500 &&
Player.dexterity >= 1500 &&
Player.agility >= 1500));
if(!fulfilled) {
post("Augmentations: " + Player.augmentations.length + " / 30");
post("Money: " + numeral(Player.money.toNumber()).format('($0.000a)') + " / " + numeral(1e11).format('($0.000a)'));
post("One path below must be fulfilled...");
post("----------HACKING PATH----------");
post("Hacking skill: " + Player.hacking_skill + " / 2500");
post("----------COMBAT PATH----------");
post("Strength: " + Player.strength + " / 1500");
post("Defense: " + Player.defense + " / 1500");
post("Dexterity: " + Player.dexterity + " / 1500");
post("Agility: " + Player.agility + " / 1500");
return;
}
post("We will contact you.");
post("-- Daedalus --");
};
programHandlers[Programs.BitFlume.name] = () => {
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Travel to BitNode Nexus";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function() {
hackWorldDaemon(Player.bitNodeN, true);
return yesNoBoxClose();
});
noBtn.addEventListener("click", function() {
return yesNoBoxClose();
});
yesNoBoxCreate("WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.<br><br>" +
"Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode " +
"and select a new one.");
};
if (!programHandlers.hasOwnProperty(programName)){
post("Invalid executable. Cannot be run");
return;
}
programHandlers[programName](s, splitArgs);
}, },
runScript: function(scriptName) { runScript: function(scriptName) {

@ -20,6 +20,7 @@ import {Augmentations, installAugmentations,
import {BitNodes, initBitNodes, import {BitNodes, initBitNodes,
initBitNodeMultipliers} from "./BitNode"; initBitNodeMultipliers} from "./BitNode";
import {Bladeburner} from "./Bladeburner"; import {Bladeburner} from "./Bladeburner";
import {CharacterOverview} from "./CharacterOverview";
import {cinematicTextFlag} from "./CinematicText"; import {cinematicTextFlag} from "./CinematicText";
import {CompanyPositions, initCompanies} from "./Company"; import {CompanyPositions, initCompanies} from "./Company";
import {Corporation} from "./CompanyManagement"; import {Corporation} from "./CompanyManagement";
@ -65,6 +66,19 @@ import {StockMarket, StockSymbols,
displayStockMarketContent} from "./StockMarket"; displayStockMarketContent} from "./StockMarket";
import {Terminal, postNetburnerText, post, KEY} from "./Terminal"; import {Terminal, postNetburnerText, post, KEY} from "./Terminal";
// 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.
import "../css/styles.scss";
import "../css/terminal.scss";
import "../css/menupages.scss";
import "../css/workinprogress.scss";
import "../css/popupboxes.scss";
import "../css/interactivetutorial.scss";
import "../css/loader.scss";
import "../css/missions.scss";
import "../css/companymanagement.scss";
import "../css/bladeburner.scss";
/* Shortcuts to navigate through the game /* Shortcuts to navigate through the game
* Alt-t - Terminal * Alt-t - Terminal
* Alt-c - Character * Alt-c - Character
@ -267,6 +281,7 @@ let Engine = {
Bladeburner: "Bladeburner", Bladeburner: "Bladeburner",
}, },
currentPage: null, currentPage: null,
overview: new CharacterOverview(),
//Time variables (milliseconds unix epoch time) //Time variables (milliseconds unix epoch time)
@ -557,20 +572,7 @@ let Engine = {
}, },
displayCharacterOverviewInfo: function() { displayCharacterOverviewInfo: function() {
if (Player.hp == null) {Player.hp = Player.max_hp;} Engine.overview.update();
var overviewText = "Hp: " + Player.hp + " / " + Player.max_hp + "<br>" +
"Money: " + numeral(Player.money.toNumber()).format('($0.000a)') + "<br>" +
"Hack: " + (Player.hacking_skill).toLocaleString() + "<br>" +
"Str: " + (Player.strength).toLocaleString() + "<br>" +
"Def: " + (Player.defense).toLocaleString() + "<br>" +
"Dex: " + (Player.dexterity).toLocaleString() + "<br>" +
"Agi: " + (Player.agility).toLocaleString() + "<br>" +
"Cha: " + (Player.charisma).toLocaleString();
if (Player.intelligence >= 1) {
overviewText += "<br>Int: " + (Player.intelligence).toLocaleString();
}
document.getElementById("character-overview-text").innerHTML = overviewText.replace( / /g, "&nbsp;");
const save = document.getElementById("character-overview-save-button"); const save = document.getElementById("character-overview-save-button");
@ -1656,12 +1658,12 @@ let Engine = {
Engine.Clickables.devMenuProgramsDropdown = document.getElementById("dev-menu-add-program-dropdown"); Engine.Clickables.devMenuProgramsDropdown = document.getElementById("dev-menu-add-program-dropdown");
const programsDD = Engine.Clickables.devMenuProgramsDropdown; const programsDD = Engine.Clickables.devMenuProgramsDropdown;
for(const i in Programs) { for(const i in Programs) {
programsDD.options[programsDD.options.length] = new Option(Programs[i], Programs[i]); programsDD.options[programsDD.options.length] = new Option(Programs[i].name, Programs[i].name);
} }
Engine.Clickables.devMenuAddProgram = document.getElementById("dev-add-program"); Engine.Clickables.devMenuAddProgram = document.getElementById("dev-add-program");
Engine.Clickables.devMenuAddProgram.addEventListener("click", function() { Engine.Clickables.devMenuAddProgram.addEventListener("click", function() {
const program = programsDD.options[programsDD.selectedIndex].value;; const program = programsDD.options[programsDD.selectedIndex].value;
if(!Player.hasProgram(program)) { if(!Player.hasProgram(program)) {
Player.getHomeComputer().programs.push(program); Player.getHomeComputer().programs.push(program);
} }

@ -130,13 +130,17 @@ module.exports = {
"no-missing-end-of-source-newline": true, "no-missing-end-of-source-newline": true,
"no-unknown-animations": true, "no-unknown-animations": true,
"number-leading-zero": "always", "number-leading-zero": "always",
"number-max-precision": [3, { ignoreUnits: [ "%" ] }], "number-max-precision": [4, { ignoreUnits: [ "%" ] }],
// "number-no-trailing-zeros": true, // "number-no-trailing-zeros": true,
"order/order": [ "order/order": [
[ [
"dollar-variables", "dollar-variables",
"at-variables", "at-variables",
"custom-properties", "custom-properties",
{
type: "at-rule",
name: "extend"
},
{ {
type: "at-rule", type: "at-rule",
name: "include" name: "include"

@ -0,0 +1,9 @@
export function getTimestamp() {
const d: Date = new Date();
// A negative slice value takes from the end of the string rather than the beginning.
const stringWidth: number = -2;
const formattedHours: string = `0${d.getHours()}`.slice(stringWidth);
const formattedMinutes: string = `0${d.getMinutes()}`.slice(stringWidth);
return `${d.getMonth() + 1}/${d.getDate()} ${formattedHours}:${formattedMinutes}`;
}

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, argv) => ({ module.exports = (env, argv) => ({
plugins: [ plugins: [
@ -14,6 +15,10 @@ module.exports = (env, argv) => ({
jquery: "jquery", jquery: "jquery",
jQuery: "jquery", jQuery: "jquery",
$: "jquery" $: "jquery"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}) })
], ],
target: "web", target: "web",
@ -32,7 +37,15 @@ module.exports = (env, argv) => ({
test: /\.tsx?$/, test: /\.tsx?$/,
loader: 'ts-loader', loader: 'ts-loader',
exclude: /node_modules/ exclude: /node_modules/
} },
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
},
] ]
}, },
optimization: { optimization: {