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

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

@ -1,3 +1,6 @@
@import "mixins";
@import "theme";
/* interactivetutorial.css */
#interactive-tutorial-wrapper {
position: relative;
@ -15,10 +18,10 @@
overflow: auto; /* Enable scroll if needed */
background-color: #444; /* Fallback color */
color: #fff;
}
#interactive-tutorial-container > strong {
background-color: #444;
> strong {
background-color: #444;
}
}
#interactive-tutorial-text {
@ -31,16 +34,20 @@
#interactive-tutorial-exit,
#interactive-tutorial-next,
#interactive-tutorial-back {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
font-size: 20px;
font-size: $defaultFontSize * 1.25;
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;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
}
#interactive-tutorial-exit {
@ -55,14 +62,3 @@
#interactive-tutorial-next {
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
terminal which has its own page) */
@ -32,7 +35,7 @@
border: 2px solid var(--my-highlight-color);
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,
@ -43,7 +46,7 @@
}
.ace_text-input {
font-size: 16px;
font-size: $defaultFontSize;
background-color: transparent;
}
@ -89,6 +92,9 @@
}
#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;
display: inline-block;
float: center;
@ -99,15 +105,6 @@
padding: 2px;
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 {
@ -132,7 +129,7 @@
margin-top: 8px;
margin-bottom: 8px;
padding: 2px;
font-size: 12px;
font-size: $defaultFontSize * 0.75;
}
/* Active scripts */
@ -154,7 +151,7 @@
.active-scripts-server-header {
background-color: #444;
font-size: 20px;
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
@ -176,7 +173,7 @@
.active-scripts-server-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: 13px;
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
@ -184,7 +181,7 @@
.active-scripts-server-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: 13px;
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
@ -227,7 +224,7 @@
.active-scripts-script-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: 13px;
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
@ -235,7 +232,7 @@
.active-scripts-script-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: 13px;
font-size: $defaultFontSize * 0.8125;
color: var(--my-font-color);
float: right;
margin-left: 5px;
@ -260,15 +257,12 @@
}
.active-scripts-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
font-size: 16px;
font-size: $defaultFontSize;
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;
padding: 4px;
background-color: #000;
@ -323,19 +317,13 @@
}
.hacknet-node {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
margin: 6px;
padding: 6px;
width: 34vw;
border: 2px solid var(--my-highlight-color);
-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 {
@ -355,7 +343,7 @@
display: inline-block;
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
padding: 0 4px;
width: 48px; /* Four times font-size */
width: $defaultFontSize * 4;
}
.menu-page-text {

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

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

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

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

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
=========
v0.40.1 - 8/5/2018 - Community Update
-------------------------------------
* Added getPurchasedServerCost() Netscript function (by kopelli)
* Added getFavorToDonate() Netscript function (by hydroflame)
* Added getFactionFavorGain() and getCompanyFavorGain() Singularity functions (by hydroflame)
* Accumulated 'bonus' time in Bladeburner is now displayed in the UI (by hydroflame)
* The Red Pill can now be purchased with negative money (since its supposed to be free) (by hydroflame)
* Cranial Signal Processor Augmentations now have the previous generation as a prerequisite. i.e. Cranial Signal Processor - Gen II requires Gen I (by Kline-)
* Terminal now supports semicolon usage (end of command). This allows chaining multiple Terminal commands (by hydroflame)
* Bladeburner Raid operations can no longer be performed if your estimate of Synthoid communities is zero (by hydroflame)
* The difficulty of BN-12 now scales faster (by hydroflame)
* Active Scripts UI now shows a RAM Usage bar for each server (by kopelli)
* Bug Fix: Corrected terminal timestamp format (by kopelli)
* Bug Fix: NetscriptJS scripts should now die properly if they don't have a 'main' function (by hydroflame)
* Bug Fix: write(), read(), and tryWrite() Netscript functions should now work properly for writing Arrays/objects to Netscript Ports
* Various minor UI/QOL fixes by hydroflame, kopelli, and Kline-
v0.40.0 - 7/28/2018
-------------------
* **WARNING: This update makes some significant changes to Netscript and therefore you may need to make some changes to your scripts. See** `this post <https://www.reddit.com/r/Bitburner/comments/9252j4/psa_netscript_10_changes_in_next_version_v0400/>`_ **this post for details**

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

@ -312,6 +312,18 @@ getCompanyFavor
This function will return the amount of favor you have at the specified company.
If the company passed in as an argument is invalid, -1 will be returned.
getCompanyFavorGain
-------------------
.. js:function:: getCompanyFavorGain(companyName)
:param string companyName: Name of the company. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function will return the amount of favor you will gain for the specified company
when you reset by installing Augmentations.
checkFactionInvitations
-----------------------
@ -387,6 +399,17 @@ getFactionFavor
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
-------------
@ -484,7 +507,7 @@ getOwnedAugmentations
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.

@ -14,16 +14,7 @@
<meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="dist/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" type="text/css" href="css/styles.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" />
<link rel="stylesheet" type="text/css" href="dist/engine.css" />
<!-- Google Analytics -->
<script>
@ -119,7 +110,7 @@
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
</div>
<div id="javascript-editor"></div>
@ -169,7 +160,7 @@
<fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" />
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset>
</div> <!-- End script editor options panel -->
@ -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
provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p>
<p id="active-scripts-total-prod"> Total online production 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>
</div>
@ -220,7 +214,7 @@
<br>
<div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money" ></span><br />
<span>Money:</span><span id="hacknet-nodes-player-money"></span><br/>
<span>Total Hacknet Node Prodution:</span><span id="hacknet-nodes-total-production"></span>
</p>
<span id="hacknet-nodes-multipliers">
@ -497,24 +491,24 @@
<!-- dev menu -->
<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-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 -->
<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>
<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>
<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>
<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>
<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-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>
<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)">
<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)">
@ -650,7 +644,7 @@
<a id="location-slums-heist" class="a-link-button tooltip"> Heist </a>
<!-- 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 -->
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
@ -696,7 +690,7 @@
you 'reset' by installing Augmentations.
</p>
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
<a id="stock-market-investopedia" class='a-link-button'>Investopedia</a>
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
<p id="stock-market-commission"> </p>
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
@ -728,7 +722,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"/>
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
</div>
@ -790,9 +784,41 @@
<!-- Character Overview Screen -->
<div id="character-overview-wrapper">
<div id="character-overview-container">
<p id="character-overview-text"> </p>
<span id="character-overview-save-button"> Save Game </span>
<span id="character-overview-options-button"> Options </span>
<div id="character-overview-text">
<table>
<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>
@ -818,7 +844,7 @@
</span>
</label>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25" />
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -832,7 +858,7 @@
</span>
</label>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" />
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50"/>
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -846,7 +872,7 @@
</span>
</label>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" />
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50"/>
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -858,7 +884,7 @@
</span>
</label>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" />
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60"/>
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset>

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

