Merge pull request #2 from danielyxie/dev

Merge upstream
This commit is contained in:
Matthew Goff
2018-08-02 18:59:54 -05:00
committed by GitHub
50 changed files with 4709 additions and 1032 deletions

1
.gitignore vendored
View File

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

61
css/_mixins.scss Normal file
View 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
View 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
View File

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

View File

@ -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 {
%bladburner-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 %bladburner-nav-button;
color: #fff;
&:hover {
background-color: #3d4044;
}
}
.bladeburner-nav-button-inactive {
border: 1px solid #fff;
padding: 2px;
margin: 2px;
@extend %bladburner-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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
@ -446,15 +451,12 @@ a:link, a:visited {
#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;
}
@ -485,7 +487,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 +509,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 +517,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;

View File

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

View File

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

1857
dist/engine.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,33 @@
Changelog
=========
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**
* 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
* 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)
* Added getCurrentAction() to Bladeburner API
* Added a variety of functions to Bladeburner API that deal with action levels (change by hydroflame)
* Added getPurchasedServerLimit() and getPurchasedServerMaxRam() functions to Netscript (change by hydroflame & kopelli)
* Added getOwnedSourceFiles() Singularity function (by hydroflame)
* Completely re-designed the Hacknet Node API
* 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
* Minimum Netscript execution time reduced from 15ms to 10ms (configurable in Options)
* Company reputation needed to get invited to Megacorporation factions decreased from 250k to 200k
* HP is now reset (restored) when Augmenting
* Source-File 6 now increases both the level and experience gain of all combat stats (it was only experience gain previously)
* Reverted a previous change for Source-File 12. It's benefits are now multiplicative rather than additive
* Starting Infiltration security level for almost every location decreased by ~10%
* Changed 'fl1ght.exe' message when its listed conditions are fulfilled (by hydroflame)
* The 'Save Game' button in the top-right overview panel now flashes red if autosave is disabled
* Bug Fix: Infiltration buttons can no longer be clicked through NetscriptJS
* Bug Fix: Bladeburner 'Overclock' skill can no longer be leveled above max level through the API (by hydroflame)
* Bug Fix: Healthcare division in Bladeburner should no longer cause game to crash
v0.39.1 - 7/4/2018
------------------

View File

@ -14,10 +14,9 @@ to reach out to the developer!
:maxdepth: 5
:caption: Sections:
Learn to Program <netscriptlearntoprogram>
Netscript 1.0 <netscript1>
NetscriptJS (Netscript 2.0) <netscriptjs>
Data Types and Variables <netscriptdatatypes>
Operators <netscriptoperators>
Loops and Conditionals <netscriptloopsandconditionals>
Script Arguments <netscriptscriptarguments>
Basic Functions <netscriptfunctions>
Advanced Functions <netscriptadvancedfunctions>

32
doc/source/netscript1.rst Normal file
View File

@ -0,0 +1,32 @@
.. _netscript1:
Netscript 1.0
=============
Netscript 1.0 is implemented using modified version of Neil Fraser's
`JS-Interpreter <https://github.com/NeilFraser/JS-Interpreter>`_.
This interpreter was created for ES5, which means that the code written
for Netscript 1.0 must be compliant for that version. However, some additional
ES6+ features are implemented through polyfills.
Netscript 1.0 scripts end with the ".script" extension.
Which ES6+ features are supported?
----------------------------------
Netscript 1.0 is a ES5 interpreter, but the following features from versions ES6 and
above are supported as well.
If there is an additional ES6+ feature you would like to see implemented with a polyfill,
feel free to `open an issue <https://github.com/danielyxie/bitburner/issues>`_ (and provide
the polyfill if possible).
* import - See :ref:`netscriptimporting`
* `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_
* `find() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find>`_
* `findIndex() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex>`_
* `includes() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes>`_
* `String <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String>`_
* `endsWith() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith>`_
* `includes() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes>`_
* `startsWith() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith>`_

View File

@ -166,7 +166,9 @@ getActionMaxLevel
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
Return the maximum level for this action.
Returns the maximum level for this action.
Returns -1 if an invalid action is specified.
getActionCurrentLevel
---------------------
@ -176,7 +178,9 @@ getActionCurrentLevel
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
Return the current level of this action.
Returns the current level of this action.
Returns -1 if an invalid action is specified.
getActionAutolevel
------------------
@ -186,7 +190,9 @@ getActionAutolevel
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
Return wether of not this action is currently autoleveling.
Return a boolean indicating whether or not this action is currently set to autolevel.
Returns false if an invalid action is specified.
setActionAutolevel
------------------
@ -195,7 +201,9 @@ setActionAutolevel
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
:param boolean autoLevel: wether or not to autolevel this action
:param boolean autoLevel: Whether or not to autolevel this action
Enable/disable autoleveling for the specified action.
setActionLevel
--------------
@ -204,7 +212,9 @@ setActionLevel
:param string type: Type of action. See :ref:`bladeburner_action_types`
:param string name: Name of action. Must be an exact match
:param level int: the level to set this action to
:param level int: Level to set this action to
Set the level for the specified action.
getRank
-------

View File

@ -1,44 +0,0 @@
Netscript Data Types and Variables
==================================
Data Types
----------
Netscript supports three primitive data types:
**Numbers** - Positive numerics, such as integers and floats. Examples: 6, 0, 10.5
**Strings** - A sequence of characters that represents text. The characters must be encapsulated by single or
double quotes. Example: "This is a string" or equivalently 'This is a string'.
*Strings are fully functional* `Javascript strings <https://www.w3schools.com/jsref/jsref_obj_string.asp>`_,
*which means that all of the member functions of Javascript strings such as toLowerCase() and includes() are also available in Netscript!*
**Boolean** - true or false
**Array** - An array is a special container object that is capable of holding many different values. Arrays are simply Javascript
arrays, and most Javascript array methods can be used in Netscript as well (join(), pop(), splice(), etc.). You can read more about
`Javascript arrays here <https://www.w3schools.com/js/js_arrays.asp>`_
Variables
---------
Variables can be thought of as named containers. Their purpose is to label and store data. The data stored in the
variable can then be accessed and changed by referring to the variable's name. The name of a variable must start with
either a letter or an underscore. The rest of the variable name can contain any alphanumeric (letters and numbers),
as well as hyphens and underscores.
The Netscript language is untyped, meaning that any variable can hold any of the data types above. The value type of a variable
can also change. For example, if a variable initially holds a number, it can later hold a string.
The following shows how you can declare and initialize variables::
i = 1;
s = "This is a string";
b = false;
After declaring a variable, the values in variables can be used simply by referencing the name. For example::
j = i + 5;
s2 = s + " Adding more letters onto the string"
The first command above will store the value 6 in the variable j. The second command will store the string "This is a string Adding more letters onto the string" into the variable s2.

View File

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

View File

@ -8,7 +8,7 @@ still being able to access the Netscript functions.
NetscriptJS was developed primarily by `Github user jaguilar <https://github.com/jaguilar>`_
On top of having almost all of the features and capabilities of Javascript, NetscriptJS is also
On top of having almost all of the features and capabilities of JavaScript, NetscriptJS is also
significantly faster than Netscript 1.0.
This documentation will not go over any of the additional features of NetscriptJS, since

View File