1918
package-lock.json generated

File diff suppressed because it is too large Load Diff

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

@ -1,6 +1,5 @@
import {Engine} from "./engine";
import {workerScripts,
addWorkerScript,
killWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import {getServer} from "./Server";
@ -8,12 +7,15 @@ import {dialogBoxCreate} from "../utils/DialogBox";
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
import {arrayToString} from "../utils/helpers/arrayToString";
import {createElement} from "../utils/uiHelpers/createElement";
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getElementById} from "../utils/uiHelpers/getElementById";
import {logBoxCreate} from "../utils/LogBox";
import numeral from "numeral/min/numeral.min";
import {formatNumber} from "../utils/StringHelperFunctions";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElement} from "../utils/uiHelpers/removeElement";
import {roundToTwo} from "../utils/helpers/roundToTwo";
/* {
* serverName: {
@ -28,12 +30,39 @@ import {removeElement} from "../utils/uiHelpers/removeElement";
let ActiveScriptsUI = {};
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) {
let hostname = server.hostname;
var activeScriptsList = document.getElementById("active-scripts-list");
let res = createAccordionElement({hdrText:hostname});
let res = createAccordionElement({
hdrText: getHeaderHtml(server)
});
let li = res[0];
var hdr = res[1];
let panel = res[2];
@ -116,20 +145,27 @@ function addActiveScriptsItem(workerscript) {
"Args: " + arrayToString(workerscript.args)
}));
var panelText = createElement("p", {
innerText:"Loading...", fontSize:"14px",
innerText: "Loading...",
fontSize: "14px",
});
panel.appendChild(panelText);
panel.appendChild(createElement("br"));
panel.appendChild(createElement("span", {
innerText:"Log", class:"active-scripts-button", margin:"4px", padding:"4px",
clickListener:()=>{
innerText: "Log",
class: "active-scripts-button",
margin: "4px",
padding: "4px",
clickListener: () => {
logBoxCreate(workerscript.scriptRef);
return false;
}
}));
panel.appendChild(createElement("span", {
innerText:"Kill Script", class:"active-scripts-button", margin:"4px", padding:"4px",
clickListener:()=>{
innerText: "Kill Script",
class: "active-scripts-button",
margin: "4px",
padding: "4px",
clickListener: () => {
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.scriptRef.server);
dialogBoxCreate("Killing script, may take a few minutes to complete...");
return false;
@ -205,11 +241,10 @@ function updateActiveScriptsItems(maxTasks=150) {
exceptionAlert(e);
}
}
document.getElementById("active-scripts-total-prod").innerHTML =
"Total online production of Active Scripts: " + numeral(total).format('$0.000a') + " / sec<br>" +
"Total online production since last Aug installation: " +
numeral(Player.scriptProdSinceLastAug).format('$0.000a') + " (" +
numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a') + " / sec)";
getElementById("active-scripts-total-production-active").innerText = numeral(total).format('$0.000a');
getElementById("active-scripts-total-prod-aug-total").innerText = numeral(Player.scriptProdSinceLastAug).format('$0.000a');
getElementById("active-scripts-total-prod-aug-avg").innerText = numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a');
return total;
}
@ -225,6 +260,8 @@ function updateActiveScriptsItemContent(workerscript) {
return; //Hasn't been created yet. We'll skip it
}
updateHeaderHtml(server);
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i]));
@ -252,6 +289,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
return;
}
updateHeaderHtml(server);
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
//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;
}
}
var inc = Math.pow(1.01, sf12Lvl);
var dec = Math.pow(0.99, sf12Lvl);
var inc = Math.pow(1.02, sf12Lvl);
var dec = 1/inc;
BitNodeMultipliers.HackingLevelMultiplier = dec;
BitNodeMultipliers.ServerMaxMoney = dec;

@ -1094,6 +1094,7 @@ Bladeburner.prototype.startAction = function(actionId) {
throw new Error ("Failed to get Operation Object for: " + actionId.name);
}
if (action.count < 1) {return this.resetAction();}
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) {return this.resetAction();}
this.actionTimeToComplete = action.getActionTime(this);
} catch(e) {
exceptionAlert(e);
@ -1420,13 +1421,6 @@ Bladeburner.prototype.completeOperation = function(success) {
}
var city = this.getCurrentCity();
if (this.logging.ops) {
if (success) {
this.log(action.name + " completed successfully! ")
} else {
}
}
switch (action.name) {
case "Investigation":
if (success) {
@ -1663,6 +1657,7 @@ Bladeburner.prototype.initializeDomElementRefs = function() {
overviewEstComms: null,
overviewChaos: null,
overviewSkillPoints: null,
overviewBonusTime: null,
overviewAugSuccessMult: null,
overviewAugMaxStaminaMult: null,
overviewAugStaminaGainMult: null,
@ -1826,8 +1821,15 @@ Bladeburner.prototype.createOverviewContent = function() {
"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.overviewAugSuccessMult = createElement("p", {display:"block"});
DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"});
DomElems.overviewAugStaminaGainMult = createElement("p", {display:"block"});
@ -1846,6 +1848,7 @@ Bladeburner.prototype.createOverviewContent = function() {
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewChaos);
appendLineBreaks(DomElems.overviewDiv, 2);
DomElems.overviewDiv.appendChild(DomElems.overviewBonusTime);
DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints);
appendLineBreaks(DomElems.overviewDiv, 1);
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.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
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.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) + "%";

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];
advertisingInfo =
"<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>" +
"Popularity Bonus: x" + formatNumber(Math.pow(popularityFac, 0.85), 3) + "<br>" +
"Ratio Multiplier: x" + formatNumber(Math.pow(ratioFac, 0.85), 3) + "</span></p><br>"

@ -1,5 +1,5 @@
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
//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,
/* Faction and Company favor */
BaseFavorToDonate: 150,
FactionReputationToFavorBase: 500,
FactionReputationToFavorMult: 1.02,
CompanyReputationToFavorBase: 500,
@ -70,6 +71,7 @@ let CONSTANTS = {
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
ScriptGetPurchaseServerRamCost: 0.25,
ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05,
ScriptGetPurchasedServerMaxRam: 0.05,
@ -78,6 +80,7 @@ let CONSTANTS = {
ScriptArbScriptRamCost: 1.0, //Functions that apply to all scripts regardless of args
ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2,
@ -488,33 +491,21 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>",
LatestUpdate:
"v0.40.0<br>" +
"* <b>WARNING: This update makes some significant changes to Netscript and therefore you may need to " +
"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'> " +
"this post for details</a></b><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." +
" However this also means any ES6+ features are no longer supported in NS1 <br>" +
"* When a server is hacked with a very large number of threads and left with no money, the server's security level " +
"now only increases by however many threads were needed to drain the server. For example, if you hack a server with " +
"5000 threads but it only needed 2000 threads to deplete the server's money, then the server's security will only increase " +
"as if you had hacked it with 2000 threads (change by hydroflame)<br>" +
"* Added getCurrentAction() to Bladeburner API<br>" +
"* Added a variety of functions to Bladeburner API that deal with action levels (change by hydroflame)<br>" +
"* Added getPurchasedServerLimit() and getPurchasedServerMaxRam() functions to Netscript (change by hydroflame & kopelli)<br>" +
"* Added getOwnedSourceFiles() Singularity function (by hydroflame)<br>" +
"* Completely re-designed the Hacknet Node API<br>" +
"* 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"
"v0.40.1 - Community Update<br>" +
"* Added getPurchasedServerCost() Netscript function (by kopelli)<br>" +
"* Added getFavorToDonate() Netscript function (by hydroflame)<br>" +
"* Added getFactionFavorGain() and getCompanyFavorGain() Singularity functions (by hydroflame)<br>" +
"* Accumulated 'bonus' time in Bladeburner is now displayed in the UI (by hydroflame)<br>" +
"* The Red Pill can now be purchased with negative money (since its supposed to be free) (by hydroflame)<br>" +
"* Cranial Signal Processor Augmentations now have the previous generation as a prerequisite. i.e. Cranial Signal Processor - Gen II requires Gen I (by Kline-)<br>" +
"* Terminal now supports semicolon usage (end of command). This allows chaining multiple Terminal commands (by hydroflame)<br>" +
"* Bladeburner Raid operations can no longer be performed if your estimate of Synthoid communities is zero (by hydroflame)<br>" +
"* The difficulty of BN-12 now scales faster (by hydroflame)<br>" +
"* Active Scripts UI now shows a RAM Usage bar for each server (by kopelli)<br>" +
"* Bug Fix: Corrected terminal timestamp format (by kopelli)<br>" +
"* Bug Fix: NetscriptJS scripts should now die properly if they don't have a 'main' function (by hydroflame)<br>" +
"* Bug Fix: write(), read(), and tryWrite() Netscript functions should now work properly for writing Arrays/objects to Netscript Ports<br>" +
"* Various minor UI/QOL fixes by hydroflame, kopelli, and Kline-"
}

@ -55,7 +55,7 @@ Faction.prototype.gainFavor = function() {
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() {
if (this.favor == null || this.favor == undefined) {this.favor = 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");
}
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";
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 " +
"purchase this one.";
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;
if (sing) {return 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;
if (sing) {return 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) {
Player.firstAugPurchased = true;
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);
}
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 {
hacknet : {
@ -1597,6 +1615,22 @@ function NetscriptFunctions(workerScript) {
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) {
if (workerScript.checkingRam) {
return updateStaticRam("purchaseServer", CONSTANTS.ScriptPurchaseServerRamCost);
@ -1615,18 +1649,14 @@ function NetscriptFunctions(workerScript) {
return "";
}
ram = Math.round(ram);
if (isNaN(ram) || !isPowerOfTwo(ram)) {
workerScript.scriptRef.log("ERROR: purchaseServer() failed due to invalid ram argument. Must be numeric and a power of 2");
let cost = 0;
try {
cost = getPurchaseServerRamCostGuard(ram);
} catch (e) {
workerScript.scriptRef.log("ERROR: 'purchaseServer()' " + e.message);
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)) {
workerScript.scriptRef.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2));
return "";
@ -2112,6 +2142,13 @@ function NetscriptFunctions(workerScript) {
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 */
universityCourse : function(universityName, className) {
@ -2747,6 +2784,27 @@ function NetscriptFunctions(workerScript) {
}
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() {
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
@ -2944,6 +3002,27 @@ function NetscriptFunctions(workerScript) {
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) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
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
// "SyntaxError: unexpected reserved word" with no line number information.
if (!loadedModule.main) {
throw makeRuntimeRejectMsg(script.filename +
" did not have a main function, cannot run it.");
throw makeRuntimeRejectMsg(workerScript, `${script.filename} cannot be run because it does not have a main function.`);
}
return loadedModule.main(ns);
} finally {

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

@ -435,7 +435,7 @@ PlayerObject.prototype.calculateSkill = function(exp, mult=1) {
}
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.defense = this.calculateSkill(this.defense_exp, this.defense_mult);
this.dexterity = this.calculateSkill(this.dexterity_exp, this.dexterity_mult);

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

@ -38,6 +38,7 @@ import {containsAllStrings, longestCommonStart,
import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString";
import {arrayToString} from "../utils/helpers/arrayToString";
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {logBoxCreate} from "../utils/LogBox";
import {yesNoBoxCreate,
yesNoBoxGetYesButton,
@ -112,16 +113,20 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val();
if (command.length > 0) {
post(
"[" +
(FconfSettings.ENABLE_TIMESTAMPS ? Terminal.getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
);
post(
"[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
);
if (command.length > 0) {
Terminal.resetTerminalInput(); //Clear input first
Terminal.executeCommand(command);
const commands = command.split(";");
for(let i = 0; i < commands.length; i++) {
if(commands[i].match(/^\s*$/)) { continue; }
Terminal.executeCommand(commands[i]);
}
}
}
@ -197,6 +202,12 @@ $(document).keydown(function(event) {
if (terminalInput == null) {return;}
var input = terminalInput.value;
if (input == "") {return;}
const semiColonIndex = input.lastIndexOf(";");
if(semiColonIndex !== -1) {
input = input.slice(semiColonIndex+1);
}
input = input.trim();
input = input.replace(/\s\s+/g, ' ');
@ -349,7 +360,18 @@ function tabCompletion(command, arg, allPossibilities, index=0) {
} else {
val = command + " " + allPossibilities[0];
}
document.getElementById("terminal-input-text-box").value = val;
const textBox = document.getElementById("terminal-input-text-box");
const oldValue = textBox.value;
const semiColonIndex = oldValue.lastIndexOf(";");
if(semiColonIndex === -1) {
// no ; replace the whole thing.
textBox.value = val;
} else {
// replace just after the last semicolon
textBox.value = textBox.value.slice(0, semiColonIndex+1)+" "+val;
}
document.getElementById("terminal-input-text-box").focus();
} else {
var longestStartSubstr = longestCommonStart(allPossibilities);
@ -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) {
if (Terminal.hackFlag) {
Terminal.finishHack(cancelled);
@ -1831,142 +1848,164 @@ let Terminal = {
if (splitArgs.length > 1) {
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)'));
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");
} else {
post("We will contact you.");
post("-- Daedalus --");
}
break;
case Programs.BitFlume.name:
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.");
// TODO: refactor this/these out of Terminal. This logic could reside closer to the Programs themselves.
/**
* @typedef {function (server=, args=)} ProgramHandler
* @param {Server} server The current server the program is being executed against
* @param {string[]} args The command line arguments passed in to the program
* @returns {void}
*/
/**
* @type {Object.<string, ProgramHandler}
*/
const programHandlers = {};
programHandlers[Programs.NukeProgram.name] = (server) => {
if (server.hasAdminRights) {
post("You already have root access to this computer. There is no reason to run NUKE.exe");
return;
}
break;
default:
post("Invalid executable. Cannot be run");
return;
}
if (server.openPortCount >= Player.getCurrentServer().numOpenPortsRequired) {
server.hasAdminRights = true;
post("NUKE successful! Gained root access to " + Player.getCurrentServer().hostname);
// 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) {

@ -20,6 +20,7 @@ import {Augmentations, installAugmentations,
import {BitNodes, initBitNodes,
initBitNodeMultipliers} from "./BitNode";
import {Bladeburner} from "./Bladeburner";
import {CharacterOverview} from "./CharacterOverview";
import {cinematicTextFlag} from "./CinematicText";
import {CompanyPositions, initCompanies} from "./Company";
import {Corporation} from "./CompanyManagement";
@ -65,6 +66,19 @@ import {StockMarket, StockSymbols,
displayStockMarketContent} from "./StockMarket";
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
* Alt-t - Terminal
* Alt-c - Character
@ -267,6 +281,7 @@ let Engine = {
Bladeburner: "Bladeburner",
},
currentPage: null,
overview: new CharacterOverview(),
//Time variables (milliseconds unix epoch time)
@ -557,20 +572,7 @@ let Engine = {
},
displayCharacterOverviewInfo: function() {
if (Player.hp == null) {Player.hp = Player.max_hp;}
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;");
Engine.overview.update();
const save = document.getElementById("character-overview-save-button");
@ -1656,12 +1658,12 @@ let Engine = {
Engine.Clickables.devMenuProgramsDropdown = document.getElementById("dev-menu-add-program-dropdown");
const programsDD = Engine.Clickables.devMenuProgramsDropdown;
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.addEventListener("click", function() {
const program = programsDD.options[programsDD.selectedIndex].value;;
const program = programsDD.options[programsDD.selectedIndex].value;
if(!Player.hasProgram(program)) {
Player.getHomeComputer().programs.push(program);
}

@ -130,13 +130,17 @@ module.exports = {
"no-missing-end-of-source-newline": true,
"no-unknown-animations": true,
"number-leading-zero": "always",
"number-max-precision": [3, { ignoreUnits: [ "%" ] }],
"number-max-precision": [4, { ignoreUnits: [ "%" ] }],
// "number-no-trailing-zeros": true,
"order/order": [
[
"dollar-variables",
"at-variables",
"custom-properties",
{
type: "at-rule",
name: "extend"
},
{
type: "at-rule",
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 webpack = require('webpack');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, argv) => ({
plugins: [
@ -14,6 +15,10 @@ module.exports = (env, argv) => ({
jquery: "jquery",
jQuery: "jquery",
$: "jquery"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
target: "web",
@ -32,7 +37,15 @@ module.exports = (env, argv) => ({
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
}
},
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
},
]
},
optimization: {