@ -0,0 +1,70 @@
.. _netscriptlearntoprogram:
Learn to Program in Netscript
=============================
Netscript is simply a subset of
`JavaScript <https://developer.mozilla.org/en-US/docs/Web/JavaScript>`_,
with some additional functions added in to allow interaction with the game.
For Beginner Programmers
------------------------
If you have little to no programming experience, that's okay! You don't need to be
a great programmer in order to enjoy or play this game. In fact, this game could
help you learn some basic programming concepts.
Here are some good tutorials for learning programming/JavaScript as a beginner:
* `Learn-JS <http://www.learn-js.org/en/Welcome>`_
* `Speaking JavaScript <http://speakingjs.com/es5/index.html>`_
This is a bit on the longer side. You can skip all of the historical
background stuff. Recommended chapters: 1, 7-18
For Experienced Programmers
---------------------------
The following section lists several good tutorials/resources for those who have experience
programming but who have not worked extensively with JavaScript before.
Before that, however, it's important to clarify some terminology about the different
versions of JavaScript. These are summarized in this article:
`WTF is ES6, ES8, ES2017, ECMAScript... <https://codeburst.io/javascript-wtf-is-es6-es8-es-2017-ecmascript-dca859e4821c>`_
An important takeaway from this article is that ES6, also known as ES2015, introduced
many major features that are commonly seen in modern JavaScript programming. However, this
means that ES5 engines and interpreters will fail if they encounters these ES6 features. You'll see why this
is important further down.
* `MDN Introduction to JS <https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript>`_
* `Eloquent JavaScript (ES6+) <http://eloquentjavascript.net/>`_
Recommended Chapters: Introduction, 1-6
* `Modern Javascript Tutorial (ES6+) <https://javascript.info/>`_
Recommended Chapters: 2, 4-6
Netscript 1.0 vs Netscript 2.0
------------------------------
There are two versions of Netscript:
* :doc:`netscript1`
* :doc:`netscriptjs`
Visit the pages above to get more details about each version. If you are new
to programming or unfamiliar with JavaScript, I would recommend starting out
with :doc:`netscript1`. Experienced web developers can use :doc:`netscriptjs`
to take advantage of faster speeds and additional features.
Here is a short summary of the differences between Netscript 1.0 and Netscript 2.0:
**Netscript 1.0**
* ES5
* Some ES6 features implemented with polyfills
* Slow compared to NetscriptJS (interpreter runs at the "Netscript Exec Time" speed configured in options)
* Compatible with all browsers
**Netscript JS (Netscript 2.0)**
* Supports (almost) all features of modern JavaScript
* Extremely fast - code is executed as an Async Function
* Currently only works with Google Chrome browser
* Each script becomes a module and therefore all instances of that script can easily
share data between each other (essentially global/static variables)

View File

@ -1,39 +0,0 @@
Netscript Loops and Conditionals
================================
Netscript loops and conditionals are the same as Javascript. However, the one caveat is that when declaring variables such as the
iterator for traversing a loop, you should not use the 'var' or 'let' keyword. For reference, you can see the Javascript
documentation for loops/conditionals here:
`While loops <https://www.w3schools.com/js/js_loop_while.asp>`_
`For loops <https://www.w3schools.com/js/js_loop_for.asp>`_
`Conditionals (If/Else statements) <https://www.w3schools.com/js/js_if_else.asp>`_
Here are some simple code examples that show the use of loops and conditionals in Netscript.
The following is a while loop that runs the hack() Netscript function ten times::
i = 0;
while (i < 10) {
hack('foodnstuff');
i = i + 1;
}
The following is a for loop that runs the hack() Netscript function ten times::
for (i = 0; i < 10; ++i) {
hack("foodnstuff");
}
The following is a conditional that uses the getServerMoneyAvailable() Netscript function to check how much money
exists on the 'foodnstuff' server. If there is more than $200,000 on the server, then the server will be hacked.
Otherwise, the money available on the server will be grown using the grow() Netscript function::
if (getServerMoneyAvailable('foodnstuff') > 200000) {
hack("foodnstuff");
} else {
grow("foodnstuff");
}

View File

@ -142,6 +142,8 @@ Comments are not evaluated as code, and can be used to document and/or explain c
* comment */
print("This code will actually get executed");
.. _netscriptimporting:
Importing Functions
-------------------
@ -201,7 +203,7 @@ to specify a namespace for the import::
//...
}
Note that exporting functions is not required.
Note that exporting functions is not required.
Javascript Math Module

View File

@ -1,53 +0,0 @@
Netscript Operators
===================
Operators
---------
Binary Operators
^^^^^^^^^^^^^^^^
Binary operators require two operands and produce a result based on their values. In general, binary
operators do not change the value of the operands.
=========== =========================== ==============================================================
Operator Name Example/Comments
=========== =========================== ==============================================================
= Assignment i = 5 would assign the value 5 to the variable i
\+ Addition 5 + 12 would return 17
\- Subtraction 20 - 8 would return 12
\* Multiplication 4 * 5 would return 20
\/ Division 50 / 10 would return 5
% Modulo 50 % 9 would return 5
&& Logical AND true && false would return false
|| Logical OR true || false would return true
< Less than 4 < 5 would return true
> Greater than 4 > 5 would return false
<= Less than or equal to 5 <= 5 would return true
>= Greater than or equal to 5 >= 4 would return true
== Equality 1 == 1 would return true
!= Inequality 4 != 5 would return true
=== Strict equality 1 === "1" would return false
!== Strict inequality 1 !== "1" would return true
=========== =========================== ==============================================================
Unary Operators
^^^^^^^^^^^^^^^
Unary operators require only a single operand and produce a result based on their values. Some unary operators will
change the value of their operands. For example::
i = 0;
++i;
Running the pre-increment unary operator (++) in the code above changes the value of the variable i.
=============== =========================== ==============================================================================================
Operator Name Example/comments
=============== =========================== ==============================================================================================
! Logical NOT operator !true would return false, and !false would return true. Does not change operand's value
\- Negation Negates a number. Only works for numerics. Does not change operand's value
++ Pre-increment ++i or i++. WARNING: This only pre-increments, even if you put i++. Changes operand's value
-- Pre-decrement --i or i--. WARNING: This only pre-decrements, even if you put i--. Changes operand's value
=============== =========================== ==============================================================================================

View File

@ -481,6 +481,17 @@ getOwnedAugmentations
This function returns an array containing the names (as strings) of all Augmentations you have.
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.
Returns an array of source files
[{n: 1, lvl: 3}, {n: 4, lvl: 3}]
getAugmentationsFromFaction
---------------------------

View File

@ -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>
@ -814,7 +805,7 @@
<span class="tooltiptext">
The minimum number of milliseconds it takes to execute an operation in Netscript.
Setting this too low can result in poor performance if you have many scripts running.
The default value is 50ms.
The default value is 25ms.
</span>
</label>

View File

@ -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|" +
@ -82,9 +83,9 @@ let NetscriptFunctions =
"getCompanyFavor|stopAction|getFactionFavor|" +
"checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" +
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getAugmentationsFromFaction|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" +
"getAugmentationCost|purchaseAugmentation|" +
"installAugmentations|" +
"installAugmentations|" +
"getStockPrice|getStockPosition|buyStock|sellStock|shortStock|sellShort|" +
"placeOrder|cancelOrder|" +
//Hacknet Node API
@ -96,6 +97,8 @@ let NetscriptFunctions =
"bladeburner|getContractNames|getOperationNames|getBlackOpNames|" +
"getGeneralActionNames|getSkillNames|startAction|stopBladeburnerAction|" +
"getActionTime|getActionEstimatedSuccessChance|getActionCountRemaining|" +
"getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" +
"setActionAutolevel|setActionLevel|" +
"getRank|getSkillPoints|getSkillLevel|upgradeSkill|getTeamSize|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction";

1918
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -739,7 +739,8 @@ function initAugmentations() {
"This augmentation: <br>" +
"Increases the player's hacking speed by 2%<br>" +
"Increases the player's chance of successfully performing a hack by 5%<br>" +
"Increases the player's hacking skill by 7%"
"Increases the player's hacking skill by 7%",
prereqs:[AugmentationNames.CranialSignalProcessorsG1]
});
CranialSignalProcessorsG2.addToFactions(["NiteSec"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG2)) {
@ -756,7 +757,8 @@ function initAugmentations() {
"This augmentation:<br>" +
"Increases the player's hacking speed by 2%<br>" +
"Increases the amount of money the player gains from hacking by 15%<br>" +
"Increases the player's hacking skill by 9%"
"Increases the player's hacking skill by 9%",
prereqs:[AugmentationNames.CranialSignalProcessorsG2]
});
CranialSignalProcessorsG3.addToFactions(["NiteSec", "The Black Hand"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG3)) {
@ -773,7 +775,8 @@ function initAugmentations() {
"This augmentation: <br>" +
"Increases the player's hacking speed by 2%<br>" +
"Increases the amount of money the player gains from hacking by 20%<br>" +
"Increases the amount of money the player can inject into servers using grow() by 25%"
"Increases the amount of money the player can inject into servers using grow() by 25%",
prereqs:[AugmentationNames.CranialSignalProcessorsG3]
});
CranialSignalProcessorsG4.addToFactions(["The Black Hand"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG4)) {
@ -784,13 +787,14 @@ function initAugmentations() {
var CranialSignalProcessorsG5 = new Augmentation({
name:AugmentationNames.CranialSignalProcessorsG5, repCost:100e3, moneyCost:450e6,
info:"The fifth generation of Cranial Signal Processors. Cranial Signal Processors " +
"are a set of specialized microprocessors that are attached to " +
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
"so that the brain doesn't have to. <br><br>" +
"This augmentation:<br>" +
"Increases the player's hacking skill by 30%<br>" +
"Increases the amount of money the player gains from hacking by 25%<br>" +
"Increases the amount of money the player can inject into servers using grow() by 75%"
"are a set of specialized microprocessors that are attached to " +
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
"so that the brain doesn't have to. <br><br>" +
"This augmentation:<br>" +
"Increases the player's hacking skill by 30%<br>" +
"Increases the amount of money the player gains from hacking by 25%<br>" +
"Increases the amount of money the player can inject into servers using grow() by 75%",
prereqs:[AugmentationNames.CranialSignalProcessorsG4]
});
CranialSignalProcessorsG5.addToFactions(["BitRunners"]);
if (augmentationExists(AugmentationNames.CranialSignalProcessorsG5)) {
@ -884,7 +888,7 @@ function initAugmentations() {
"This augmentation:<br>" +
"Increases the amount of reputation the player gains when working for a company by 75%<br>" +
"Increases the player's hacking skill by 10%",
prereqs:[AugmentationNames.PCDNI],
prereqs:[AugmentationNames.PCDNI]
});
PCDNIOptimizer.addToFactions(["Fulcrum Secret Technologies", "ECorp", "Blade Industries"]);
if (augmentationExists(AugmentationNames.PCDNIOptimizer)) {
@ -902,7 +906,7 @@ function initAugmentations() {
"Increases the amount of reputation the player gains when working for a company by 100%<br>" +
"Increases the player's hacking skill by 10%<br>" +
"Increases the player's hacking speed by 5%",
prereqs:[AugmentationNames.PCDNI],
prereqs:[AugmentationNames.PCDNI]
});
PCDNINeuralNetwork.addToFactions(["Fulcrum Secret Technologies"]);
if (augmentationExists(AugmentationNames.PCDNINeuralNetwork)) {
@ -1112,7 +1116,8 @@ function initAugmentations() {
"This augmentation: <br>" +
"Increases the player's agility by 10% <br>" +
"Increases the player's defense by 10% <br>" +
"Increases the amount of money the player gains from crimes by 25%"
"Increases the amount of money the player gains from crimes by 25%",
prereqs:[AugmentationNames.LuminCloaking1]
});
LuminCloaking2.addToFactions(["Slum Snakes", "Tetrads"]);
if (augmentationExists(AugmentationNames.LuminCloaking2)) {

View File

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

View File

@ -358,7 +358,7 @@ function Skill(params={name:"foo", desc:"foo"}) {
}
Skill.prototype.calculateCost = function(currentLevel) {
return (this.baseCost + (currentLevel * this.costInc)) * BitNodeMultipliers.BladeburnerSkillCost;
return Math.floor((this.baseCost + (currentLevel * this.costInc)) * BitNodeMultipliers.BladeburnerSkillCost);
}
var Skills = {};
var SkillNames = {
@ -1042,22 +1042,23 @@ Bladeburner.prototype.upgradeSkill = function(skill) {
Bladeburner.prototype.getActionObject = function(actionId) {
//Given an ActionIdentifier object, returns the corresponding
//Contract, Operation, or BlackOperation object
//GeneralAction, Contract, Operation, or BlackOperation object
switch (actionId.type) {
case ActionTypes["Contract"]:
return this.contracts[actionId.name];
break;
case ActionTypes["Operation"]:
return this.operations[actionId.name];
break;
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
return BlackOperations[actionId.name];
break;
case ActionTypes["Training"]:
return GeneralActions["Training"];
case ActionTypes["Field Analysis"]:
return GeneralActions["Field Analysis"];
case ActionTypes["Recruitment"]:
return GeneralActions["Recruitment"];
default:
return null;
console.log("WARNING: Bladeburner.getActionObject() called with an unexpected " +
"ActionIdentifier type: " + actionId.type);
}
}
@ -1093,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);
@ -1280,7 +1282,7 @@ Bladeburner.prototype.completeAction = function() {
teamLossMax = Math.floor(teamCount);
if (this.logging.blackops) {
this.log(action.name + " failed! Lost " + formatNumber(rankLoss, 1) + " rank and took" + formatNumber(damage, 0) + " damage");
this.log(action.name + " failed! Lost " + formatNumber(rankLoss, 1) + " rank and took " + formatNumber(damage, 0) + " damage");
}
}
@ -1662,6 +1664,7 @@ Bladeburner.prototype.initializeDomElementRefs = function() {
overviewEstComms: null,
overviewChaos: null,
overviewSkillPoints: null,
overviewBonusTime: null,
overviewAugSuccessMult: null,
overviewAugMaxStaminaMult: null,
overviewAugStaminaGainMult: null,
@ -1825,7 +1828,14 @@ 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 you're not performing any action. " +
"Bonus time makes the game progress faster."
});
DomElems.overviewSkillPoints = createElement("p", {display:"block"});
DomElems.overviewAugSuccessMult = createElement("p", {display:"block"});
DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"});
@ -1845,6 +1855,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);
@ -2015,7 +2026,7 @@ Bladeburner.prototype.createContractsContent = function() {
}
DomElems.actionsAndSkillsDesc.innerHTML =
"Complete contracts in order to increase your Bitburner rank and earn money. " +
"Complete contracts in order to increase your Bladeburner rank and earn money. " +
"Failing a contract will cause you to lose HP, which can lead to hospitalization.<br><br>" +
"You can unlock higher-level contracts by successfully completing them. " +
"Higher-level contracts are more difficult, but grant more rank, experience, and money.";
@ -2205,6 +2216,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) + "%";
@ -3222,6 +3234,7 @@ Bladeburner.prototype.getActionIdFromTypeAndName = function(type="", name="") {
} else {
return null;
}
break;
case "operation":
case "operations":
case "op":
@ -3233,6 +3246,7 @@ Bladeburner.prototype.getActionIdFromTypeAndName = function(type="", name="") {
} else {
return null;
}
break;
case "blackoperation":
case "black operation":
case "black operations":
@ -3247,6 +3261,7 @@ Bladeburner.prototype.getActionIdFromTypeAndName = function(type="", name="") {
} else {
return null;
}
break;
case "general":
case "general action":
case "gen":
@ -3314,7 +3329,7 @@ Bladeburner.prototype.getSkillNamesNetscriptFn = function() {
}
Bladeburner.prototype.startActionNetscriptFn = function(type, name, workerScript) {
var errorLogText = "ERROR: Bladeburner.startAction() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.startAction() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
var actionId = this.getActionIdFromTypeAndName(type, name);
@ -3326,19 +3341,19 @@ Bladeburner.prototype.startActionNetscriptFn = function(type, name, workerScript
try {
this.startAction(actionId);
if (workerScript.shouldLog("startAction")) {
workerScript.scriptRef.log("Starting Bladeburner action with type " + type + " and name " + name);
workerScript.scriptRef.log("Starting bladeburner action with type " + type + " and name " + name);
}
return true;
} catch(e) {
this.resetAction();
workerScript.scriptRef.log("ERROR: Bladeburner.startAction() failed to start action of type " + type + " due to invalid name: " + name +
workerScript.scriptRef.log("ERROR: bladeburner.startAction() failed to start action of type " + type + " due to invalid name: " + name +
"Note that this name is case-sensitive and whitespace-sensitive");
return false;
}
}
Bladeburner.prototype.getActionTimeNetscriptFn = function(type, name, workerScript) {
var errorLogText = "ERROR: Bladeburner.getActionTime() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.getActionTime() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
var actionId = this.getActionIdFromTypeAndName(type, name);
@ -3372,7 +3387,7 @@ Bladeburner.prototype.getActionTimeNetscriptFn = function(type, name, workerScri
}
Bladeburner.prototype.getActionEstimatedSuccessChanceNetscriptFn = function(type, name, workerScript) {
var errorLogText = "ERROR: Bladeburner.getActionEstimatedSuccessChance() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.getActionEstimatedSuccessChance() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
var actionId = this.getActionIdFromTypeAndName(type, name);
@ -3406,7 +3421,7 @@ Bladeburner.prototype.getActionEstimatedSuccessChanceNetscriptFn = function(type
}
Bladeburner.prototype.getActionCountRemainingNetscriptFn = function(type, name, workerScript) {
var errorLogText = "ERROR: Bladeburner.getActionCountRemaining() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.getActionCountRemaining() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
var actionId = this.getActionIdFromTypeAndName(type, name);
@ -3438,7 +3453,7 @@ Bladeburner.prototype.getActionCountRemainingNetscriptFn = function(type, name,
}
Bladeburner.prototype.getSkillLevelNetscriptFn = function(skillName, workerScript) {
var errorLogText = "ERROR: Bladeburner.getSkillLevel() failed due to an invalid skill specified: " +
var errorLogText = "ERROR: bladeburner.getSkillLevel() failed due to an invalid skill specified: " +
skillName + ". Note that the name of the skill is case-sensitive";
if (skillName === "") {
@ -3458,7 +3473,7 @@ Bladeburner.prototype.getSkillLevelNetscriptFn = function(skillName, workerScrip
}
Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript) {
var errorLogText = "ERROR: Bladeburner.upgradeSkill() failed due to an invalid skill specified: " +
var errorLogText = "ERROR: bladeburner.upgradeSkill() failed due to an invalid skill specified: " +
skillName + ". Note that the name of the skill is case-sensitive";
if (!Skills.hasOwnProperty(skillName)) {
workerScript.log(errorLogText);
@ -3472,9 +3487,16 @@ Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript
}
var cost = skill.calculateCost(currentLevel);
if(skill.maxLvl && currentLevel >= skill.maxLvl) {
if (workerScript.shouldLog("upgradeSkill")) {
workerScript.log(`bladeburner.upgradeSkill() failed because ${skillName} is already maxed`);
}
return false;
}
if (this.skillPoints < cost) {
if (workerScript.shouldLog("upgradeSkill")) {
workerScript.log("Bladeburner.upgradeSkill() failed because you do not have enough " +
workerScript.log("bladeburner.upgradeSkill() failed because you do not have enough " +
"skill points to upgrade " + skillName + " (You have " +
this.skillPoints + ", you need " + cost + ")");
}
@ -3497,7 +3519,7 @@ Bladeburner.prototype.getTeamSizeNetscriptFn = function(type, name, workerScript
return this.teamSize;
}
var errorLogText = "ERROR: Bladeburner.getTeamSize() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.getTeamSize() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
@ -3523,7 +3545,7 @@ Bladeburner.prototype.getTeamSizeNetscriptFn = function(type, name, workerScript
}
Bladeburner.prototype.setTeamSizeNetscriptFn = function(type, name, size, workerScript) {
var errorLogText = "ERROR: Bladeburner.setTeamSize() failed due to an invalid action specified. " +
var errorLogText = "ERROR: bladeburner.setTeamSize() failed due to an invalid action specified. " +
"Type: " + type + ", Name: " + name + ". Note that for contracts and operations, the " +
"name of the operation is case-sensitive.";
var actionId = this.getActionIdFromTypeAndName(type, name);
@ -3535,7 +3557,7 @@ Bladeburner.prototype.setTeamSizeNetscriptFn = function(type, name, size, worker
if (actionId.type !== ActionTypes["Operation"] &&
actionId.type !== ActionTypes["BlackOp"] &&
actionId.type !== ActionTypes["BlackOperation"]) {
workerScript.log("ERROR: Bladeburner.setTeamSize() failed. This function " +
workerScript.log("ERROR: bladeburner.setTeamSize() failed. This function " +
"only works for Operations and BlackOps");
return -1;
}
@ -3548,7 +3570,7 @@ Bladeburner.prototype.setTeamSizeNetscriptFn = function(type, name, size, worker
var sanitizedSize = Math.round(size);
if (isNaN(sanitizedSize)) {
workerScript.log("ERROR: Bladeburner.setTeamSize() failed due to an invalid 'size' argument: " + size);
workerScript.log("ERROR: bladeburner.setTeamSize() failed due to an invalid 'size' argument: " + size);
return -1;
}
if (this.teamSize < sanitizedSize) {sanitizedSize = this.teamSize;}
@ -3561,7 +3583,7 @@ Bladeburner.prototype.setTeamSizeNetscriptFn = function(type, name, size, worker
Bladeburner.prototype.getCityEstimatedPopulationNetscriptFn = function(cityName, workerScript) {
if (!this.cities.hasOwnProperty(cityName)) {
workerScript.log("ERROR: Bladeburner.getCityEstimatedPopulation() failed because the specified " +
workerScript.log("ERROR: bladeburner.getCityEstimatedPopulation() failed because the specified " +
"city was invalid: " + cityName + ". Note that this city argument is case-sensitive");
return -1;
}
@ -3570,7 +3592,7 @@ Bladeburner.prototype.getCityEstimatedPopulationNetscriptFn = function(cityName,
Bladeburner.prototype.getCityEstimatedCommunitiesNetscriptFn = function(cityName, workerScript) {
if (!this.cities.hasOwnProperty(cityName)) {
workerScript.log("ERROR: Bladeburner.getCityEstimatedCommunities() failed because the specified " +
workerScript.log("ERROR: bladeburner.getCityEstimatedCommunities() failed because the specified " +
"city was invalid: " + cityName + ". Note that this city argument is case-sensitive");
return -1;
}
@ -3579,7 +3601,7 @@ Bladeburner.prototype.getCityEstimatedCommunitiesNetscriptFn = function(cityName
Bladeburner.prototype.getCityChaosNetscriptFn = function(cityName, workerScript) {
if (!this.cities.hasOwnProperty(cityName)) {
workerScript.log("ERROR: Bladeburner.getCityChaos() failed because the specified " +
workerScript.log("ERROR: bladeburner.getCityChaos() failed because the specified " +
"city was invalid: " + cityName + ". Note that this city argument is case-sensitive");
return -1;
}
@ -3588,7 +3610,7 @@ Bladeburner.prototype.getCityChaosNetscriptFn = function(cityName, workerScript)
Bladeburner.prototype.switchCityNetscriptFn = function(cityName, workerScript) {
if (!this.cities.hasOwnProperty(cityName)) {
workerScript.log("ERROR: Bladeburner.switchCity() failed because the specified " +
workerScript.log("ERROR: bladeburner.switchCity() failed because the specified " +
"city was invalid: " + cityName + ". Note that this city argument is case-sensitive");
return false;
}
@ -3636,7 +3658,7 @@ function initBladeburner() {
Skills[SkillNames.BladesIntuition] = new Skill({
name:SkillNames.BladesIntuition,
desc:"Each level of this skill increases your success chance " +
"for all contracts and operations by 3%",
"for all Contracts, Operations, and BlackOps by 3%",
baseCost:5, costInc:2,
successChanceAll:3
});
@ -3650,7 +3672,7 @@ function initBladeburner() {
Skills[SkillNames.Cloak] = new Skill({
name:SkillNames.Cloak,
desc:"Each level of this skill increases your " +
"success chance in stealth-related contracts and operations by 5.5%",
"success chance in stealth-related Contracts, Operations, and BlackOps by 5.5%",
baseCost:3, costInc:1,
successChanceStealth:5.5
});
@ -3661,7 +3683,7 @@ function initBladeburner() {
Skills[SkillNames.Overclock] = new Skill({
name:SkillNames.Overclock,
desc:"Each level of this skill decreases the time it takes " +
"to attempt a contract or operation by 1% (Max Level: 95)",
"to attempt a Contract, Operation, and BlackOp by 1% (Max Level: 95)",
baseCost:5, costInc:1, maxLvl:95,
actionTime:1
});
@ -3675,14 +3697,14 @@ function initBladeburner() {
Skills[SkillNames.ShortCircuit] = new Skill({
name:SkillNames.ShortCircuit,
desc:"Each level of this skill increases your success chance " +
"in contracts and operations that involve retirement by 5.5%",
"in Contracts, Operations, and BlackOps that involve retirement by 5.5%",
baseCost:3, costInc:2,
successChanceKill:5.5
});
Skills[SkillNames.DigitalObserver] = new Skill({
name:SkillNames.DigitalObserver,
desc:"Each level of this skill increases your success chance in " +
"all operations by 4%",
"all Operations and BlackOps by 4%",
baseCost:5, costInc:2,
successChanceOperation:4
});
@ -3698,7 +3720,7 @@ function initBladeburner() {
Skills[SkillNames.Tracer] = new Skill({
name:SkillNames.Tracer,
desc:"Each level of this skill increases your success chance in " +
"all contracts by 4%",
"all Contracts by 4%",
baseCost:3, costInc:2,
successChanceContract:4
});

View File

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

View File

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.39.1",
Version: "0.40.0",
//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
@ -7,7 +7,7 @@ let CONSTANTS = {
MaxSkillLevel: 975,
//How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 250000,
CorpFactionRepRequirement: 200e3,
/* Base costs */
BaseCostFor1GBOfRamHome: 32000,
@ -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,
@ -489,7 +492,10 @@ let CONSTANTS = {
LatestUpdate:
"v0.40.0<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." +
"* <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 " +
@ -497,15 +503,22 @@ let CONSTANTS = {
"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 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>" +
"* Bug Fix: Infiltration buttons can no longer be clicked through NetscriptJS<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"
}
export {CONSTANTS};

View File

@ -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";
@ -577,7 +577,6 @@ function createFactionAugmentationDisplayElements(augmentationsList, augs, facti
var aElem = createElement("a", {
innerText:aug.name, display:"inline",
clickListener:()=>{
console.log('sup buy in fac: '+Settings.SuppressBuyAugmentationConfirmation);
if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(aug, faction);
} else {
@ -670,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);
@ -678,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";

View File

@ -30,9 +30,12 @@ import * as acorn from "../utils/acorn";
* @param {Function=} opt_initFunc Optional initialization function. Used to
* define APIs. When called it is passed the interpreter object and the
* global scope object.
* @param {Number} Bitburner-specific number used for determining exception line numbers
* @constructor
*/
var Interpreter = function(code, opt_initFunc) {
var Interpreter = function(code, opt_initFunc, lineOffset=0) {
this.sourceCode = code;
this.sourceCodeLineOffset = lineOffset;
if (typeof code === 'string') {
code = acorn.parse(code, Interpreter.PARSE_OPTIONS);
}
@ -81,7 +84,8 @@ var Interpreter = function(code, opt_initFunc) {
* @const {!Object} Configuration used for all Acorn parsing.
*/
Interpreter.PARSE_OPTIONS = {
ecmaVersion: 5
ecmaVersion: 5,
locations: true
};
/**
@ -147,6 +151,37 @@ Interpreter.VALUE_IN_DESCRIPTOR = {};
*/
Interpreter.toStringCycles_ = [];
/**
* Determine error/exception line number in Bitburner source code
* @param {Object} AST Node that causes Error/Exception
*/
Interpreter.prototype.getErrorLineNumber = function(node) {
var code = this.sourceCode;
if (node == null || node.start == null) {return NaN;}
try {
code = code.substring(0, node.start);
return (code.match(/\n/g) || []).length + 1 - this.sourceCodeLineOffset;
} catch(e) {
return NaN;
}
}
/**
* Generate the appropriate line number error message for Bitburner
* @param {Number} lineNumber
*/
Interpreter.prototype.getErrorLineNumberMessage = function(lineNumber) {
if (isNaN(lineNumber)) {
return " (Unknown line number)";
} else if (lineNumber <= 0) {
return " (Error occurred in an imported function)";
} else {
return " (Line Number " + lineNumber + ". This line number is probably incorrect " +
"if your script is importing any functions. This is being worked on)";
}
}
/**
* Add more code to the interpreter.
* @param {string|!Object} code Raw JavaScript text or AST.
@ -910,6 +945,66 @@ Interpreter.prototype.initArray = function(scope) {
"}",
"});",
// Polyfill copied from:
// https://tc39.github.io/ecma262/#sec-array.prototype.find
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
"if (!Array.prototype.find) {",
"Object.defineProperty(Array.prototype, 'find', {",
"value: function(predicate) {",
"if (this == null) {",
"throw new TypeError('\"this\" is null or not defined');",
"}",
"var o = Object(this);",
"var len = o.length >>> 0;",
"if (typeof predicate !== 'function') {",
"throw new TypeError('predicate must be a function');",
"}",
"var thisArg = arguments[1];",
"var k = 0;",
"while (k < len) {",
"var kValue = o[k];",
"if (predicate.call(thisArg, kValue, k, o)) {",
"return kValue;",
"}",
"k++;",
"}",
"return undefined;",
"},",
"configurable: true,",
"writable: true",
"});",
"}",
// Poly fill copied from:
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
"if (!Array.prototype.findIndex) {",
"Object.defineProperty(Array.prototype, 'findIndex', {",
"value: function(predicate) {",
"if (this == null) {",
"throw new TypeError('\"this\" is null or not defined');",
"}",
"var o = Object(this);",
"var len = o.length >>> 0;",
"if (typeof predicate !== 'function') {",
"throw new TypeError('predicate must be a function');",
"}",
"var thisArg = arguments[1];",
"var k = 0;",
"while (k < len) {",
"var kValue = o[k];",
"if (predicate.call(thisArg, kValue, k, o)) {",
"return k;",
"}",
"k++;",
"}",
"return -1;",
"},",
"configurable: true,",
"writable: true",
"});",
"}",
// Polyfill copied from:
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
"Object.defineProperty(Array.prototype, 'forEach',",
@ -928,6 +1023,48 @@ Interpreter.prototype.initArray = function(scope) {
"}",
"});",
// Polyfill copied from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
"Object.defineProperty(Array.prototype, 'includes', {",
"value: function(searchElement, fromIndex) {",
"if (this == null) {",
"throw new TypeError('\"this\" is null or not defined');",
"}",
"// 1. Let O be ? ToObject(this value).",
"var o = Object(this);",
"// 2. Let len be ? ToLength(? Get(O, \"length\")).",
"var len = o.length >>> 0;",
"// 3. If len is 0, return false.",
"if (len === 0) {",
"return false;",
"}",
"// 4. Let n be ? ToInteger(fromIndex).",
"// (If fromIndex is undefined, this step produces the value 0.)",
"var n = fromIndex | 0;",
"// 5. If n ≥ 0, then",
"// a. Let k be n.",
"// 6. Else n < 0,",
"// a. Let k be len + n.",
"// b. If k < 0, let k be 0.",
"var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);",
"function sameValueZero(x, y) {",
"return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));",
"}",
"// 7. Repeat, while k < len",
"while (k < len) {",
"// a. Let elementK be the result of ? Get(O, ! ToString(k)).",
"// b. If SameValueZero(searchElement, elementK) is true, return true.",
"if (sameValueZero(o[k], searchElement)) {",
"return true;",
"}",
"// c. Increase k by 1. ",
"k++;",
"}",
"// 8. Return false",
"return false;",
"}",
"});",
// Polyfill copied from:
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map
"Object.defineProperty(Array.prototype, 'map',",
@ -1012,48 +1149,6 @@ Interpreter.prototype.initArray = function(scope) {
"}",
"});",
// Polyfill copied from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
"Object.defineProperty(Array.prototype, 'includes', {",
"value: function(searchElement, fromIndex) {",
"if (this == null) {",
"throw new TypeError('\"this\" is null or not defined');",
"}",
"// 1. Let O be ? ToObject(this value).",
"var o = Object(this);",
"// 2. Let len be ? ToLength(? Get(O, \"length\")).",
"var len = o.length >>> 0;",
"// 3. If len is 0, return false.",
"if (len === 0) {",
"return false;",
"}",
"// 4. Let n be ? ToInteger(fromIndex).",
"// (If fromIndex is undefined, this step produces the value 0.)",
"var n = fromIndex | 0;",
"// 5. If n ≥ 0, then",
"// a. Let k be n.",
"// 6. Else n < 0,",
"// a. Let k be len + n.",
"// b. If k < 0, let k be 0.",
"var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);",
"function sameValueZero(x, y) {",
"return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));",
"}",
"// 7. Repeat, while k < len",
"while (k < len) {",
"// a. Let elementK be the result of ? Get(O, ! ToString(k)).",
"// b. If SameValueZero(searchElement, elementK) is true, return true.",
"if (sameValueZero(o[k], searchElement)) {",
"return true;",
"}",
"// c. Increase k by 1. ",
"k++;",
"}",
"// 8. Return false",
"return false;",
"}",
"});",
"(function() {",
"var sort_ = Array.prototype.sort;",
"Array.prototype.sort = function(opt_comp) {",
@ -1203,6 +1298,43 @@ Interpreter.prototype.initString = function(scope) {
"return str;",
"};",
"})();",
// Polyfill copied from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
"if (!String.prototype.endsWith) {",
"String.prototype.endsWith = function(search, this_len) {",
"if (this_len === undefined || this_len > this.length) {",
"this_len = this.length;",
"}",
"return this.substring(this_len - search.length, this_len) === search;",
"};",
"}",
//Polyfill copied from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
"if (!String.prototype.includes) {",
"String.prototype.includes = function(search, start) {",
"'use strict';",
"if (typeof start !== 'number') {",
"start = 0;",
"}",
" ",
"if (start + search.length > this.length) {",
"return false;",
"} else {",
"return this.indexOf(search, start) !== -1;",
"}",
"};",
"}",
// Polyfill copied from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
"if (!String.prototype.startsWith) {",
"String.prototype.startsWith = function(search, pos) {",
"return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;",
"};",
"}",
"");
};
@ -1994,13 +2126,18 @@ Interpreter.prototype.getPrototype = function(value) {
* Fetch a property value from a data object.
* @param {Interpreter.Value} obj Data object.
* @param {Interpreter.Value} name Name of property.
* @param {Acorn AST Node} node Node that triggered this function. Used by Bitburner for getting error line numbers
* @return {Interpreter.Value} Property value (may be undefined).
*/
Interpreter.prototype.getProperty = function(obj, name) {
Interpreter.prototype.getProperty = function(obj, name, node) {
name = String(name);
if (obj === undefined || obj === null) {
let lineNum;
if (node != null && node.loc != null && node.loc.start != null) {
lineNum = node.loc.start.line;
}
this.throwException(this.TYPE_ERROR,
"Cannot read property '" + name + "' of " + obj);
"Cannot read property '" + name + "' of " + obj, lineNum);
}
if (name === 'length') {
// Special cases for magic length property.
@ -2294,11 +2431,12 @@ Interpreter.prototype.createSpecialScope = function(parentScope, opt_scope) {
/**
* Retrieves a value from the scope chain.
* @param {string} name Name of variable.
* @param {Acorn AST Node} node Node that triggered this function. Used by Bitburner for getting error line number
* @return {Interpreter.Value} Any value.
* May be flagged as being a getter and thus needing immediate execution
* (rather than being the value of the property).
*/
Interpreter.prototype.getValueFromScope = function(name) {
Interpreter.prototype.getValueFromScope = function(name, node) {
var scope = this.getScope();
while (scope && scope !== this.global) {
if (name in scope.properties) {
@ -2317,7 +2455,12 @@ Interpreter.prototype.getValueFromScope = function(name) {
prevNode['operator'] === 'typeof') {
return undefined;
}
this.throwException(this.REFERENCE_ERROR, name + ' is not defined');
var lineNum;
if (node != null && node.loc != null && node.loc.start != null) {
lineNum = node.loc.start.line;
}
this.throwException(this.REFERENCE_ERROR, name + ' is not defined', lineNum);
};
/**
@ -2426,17 +2569,18 @@ Interpreter.prototype.calledWithNew = function() {
/**
* Gets a value from the scope chain or from an object property.
* @param {!Array} ref Name of variable or object/propname tuple.
* @param {Acorn AST Node} node Node that triggered this function. Used by Bitburner for getting error line number
* @return {Interpreter.Value} Any value.
* May be flagged as being a getter and thus needing immediate execution
* (rather than being the value of the property).
*/
Interpreter.prototype.getValue = function(ref) {
Interpreter.prototype.getValue = function(ref, node) {
if (ref[0] === Interpreter.SCOPE_REFERENCE) {
// A null/varname variable lookup.
return this.getValueFromScope(ref[1]);
return this.getValueFromScope(ref[1], node);
} else {
// An obj/prop components tuple (foo.bar).
return this.getProperty(ref[0], ref[1]);
return this.getProperty(ref[0], ref[1], node);
}
};
@ -2478,7 +2622,7 @@ Interpreter.prototype.setValue = function(ref, value) {
* provided) or the value to throw (if no message).
* @param {string=} opt_message Message being thrown.
*/
Interpreter.prototype.throwException = function(errorClass, opt_message) {
Interpreter.prototype.throwException = function(errorClass, opt_message, lineNumber) {
if (opt_message === undefined) {
var error = errorClass; // This is a value to throw, not an error class.
} else {
@ -2486,7 +2630,11 @@ Interpreter.prototype.throwException = function(errorClass, opt_message) {
this.setProperty(error, 'message', opt_message,
Interpreter.NONENUMERABLE_DESCRIPTOR);
}
this.unwind(Interpreter.Completion.THROW, error, undefined);
var lineNumErrorMsg;
if (lineNumber != null) {
lineNumErrorMsg = this.getErrorLineNumberMessage(lineNumber);
}
this.unwind(Interpreter.Completion.THROW, error, undefined, lineNumErrorMsg);
// Abort anything related to the current step.
throw Interpreter.STEP_ERROR;
};
@ -2500,7 +2648,7 @@ Interpreter.prototype.throwException = function(errorClass, opt_message) {
* @param {Interpreter.Value=} value Value computed, returned or thrown.
* @param {string=} label Target label for break or return.
*/
Interpreter.prototype.unwind = function(type, value, label) {
Interpreter.prototype.unwind = function(type, value, label, lineNumberMsg="") {
if (type === Interpreter.Completion.NORMAL) {
throw TypeError('Should not unwind for NORMAL completions');
}
@ -2548,9 +2696,9 @@ Interpreter.prototype.unwind = function(type, value, label) {
var name = this.getProperty(value, 'name').toString();
var message = this.getProperty(value, 'message').valueOf();
var type = errorTable[name] || Error;
realError = type(message);
realError = type(message + lineNumberMsg);
} else {
realError = String(value);
realError = String(value) + lineNumberMsg;
}
throw realError;
};
@ -2656,7 +2804,7 @@ Interpreter.prototype['stepAssignmentExpression'] =
state.leftValue_ = state.value;
}
if (!state.doneGetter_ && node['operator'] !== '=') {
var leftValue = this.getValue(state.leftReference_);
var leftValue = this.getValue(state.leftReference_, node);
state.leftValue_ = leftValue;
if (leftValue && typeof leftValue === 'object' && leftValue.isGetter) {
// Clear the getter flag and call the getter function.
@ -2742,15 +2890,17 @@ Interpreter.prototype['stepBinaryExpression'] = function(stack, state, node) {
case '>>>': value = leftValue >>> rightValue; break;
case 'in':
if (!rightValue || !rightValue.isObject) {
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.TYPE_ERROR,
"'in' expects an object, not '" + rightValue + "'");
"'in' expects an object, not '" + rightValue + "'", lineNum);
}
value = this.hasProperty(rightValue, leftValue);
break;
case 'instanceof':
if (!this.isa(rightValue, this.FUNCTION)) {
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.TYPE_ERROR,
'Right-hand side of instanceof is not an object');
'Right-hand side of instanceof is not an object', lineNum);
}
value = leftValue.isObject ? this.isa(leftValue, rightValue) : false;
break;
@ -2788,7 +2938,7 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
state.doneCallee_ = 2;
var func = state.value;
if (Array.isArray(func)) {
state.func_ = this.getValue(func);
state.func_ = this.getValue(func, node);
if (func[0] === Interpreter.SCOPE_REFERENCE) {
// (Globally or locally) named function. Is it named 'eval'?
state.directEval_ = (func[1] === 'eval');
@ -2823,7 +2973,8 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
if (node['type'] === 'NewExpression') {
if (func.illegalConstructor) {
// Illegal: new escape();
this.throwException(this.TYPE_ERROR, func + ' is not a constructor');
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.TYPE_ERROR, func + ' is not a constructor', lineNum);
}
// Constructor, 'this' is new object.
var proto = func.properties['prototype'];
@ -2842,7 +2993,8 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
if (!state.doneExec_) {
state.doneExec_ = true;
if (!func || !func.isObject) {
this.throwException(this.TYPE_ERROR, func + ' is not a function');
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.TYPE_ERROR, func + ' is not a function', lineNum);
}
var funcNode = func.node;
if (funcNode) {
@ -2880,7 +3032,8 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
var ast = acorn.parse(code.toString(), Interpreter.PARSE_OPTIONS);
} catch (e) {
// Acorn threw a SyntaxError. Rethrow as a trappable error.
this.throwException(this.SYNTAX_ERROR, 'Invalid code: ' + e.message);
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.SYNTAX_ERROR, 'Invalid code: ' + e.message, lineNum);
}
var evalNode = new this.nodeConstructor();
evalNode['type'] = 'EvalProgram_';
@ -2917,7 +3070,8 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
var f = new F();
f();
*/
this.throwException(this.TYPE_ERROR, func.class + ' is not a function');
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.TYPE_ERROR, func.class + ' is not a function', lineNum);
}
} else {
// Execution complete. Put the return value on the stack.
@ -3032,8 +3186,9 @@ Interpreter.prototype['stepForInStatement'] = function(stack, state, node) {
if (node['left']['declarations'] &&
node['left']['declarations'][0]['init']) {
if (state.scope.strict) {
let lineNum = this.getErrorLineNumber(node);
this.throwException(this.SYNTAX_ERROR,
'for-in loop variable declaration may not have an initializer.');
'for-in loop variable declaration may not have an initializer.', lineNum);
}
// Variable initialization: for (var x = 4 in y)
return new Interpreter.State(node['left'], state.scope);
@ -3193,7 +3348,7 @@ Interpreter.prototype['stepIdentifier'] = function(stack, state, node) {
stack[stack.length - 1].value = [Interpreter.SCOPE_REFERENCE, node['name']];
return;
}
var value = this.getValueFromScope(node['name']);
var value = this.getValueFromScope(node['name'], node);
// An identifier could be a getter if it's a property on the global object.
if (value && typeof value === 'object' && value.isGetter) {
// Clear the getter flag and call the getter function.
@ -3428,7 +3583,7 @@ Interpreter.prototype['stepSwitchStatement'] = function(stack, state, node) {
Interpreter.prototype['stepThisExpression'] = function(stack, state, node) {
stack.pop();
stack[stack.length - 1].value = this.getValueFromScope('this');
stack[stack.length - 1].value = this.getValueFromScope('this', node);
};
Interpreter.prototype['stepThrowStatement'] = function(stack, state, node) {
@ -3529,7 +3684,7 @@ Interpreter.prototype['stepUpdateExpression'] = function(stack, state, node) {
state.leftValue_ = state.value;
}
if (!state.doneGetter_) {
var leftValue = this.getValue(state.leftSide_);
var leftValue = this.getValue(state.leftSide_, node);
state.leftValue_ = leftValue;
if (leftValue && typeof leftValue === 'object' && leftValue.isGetter) {
// Clear the getter flag and call the getter function.

View File

@ -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) || !powerOfTwo(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 : {
@ -298,7 +316,13 @@ function NetscriptFunctions(workerScript) {
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success!
const percentHacked = scriptCalculatePercentMoneyHacked(server);
const maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax))
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) {
//Server has a 'max money' of 0 (probably).
//We'll set this to an arbitrarily large value
maxThreadNeeded = 1e6;
}
let moneyGained = Math.floor(server.moneyAvailable * percentHacked) * threads;
//Over-the-top safety checks
@ -1591,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);
@ -1609,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 "";
@ -2106,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) {
@ -2741,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;}
@ -2938,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;}
@ -3042,7 +3127,7 @@ function NetscriptFunctions(workerScript) {
const crime = findCrime(crimeRoughName.toLowerCase());
if(crime == null) { // couldn't find crime
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crime);
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crimeRoughName);
}
if(workerScript.disableLogs.ALL == null && workerScript.disableLogs.commitCrime == null) {
workerScript.scriptRef.log("Attempting to commit crime: "+crime.name+"...");
@ -3094,6 +3179,25 @@ function NetscriptFunctions(workerScript) {
}
return res;
},
getOwnedSourceFiles : function() {
let ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}
if (workerScript.checkingRam) {
return updateStaticRam("getOwnedSourceFiles", ramCost);
}
updateDynamicRam("getOwnedSourceFiles", ramCost);
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getOwnedSourceFiles(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return [];
}
}
let res = [];
for (let i = 0; i < Player.sourceFiles.length; ++i) {
res.push({n: Player.sourceFiles[i].n, lvl: Player.sourceFiles[i].lvl});
}
return res;
},
getAugmentationsFromFaction : function(facname) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
if (Player.bitNodeN !== 4) {ramCost *= 8;}

View File

@ -39,8 +39,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 {

View File

@ -166,9 +166,11 @@ function startNetscript1Script(workerScript) {
workerScript.running = true;
//Process imports
var ast;
var codeWithImports, codeLineOffset;
try {
ast = processNetscript1Imports(code, workerScript);
let importProcessingRes = processNetscript1Imports(code, workerScript);
codeWithImports = importProcessingRes.code;
codeLineOffset = importProcessingRes.lineOffset;
} catch(e) {
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
@ -197,6 +199,23 @@ function startNetscript1Script(workerScript) {
});
}
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
} else if (name === "sprintf" || name === "vsprintf" || name === "scp") {
let tempWrapper = function() {
let fnArgs = [];
//All of the Object/array elements are in JSInterpreter format, so
//we have to convert them back to native format to pass them to these fns
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'object' || arguments[i].constructor === Array) {
fnArgs.push(int.pseudoToNative(arguments[i]));
} else {
fnArgs.push(arguments[i]);
}
}
return entry.apply(null, fnArgs);
}
int.setProperty(scope, name, int.createNativeFunction(tempWrapper));
} else {
let tempWrapper = function() {
let res = entry.apply(null, arguments);
@ -205,7 +224,6 @@ function startNetscript1Script(workerScript) {
return res;
} else if (res.constructor === Array || (res === Object(res))) {
//Objects and Arrays must be converted to the interpreter's format
console.log("Function returning object detected: " + name);
return int.nativeToPseudo(res);
} else {
return res;
@ -225,7 +243,7 @@ function startNetscript1Script(workerScript) {
var interpreter;
try {
interpreter = new Interpreter(ast, interpreterInitialization);
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
} catch(e) {
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
@ -273,7 +291,11 @@ function startNetscript1Script(workerScript) {
we'll implement it ourselves by parsing the Nodes in the AST out.
@param code - The script's code
@returns - ES5-compliant AST with properly imported functions
@returns {Object} {
code: Newly-generated code with imported functions
lineOffset: Net number of lines of code added/removed due to imported functions
Should typically be positive
}
*/
function processNetscript1Imports(code, workerScript) {
//allowReserved prevents 'import' from throwing error in ES5
@ -294,10 +316,12 @@ function processNetscript1Imports(code, workerScript) {
}
var generatedCode = ""; //Generated Javascript Code
var hasImports = false;
//Walk over the tree and process ImportDeclaration nodes
walk.simple(ast, {
ImportDeclaration: (node) => {
hasImports = true;
let scriptName = node.source.value;
let script = getScript(scriptName);
if (script == null) {
@ -336,7 +360,7 @@ function processNetscript1Imports(code, workerScript) {
//Finish
generatedCode += (
"})(" + namespace + " || " + "(" + namespace + " = {}));"
"})(" + namespace + " || " + "(" + namespace + " = {}));\n"
)
} else {
//import {...} from script
@ -366,22 +390,34 @@ function processNetscript1Imports(code, workerScript) {
}
});
//If there are no imports, just return the original code
if (!hasImports) {return {code:code, lineOffset:0};}
//Remove ImportDeclarations from AST. These ImportDeclarations must be in top-level
var linesRemoved = 0;
if (ast.type !== "Program" || ast.body == null) {
throw new Error("Code could not be properly parsed");
}
for (let i = ast.body.length-1; i >= 0; --i) {
if (ast.body[i].type === "ImportDeclaration") {
ast.body.splice(i, 1);
++linesRemoved;
}
}
//Calculated line offset
var lineOffset = (generatedCode.match(/\n/g) || []).length - linesRemoved;
//Convert the AST back into code
code = generate(ast);
//Add the imported code and re-generate in ES5 (JS Interpreter for NS1 only supports ES5);
code = generatedCode + code;
return parse(code, {ecmaVersion:5});
var res = {
code: code,
lineOffset: lineOffset
}
return res;
}
//Loop through workerScripts and run every script that is not currently running

View File

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

View File

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

View File

@ -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,
@ -115,7 +116,7 @@ $(document).keydown(function(event) {
if (command.length > 0) {
post(
"[" +
(FconfSettings.ENABLE_TIMESTAMPS ? Terminal.getTimestamp() + " " : "") +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname +
" ~]> " + command
);
@ -646,11 +647,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,97 +1827,124 @@ 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:
// 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;
}
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 (serv.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)'));
@ -1933,28 +1956,35 @@ let Terminal = {
post("Defense: " + Player.defense + " / 1500");
post("Dexterity: " + Player.dexterity + " / 1500");
post("Agility: " + Player.agility + " / 1500");
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.");
return;
}
break;
default:
post("Invalid executable. Cannot be run");
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) {

View File

@ -65,6 +65,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
@ -570,6 +583,16 @@ let Engine = {
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 flashClass = "flashing-button";
if(!Settings.AutosaveInterval) {
save.classList.add(flashClass);
} else {
save.classList.remove(flashClass);
}
},
/* Display character info */
@ -1646,12 +1669,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);
}

View File

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

View File

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

View File

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