Merge pull request #1225 from danielyxie/dev

Fix scrolling and pages display
This commit is contained in:
hydroflame 2021-09-11 13:25:00 -04:00 committed by GitHub
commit 3b314f5d1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3177 additions and 4001 deletions

@ -11,5 +11,5 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
vertical-align: top; vertical-align: middle;
} }

@ -4,10 +4,7 @@
list-style-type: none; list-style-type: none;
} }
#active-scripts-container { .active-scripts-container {
position: fixed;
padding-top: 10px;
> p { > p {
width: 70%; width: 70%;
margin: 6px; margin: 6px;

@ -5,12 +5,7 @@
*/ */
@import "theme"; @import "theme";
#augmentations-container { .augmentations-content {
position: fixed;
padding-top: 10px;
}
#augmentations-content {
> p { > p {
font-size: $defaultFontSize * 0.875; font-size: $defaultFontSize * 0.875;
width: 70%; width: 70%;

@ -1,8 +1,6 @@
@import "theme"; @import "theme";
#bladeburner-container { .bladeburner-container {
position: fixed;
padding: 6px;
a, a,
div, div,
p, p,

@ -6,7 +6,9 @@
*/ */
#character-overview-wrapper { #character-overview-wrapper {
position: relative; position: fixed;
top: 0;
right: 0;
} }
#character-overview-container { #character-overview-container {

@ -1,49 +0,0 @@
@import "theme";
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0 0 6px;
}
.CodeMirror {
height: 100%;
width: 100%;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
font-size: $defaultFontSize;
}
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8f908a;
}
.CodeMirror-selection-highlight-scrollbar {
background-color: #8f908a;
}
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404f7d;
}
/**
* Vim command display
*/
#codemirror-vim-command-display-wrapper {
background-color: white;
font-size: 13px;
height: 30px;
margin-left: 6px;
}

@ -8,10 +8,10 @@
* companies * companies
*/ */
#cmpy-mgmt-container p, .cmpy-mgmt-container p,
#cmpy-mgmt-container a, .cmpy-mgmt-container a,
#cmpy-mgmt-container div, .cmpy-mgmt-container div,
#cmpy-mgmt-container br { .cmpy-mgmt-container br {
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
} }

@ -1,4 +0,0 @@
#corporation-container {
position: fixed;
padding: 6px;
}

@ -5,10 +5,7 @@
* Styling for the Gang mechanic UI (BitNode-2) * Styling for the Gang mechanic UI (BitNode-2)
*/ */
#gang-container { .gang-container {
position: fixed;
padding: 6px;
p, p,
pre { pre {
font-size: $defaultFontSize * 0.9375; font-size: $defaultFontSize * 0.9375;

@ -5,11 +5,6 @@
* Styling for the Hacknet Nodes UI Page * Styling for the Hacknet Nodes UI Page
*/ */
#hacknet-nodes-container {
position: fixed;
padding: 10px;
}
.hacknet-general-info { .hacknet-general-info {
margin: 10px; margin: 10px;
width: 70vw; width: 70vw;

@ -78,6 +78,10 @@
background-color: #aaa; background-color: #aaa;
} }
.menu-header {
position: relative;
}
#hacking-menu-header-li, #hacking-menu-header-li,
#character-menu-header-li, #character-menu-header-li,
#world-menu-header-li, #world-menu-header-li,
@ -129,7 +133,5 @@
/* Slide down transition */ /* Slide down transition */
.mainmenu-accordion-panel { .mainmenu-accordion-panel {
max-height: 0;
opacity: 1;
transition: max-height 0.2s ease-out; transition: max-height 0.2s ease-out;
} }

@ -5,23 +5,19 @@
terminal which has its own page) */ terminal which has its own page) */
.generic-menupage-container { .generic-menupage-container {
height: 100%; padding-left: 2px;
padding-left: 10px;
margin-left: 10%; margin-left: 10%;
width: 99%; -ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
}
#generic-react-container {
padding: 10px;
overflow-y: scroll; overflow-y: scroll;
} }
/* Character Info */ #generic-react-container::-webkit-scrollbar {
#character-container { display: none; /* for Chrome, Safari, and Opera */
padding-top: 10px;
position: fixed;
}
/* World */
#world-container {
position: fixed;
padding-top: 10px;
} }
#world-city-name, #world-city-name,

@ -1,5 +1,3 @@
#milestones-container { .milestones-container {
position: fixed;
padding: 6px;
width: 60%; width: 60%;
} }

@ -27,6 +27,10 @@
color: var(--my-font-color); color: var(--my-font-color);
} }
.popup-box-input-div {
margin: 2px;
}
.popup-box-button, .popup-box-button,
.popup-box-button-inactive { .popup-box-button-inactive {
color: #aaa; color: #aaa;
@ -189,11 +193,6 @@
background-color: #000; background-color: #000;
} }
/* Generic Yes No Box */
#yes-no-text-input-box-input {
color: #fff;
}
/* Game Options */ /* Game Options */
#game-options-container { #game-options-container {
transition: opacity 400ms ease-in; transition: opacity 400ms ease-in;

@ -3,11 +3,6 @@
*/ */
@import "theme"; @import "theme";
#resleeve-container {
position: fixed;
padding: 6px;
}
.resleeve-elem { .resleeve-elem {
border: 1px solid white; border: 1px solid white;
margin: 4px; margin: 4px;

@ -5,29 +5,14 @@
* Styling for Script Editor (both Ace and CodeMirror) * Styling for Script Editor (both Ace and CodeMirror)
*/ */
#script-editor-container {
background-color: transparent;
}
/* This temp element is used for auto adjusting filename field */
.tmp-element {
visibility: hidden;
white-space: pre;
}
#script-editor-container {
position: fixed;
padding-top: 10px;
}
#script-editor-buttons-wrapper { #script-editor-buttons-wrapper {
width: 100%; width: 100%;
padding-right: 0; padding-right: 0;
margin-right: 0; margin-right: 0;
} }
#script-editor-wrapper { .script-editor-wrapper {
height: 100%; height: 110vh;
width: 70%; width: 70%;
background: transparent; background: transparent;
} }

@ -3,11 +3,6 @@
*/ */
@import "theme"; @import "theme";
#sleeves-container {
position: fixed;
padding: 6px;
}
.sleeve-elem { .sleeve-elem {
border: 1px solid white; border: 1px solid white;
margin: 4px; margin: 4px;

@ -1,9 +1,6 @@
@import "theme"; @import "theme";
#stock-market-container { .stock-market-container {
position: fixed;
padding: 6px;
p { p {
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
} }

@ -12,6 +12,12 @@
body { body {
background-color: var(--my-background-color); background-color: var(--my-background-color);
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
}
body::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
} }
p, p,
@ -113,6 +119,10 @@ a:visited {
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
padding: 1px 3px; padding: 1px 3px;
font-size: $defaultFontSize * 0.625;
top: 0;
right: 0;
position: absolute;
} }
.notification-off { .notification-off {
@ -120,6 +130,21 @@ a:visited {
color: #333; color: #333;
border-radius: 0; border-radius: 0;
padding: 0; padding: 0;
display: "none";
}
.notification {
position: relative;
display: inline-block;
}
.notification .badge {
position: absolute;
top: 0;
right: 0;
padding: 2px;
background: red;
color: white;
} }
/* help tip. Question mark that opens popup with info/details */ /* help tip. Question mark that opens popup with info/details */

File diff suppressed because one or more lines are too long

@ -1,2 +1,2 @@
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],a=0,s=[];a<f.length;a++)i=f[a],Object.prototype.hasOwnProperty.call(u,i)&&u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(p&&p(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={2:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var p=c;r.push([905,0]),o()}({905:function(n,t,o){"use strict";o.r(t);o(906),o(908),o(910),o(912),o(914),o(916),o(918),o(920),o(922),o(924),o(926),o(928),o(930),o(932),o(934),o(936),o(938),o(940),o(942),o(944),o(946),o(948),o(950),o(952),o(954),o(956),o(958),o(960),o(962),o(964),o(966),o(968),o(970)},908:function(n,t,o){},910:function(n,t,o){},912:function(n,t,o){},914:function(n,t,o){},916:function(n,t,o){},918:function(n,t,o){},920:function(n,t,o){},922:function(n,t,o){},924:function(n,t,o){},926:function(n,t,o){},928:function(n,t,o){},930:function(n,t,o){},932:function(n,t,o){},934:function(n,t,o){},936:function(n,t,o){},938:function(n,t,o){},940:function(n,t,o){},942:function(n,t,o){},944:function(n,t,o){},946:function(n,t,o){},948:function(n,t,o){},950:function(n,t,o){},952:function(n,t,o){},954:function(n,t,o){},956:function(n,t,o){},958:function(n,t,o){},960:function(n,t,o){},962:function(n,t,o){},964:function(n,t,o){},966:function(n,t,o){},968:function(n,t,o){},970:function(n,t,o){}}); !function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],a=0,s=[];a<f.length;a++)i=f[a],Object.prototype.hasOwnProperty.call(r,i)&&r[i]&&s.push(r[i][0]),r[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(p&&p(t);s.length;)s.shift()();return u.push.apply(u,l||[]),o()}function o(){for(var n,t=0;t<u.length;t++){for(var o=u[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==r[c]&&(e=!1)}e&&(u.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},r={2:0},u=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var p=c;u.push([910,0]),o()}({910:function(n,t,o){"use strict";o.r(t);o(911),o(913),o(915),o(917),o(919),o(921),o(923),o(925),o(927),o(929),o(931),o(933),o(935),o(937),o(939),o(941),o(943),o(945),o(947),o(949),o(951),o(953),o(955),o(957),o(959),o(961),o(963),o(965),o(967),o(969),o(971)},913:function(n,t,o){},915:function(n,t,o){},917:function(n,t,o){},919:function(n,t,o){},921:function(n,t,o){},923:function(n,t,o){},925:function(n,t,o){},927:function(n,t,o){},929:function(n,t,o){},931:function(n,t,o){},933:function(n,t,o){},935:function(n,t,o){},937:function(n,t,o){},939:function(n,t,o){},941:function(n,t,o){},943:function(n,t,o){},945:function(n,t,o){},947:function(n,t,o){},949:function(n,t,o){},951:function(n,t,o){},953:function(n,t,o){},955:function(n,t,o){},957:function(n,t,o){},959:function(n,t,o){},961:function(n,t,o){},963:function(n,t,o){},965:function(n,t,o){},967:function(n,t,o){},969:function(n,t,o){},971:function(n,t,o){}});
//# sourceMappingURL=engineStyle.bundle.js.map //# sourceMappingURL=engineStyle.bundle.js.map

272
dist/engineStyle.css vendored

@ -12,7 +12,7 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
vertical-align: top; } vertical-align: middle; }
:root { :root {
--my-font-color: #6f3; --my-font-color: #6f3;
@ -21,7 +21,15 @@
--my-prompt-color: #f92672; } --my-prompt-color: #f92672; }
body { body {
background-color: var(--my-background-color); } background-color: var(--my-background-color);
-ms-overflow-style: none;
/* for Internet Explorer, Edge */
scrollbar-width: none;
/* for Firefox */ }
body::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */ }
p, p,
pre, pre,
@ -105,13 +113,30 @@ a:visited {
background-color: #fa3e3e; background-color: #fa3e3e;
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
padding: 1px 3px; } padding: 1px 3px;
font-size: 10px;
top: 0;
right: 0;
position: absolute; }
.notification-off { .notification-off {
background-color: #333; background-color: #333;
color: #333; color: #333;
border-radius: 0; border-radius: 0;
padding: 0; } padding: 0;
display: "none"; }
.notification {
position: relative;
display: inline-block; }
.notification .badge {
position: absolute;
top: 0;
right: 0;
padding: 2px;
background: red;
color: white; }
/* help tip. Question mark that opens popup with info/details */ /* help tip. Question mark that opens popup with info/details */
.help-tip { .help-tip {
@ -545,7 +570,7 @@ input[type="checkbox"] {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
vertical-align: top; } vertical-align: middle; }
:root { :root {
--my-font-color: #6f3; --my-font-color: #6f3;
@ -554,7 +579,15 @@ input[type="checkbox"] {
--my-prompt-color: #f92672; } --my-prompt-color: #f92672; }
body { body {
background-color: var(--my-background-color); } background-color: var(--my-background-color);
-ms-overflow-style: none;
/* for Internet Explorer, Edge */
scrollbar-width: none;
/* for Firefox */ }
body::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */ }
p, p,
pre, pre,
@ -638,13 +671,30 @@ a:visited {
background-color: #fa3e3e; background-color: #fa3e3e;
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
padding: 1px 3px; } padding: 1px 3px;
font-size: 10px;
top: 0;
right: 0;
position: absolute; }
.notification-off { .notification-off {
background-color: #333; background-color: #333;
color: #333; color: #333;
border-radius: 0; border-radius: 0;
padding: 0; } padding: 0;
display: "none"; }
.notification {
position: relative;
display: inline-block; }
.notification .badge {
position: absolute;
top: 0;
right: 0;
padding: 2px;
background: red;
color: white; }
/* help tip. Question mark that opens popup with info/details */ /* help tip. Question mark that opens popup with info/details */
.help-tip { .help-tip {
@ -1121,6 +1171,9 @@ button {
.mainmenu > li button.active:hover { .mainmenu > li button.active:hover {
background-color: #aaa; } background-color: #aaa; }
.menu-header {
position: relative; }
#hacking-menu-header-li, #hacking-menu-header-li,
#character-menu-header-li, #character-menu-header-li,
#world-menu-header-li, #world-menu-header-li,
@ -1166,8 +1219,6 @@ button {
/* Slide down transition */ /* Slide down transition */
.mainmenu-accordion-panel { .mainmenu-accordion-panel {
max-height: 0;
opacity: 1;
transition: max-height 0.2s ease-out; } transition: max-height 0.2s ease-out; }
/* COLORS */ /* COLORS */
@ -1176,7 +1227,9 @@ button {
* Styling for the Character Overview Panel (top-right panel) * Styling for the Character Overview Panel (top-right panel)
*/ */
#character-overview-wrapper { #character-overview-wrapper {
position: relative; } position: fixed;
top: 0;
right: 0; }
#character-overview-container { #character-overview-container {
display: none; display: none;
@ -1351,25 +1404,13 @@ button {
/** /**
* Styling for Script Editor (both Ace and CodeMirror) * Styling for Script Editor (both Ace and CodeMirror)
*/ */
#script-editor-container {
background-color: transparent; }
/* This temp element is used for auto adjusting filename field */
.tmp-element {
visibility: hidden;
white-space: pre; }
#script-editor-container {
position: fixed;
padding-top: 10px; }
#script-editor-buttons-wrapper { #script-editor-buttons-wrapper {
width: 100%; width: 100%;
padding-right: 0; padding-right: 0;
margin-right: 0; } margin-right: 0; }
#script-editor-wrapper { .script-editor-wrapper {
height: 100%; height: 110vh;
width: 70%; width: 70%;
background: transparent; } background: transparent; }
@ -1437,62 +1478,17 @@ button {
align-items: center; align-items: center;
justify-content: start; } justify-content: start; }
/* COLORS */
/* Attributes */
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0 0 6px; }
.CodeMirror {
height: 100%;
width: 100%;
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-size: 16px; }
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8f908a; }
.CodeMirror-selection-highlight-scrollbar {
background-color: #8f908a; }
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404f7d; }
/**
* Vim command display
*/
#codemirror-vim-command-display-wrapper {
background-color: white;
font-size: 13px;
height: 30px;
margin-left: 6px; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
.active-scripts-list { .active-scripts-list {
list-style-type: none; } list-style-type: none; }
#active-scripts-container { .active-scripts-container > p {
position: fixed;
padding-top: 10px; }
#active-scripts-container > p {
width: 70%; width: 70%;
margin: 6px; margin: 6px;
padding: 4px; } padding: 4px; }
#active-scripts-container .accordion-header > pre {
.active-scripts-container .accordion-header > pre {
color: white; } color: white; }
.active-scripts-server-header { .active-scripts-server-header {
@ -1588,10 +1584,6 @@ button {
/** /**
* Styling for the Hacknet Nodes UI Page * Styling for the Hacknet Nodes UI Page
*/ */
#hacknet-nodes-container {
position: fixed;
padding: 10px; }
.hacknet-general-info { .hacknet-general-info {
margin: 10px; margin: 10px;
width: 70vw; } width: 70vw; }
@ -1646,21 +1638,20 @@ button {
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding /* CSS for different main menu pages, such as character info, script editor, etc (but excluding
terminal which has its own page) */ terminal which has its own page) */
.generic-menupage-container { .generic-menupage-container {
height: 100%; padding-left: 2px;
padding-left: 10px;
margin-left: 10%; margin-left: 10%;
width: 99%; -ms-overflow-style: none;
/* for Internet Explorer, Edge */
scrollbar-width: none;
/* for Firefox */ }
#generic-react-container {
padding: 10px;
overflow-y: scroll; } overflow-y: scroll; }
/* Character Info */ #generic-react-container::-webkit-scrollbar {
#character-container { display: none;
padding-top: 10px; /* for Chrome, Safari, and Opera */ }
position: fixed; }
/* World */
#world-container {
position: fixed;
padding-top: 10px; }
#world-city-name, #world-city-name,
#world-city-desc { #world-city-desc {
@ -1781,11 +1772,7 @@ button {
*/ */
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#augmentations-container { .augmentations-content > p {
position: fixed;
padding-top: 10px; }
#augmentations-content > p {
font-size: 14px; font-size: 14px;
width: 70%; } width: 70%; }
@ -1825,12 +1812,10 @@ button {
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#stock-market-container { .stock-market-container p {
position: fixed;
padding: 6px; }
#stock-market-container p {
font-size: 13px; } font-size: 13px; }
#stock-market-container a {
.stock-market-container a {
font-size: 14px; } font-size: 14px; }
.stock-market-info-and-purchases > h2 { .stock-market-info-and-purchases > h2 {
@ -1959,7 +1944,7 @@ button {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
vertical-align: top; } vertical-align: middle; }
:root { :root {
--my-font-color: #6f3; --my-font-color: #6f3;
@ -1968,7 +1953,15 @@ button {
--my-prompt-color: #f92672; } --my-prompt-color: #f92672; }
body { body {
background-color: var(--my-background-color); } background-color: var(--my-background-color);
-ms-overflow-style: none;
/* for Internet Explorer, Edge */
scrollbar-width: none;
/* for Firefox */ }
body::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */ }
p, p,
pre, pre,
@ -2052,13 +2045,30 @@ a:visited {
background-color: #fa3e3e; background-color: #fa3e3e;
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
padding: 1px 3px; } padding: 1px 3px;
font-size: 10px;
top: 0;
right: 0;
position: absolute; }
.notification-off { .notification-off {
background-color: #333; background-color: #333;
color: #333; color: #333;
border-radius: 0; border-radius: 0;
padding: 0; } padding: 0;
display: "none"; }
.notification {
position: relative;
display: inline-block; }
.notification .badge {
position: absolute;
top: 0;
right: 0;
padding: 2px;
background: red;
color: white; }
/* help tip. Question mark that opens popup with info/details */ /* help tip. Question mark that opens popup with info/details */
.help-tip { .help-tip {
@ -2393,6 +2403,9 @@ input[type="checkbox"] {
/* Sit on top of the container */ /* Sit on top of the container */
color: var(--my-font-color); } color: var(--my-font-color); }
.popup-box-input-div {
margin: 2px; }
.popup-box-button, .popup-box-button,
.popup-box-button-inactive { .popup-box-button-inactive {
color: #aaa; color: #aaa;
@ -2535,10 +2548,6 @@ input[type="checkbox"] {
#infiltration-faction-select { #infiltration-faction-select {
background-color: #000; } background-color: #000; }
/* Generic Yes No Box */
#yes-no-text-input-box-input {
color: #fff; }
/* Game Options */ /* Game Options */
#game-options-container { #game-options-container {
transition: opacity 400ms ease-in; } transition: opacity 400ms ease-in; }
@ -2695,7 +2704,7 @@ input[type="checkbox"] {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
vertical-align: top; } vertical-align: middle; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
@ -2997,10 +3006,10 @@ input[type="checkbox"] {
* for the mechanic before it got changed to avoid confusion with normal * for the mechanic before it got changed to avoid confusion with normal
* companies * companies
*/ */
#cmpy-mgmt-container p, .cmpy-mgmt-container p,
#cmpy-mgmt-container a, .cmpy-mgmt-container a,
#cmpy-mgmt-container div, .cmpy-mgmt-container div,
#cmpy-mgmt-container br { .cmpy-mgmt-container br {
font-size: 13px; } font-size: 13px; }
/* Header tabs */ /* Header tabs */
@ -3130,14 +3139,11 @@ input[type="checkbox"] {
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#bladeburner-container { .bladeburner-container a,
position: fixed; .bladeburner-container div,
padding: 6px; } .bladeburner-container p,
#bladeburner-container a, .bladeburner-container pre,
#bladeburner-container div, .bladeburner-container td {
#bladeburner-container p,
#bladeburner-container pre,
#bladeburner-container td {
font-size: 13px; } font-size: 13px; }
.bladeburner-action { .bladeburner-action {
@ -3245,13 +3251,11 @@ input[type="checkbox"] {
/** /**
* Styling for the Gang mechanic UI (BitNode-2) * Styling for the Gang mechanic UI (BitNode-2)
*/ */
#gang-container { .gang-container p,
position: fixed; .gang-container pre {
padding: 6px; }
#gang-container p,
#gang-container pre {
font-size: 15px; } font-size: 15px; }
#gang-container select {
.gang-container select {
background-color: black; background-color: black;
color: white; } color: white; }
@ -3283,10 +3287,6 @@ input[type="checkbox"] {
*/ */
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#sleeves-container {
position: fixed;
padding: 6px; }
.sleeve-elem { .sleeve-elem {
border: 1px solid white; border: 1px solid white;
margin: 4px; margin: 4px;
@ -3308,10 +3308,6 @@ input[type="checkbox"] {
*/ */
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
#resleeve-container {
position: fixed;
padding: 6px; }
.resleeve-elem { .resleeve-elem {
border: 1px solid white; border: 1px solid white;
margin: 4px; margin: 4px;
@ -6243,9 +6239,7 @@ html {
.casino-card.black { .casino-card.black {
color: black; } color: black; }
#milestones-container { .milestones-container {
position: fixed;
padding: 6px;
width: 60%; } width: 60%; }
/* COLORS */ /* COLORS */
@ -6294,9 +6288,5 @@ html {
50% { 50% {
color: #adff2f; } } color: #adff2f; } }
#corporation-container {
position: fixed;
padding: 6px; }
/*# sourceMappingURL=engineStyle.css.map*/ /*# sourceMappingURL=engineStyle.css.map*/

30
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -41,159 +41,7 @@
<div id="entire-game-container" style="visibility: hidden"> <div id="entire-game-container" style="visibility: hidden">
<div id="mainmenu-container"> <div id="mainmenu-container">
<!-- Main menu --> <!-- Main menu -->
<ul id="mainmenu" class="mainmenu noscrollbar"> <div id="sidebar"></div>
<!-- Hacking dropdown -->
<li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header noselect">Hacking</button>
</li>
<li id="terminal-tab" class="mainmenu-accordion-panel noselect">
<button id="terminal-menu-link">Terminal</button>
</li>
<li id="create-script-tab" class="mainmenu-accordion-panel noselect">
<button id="create-script-menu-link">Create Script</button>
</li>
<li id="active-scripts-tab" class="mainmenu-accordion-panel noselect">
<button id="active-scripts-menu-link">Active Scripts</button>
</li>
<li id="create-program-tab" class="mainmenu-accordion-panel noselect">
<button id="create-program-menu-link">Create Program</button>
<span id="create-program-notification" class="notification-off"> </span>
</li>
<!-- Character dropdown -->
<li id="character-menu-header-li">
<button id="character-menu-header" class="mainmenu-accordion-header noselect">Character</button>
</li>
<li id="stats-tab" class="mainmenu-accordion-panel noselect">
<button id="stats-menu-link">Stats</button>
</li>
<li id="factions-tab" class="mainmenu-accordion-panel noselect">
<button id="factions-menu-link">Factions</button>
<span id="factions-notification" class="notification-off"> </span>
</li>
<li id="augmentations-tab" class="mainmenu-accordion-panel noselect">
<button id="augmentations-menu-link" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
Augmentations
</button>
<span id="augmentations-notification" class="notification-off"> </span>
</li>
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel noselect">
<button id="hacknet-nodes-menu-link">Hacknet</button>
</li>
<li id="sleeves-tab" class="mainmenu-accordion-panel noselect">
<button id="sleeves-menu-link">Sleeves</button>
</li>
<!-- World dropdown -->
<li id="world-menu-header-li">
<button id="world-menu-header" class="mainmenu-accordion-header noselect">World</button>
</li>
<li id="city-tab" class="mainmenu-accordion-panel noselect">
<button id="city-menu-link">City</button>
</li>
<li id="travel-tab" class="mainmenu-accordion-panel noselect">
<button id="travel-menu-link">Travel</button>
</li>
<li id="job-tab" class="mainmenu-accordion-panel noselect">
<button id="job-menu-link">Job</button>
</li>
<li id="stock-market-tab" class="mainmenu-accordion-panel noselect">
<button id="stock-market-menu-link">Stock Market</button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel noselect">
<button id="bladeburner-menu-link">Bladeburner</button>
</li>
<li id="corporation-tab" class="mainmenu-accordion-panel noselect">
<button id="corporation-menu-link">Corp</button>
</li>
<li id="gang-tab" class="mainmenu-accordion-panel noselect">
<button id="gang-menu-link">Gang</button>
</li>
<li id="help-menu-header-li">
<button id="help-menu-header" class="mainmenu-accordion-header noselect">Help</button>
</li>
<li id="milestones-tab" class="mainmenu-accordion-panel noselect">
<button id="milestones-menu-link">Milestones</button>
</li>
<li id="tutorial-tab" class="mainmenu-accordion-panel noselect">
<button id="tutorial-menu-link">Tutorial</button>
</li>
<li id="options-tab" class="mainmenu-accordion-panel noselect">
<button id="options-menu-link">Options</button>
</li>
<li id="dev-tab" class="mainmenu-accordion-panel noselect">
<button id="dev-menu-link">Dev</button>
</li>
</ul>
</div>
<div id="script-editor-container" class="generic-menupage-container">
<div id="script-editor-wrapper">
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag">
<strong style="background-color: #555">Script name: </strong>
</p>
<input id="script-editor-filename" type="text" maxlength="100" tabindex="1"/>
</div>
<div id="monaco-editor"></div>
<div id="script-editor-buttons-wrapper"></div>
<!-- Buttons below script editor -->
</div>
<!-- End wrapper -->
<div id="script-editor-options-panel">
<h1 style="color: white">Script Editor Options</h1>
<fieldset>
<label for="script-editor-option-editor">Editor</label>
<select id="script-editor-option-editor" class="dropdown">
<option value="Ace">Ace</option>
<option value="CodeMirror">CodeMirror</option>
</select>
</fieldset>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme" class="dropdown"></select>
</fieldset>
<fieldset>
<label for="script-editor-option-keybinding">Key Binding</label>
<select id="script-editor-option-keybinding" class="dropdown"></select>
</fieldset>
<fieldset>
<label for="script-editor-option-highlightactiveline">Highlight Active Line</label>
<input type="checkbox" class="optionCheckbox" name="script-editor-option-highlightactiveline" id="script-editor-option-highlightactiveline" checked/>
</fieldset>
<fieldset>
<label for="script-editor-option-showinvisibles">Show Invisibles</label>
<input type="checkbox" class="optionCheckbox" name="script-editor-option-showinvisibles" id="script-editor-option-showinvisibles"/>
</fieldset>
<fieldset>
<label for="script-editor-option-usesofttab">Use Soft Tab</label>
<input type="checkbox" class="optionCheckbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked/>
</fieldset>
<fieldset id="script-editor-option-flex1-fieldset"></fieldset>
<fieldset id="script-editor-option-flex2-fieldset"></fieldset>
<fieldset id="script-editor-option-flex3-fieldset"></fieldset>
<fieldset id="script-editor-option-flex4-fieldset"></fieldset>
</div>
<!-- End script editor options panel -->
<!-- TODO(hydroflame): remove this once Monaco is implemented -->
<div id="ace-editor" style="display: none"></div>
<form id="codemirror-form-wrapper" style="display: none">
<textarea id="codemirror-editor"></textarea>
</form>
<div id="codemirror-vim-command-display-wrapper" style="display: none">
Key Buffer: <span id="codemirror-vim-command-display"></span>
</div>
</div> </div>
<!-- Terminal page --> <!-- Terminal page -->
@ -208,92 +56,10 @@
</table> </table>
</div> </div>
<!-- Character Info page --> <div class="generic-menupage-container">
<div id="character-container" class="generic-menupage-container"> <div id="generic-react-container"></div>
<div id="character-content"></div>
</div> </div>
<!-- Active scripts info page -->
<div id="active-scripts-container" class="generic-menupage-container">
<p id="active-scripts-text">
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the
servers on which they are running.
</p>
<p id="active-scripts-total-prod">
Total online production of Active scripts:
<span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br/>
Total online production since last Aug installation:
<span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span class="money-gold"><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span>)
</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none"></ul>
</div>
<!-- Hacknet Nodes -->
<div id="hacknet-nodes-container" class="generic-menupage-container">
<!-- React Component -->
</div>
<!-- Create a program(executable) -->
<div id="create-program-container" class="generic-menupage-container">
<p id="create-program-page-text">
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating a program you can cancel at any
time. Your progress will be saved and you can continue later.
</p>
<ul id="create-program-list"></ul>
</div>
<!-- Factions -->
<div id="factions-container" class="generic-menupage-container"></div>
<!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div>
<!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div>
<!-- Milestones content -->
<div id="milestones-container" class="generic-menupage-container"></div>
<!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container">
<h1>Tutorial (AKA Links to Documentation)</h1>
<a id="tutorial-getting-started-link" class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html">
Getting Started</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html">
Servers & Networking</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html">
Hacking</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html">
Scripts</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
Netscript Programming Language</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html">
Traveling</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html">
Companies</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html">
Infiltration</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html">
Factions</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html">
Augmentations</a><br/><br/>
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
Keyboard Shortcuts</a>
</div>
<!-- Location (visiting a location in World) -->
<div id="location-container" class="generic-menupage-container"></div>
<div id="infiltration-container" class="generic-fullscreen-container"></div> <div id="infiltration-container" class="generic-fullscreen-container"></div>
<div id="stock-market-container" class="generic-menupage-container"></div>
<div id="bladeburner-container" class="generic-menupage-container"></div>
<div id="resleeve-container" class="generic-menupage-container"></div>
<div id="gang-container" class="generic-menupage-container"></div>
<div id="corporation-container" class="generic-menupage-container"></div>
<div id="sleeves-container" class="generic-menupage-container"></div>
<!-- Generic Yes/No Pop Up box --> <!-- Generic Yes/No Pop Up box -->
<div id="yes-no-box-container" class="popup-box-container"> <div id="yes-no-box-container" class="popup-box-container">
@ -329,19 +95,6 @@
</div> </div>
</div> </div>
<!-- End of Infiltration pop up box -->
<div id="infiltration-box-container" class="popup-box-container">
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"></p>
<button id="infiltration-box-sell" class="a-link-button">Sell on Black Market</button>
<br/><br/>
<select id="infiltration-faction-select" class="dropdown"></select>
<br/>
<button id="infiltration-box-faction" class="a-link-button">Give to Faction for Reputation</button>
</div>
</div>
<!-- Mission container --> <!-- Mission container -->
<div id="mission-container" class="generic-fullscreen-container"></div> <div id="mission-container" class="generic-fullscreen-container"></div>

13
package-lock.json generated

@ -61,6 +61,7 @@
"@testing-library/cypress": "^8.0.1", "@testing-library/cypress": "^8.0.1",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.9.1",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6", "babel-jest": "^27.0.6",
@ -3565,9 +3566,9 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.7.2", "version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
"integrity": "sha512-TbG4TOx9hng8FKxaVrCisdaxKxqEwJ3zwHoCWXZ0Jw6mnvTInpaB99/2Cy4+XxpXtjNv9/TgfGSvZFyfV/t8Fw==", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
"dev": true "dev": true
}, },
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
@ -28624,9 +28625,9 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
}, },
"@types/node": { "@types/node": {
"version": "16.7.2", "version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
"integrity": "sha512-TbG4TOx9hng8FKxaVrCisdaxKxqEwJ3zwHoCWXZ0Jw6mnvTInpaB99/2Cy4+XxpXtjNv9/TgfGSvZFyfV/t8Fw==", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
"dev": true "dev": true
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {

@ -59,6 +59,7 @@
"@testing-library/cypress": "^8.0.1", "@testing-library/cypress": "^8.0.1",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.9.1",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6", "babel-jest": "^27.0.6",

@ -2455,22 +2455,6 @@ function augmentationExists(name) {
return Augmentations.hasOwnProperty(name); return Augmentations.hasOwnProperty(name);
} }
export function displayAugmentationsContent(contentEl) {
if (!routing.isOn(Page.Augmentations)) {
return;
}
if (!(contentEl instanceof HTMLElement)) {
return;
}
function backup() {
saveObject.exportGame();
onExport(Player);
}
ReactDOM.render(<AugmentationsRoot exportGameFn={backup} installAugmentationsFn={installAugmentations} />, contentEl);
}
export function isRepeatableAug(aug) { export function isRepeatableAug(aug) {
const augName = aug instanceof Augmentation ? aug.name : aug; const augName = aug instanceof Augmentation ? aug.name : aug;

@ -44,7 +44,8 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
} }
return ( return (
<div id="augmentations-content"> <>
<div className="augmentations-content">
<h1>Purchased Augmentations</h1> <h1>Purchased Augmentations</h1>
<p> <p>
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
@ -62,8 +63,8 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
<br /> <br />
<p> <p>
Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations
you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but you you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but
will lose all programs besides NUKE.exe) you will lose all programs besides NUKE.exe)
</p> </p>
<StdButton <StdButton
onClick={this.props.installAugmentationsFn} onClick={this.props.installAugmentationsFn}
@ -86,6 +87,7 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
<br /> <br /> <br /> <br />
<PlayerMultipliers /> <PlayerMultipliers />
</div> </div>
</>
); );
} }
} }

@ -15,7 +15,7 @@ interface IProps {
export function Root(props: IProps): React.ReactElement { export function Root(props: IProps): React.ReactElement {
return ( return (
<> <div className="bladeburner-container">
<div style={{ height: "60%", display: "block", position: "relative" }}> <div style={{ height: "60%", display: "block", position: "relative" }}>
<div <div
style={{ style={{
@ -41,6 +41,6 @@ export function Root(props: IProps): React.ReactElement {
> >
<AllPages bladeburner={props.bladeburner} player={props.player} /> <AllPages bladeburner={props.bladeburner} player={props.player} />
</div> </div>
</> </div>
); );
} }

@ -20,7 +20,8 @@ function ExpandButton(props: IExpandButtonProps): React.ReactElement {
const allIndustries = Object.keys(Industries).sort(); const allIndustries = Object.keys(Industries).sort();
const possibleIndustries = allIndustries const possibleIndustries = allIndustries
.filter( .filter(
(industryType: string) => props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, (industryType: string) =>
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
) )
.sort(); .sort();
if (possibleIndustries.length === 0) return <></>; if (possibleIndustries.length === 0) return <></>;
@ -50,12 +51,12 @@ export function CorporationRoot(props: IProps): React.ReactElement {
const [divisionName, setDivisionName] = useState("Overview"); const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => { useEffect(() => {
const id = setInterval(rerender, 150); const id = setInterval(rerender, 1000);
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
return ( return (
<> <div className="cmpy-mgmt-container">
<div> <div>
<HeaderTab <HeaderTab
current={divisionName === "Overview"} current={divisionName === "Overview"}
@ -74,6 +75,6 @@ export function CorporationRoot(props: IProps): React.ReactElement {
<ExpandButton corp={props.corp} setDivisionName={setDivisionName} /> <ExpandButton corp={props.corp} setDivisionName={setDivisionName} />
</div> </div>
<MainPanel rerender={rerender} corp={props.corp} divisionName={divisionName} player={props.player} /> <MainPanel rerender={rerender} corp={props.corp} divisionName={divisionName} player={props.player} />
</> </div>
); );
} }

@ -0,0 +1,79 @@
import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox";
interface IProps {
player: IPlayer;
popupId: string;
}
export function CreateCorporationPopup(props: IProps): React.ReactElement {
if (!props.player.canAccessCorporation() || props.player.hasCorporation()) {
removePopup(props.popupId);
return <></>;
}
const [name, setName] = useState("");
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function selfFund(): void {
if (!props.player.canAfford(150e9)) {
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b.");
return;
}
if (name == "") {
dialogBoxCreate("Invalid company name!");
return;
}
props.player.startCorporation(name);
props.player.loseMoney(150e9);
dialogBoxCreate(
"Congratulations! You just self-funded your own corporation. You can visit " +
"and manage your company in the City.",
);
removePopup(props.popupId);
}
function seed(): void {
if (name == "") {
dialogBoxCreate("Invalid company name!");
return;
}
props.player.startCorporation(name, 500e6);
dialogBoxCreate(
"Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City.",
);
removePopup(props.popupId);
}
return (
<>
<p>
Would you like to start a corporation? This will require $150b for registration and initial funding. This $150b
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
shares
<br />
<br />
If you would like to start one, please enter a name for your corporation below:
</p>
<input autoFocus={true} className="text-input" placeholder="Corporation Name" onChange={onChange} value={name} />
<button className="std-button" onClick={seed} disabled={name == ""}>
Use seed money
</button>
<button className="std-button" onClick={selfFund} disabled={name == "" || !props.player.canAfford(150e9)}>
Self-Fund (<Money money={150e9} player={props.player} />)
</button>
</>
);
}

@ -59,6 +59,14 @@ export function ExportPopup(props: IProps): React.ReactElement {
} }
const currentDivision = props.corp.divisions.find((division: IIndustry) => division.name === industry); const currentDivision = props.corp.divisions.find((division: IIndustry) => division.name === industry);
if (currentDivision === undefined)
throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`);
const possibleCities = Object.keys(currentDivision.warehouses).filter(
(city) => currentDivision.warehouses[city] !== 0,
);
if (possibleCities.length > 0 && !possibleCities.includes(city)) {
setCity(possibleCities[0]);
}
return ( return (
<> <>
@ -74,8 +82,7 @@ export function ExportPopup(props: IProps): React.ReactElement {
))} ))}
</select> </select>
<select className="dropdown" onChange={onCityChange} defaultValue={city}> <select className="dropdown" onChange={onCityChange} defaultValue={city}>
{currentDivision && {possibleCities.map((cityName: string) => {
Object.keys(currentDivision.warehouses).map((cityName: string) => {
if (currentDivision.warehouses[cityName] === 0) return; if (currentDivision.warehouses[cityName] === 0) return;
return ( return (
<option key={cityName} value={cityName}> <option key={cityName} value={cityName}>

@ -273,7 +273,7 @@ export function Overview(props: IProps): React.ReactElement {
function Upgrades(): React.ReactElement { function Upgrades(): React.ReactElement {
// Don't show upgrades // Don't show upgrades
if (props.corp.divisions.length <= 0) { if (props.corp.divisions.length <= 0) {
return <></>; return <h1>Unlock upgrades after creating your first division</h1>;
} }
return ( return (

@ -29,8 +29,8 @@ function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
} else { } else {
return ( return (
<> <>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{props.mat.name} will cost {numeralWrapper.formatMoney(cost)} {numeralWrapper.formatMoney(cost)}
</> </>
); );
} }

File diff suppressed because it is too large Load Diff

1607
src/DevMenu.tsx Normal file

File diff suppressed because it is too large Load Diff

@ -86,7 +86,7 @@ export function displayFactionContent(factionName, initiallyOnAugmentationsPage
p={Player} p={Player}
startHackingMissionFn={startHackingMission} startHackingMissionFn={startHackingMission}
/>, />,
Engine.Display.factionContent, Engine.Display.content,
); );
} }

@ -0,0 +1,58 @@
/**
* React Component for the popup used to create a new gang.
*/
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
import { IEngine } from "../../IEngine";
interface ICreateGangPopupProps {
popupId: string;
facName: string;
p: IPlayer;
engine: IEngine;
}
export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement {
const combatGangText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.";
const hackingGangText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.";
function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName);
}
function createGang(): void {
props.p.startGang(props.facName, isHacking());
removePopup(props.popupId);
props.engine.loadGangContent();
}
function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang();
}
return (
<>
Would you like to create a new Gang with {props.facName}?
<br/>
<br/>
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It also resets your reputation with this faction.
<br/>
<br/>
{ (isHacking()) ? hackingGangText : combatGangText }
<br/>
<br/>
Other than hacking vs combat, there are NO differences between the Factions you can create a Gang with, and each of these Factions have all Augmentations available.
<div className="popup-box-input-div" >
<StdButton onClick={createGang} onKeyUp={onKeyUp} text="Create Gang" style={{float: "right"}} autoFocus={true}/>
</div>
</>
);
}

@ -19,7 +19,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases"; import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { yesNoBoxClose, yesNoBoxCreate, yesNoBoxGetNoButton, yesNoBoxGetYesButton } from "../../../utils/YesNoBox"; import { createPopup } from "../../ui/React/createPopup";
import { CreateGangPopup } from "./CreateGangPopup";
type IProps = { type IProps = {
engine: IEngine; engine: IEngine;
@ -97,59 +98,13 @@ export class FactionRoot extends React.Component<IProps, IState> {
return this.props.engine.loadGangContent(); return this.props.engine.loadGangContent();
} }
// Otherwise, we have to create the gang const popupId = "create-gang-popup";
const facName = this.props.faction.name; createPopup(popupId, CreateGangPopup, {
let isHacking = false; popupId: popupId,
if (facName === "NiteSec" || facName === "The Black Hand") { facName: this.props.faction.name,
isHacking = true; p: this.props.p,
} engine: this.props.engine
// A Yes/No popup box will allow the player to confirm gang creation
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) {
return;
}
yesBtn.innerHTML = "Create Gang";
yesBtn.addEventListener("click", () => {
this.props.p.startGang(facName, isHacking);
const worldMenuHeader = document.getElementById("world-menu-header");
if (worldMenuHeader instanceof HTMLElement) {
worldMenuHeader.click();
worldMenuHeader.click();
}
this.props.engine.loadGangContent();
yesNoBoxClose();
}); });
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
// Pop-up text
let gangTypeText = "";
if (isHacking) {
gangTypeText =
"This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.<br><br>";
} else {
gangTypeText =
"This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
}
yesNoBoxCreate(
`Would you like to create a new Gang with ${facName}?<br><br>` +
"Note that this will prevent you from creating a Gang with any other Faction until " +
"this BitNode is destroyed. It also resets your reputation with this faction.<br><br>" +
gangTypeText +
"Other than hacking vs combat, there are NO differences between the Factions you can " +
"create a Gang with, and each of these Factions have all Augmentations available.",
);
} }
rerender(): void { rerender(): void {
@ -223,7 +178,7 @@ export class FactionRoot extends React.Component<IProps, IState> {
} }
return ( return (
<div> <div id="faction-container">
<h1>{faction.name}</h1> <h1>{faction.name}</h1>
<Info faction={faction} factionInfo={factionInfo} /> <Info faction={faction} factionInfo={factionInfo} />
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={this.manageGang} />} {canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={this.manageGang} />}
@ -266,9 +221,9 @@ export class FactionRoot extends React.Component<IProps, IState> {
renderAugmentationsPage(): React.ReactNode { renderAugmentationsPage(): React.ReactNode {
return ( return (
<div> <>
<AugmentationsPage faction={this.props.faction} p={this.props.p} routeToMainPage={this.routeToMain} /> <AugmentationsPage faction={this.props.faction} p={this.props.p} routeToMainPage={this.routeToMain} />
</div> </>
); );
} }
} }

@ -30,7 +30,7 @@ export function Root(props: IProps): React.ReactElement {
} }
return ( return (
<> <div className="gang-container">
<a className="a-link-button" style={{ display: "inline-block" }} onClick={back}> <a className="a-link-button" style={{ display: "inline-block" }} onClick={back}>
Back Back
</a> </a>
@ -53,6 +53,6 @@ export function Root(props: IProps): React.ReactElement {
) : ( ) : (
<TerritorySubpage gang={props.gang} /> <TerritorySubpage gang={props.gang} />
)} )}
</> </div>
); );
} }

@ -121,7 +121,7 @@ export function HacknetRoot(props: IProps): React.ReactElement {
}); });
return ( return (
<div> <>
<h1>Hacknet {hasHacknetServers(props.player) ? "Servers" : "Nodes"}</h1> <h1>Hacknet {hasHacknetServers(props.player) ? "Servers" : "Nodes"}</h1>
<GeneralInfo hasHacknetServers={hasHacknetServers(props.player)} /> <GeneralInfo hasHacknetServers={hasHacknetServers(props.player)} />
@ -140,6 +140,6 @@ export function HacknetRoot(props: IProps): React.ReactElement {
)} )}
<ul id={"hacknet-nodes-list"}>{nodes}</ul> <ul id={"hacknet-nodes-list"}>{nodes}</ul>
</div> </>
); );
} }

@ -3,15 +3,30 @@
* to TypeScript at the moment * to TypeScript at the moment
*/ */
export interface IEngine { export interface IEngine {
indexedDb: any;
_lastUpdate: number;
hideAllContent: () => void; hideAllContent: () => void;
loadBladeburnerContent: () => void; loadTerminalContent: () => void;
loadFactionContent: () => void; loadScriptEditorContent: () => void;
loadActiveScriptsContent: () => void;
loadCreateProgramContent: () => void;
loadCharacterContent: () => void;
loadFactionsContent: () => void; loadFactionsContent: () => void;
loadGangContent: () => void; loadAugmentationsContent: () => void;
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void; loadHacknetNodesContent: () => void;
loadSleevesContent: () => void;
loadLocationContent: () => void; loadLocationContent: () => void;
loadTravelContent: () => void;
loadJobContent: () => void;
loadStockMarketContent: () => void;
loadBladeburnerContent: () => void;
loadCorporationContent: () => void;
loadGangContent: () => void;
loadMilestonesContent: () => void;
loadTutorialContent: () => void;
loadDevMenuContent: () => void;
loadFactionContent: () => void;
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void;
loadMissionContent: () => void; loadMissionContent: () => void;
loadResleevingContent: () => void; loadResleevingContent: () => void;
loadStockMarketContent: () => void;
loadTerminalContent: () => void;
} }

@ -104,18 +104,18 @@ function iTutorialEvaluateStep() {
} }
// Disable and clear main menu // Disable and clear main menu
const terminalMainMenu = clearEventListeners("terminal-menu-link"); // const terminalMainMenu = clearEventListeners("terminal-menu-link");
const statsMainMenu = clearEventListeners("stats-menu-link"); // const statsMainMenu = clearEventListeners("stats-menu-link");
const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link"); // const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link"); // const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
const cityMainMenu = clearEventListeners("city-menu-link"); // const cityMainMenu = clearEventListeners("city-menu-link");
const tutorialMainMenu = clearEventListeners("tutorial-menu-link"); // const tutorialMainMenu = clearEventListeners("tutorial-menu-link");
terminalMainMenu.removeAttribute("class"); // terminalMainMenu.removeAttribute("class");
statsMainMenu.removeAttribute("class"); // statsMainMenu.removeAttribute("class");
activeScriptsMainMenu.removeAttribute("class"); // activeScriptsMainMenu.removeAttribute("class");
hacknetMainMenu.removeAttribute("class"); // hacknetMainMenu.removeAttribute("class");
cityMainMenu.removeAttribute("class"); // cityMainMenu.removeAttribute("class");
tutorialMainMenu.removeAttribute("class"); // tutorialMainMenu.removeAttribute("class");
// Interactive Tutorial Next button // Interactive Tutorial Next button
const nextBtn = document.getElementById("interactive-tutorial-next"); const nextBtn = document.getElementById("interactive-tutorial-next");
@ -138,14 +138,6 @@ function iTutorialEvaluateStep() {
"the main navigation menu (left-hand side of the screen)", "the main navigation menu (left-hand side of the screen)",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Stats' menu and set its tutorial click handler
statsMainMenu.setAttribute("class", "flashing-button");
statsMainMenu.addEventListener("click", function () {
Engine.loadCharacterContent();
iTutorialNextStep(); //Opening the character page will go to the next step
return false;
});
break; break;
case iTutorialSteps.CharacterPage: case iTutorialSteps.CharacterPage:
Engine.loadCharacterContent(); Engine.loadCharacterContent();
@ -162,14 +154,6 @@ function iTutorialEvaluateStep() {
"main navigation menu.", "main navigation menu.",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Terminal' menu and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function () {
Engine.loadTerminalContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.TerminalIntro: case iTutorialSteps.TerminalIntro:
Engine.loadTerminalContent(); Engine.loadTerminalContent();
@ -345,14 +329,6 @@ function iTutorialEvaluateStep() {
"<code class='interactive-tutorial-tab flashing-button'>Active Scripts</code> link in the main navigation menu.", "<code class='interactive-tutorial-tab flashing-button'>Active Scripts</code> link in the main navigation menu.",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Active Scripts' menu and set its tutorial click handler
activeScriptsMainMenu.setAttribute("class", "flashing-button");
activeScriptsMainMenu.addEventListener("click", function () {
Engine.loadActiveScriptsContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.ActiveScriptsPage: case iTutorialSteps.ActiveScriptsPage:
Engine.loadActiveScriptsContent(); Engine.loadActiveScriptsContent();
@ -362,14 +338,6 @@ function iTutorialEvaluateStep() {
"your scripts are doing. Let's go back to the <code class='interactive-tutorial-tab flashing-button'>Terminal</code>", "your scripts are doing. Let's go back to the <code class='interactive-tutorial-tab flashing-button'>Terminal</code>",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Terminal' button and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function () {
Engine.loadTerminalContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.ActiveScriptsToTerminal: case iTutorialSteps.ActiveScriptsToTerminal:
Engine.loadTerminalContent(); Engine.loadTerminalContent();
@ -402,14 +370,6 @@ function iTutorialEvaluateStep() {
"the <code class='interactive-tutorial-tab flashing-button'>Hacknet</code> page through the main navigation menu now.", "the <code class='interactive-tutorial-tab flashing-button'>Hacknet</code> page through the main navigation menu now.",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Hacknet' menu and set its tutorial click handler
hacknetMainMenu.setAttribute("class", "flashing-button");
hacknetMainMenu.addEventListener("click", function () {
Engine.loadHacknetNodesContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.HacknetNodesIntroduction: case iTutorialSteps.HacknetNodesIntroduction:
Engine.loadHacknetNodesContent(); Engine.loadHacknetNodesContent();
@ -428,14 +388,6 @@ function iTutorialEvaluateStep() {
"Let's go to the <code class='interactive-tutorial-tab flashing-button'>City</code> page through the main navigation menu.", "Let's go to the <code class='interactive-tutorial-tab flashing-button'>City</code> page through the main navigation menu.",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'City' menu and set its tutorial click handler
cityMainMenu.setAttribute("class", "flashing-button");
cityMainMenu.addEventListener("click", function () {
Engine.loadLocationContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.WorldDescription: case iTutorialSteps.WorldDescription:
Engine.loadLocationContent(); Engine.loadLocationContent();
@ -447,14 +399,6 @@ function iTutorialEvaluateStep() {
"Lastly, click on the <code class='interactive-tutorial-tab flashing-button'>Tutorial</code> link in the main navigation menu.", "Lastly, click on the <code class='interactive-tutorial-tab flashing-button'>Tutorial</code> link in the main navigation menu.",
); );
nextBtn.style.display = "none"; nextBtn.style.display = "none";
// Flash 'Tutorial' menu and set its tutorial click handler
tutorialMainMenu.setAttribute("class", "flashing-button");
tutorialMainMenu.addEventListener("click", function () {
Engine.loadTutorialContent();
iTutorialNextStep();
return false;
});
break; break;
case iTutorialSteps.TutorialPageInfo: case iTutorialSteps.TutorialPageInfo:
Engine.loadTutorialContent(); Engine.loadTutorialContent();
@ -481,29 +425,6 @@ function iTutorialEvaluateStep() {
// Go to the next step and evaluate it // Go to the next step and evaluate it
function iTutorialNextStep() { function iTutorialNextStep() {
// Special behavior for certain steps
if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
document.getElementById("stats-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.CharacterGoToTerminalPage) {
document.getElementById("terminal-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.TerminalGoToActiveScriptsPage) {
document.getElementById("active-scripts-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.ActiveScriptsPage) {
document.getElementById("terminal-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.GoToHacknetNodesPage) {
document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.HacknetNodesGoToWorldPage) {
document.getElementById("city-menu-link").removeAttribute("class");
}
if (ITutorial.currStep === iTutorialSteps.WorldDescription) {
document.getElementById("tutorial-menu-link").removeAttribute("class");
}
ITutorial.stepIsDone[ITutorial.currStep] = true; ITutorial.stepIsDone[ITutorial.currStep] = true;
if (ITutorial.currStep < iTutorialSteps.End) { if (ITutorial.currStep < iTutorialSteps.End) {
ITutorial.currStep += 1; ITutorial.currStep += 1;
@ -527,17 +448,6 @@ function iTutorialEnd() {
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5; Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
} }
// Initialize references to main menu links
// We have to call initializeMainMenuLinks() again because the Interactive Tutorial
// re-creates Main menu links with clearEventListeners()
if (!initializeMainMenuLinks()) {
const errorMsg =
"Failed to initialize Main Menu Links. Please try refreshing the page. " +
"If that doesn't work, report the issue to the developer";
exceptionAlert(new Error(errorMsg));
console.error(errorMsg);
return;
}
Engine.init(); Engine.init();
ITutorial.currStep = iTutorialSteps.End; ITutorial.currStep = iTutorialSteps.End;

@ -9,24 +9,13 @@ import { CityName } from "./data/CityNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { AddToAllServers, createUniqueRandomIp } from "../Server/AllServers"; import { AddToAllServers, createUniqueRandomIp } from "../Server/AllServers";
import { safetlyCreateUniqueServer } from "../Server/ServerHelpers"; import { safetlyCreateUniqueServer } from "../Server/ServerHelpers";
import { getPurchaseServerCost, purchaseServer } from "../Server/ServerPurchases";
import { SpecialServerIps } from "../Server/SpecialServerIps"; import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { import { yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose, yesNoBoxCreate } from "../../utils/YesNoBox";
yesNoBoxGetYesButton,
yesNoBoxGetNoButton,
yesNoBoxClose,
yesNoBoxCreate,
yesNoTxtInpBoxGetYesButton,
yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxClose,
yesNoTxtInpBoxCreate,
} from "../../utils/YesNoBox";
import { createElement } from "../../utils/uiHelpers/createElement"; import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup"; import { createPopup } from "../../utils/uiHelpers/createPopup";
@ -34,232 +23,6 @@ import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseBu
import { removeElementById } from "../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../utils/uiHelpers/removeElementById";
import * as React from "react"; import * as React from "react";
/**
* Create a pop-up box that lets the player confirm traveling to a different city.
* If settings are configured to suppress this popup, just instantly travel.
* The actual "Travel" implementation is implemented in the UI, and is passed in
* as an argument.
* @param {CityName} destination - City that the player is traveling to
* @param {Function} travelFn - Function that changes the player's state for traveling
*/
type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost: number = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) {
travelFn(destination);
return;
}
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) {
console.warn(`Could not find YesNo pop-up box buttons`);
return;
}
yesBtn.innerHTML = "Yes";
yesBtn.addEventListener("click", () => {
yesNoBoxClose();
travelFn(destination);
return false;
});
noBtn.innerHTML = "No";
noBtn.addEventListener("click", () => {
yesNoBoxClose();
return false;
});
yesNoBoxCreate(
<span>
Would you like to travel to {destination}? The trip will cost <Money money={cost} />.
</span>,
);
}
/**
* Create a pop-up box that lets the player purchase a server.
* @param {number} ram - Amount of RAM (GB) on server
* @param {IPlayer} p - Player object
*/
export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
const cost = getPurchaseServerCost(ram);
if (cost === Infinity) {
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer.");
return;
}
const yesBtn = yesNoTxtInpBoxGetYesButton();
const noBtn = yesNoTxtInpBoxGetNoButton();
if (yesBtn == null || noBtn == null) {
return;
}
yesBtn.innerHTML = "Purchase Server";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function () {
purchaseServer(ram, p);
yesNoTxtInpBoxClose();
});
noBtn.addEventListener("click", function () {
yesNoTxtInpBoxClose();
});
yesNoTxtInpBoxCreate(
<>
Would you like to purchase a new server with {numeralWrapper.formatRAM(ram)} of RAM for{" "}
<Money money={cost} player={p} />?
<br />
<br />
Please enter the server hostname below:
<br />
</>,
);
}
/**
* Create a popup that lets the player start a Corporation
* @param {IPlayer} p - Player object
*/
export function createStartCorporationPopup(p: IPlayer): void {
if (!p.canAccessCorporation() || p.hasCorporation()) {
return;
}
const popupId = "create-corporation-popup";
const txt = createElement("p", {
innerHTML:
"Would you like to start a corporation? This will require $150b for registration " +
"and initial funding. This $150b can either be self-funded, or you can obtain " +
"the seed money from the government in exchange for 500 million shares<br><br>" +
"If you would like to start one, please enter a name for your corporation below:",
});
const nameInput = createElement("input", {
class: "text-input",
placeholder: "Corporation Name",
}) as HTMLInputElement;
const selfFundedButton = createElement("button", {
class: "popup-box-button",
innerText: "Self-Fund",
clickListener: () => {
if (!p.canAfford(150e9)) {
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b.");
return false;
}
const companyName = nameInput.value;
if (companyName == null || companyName == "") {
dialogBoxCreate("Invalid company name!");
return false;
}
p.startCorporation(companyName);
p.loseMoney(150e9);
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
dialogBoxCreate(
"Congratulations! You just self-funded your own corporation. You can visit " +
"and manage your company in the City.",
);
removeElementById(popupId);
return false;
},
});
const seedMoneyButton = createElement("button", {
class: "popup-box-button",
innerText: "Use Seed Money",
clickListener: () => {
const companyName = nameInput.value;
if (companyName == null || companyName == "") {
dialogBoxCreate("Invalid company name!");
return false;
}
p.startCorporation(companyName, 500e6);
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
dialogBoxCreate(
"Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City.",
);
removeElementById(popupId);
return false;
},
});
const cancelBtn = createPopupCloseButton(popupId, {
class: "popup-box-button",
});
createPopup(popupId, [txt, nameInput, cancelBtn, selfFundedButton, seedMoneyButton]);
nameInput.focus();
}
/**
* Create a popup that lets the player upgrade the cores on his/her home computer
* @param {IPlayer} p - Player object
*/
export function createUpgradeHomeCoresPopup(p: IPlayer): void {
const currentCores = p.getHomeComputer().cpuCores;
if (currentCores >= 8) {
dialogBoxCreate(<>You have the maximum amount of CPU cores on your home computer.</>);
return;
}
// Cost of purchasing another cost is found by indexing this array with number of current cores
const allCosts = [0, 10e9, 250e9, 5e12, 100e12, 1e15, 20e15, 200e15];
const cost: number = allCosts[currentCores];
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) {
return;
}
yesBtn.innerHTML = "Purchase";
yesBtn.addEventListener("click", () => {
if (!p.canAfford(cost)) {
dialogBoxCreate("You do not have enough money to purchase an additional CPU Core for your home computer!");
} else {
p.loseMoney(cost);
p.getHomeComputer().cpuCores++;
dialogBoxCreate(
"You purchased an additional CPU Core for your home computer! It now has " +
p.getHomeComputer().cpuCores +
" cores.",
);
}
yesNoBoxClose();
});
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
yesNoBoxCreate(
<>
Would you like to purchase an additional CPU Core for your home computer? Each CPU Core lets you start with an
additional Core Node in Hacking Missions.
<br />
<br />
Purchasing an additional core (for a total of {p.getHomeComputer().cpuCores + 1}) will cost{" "}
<Money money={cost} player={p} />
</>,
);
}
/** /**
* Attempt to purchase a TOR router * Attempt to purchase a TOR router
* @param {IPlayer} p - Player object * @param {IPlayer} p - Player object

@ -0,0 +1,58 @@
/**
* React Component for the popup used to purchase a new server.
*/
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { purchaseServer } from "../../Server/ServerPurchases";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
interface IPurchaseServerPopupProps {
ram: number;
cost: number;
p: IPlayer;
popupId: string;
rerender: () => void;
}
export function PurchaseServerPopup(props: IPurchaseServerPopupProps): React.ReactElement {
const [hostname, setHostname] = useState("");
function tryToPurchaseServer(): void {
purchaseServer(hostname, props.ram, props.cost, props.p);
removePopup(props.popupId);
}
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) tryToPurchaseServer();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setHostname(event.target.value);
}
return (
<>
Would you like to purchase a new server with {numeralWrapper.formatRAM(props.ram)} of RAM for{" "}
<Money money={props.cost} player={props.p} />?
<br />
<br />
Please enter the server hostname below:
<br />
<div className="popup-box-input-div">
<input
autoFocus
onKeyUp={onKeyUp}
onChange={onChange}
className="text-input noselect"
type="text"
placeholder="Unique Hostname"
/>
<StdButton onClick={tryToPurchaseServer} text="Purchase Server" disabled={!props.p.canAfford(props.cost)} />
</div>
</>
);
}

@ -13,7 +13,8 @@
import * as React from "react"; import * as React from "react";
import { Location } from "../Location"; import { Location } from "../Location";
import { createStartCorporationPopup } from "../LocationsHelpers"; import { CreateCorporationPopup } from "../../Corporation/ui/CreateCorporationPopup";
import { createPopup } from "../../ui/React/createPopup";
import { LocationName } from "../data/LocationNames"; import { LocationName } from "../data/LocationNames";
import { IEngine } from "../../IEngine"; import { IEngine } from "../../IEngine";
@ -59,7 +60,11 @@ export class SpecialLocation extends React.Component<IProps, IState> {
* Click handler for "Create Corporation" button at Sector-12 City Hall * Click handler for "Create Corporation" button at Sector-12 City Hall
*/ */
createCorporationPopup(): void { createCorporationPopup(): void {
createStartCorporationPopup(this.props.p); const popupId = `create-start-corporation-popup`;
createPopup(popupId, CreateCorporationPopup, {
player: this.props.p,
popupId: popupId,
});
} }
/** /**

@ -3,10 +3,9 @@
* *
* This subcomponent renders all of the buttons for purchasing things from tech vendors * This subcomponent renders all of the buttons for purchasing things from tech vendors
*/ */
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Location } from "../Location"; import { Location } from "../Location";
import { createPurchaseServerPopup } from "../LocationsHelpers";
import { RamButton } from "./RamButton"; import { RamButton } from "./RamButton";
import { TorButton } from "./TorButton"; import { TorButton } from "./TorButton";
import { CoresButton } from "./CoresButton"; import { CoresButton } from "./CoresButton";
@ -14,8 +13,12 @@ import { CoresButton } from "./CoresButton";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { getPurchaseServerCost } from "../../Server/ServerPurchases"; import { getPurchaseServerCost } from "../../Server/ServerPurchases";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { createPopup } from "../../ui/React/createPopup";
import { PurchaseServerPopup } from "./PurchaseServerPopup";
type IProps = { type IProps = {
loc: Location; loc: Location;
@ -23,10 +26,28 @@ type IProps = {
}; };
export function TechVendorLocation(props: IProps): React.ReactElement { export function TechVendorLocation(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
} }
function openPurchaseServer(ram: number, cost: number, p: IPlayer): void {
const popupId = "purchase-server-popup";
createPopup(popupId, PurchaseServerPopup, {
ram: ram,
cost: cost,
p: p,
popupId: popupId,
rerender: rerender
});
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
const btnStyle = { display: "block" }; const btnStyle = { display: "block" };
const purchaseServerButtons: React.ReactNode[] = []; const purchaseServerButtons: React.ReactNode[] = [];
@ -35,7 +56,7 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
purchaseServerButtons.push( purchaseServerButtons.push(
<StdButton <StdButton
key={i} key={i}
onClick={() => createPurchaseServerPopup(i, props.p)} onClick={() => openPurchaseServer(i, cost, props.p)}
style={btnStyle} style={btnStyle}
text={ text={
<> <>

@ -6,13 +6,14 @@
import * as React from "react"; import * as React from "react";
import { CityName } from "../data/CityNames"; import { CityName } from "../data/CityNames";
import { createTravelPopup } from "../LocationsHelpers"; import { TravelConfirmationPopup } from "./TravelConfirmationPopup";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap"; import { WorldMap } from "../../ui/React/WorldMap";
@ -21,67 +22,63 @@ type IProps = {
travel: (to: CityName) => void; travel: (to: CityName) => void;
}; };
export class TravelAgencyLocation extends React.Component<IProps, any> { function createTravelPopup(p: IPlayer, city: string, travel: () => void): void {
/** if (Settings.SuppressTravelConfirmation) {
* Stores button styling that sets them all to block display travel();
*/ return;
btnStyle: any;
constructor(props: IProps) {
super(props);
this.btnStyle = { display: "block" };
} }
const popupId = `travel-confirmation`;
createPopup(popupId, TravelConfirmationPopup, {
player: p,
city: city,
travel: travel,
popupId: popupId,
});
}
asciiWorldMap(): React.ReactNode { function ASCIIWorldMap(props: IProps): React.ReactElement {
// map needs all this whitespace!
// prettier-ignore
return ( return (
<div className="noselect"> <div className="noselect">
<p> <p>
From here, you can travel to any other city! A ticket costs{" "} From here, you can travel to any other city! A ticket costs{" "}
<Money money={CONSTANTS.TravelCost} player={this.props.p} />. <Money money={CONSTANTS.TravelCost} player={props.p} />.
</p> </p>
<WorldMap currentCity={this.props.p.city} onTravel={(city: CityName) => createTravelPopup(city, this.props.travel)} /> <WorldMap
currentCity={props.p.city}
onTravel={(city: CityName) => createTravelPopup(props.p, city, () => props.travel(city))}
/>
</div> </div>
); );
} }
listWorldMap(): React.ReactNode {
const travelBtns: React.ReactNode[] = [];
for (const key in CityName) {
const city: CityName = (CityName as any)[key];
// Skip current city
if (city === this.props.p.city) {
continue;
}
travelBtns.push(
<StdButton
key={city}
onClick={createTravelPopup.bind(null, city, this.props.travel)}
style={this.btnStyle}
text={`Travel to ${city}`}
/>,
);
}
function ListWorldMap(props: IProps): React.ReactElement {
return ( return (
<div> <div>
<p> <p>
From here, you can travel to any other city! A ticket costs <Money money={CONSTANTS.TravelCost} />. From here, you can travel to any other city! A ticket costs{" "}
<Money money={CONSTANTS.TravelCost} player={props.p} />.
</p> </p>
{travelBtns} {Object.values(CityName)
.filter((city: string) => city != props.p.city)
.map((city: string) => (
<StdButton
key={city}
onClick={() =>
createTravelPopup(props.p, city, () => props.travel(CityName[city as keyof typeof CityName]))
}
style={{ display: "block" }}
text={`Travel to ${city}`}
/>
))}
</div> </div>
); );
} }
render(): React.ReactNode { export function TravelAgencyLocation(props: IProps): React.ReactElement {
console.log(Settings.DisableASCIIArt);
if (Settings.DisableASCIIArt) { if (Settings.DisableASCIIArt) {
return this.listWorldMap(); return <ListWorldMap p={props.p} travel={props.travel} />;
} else { } else {
return this.asciiWorldMap(); return <ASCIIWorldMap p={props.p} travel={props.travel} />;
}
} }
} }

@ -0,0 +1,33 @@
import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CONSTANTS } from "../../Constants";
import { Money } from "../../ui/React/Money";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
city: string;
travel: () => void;
popupId: string;
}
export function TravelConfirmationPopup(props: IProps): React.ReactElement {
const cost = CONSTANTS.TravelCost;
function travel(): void {
props.travel();
removePopup(props.popupId);
}
return (
<>
<span>
Would you like to travel to {props.city}? The trip will cost <Money money={cost} player={props.player} />.
</span>
<br />
<br />
<button className="std-button" onClick={travel}>
Travel
</button>
</>
);
}

@ -1,26 +0,0 @@
import { Page, routing } from ".././ui/navigationTracking";
import { Root } from "./ui/Root";
import { Player } from "../Player";
import * as React from "react";
import * as ReactDOM from "react-dom";
let milestonesContainer: HTMLElement | null = null;
(function () {
function setContainer(): void {
milestonesContainer = document.getElementById("milestones-container");
document.removeEventListener("DOMContentLoaded", setContainer);
}
document.addEventListener("DOMContentLoaded", setContainer);
})();
export function displayMilestonesContent(): void {
if (!routing.isOn(Page.Milestones)) {
return;
}
if (milestonesContainer instanceof HTMLElement) {
ReactDOM.render(<Root player={Player} />, milestonesContainer);
}
}

@ -16,7 +16,7 @@ function highestMilestone(p: IPlayer, milestones: Milestone[]): number {
return n; return n;
} }
export function Root(props: IProps): JSX.Element { export function MilestonesRoot(props: IProps): JSX.Element {
const n = highestMilestone(props.player, Milestones); const n = highestMilestone(props.player, Milestones);
const milestones = Milestones.map((milestone: Milestone, i: number) => { const milestones = Milestones.map((milestone: Milestone, i: number) => {
if (i <= n + 1) { if (i <= n + 1) {
@ -30,7 +30,7 @@ export function Root(props: IProps): JSX.Element {
} }
}); });
return ( return (
<> <div className="milestones-container">
<h1>Milestones</h1> <h1>Milestones</h1>
<p> <p>
Milestones don't reward you for completing them. They are here to guide you if you're lost. They will reset when Milestones don't reward you for completing them. They are here to guide you if you're lost. They will reset when
@ -40,6 +40,6 @@ export function Root(props: IProps): JSX.Element {
<h2>Completing fl1ght.exe</h2> <h2>Completing fl1ght.exe</h2>
<li>{milestones}</li> <li>{milestones}</li>
</> </div>
); );
} }

@ -21,15 +21,18 @@ import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile";
import { MoneySourceTracker } from "../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../utils/MoneySourceTracker";
import { Exploit } from "../Exploits/Exploit"; import { Exploit } from "../Exploits/Exploit";
import { ICorporation } from "../Corporation/ICorporation"; import { ICorporation } from "../Corporation/ICorporation";
import { IGang } from "../Gang/IGang";
import { IBladeburner } from "../Bladeburner/IBladeburner";
export interface IPlayer { export interface IPlayer {
// Class members // Class members
augmentations: IPlayerOwnedAugmentation[]; augmentations: IPlayerOwnedAugmentation[];
bladeburner: any;
bitNodeN: number; bitNodeN: number;
city: CityName; city: CityName;
companyName: string; companyName: string;
corporation: ICorporation; corporation: ICorporation;
gang: IGang;
bladeburner: IBladeburner;
currentServer: string; currentServer: string;
factions: string[]; factions: string[];
factionInvitations: string[]; factionInvitations: string[];
@ -63,6 +66,7 @@ export interface IPlayer {
sleevesFromCovenant: number; sleevesFromCovenant: number;
sourceFiles: IPlayerOwnedSourceFile[]; sourceFiles: IPlayerOwnedSourceFile[];
exploits: Exploit[]; exploits: Exploit[];
lastUpdate: number;
totalPlaytime: number; totalPlaytime: number;
// Stats // Stats
@ -81,6 +85,7 @@ export interface IPlayer {
dexterity_exp: number; dexterity_exp: number;
agility_exp: number; agility_exp: number;
charisma_exp: number; charisma_exp: number;
intelligence_exp: number;
// Multipliers // Multipliers
hacking_chance_mult: number; hacking_chance_mult: number;
@ -192,4 +197,8 @@ export interface IPlayer {
getCasinoWinnings(): number; getCasinoWinnings(): number;
quitJob(company: string): void; quitJob(company: string): void;
createHacknetServer(): void; createHacknetServer(): void;
startCreateProgramWork(programName: string, time: number, reqLevel: number): void;
queueAugmentation(augmentationName: string): void;
receiveInvite(factionName: string): void;
updateSkillLevels(): void;
} }

@ -160,13 +160,13 @@ export function PlayerObject() {
this.has4SDataTixApi = false; this.has4SDataTixApi = false;
//Gang //Gang
this.gang = 0; this.gang = null;
//Corporation //Corporation
this.corporation = 0; this.corporation = null;
//Bladeburner //Bladeburner
this.bladeburner = 0; this.bladeburner = null;
this.bladeburner_max_stamina_mult = 1; this.bladeburner_max_stamina_mult = 1;
this.bladeburner_stamina_gain_mult = 1; this.bladeburner_stamina_gain_mult = 1;
this.bladeburner_analysis_mult = 1; //Field Analysis Only this.bladeburner_analysis_mult = 1; //Field Analysis Only

@ -60,9 +60,11 @@ const SortFunctions: {
Dexterity: (a: Resleeve, b: Resleeve): number => a.dexterity - b.dexterity, Dexterity: (a: Resleeve, b: Resleeve): number => a.dexterity - b.dexterity,
Agility: (a: Resleeve, b: Resleeve): number => a.agility - b.agility, Agility: (a: Resleeve, b: Resleeve): number => a.agility - b.agility,
Charisma: (a: Resleeve, b: Resleeve): number => a.charisma - b.charisma, Charisma: (a: Resleeve, b: Resleeve): number => a.charisma - b.charisma,
AverageCombatStats: (a: Resleeve, b: Resleeve): number => getAverage(a.strength, a.defense, a.dexterity, a.agility) - AverageCombatStats: (a: Resleeve, b: Resleeve): number =>
getAverage(a.strength, a.defense, a.dexterity, a.agility) -
getAverage(b.strength, b.defense, b.dexterity, b.agility), getAverage(b.strength, b.defense, b.dexterity, b.agility),
AverageAllStats: (a: Resleeve, b: Resleeve): number => getAverage(a.hacking_skill, a.strength, a.defense, a.dexterity, a.agility, a.charisma) - AverageAllStats: (a: Resleeve, b: Resleeve): number =>
getAverage(a.hacking_skill, a.strength, a.defense, a.dexterity, a.agility, a.charisma) -
getAverage(b.hacking_skill, b.strength, b.defense, b.dexterity, b.agility, b.charisma), getAverage(b.hacking_skill, b.strength, b.defense, b.dexterity, b.agility, b.charisma),
TotalNumAugmentations: (a: Resleeve, b: Resleeve): number => a.augmentations.length - b.augmentations.length, TotalNumAugmentations: (a: Resleeve, b: Resleeve): number => a.augmentations.length - b.augmentations.length,
}; };

@ -875,12 +875,14 @@ export class Sleeve extends Person {
if (!factionInfo.offerHackingWork) { if (!factionInfo.offerHackingWork) {
return false; return false;
} }
console.log("Hacking");
this.factionWorkType = FactionWorkType.Hacking; this.factionWorkType = FactionWorkType.Hacking;
this.gainRatesForTask.hack = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("field")) { } else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) { if (!factionInfo.offerFieldWork) {
return false; return false;
} }
console.log("Field");
this.factionWorkType = FactionWorkType.Field; this.factionWorkType = FactionWorkType.Field;
this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.str = 0.1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -892,6 +894,7 @@ export class Sleeve extends Person {
if (!factionInfo.offerSecurityWork) { if (!factionInfo.offerSecurityWork) {
return false; return false;
} }
console.log("Security");
this.factionWorkType = FactionWorkType.Security; this.factionWorkType = FactionWorkType.Security;
this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.str = 0.15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -905,6 +908,7 @@ export class Sleeve extends Person {
this.currentTaskLocation = factionName; this.currentTaskLocation = factionName;
this.currentTask = SleeveTaskType.Faction; this.currentTask = SleeveTaskType.Faction;
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours; this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours;
console.log(this.currentTaskLocation);
return true; return true;
} }

@ -64,6 +64,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
} }
function setTask(): void { function setTask(): void {
console.log(abc);
props.sleeve.resetTaskStatus(); // sets to idle props.sleeve.resetTaskStatus(); // sets to idle
switch (abc[0]) { switch (abc[0]) {
case "------": case "------":
@ -108,10 +109,13 @@ export function SleeveElem(props: IProps): React.ReactElement {
switch (props.sleeve.factionWorkType) { switch (props.sleeve.factionWorkType) {
case FactionWorkType.Field: case FactionWorkType.Field:
doing = "Field work"; doing = "Field work";
break;
case FactionWorkType.Hacking: case FactionWorkType.Hacking:
doing = "Hacking contracts"; doing = "Hacking contracts";
break;
case FactionWorkType.Security: case FactionWorkType.Security:
doing = "Security work"; doing = "Security work";
break;
} }
desc = ( desc = (
<> <>

@ -53,7 +53,10 @@ function possibleJobs(player: IPlayer, sleeve: Sleeve): string[] {
function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] { function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
// Array of all factions that other sleeves are working for // Array of all factions that other sleeves are working for
const forbiddenFactions = []; const forbiddenFactions = ["Bladeburners"];
if (player.gang) {
forbiddenFactions.push(player.gang.facName);
}
for (const otherSleeve of player.sleeves) { for (const otherSleeve of player.sleeves) {
if (sleeve === otherSleeve) { if (sleeve === otherSleeve) {
continue; continue;
@ -180,8 +183,10 @@ const canDo: {
"Work for Company": (player: IPlayer, sleeve: Sleeve) => possibleJobs(player, sleeve).length > 0, "Work for Company": (player: IPlayer, sleeve: Sleeve) => possibleJobs(player, sleeve).length > 0,
"Work for Faction": (player: IPlayer, sleeve: Sleeve) => possibleFactions(player, sleeve).length > 0, "Work for Faction": (player: IPlayer, sleeve: Sleeve) => possibleFactions(player, sleeve).length > 0,
"Commit Crime": () => true, "Commit Crime": () => true,
"Take University Course": (player: IPlayer, sleeve: Sleeve) => [CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city), "Take University Course": (player: IPlayer, sleeve: Sleeve) =>
"Workout at Gym": (player: IPlayer, sleeve: Sleeve) => [CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city), [CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
"Workout at Gym": (player: IPlayer, sleeve: Sleeve) =>
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
"Shock Recovery": (player: IPlayer, sleeve: Sleeve) => sleeve.shock < 100, "Shock Recovery": (player: IPlayer, sleeve: Sleeve) => sleeve.shock < 100,
Synchronize: (player: IPlayer, sleeve: Sleeve) => sleeve.sync < 100, Synchronize: (player: IPlayer, sleeve: Sleeve) => sleeve.sync < 100,
}; };
@ -226,7 +231,8 @@ export function TaskSelector(props: IProps): React.ReactElement {
const [s1, setS1] = useState(abc[1]); const [s1, setS1] = useState(abc[1]);
const [s2, setS2] = useState(abc[2]); const [s2, setS2] = useState(abc[2]);
const validActions = Object.keys(canDo).filter((k) => (canDo[k] as (player: IPlayer, sleeve: Sleeve) => boolean)(props.player, props.sleeve), const validActions = Object.keys(canDo).filter((k) =>
(canDo[k] as (player: IPlayer, sleeve: Sleeve) => boolean)(props.player, props.sleeve),
); );
const detailsF = tasks[s0]; const detailsF = tasks[s0];
@ -234,6 +240,15 @@ export function TaskSelector(props: IProps): React.ReactElement {
const details = detailsF(props.player, props.sleeve); const details = detailsF(props.player, props.sleeve);
const details2 = details.second(s1); const details2 = details.second(s1);
if (details.first.length > 0 && !details.first.includes(s1)) {
setS1(details.first[0]);
props.setABC([s0, details.first[0], s2]);
}
if (details2.length > 0 && !details2.includes(s2)) {
setS2(details2[0]);
props.setABC([s0, s1, details2[0]]);
}
function onS0Change(event: React.ChangeEvent<HTMLSelectElement>): void { function onS0Change(event: React.ChangeEvent<HTMLSelectElement>): void {
const n = event.target.value; const n = event.target.value;
const detailsF = tasks[n]; const detailsF = tasks[n];

@ -23,7 +23,7 @@ import { prestigeHomeComputer } from "./Server/ServerHelpers";
import { SourceFileFlags, updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags, updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SpecialServerIps, prestigeSpecialServerIps, SpecialServerNames } from "./Server/SpecialServerIps"; import { SpecialServerIps, prestigeSpecialServerIps, SpecialServerNames } from "./Server/SpecialServerIps";
import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket"; import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket";
import { Terminal, postNetburnerText } from "./Terminal"; import { Terminal, postVersion } from "./Terminal";
import { Page, routing } from "./ui/navigationTracking"; import { Page, routing } from "./ui/navigationTracking";
@ -52,7 +52,7 @@ function prestigeAugmentation() {
Terminal.resetTerminalInput(); Terminal.resetTerminalInput();
Engine.loadTerminalContent(); Engine.loadTerminalContent();
$("#terminal tr:not(:last)").remove(); $("#terminal tr:not(:last)").remove();
postNetburnerText(); postVersion();
// Delete all Worker Scripts objects // Delete all Worker Scripts objects
prestigeWorkerScripts(); prestigeWorkerScripts();
@ -233,7 +233,7 @@ function prestigeSourceFile(flume) {
// Clear terminal // Clear terminal
$("#terminal tr:not(:last)").remove(); $("#terminal tr:not(:last)").remove();
postNetburnerText(); postVersion();
// Messages // Messages
initMessages(); initMessages();

@ -1,74 +0,0 @@
import { Programs } from "./Programs";
import { Player } from "../Player";
import { createElement } from "../../utils/uiHelpers/createElement";
// this has the same key as 'Programs', not program names
const aLinks = {};
function displayCreateProgramContent() {
for (const key in aLinks) {
const p = Programs[key];
aLinks[key].style.display = "none";
if (!Player.hasProgram(p.name) && p.create.req(Player)) {
aLinks[key].style.display = "inline-block";
}
}
}
//Returns the number of programs that are currently available to be created
function getNumAvailableCreateProgram() {
var count = 0;
for (const key in Programs) {
// Non-creatable program
if (Programs[key].create == null) {
continue;
}
// Already has program
if (Player.hasProgram(Programs[key].name)) {
continue;
}
// Does not meet requirements
if (!Programs[key].create.req(Player)) {
continue;
}
count++;
}
if (Player.firstProgramAvailable === false && count > 0) {
Player.firstProgramAvailable = true;
document.getElementById("hacking-menu-header").click();
document.getElementById("hacking-menu-header").click();
}
return count;
}
function initCreateProgramButtons() {
const createProgramList = document.getElementById("create-program-list");
for (const key in Programs) {
if (Programs[key].create === null) {
continue;
}
const elem = createElement("a", {
class: "a-link-button",
id: Programs[key].htmlID(),
innerText: Programs[key].name,
tooltip: Programs[key].create.tooltip,
});
aLinks[key] = elem;
createProgramList.appendChild(elem);
}
for (const key in aLinks) {
const p = Programs[key];
aLinks[key].addEventListener("click", function () {
Player.startCreateProgramWork(p.name, p.create.time, p.create.level);
return false;
});
}
}
export { displayCreateProgramContent, getNumAvailableCreateProgram, initCreateProgramButtons };

@ -0,0 +1,25 @@
import { Programs } from "./Programs";
import { Program } from "./Program";
import { IPlayer } from "../PersonObjects/IPlayer";
import { createElement } from "../../utils/uiHelpers/createElement";
//Returns the programs this player can create.
export function getAvailableCreatePrograms(player: IPlayer): Program[] {
const programs: Program[] = [];
for (const key in Programs) {
// Non-creatable program
const create = Programs[key].create;
if (create == null) continue;
// Already has program
if (player.hasProgram(Programs[key].name)) continue;
// Does not meet requirements
if (!create.req(player)) continue;
programs.push(Programs[key]);
}
return programs;
}

@ -0,0 +1,48 @@
import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Programs } from "../Programs";
import { getAvailableCreatePrograms } from "../ProgramHelpers";
interface IProps {
player: IPlayer;
}
export function ProgramsRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
return (
<>
<p id="create-program-page-text">
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating a program you can cancel at any
time. Your progress will be saved and you can continue later.
</p>
<ul id="create-program-list">
{getAvailableCreatePrograms(props.player).map((program) => {
const create = program.create;
if (create === null) return <></>;
return (
<a
key={program.name}
className="a-link-button tooltip"
onClick={() => props.player.startCreateProgramWork(program.name, create.time, create.level)}
>
{program.name}
<span className="tooltiptext">{create.tooltip}</span>
</a>
);
})}
</ul>
</>
);
}

1
src/SaveObject.d.ts vendored Normal file

@ -0,0 +1 @@
export declare const saveObject: any;

@ -308,7 +308,7 @@ export function Root(props: IProps): React.ReactElement {
} }
return ( return (
<div id="script-editor-wrapper"> <div className="script-editor-wrapper">
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag" className="noselect"> <p id="script-editor-filename-tag" className="noselect">
{" "} {" "}

@ -10,7 +10,6 @@ import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { yesNoTxtInpBoxGetInput } from "../../utils/YesNoBox";
import { isPowerOfTwo } from "../../utils/helpers/isPowerOfTwo"; import { isPowerOfTwo } from "../../utils/helpers/isPowerOfTwo";
// Returns the cost of purchasing a server with the given RAM // Returns the cost of purchasing a server with the given RAM
@ -44,9 +43,7 @@ export function getPurchaseServerMaxRam(): number {
} }
// Manually purchase a server (NOT through Netscript) // Manually purchase a server (NOT through Netscript)
export function purchaseServer(ram: number, p: IPlayer): void { export function purchaseServer(hostname: string, ram: number, cost: number, p: IPlayer): void {
const cost = getPurchaseServerCost(ram);
//Check if player has enough money //Check if player has enough money
if (!p.canAfford(cost)) { if (!p.canAfford(cost)) {
dialogBoxCreate("You don't have enough money to purchase this server!", false); dialogBoxCreate("You don't have enough money to purchase this server!", false);
@ -65,7 +62,6 @@ export function purchaseServer(ram: number, p: IPlayer): void {
return; return;
} }
const hostname = yesNoTxtInpBoxGetInput();
if (hostname == "") { if (hostname == "") {
dialogBoxCreate("You must enter a hostname for your new server!"); dialogBoxCreate("You must enter a hostname for your new server!");
return; return;

@ -0,0 +1,274 @@
import React, { useState, useEffect } from "react";
import { IEngine } from "../../IEngine";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
import { ICorporation } from "../../Corporation/ICorporation";
import { IGang } from "../../Gang/IGang";
interface IProps {
player: IPlayer;
engine: IEngine;
}
export function SidebarRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => {
const id = setInterval(rerender, 20);
return () => clearInterval(id);
}, []);
const [hackingOpen, setHackingOpen] = useState(true);
const [characterOpen, setCharacterOpen] = useState(true);
const [worldOpen, setWorldOpen] = useState(true);
const [helpOpen, setHelpOpen] = useState(true);
const flashTerminal =
ITutorial.currStep === iTutorialSteps.CharacterGoToTerminalPage ||
ITutorial.currStep === iTutorialSteps.ActiveScriptsPage;
const flashStats = ITutorial.currStep === iTutorialSteps.GoToCharacterPage;
const flashActiveScripts = ITutorial.currStep === iTutorialSteps.TerminalGoToActiveScriptsPage;
const flashHacknet = ITutorial.currStep === iTutorialSteps.GoToHacknetNodesPage;
const flashCity = ITutorial.currStep === iTutorialSteps.HacknetNodesGoToWorldPage;
const flashTutorial = ITutorial.currStep === iTutorialSteps.WorldDescription;
function clickTerminal() {
props.engine.loadTerminalContent();
if (flashTerminal) iTutorialNextStep();
}
function clickStats() {
props.engine.loadCharacterContent();
if (flashStats) iTutorialNextStep();
}
function clickActiveScripts() {
props.engine.loadActiveScriptsContent();
if (flashActiveScripts) iTutorialNextStep();
}
function clickHacknet() {
props.engine.loadHacknetNodesContent();
if (flashHacknet) iTutorialNextStep();
}
function clickCity() {
props.engine.loadLocationContent();
if (flashCity) iTutorialNextStep();
}
function clickTutorial() {
props.engine.loadTutorialContent();
if (flashTutorial) iTutorialNextStep();
}
const programCount = getAvailableCreatePrograms(props.player).length;
const canCreateProgram =
programCount > 0 ||
props.player.augmentations.length > 0 ||
props.player.queuedAugmentations.length > 0 ||
props.player.sourceFiles.length > 0;
const canOpenFactions =
props.player.factionInvitations.length > 0 ||
props.player.factions.length > 0 ||
props.player.augmentations.length > 0 ||
props.player.queuedAugmentations.length > 0 ||
props.player.sourceFiles.length > 0;
const canOpenAugmentations =
props.player.augmentations.length > 0 ||
props.player.queuedAugmentations.length > 0 ||
props.player.sourceFiles.length > 0;
const canOpenSleeves = props.player.sleeves.length > 0;
// TODO(hydroflame): these should not as any but right now the def is that it
// can only be defined;
const canCorporation = !!(props.player.corporation as any);
const canGang = !!(props.player.gang as any);
const canJob = props.player.companyName !== "";
const canStockMarket = props.player.hasWseAccount;
const canBladeburner = !!(props.player.bladeburner as any);
return (
<ul id="mainmenu" className="mainmenu noscrollbar noselect">
{/* Hacking dropdown */}
<li className="menu-header">
<button
id="hacking-menu-header"
className={
"noselect mainmenu-accordion-header" +
(hackingOpen ? " opened" : "") +
(flashTerminal ? " flashing-button" : "")
}
onClick={() => setHackingOpen((old) => !old)}
>
Hacking
</button>
</li>
{hackingOpen && (
<>
<li className="mainmenu-accordion-panel">
<button className={flashTerminal ? "flashing-button" : ""} onClick={clickTerminal}>
Terminal
</button>
</li>
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadScriptEditorContent()}>Create Script</button>
</li>
<li className="mainmenu-accordion-panel">
<button className={flashActiveScripts ? "flashing-button" : ""} onClick={clickActiveScripts}>
Active Scripts
</button>
</li>
{canCreateProgram && (
<li className="mainmenu-accordion-panel">
<button className="notification" onClick={() => props.engine.loadCreateProgramContent()}>
Create Program
{programCount > 0 && <span className="badge">{programCount}</span>}
</button>
</li>
)}
</>
)}
{/* Character dropdown */}
<li className="menu-header">
<button
id="character-menu-header"
className={"noselect mainmenu-accordion-header" + (characterOpen ? " opened" : "")}
onClick={() => setCharacterOpen((old) => !old)}
>
Character
</button>
</li>
{characterOpen && (
<>
<li className="mainmenu-accordion-panel">
<button className={flashStats ? "flashing-button" : ""} onClick={clickStats}>
Stats
</button>
</li>
{canOpenFactions && (
<li className="mainmenu-accordion-panel">
<button className="notification" onClick={() => props.engine.loadFactionsContent()}>
Factions
{props.player.factionInvitations.length > 0 && (
<span className="badge">{props.player.factionInvitations.length}</span>
)}
</button>
</li>
)}
{canOpenAugmentations && (
<li className="mainmenu-accordion-panel">
<button className="notification" onClick={() => props.engine.loadAugmentationsContent()}>
Augmentations
{props.player.queuedAugmentations.length > 0 && (
<span className="badge">{props.player.queuedAugmentations.length}</span>
)}
</button>
</li>
)}
<li className="mainmenu-accordion-panel">
<button className={flashHacknet ? "flashing-button" : ""} onClick={clickHacknet}>
Hacknet
</button>
</li>
{canOpenSleeves && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadSleevesContent()}>Sleeves</button>
</li>
)}
</>
)}
{/* World dropdown */}
<li className="menu-header">
<button
id="world-menu-header"
className={"noselect mainmenu-accordion-header" + (worldOpen ? " opened" : "")}
onClick={() => setWorldOpen((old) => !old)}
>
World
</button>
</li>
{worldOpen && (
<>
<li className="mainmenu-accordion-panel">
<button className={flashCity ? "flashing-button" : ""} onClick={clickCity}>
City
</button>
</li>
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadTravelContent()}>Travel</button>
</li>
{canJob && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadJobContent()}>Job</button>
</li>
)}
{canStockMarket && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadStockMarketContent()}>Stock Market</button>
</li>
)}
{canBladeburner && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadBladeburnerContent()}>Bladeburner</button>
</li>
)}
{canCorporation && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadCorporationContent()}>Corp</button>
</li>
)}
{canGang && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadGangContent()}>Gang</button>
</li>
)}
</>
)}
<li className="menu-header">
<button
id="help-menu-header"
className={"noselect mainmenu-accordion-header" + (helpOpen ? " opened" : "")}
onClick={() => setHelpOpen((old) => !old)}
>
Help
</button>
</li>
{helpOpen && (
<>
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadMilestonesContent()}>Milestones</button>
</li>
<li className="mainmenu-accordion-panel">
<button className={flashTutorial ? "flashing-button" : ""} onClick={clickTutorial}>
Tutorial
</button>
</li>
{/*<li className="mainmenu-accordion-panel">
<button>Options</button>
</li>*/}
{process.env.NODE_ENV === "development" && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadDevMenuContent()}>Dev</button>
</li>
)}
</>
)}
</ul>
);
}

@ -307,7 +307,7 @@ export function processStockPrices(numCycles = 1): void {
let stockMarketContainer: HTMLElement | null = null; let stockMarketContainer: HTMLElement | null = null;
function setStockMarketContainer(): void { function setStockMarketContainer(): void {
stockMarketContainer = document.getElementById("stock-market-container"); stockMarketContainer = document.getElementById("generic-react-container");
document.removeEventListener("DOMContentLoaded", setStockMarketContainer); document.removeEventListener("DOMContentLoaded", setStockMarketContainer);
} }

@ -61,7 +61,7 @@ export class StockMarketRoot extends React.Component<IProps, IState> {
render(): React.ReactNode { render(): React.ReactNode {
return ( return (
<div> <div className="stock-market-container">
<InfoAndPurchases initStockMarket={this.props.initStockMarket} p={this.props.p} rerender={this.rerender} /> <InfoAndPurchases initStockMarket={this.props.initStockMarket} p={this.props.p} rerender={this.rerender} />
{this.props.p.hasWseAccount && ( {this.props.p.hasWseAccount && (
<StockTickers <StockTickers

@ -64,7 +64,7 @@ import * as FileSaver from "file-saver";
import * as libarg from "arg"; import * as libarg from "arg";
import React from "react"; import React from "react";
function postNetburnerText() { function postVersion() {
post("Bitburner v" + CONSTANTS.Version); post("Bitburner v" + CONSTANTS.Version);
} }
@ -2616,4 +2616,4 @@ let Terminal = {
}, },
}; };
export { postNetburnerText, Terminal }; export { postVersion, Terminal };

@ -0,0 +1,99 @@
import React from "react";
export function TutorialRoot(): React.ReactElement {
return (
<>
<h1>Tutorial (AKA Links to Documentation)</h1>
<a
id="tutorial-getting-started-link"
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html"
>
Getting Started
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html"
>
Servers & Networking
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html"
>
Hacking
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html"
>
Scripts
</a>
<br />
<br />
<a className="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
Netscript Programming Language
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html"
>
Traveling
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html"
>
Companies
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html"
>
Infiltration
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html"
>
Factions
</a>
<br />
<br />
<a
className="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html"
>
Augmentations
</a>
<br />
<br />
<a className="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
Keyboard Shortcuts
</a>
</>
);
}

@ -5,7 +5,13 @@
*/ */
import { convertTimeMsToTimeElapsedString, replaceAt } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString, replaceAt } from "../utils/StringHelperFunctions";
import { Augmentations } from "./Augmentation/Augmentations"; import { Augmentations } from "./Augmentation/Augmentations";
import { initAugmentations, displayAugmentationsContent } from "./Augmentation/AugmentationHelpers"; import {
initAugmentations,
displayAugmentationsContent,
installAugmentations,
} from "./Augmentation/AugmentationHelpers";
import { onExport } from "./ExportBonus";
import { AugmentationsRoot } from "./Augmentation/ui/Root";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Bladeburner } from "./Bladeburner/Bladeburner";
@ -15,12 +21,13 @@ import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies"; import { initCompanies } from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation"; import { Corporation } from "./Corporation/Corporation";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { createDevMenu, closeDevMenu } from "./DevMenu"; import { DevMenuRoot } from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions"; import { Factions, initFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers"; import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
import { FactionList } from "./Faction/ui/FactionList"; import { FactionList } from "./Faction/ui/FactionList";
import { Root as BladeburnerRoot } from "./Bladeburner/ui/Root"; import { Root as BladeburnerRoot } from "./Bladeburner/ui/Root";
import { Root as GangRoot } from "./Gang/ui/Root"; import { Root as GangRoot } from "./Gang/ui/Root";
import { SidebarRoot } from "./Sidebar/ui/SidebarRoot";
import { CorporationRoot } from "./Corporation/ui/CorporationRoot"; import { CorporationRoot } from "./Corporation/ui/CorporationRoot";
import { ResleeveRoot } from "./PersonObjects/Resleeving/ui/ResleeveRoot"; import { ResleeveRoot } from "./PersonObjects/Resleeving/ui/ResleeveRoot";
import { SleeveRoot } from "./PersonObjects/Sleeve/ui/SleeveRoot"; import { SleeveRoot } from "./PersonObjects/Sleeve/ui/SleeveRoot";
@ -42,11 +49,7 @@ import { workerScripts } from "./Netscript/WorkerScripts";
import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker"; import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker";
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige"; import { prestigeAugmentation } from "./Prestige";
import { import { ProgramsRoot } from "./Programs/ui/ProgramsRoot";
displayCreateProgramContent,
getNumAvailableCreateProgram,
initCreateProgramButtons,
} from "./Programs/ProgramHelpers";
import { redPillFlag } from "./RedPill"; import { redPillFlag } from "./RedPill";
import { saveObject, loadGame } from "./SaveObject"; import { saveObject, loadGame } from "./SaveObject";
import { Root as ScriptEditorRoot } from "./ScriptEditor/ui/Root"; import { Root as ScriptEditorRoot } from "./ScriptEditor/ui/Root";
@ -55,8 +58,9 @@ import { Settings } from "./Settings/Settings";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSpecialServerIps } from "./Server/SpecialServerIps"; import { initSpecialServerIps } from "./Server/SpecialServerIps";
import { initSymbolToStockMap, processStockPrices, displayStockMarketContent } from "./StockMarket/StockMarket"; import { initSymbolToStockMap, processStockPrices, displayStockMarketContent } from "./StockMarket/StockMarket";
import { displayMilestonesContent } from "./Milestones/MilestoneHelpers"; import { MilestonesRoot } from "./Milestones/ui/MilestonesRoot";
import { Terminal, postNetburnerText } from "./Terminal"; import { Terminal, postVersion } from "./Terminal";
import { TutorialRoot } from "./Tutorial/ui/TutorialRoot";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { createStatusText } from "./ui/createStatusText"; import { createStatusText } from "./ui/createStatusText";
@ -173,33 +177,15 @@ const Engine = {
// Display objects // Display objects
// TODO-Refactor this into its own component // TODO-Refactor this into its own component
Display: { Display: {
// Generic page that most react loads into.
content: null,
// Main menu content // Main menu content
terminalContent: null, terminalContent: null,
characterContent: null,
scriptEditorContent: null,
activeScriptsContent: null,
hacknetNodesContent: null,
createProgramContent: null,
factionsContent: null,
factionContent: null,
augmentationsContent: null,
milestonesContent: null,
tutorialContent: null,
infiltrationContent: null, infiltrationContent: null,
stockMarketContent: null,
gangContent: null,
bladeburnerContent: null,
resleeveContent: null,
sleeveContent: null,
corporationContent: null,
locationContent: null,
workInProgressContent: null, workInProgressContent: null,
redPillContent: null, redPillContent: null,
cinematicTextContent: null, cinematicTextContent: null,
missionContent: null, missionContent: null,
// Character info
characterInfo: null,
}, },
indexedDb: undefined, indexedDb: undefined,
@ -217,104 +203,115 @@ const Engine = {
loadCharacterContent: function () { loadCharacterContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.characterContent.style.display = "block"; Engine.Display.content.style.display = "block";
Engine.updateCharacterInfo();
routing.navigateTo(Page.CharacterInfo); routing.navigateTo(Page.CharacterInfo);
ReactDOM.render(<CharacterInfo player={Player} />, Engine.Display.content);
MainMenuLinks.Stats.classList.add("active"); MainMenuLinks.Stats.classList.add("active");
}, },
loadScriptEditorContent: function (filename = "", code = "") { loadScriptEditorContent: function (filename = "", code = "") {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.scriptEditorContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.ScriptEditor); routing.navigateTo(Page.ScriptEditor);
MainMenuLinks.ScriptEditor.classList.add("active");
ReactDOM.render( ReactDOM.render(
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />, <ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
Engine.Display.scriptEditorContent, Engine.Display.content,
); );
MainMenuLinks.ScriptEditor.classList.add("active");
}, },
loadActiveScriptsContent: function () { loadActiveScriptsContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.activeScriptsContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.ActiveScripts); routing.navigateTo(Page.ActiveScripts);
ReactDOM.render(
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
Engine.Display.activeScriptsContent,
);
MainMenuLinks.ActiveScripts.classList.add("active"); MainMenuLinks.ActiveScripts.classList.add("active");
ReactDOM.render(<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />, Engine.Display.content);
}, },
loadHacknetNodesContent: function () { loadHacknetNodesContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.hacknetNodesContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.HacknetNodes); routing.navigateTo(Page.HacknetNodes);
ReactDOM.render(<HacknetRoot player={Player} />, Engine.Display.hacknetNodesContent);
MainMenuLinks.HacknetNodes.classList.add("active"); MainMenuLinks.HacknetNodes.classList.add("active");
ReactDOM.render(<HacknetRoot player={Player} />, Engine.Display.content);
}, },
loadCreateProgramContent: function () { loadCreateProgramContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.createProgramContent.style.display = "block"; Engine.Display.content.style.display = "block";
displayCreateProgramContent();
routing.navigateTo(Page.CreateProgram); routing.navigateTo(Page.CreateProgram);
MainMenuLinks.CreateProgram.classList.add("active"); MainMenuLinks.CreateProgram.classList.add("active");
ReactDOM.render(<ProgramsRoot player={Player} />, Engine.Display.content);
}, },
loadFactionsContent: function () { loadFactionsContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.factionsContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Factions); routing.navigateTo(Page.Factions);
ReactDOM.render(<FactionList player={Player} engine={this} />, Engine.Display.factionsContent);
MainMenuLinks.Factions.classList.add("active"); MainMenuLinks.Factions.classList.add("active");
ReactDOM.render(<FactionList player={Player} engine={this} />, Engine.Display.content);
}, },
// TODO reactify
loadFactionContent: function () { loadFactionContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.factionContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Faction); routing.navigateTo(Page.Faction);
}, },
loadAugmentationsContent: function () { loadAugmentationsContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.augmentationsContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Augmentations); routing.navigateTo(Page.Augmentations);
displayAugmentationsContent(Engine.Display.augmentationsContent);
MainMenuLinks.Augmentations.classList.add("active"); MainMenuLinks.Augmentations.classList.add("active");
function backup() {
saveObject.exportGame();
onExport(Player);
}
ReactDOM.render(
<AugmentationsRoot exportGameFn={backup} installAugmentationsFn={installAugmentations} />,
Engine.Display.content,
);
}, },
loadMilestonesContent: function () { loadMilestonesContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.milestonesContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Milestones); routing.navigateTo(Page.Milestones);
displayMilestonesContent();
MainMenuLinks.Milestones.classList.add("active"); MainMenuLinks.Milestones.classList.add("active");
ReactDOM.render(<MilestonesRoot player={Player} />, Engine.Display.content);
}, },
loadTutorialContent: function () { loadTutorialContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.tutorialContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Tutorial); routing.navigateTo(Page.Tutorial);
MainMenuLinks.Tutorial.classList.add("active"); MainMenuLinks.Tutorial.classList.add("active");
ReactDOM.render(<TutorialRoot />, Engine.Display.content);
}, },
loadDevMenuContent: function () { loadDevMenuContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
createDevMenu(); if (process.env.NODE_ENV !== "development") {
throw new Error("Cannot create Dev Menu because you are not in a dev build");
}
Engine.Display.content.style.display = "block";
ReactDOM.render(<DevMenuRoot player={Player} engine={this} />, Engine.Display.content);
routing.navigateTo(Page.DevMenu); routing.navigateTo(Page.DevMenu);
MainMenuLinks.DevMenu.classList.add("active"); MainMenuLinks.DevMenu.classList.add("active");
}, },
loadLocationContent: function (initiallyInCity = true) { loadLocationContent: function (initiallyInCity = true) {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.locationContent.style.display = "block"; Engine.Display.content.style.display = "block";
MainMenuLinks.City.classList.add("active");
routing.navigateTo(Page.Location); routing.navigateTo(Page.Location);
const rootComponent = <LocationRoot initiallyInCity={initiallyInCity} engine={Engine} p={Player} />; MainMenuLinks.City.classList.add("active");
ReactDOM.render(rootComponent, Engine.Display.locationContent); ReactDOM.render(
<LocationRoot initiallyInCity={initiallyInCity} engine={Engine} p={Player} />,
Engine.Display.content,
);
}, },
loadTravelContent: function () { loadTravelContent: function () {
@ -322,12 +319,10 @@ const Engine = {
// and make sure that the 'City' main menu link doesnt become 'active' // and make sure that the 'City' main menu link doesnt become 'active'
Engine.hideAllContent(); Engine.hideAllContent();
Player.gotoLocation(LocationName.TravelAgency); Player.gotoLocation(LocationName.TravelAgency);
Engine.Display.locationContent.style.display = "block"; Engine.Display.content.style.display = "block";
MainMenuLinks.Travel.classList.add("active");
routing.navigateTo(Page.Location); routing.navigateTo(Page.Location);
const rootComponent = <LocationRoot initiallyInCity={false} engine={Engine} p={Player} />; MainMenuLinks.Travel.classList.add("active");
ReactDOM.render(rootComponent, Engine.Display.locationContent); ReactDOM.render(<LocationRoot initiallyInCity={false} engine={Engine} p={Player} />, Engine.Display.content);
}, },
loadJobContent: function () { loadJobContent: function () {
@ -341,30 +336,31 @@ const Engine = {
} }
Engine.hideAllContent(); Engine.hideAllContent();
Player.gotoLocation(Player.companyName); Player.gotoLocation(Player.companyName);
Engine.Display.locationContent.style.display = "block"; Engine.Display.content.style.display = "block";
MainMenuLinks.Job.classList.add("active");
routing.navigateTo(Page.Location); routing.navigateTo(Page.Location);
const rootComponent = <LocationRoot initiallyInCity={false} engine={Engine} p={Player} />; MainMenuLinks.Job.classList.add("active");
ReactDOM.render(rootComponent, Engine.Display.locationContent); ReactDOM.render(<LocationRoot initiallyInCity={false} engine={Engine} p={Player} />, Engine.Display.content);
}, },
// TODO reactify
loadWorkInProgressContent: function () { loadWorkInProgressContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
var mainMenu = document.getElementById("mainmenu-container"); const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden"; mainMenu.style.visibility = "hidden";
Engine.Display.workInProgressContent.style.display = "block"; Engine.Display.workInProgressContent.style.display = "block";
routing.navigateTo(Page.WorkInProgress); routing.navigateTo(Page.WorkInProgress);
}, },
// TODO reactify
loadRedPillContent: function () { loadRedPillContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
var mainMenu = document.getElementById("mainmenu-container"); const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden"; mainMenu.style.visibility = "hidden";
Engine.Display.redPillContent.style.display = "block"; Engine.Display.redPillContent.style.display = "block";
routing.navigateTo(Page.RedPill); routing.navigateTo(Page.RedPill);
}, },
// TODO reactify
loadCinematicTextContent: function () { loadCinematicTextContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
@ -373,6 +369,7 @@ const Engine = {
routing.navigateTo(Page.CinematicText); routing.navigateTo(Page.CinematicText);
}, },
// TODO reactify
loadInfiltrationContent: function (name, difficulty, maxLevel) { loadInfiltrationContent: function (name, difficulty, maxLevel) {
Engine.hideAllContent(); Engine.hideAllContent();
const mainMenu = document.getElementById("mainmenu-container"); const mainMenu = document.getElementById("mainmenu-container");
@ -384,21 +381,19 @@ const Engine = {
loadStockMarketContent: function () { loadStockMarketContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.stockMarketContent.style.display = "block"; Engine.Display.content.style.display = "block";
routing.navigateTo(Page.StockMarket); routing.navigateTo(Page.StockMarket);
MainMenuLinks.StockMarket.classList.add("active");
displayStockMarketContent(); displayStockMarketContent();
}, },
loadGangContent: function () { loadGangContent: function () {
if (!Player.inGang()) return;
Engine.hideAllContent(); Engine.hideAllContent();
if (Player.inGang()) { Engine.Display.content.style.display = "block";
Engine.Display.gangContent.style.display = "block";
routing.navigateTo(Page.Gang); routing.navigateTo(Page.Gang);
ReactDOM.render(<GangRoot engine={this} gang={Player.gang} player={Player} />, Engine.Display.gangContent); MainMenuLinks.Gang.classList.add("active");
} else { ReactDOM.render(<GangRoot engine={this} gang={Player.gang} player={Player} />, Engine.Display.content);
Engine.loadTerminalContent();
routing.navigateTo(Page.Terminal);
}
}, },
loadMissionContent: function () { loadMissionContent: function () {
@ -412,120 +407,53 @@ const Engine = {
loadCorporationContent: function () { loadCorporationContent: function () {
if (!(Player.corporation instanceof Corporation)) return; if (!(Player.corporation instanceof Corporation)) return;
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Corporation); routing.navigateTo(Page.Corporation);
Engine.Display.corporationContent.style.display = "block"; MainMenuLinks.Corporation.classList.add("active");
ReactDOM.render(<CorporationRoot corp={Player.corporation} player={Player} />, Engine.Display.corporationContent); ReactDOM.render(<CorporationRoot corp={Player.corporation} player={Player} />, Engine.Display.content);
}, },
loadBladeburnerContent: function () { loadBladeburnerContent: function () {
if (!(Player.bladeburner instanceof Bladeburner)) return; if (!(Player.bladeburner instanceof Bladeburner)) return;
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Bladeburner); routing.navigateTo(Page.Bladeburner);
Engine.Display.bladeburnerContent.style.display = "block"; MainMenuLinks.Bladeburner.classList.add("active");
ReactDOM.render( ReactDOM.render(
<BladeburnerRoot bladeburner={Player.bladeburner} player={Player} engine={this} />, <BladeburnerRoot bladeburner={Player.bladeburner} player={Player} engine={this} />,
Engine.Display.bladeburnerContent, Engine.Display.content,
); );
MainMenuLinks.Bladeburner.classList.add("active");
}, },
loadSleevesContent: function () { loadSleevesContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Sleeves); routing.navigateTo(Page.Sleeves);
Engine.Display.sleevesContent.style.display = "block"; ReactDOM.render(<SleeveRoot player={Player} />, Engine.Display.content);
ReactDOM.render(<SleeveRoot player={Player} />, Engine.Display.sleevesContent);
}, },
loadResleevingContent: function () { loadResleevingContent: function () {
Engine.hideAllContent(); Engine.hideAllContent();
routing.navigateTo(Page.Resleeves); routing.navigateTo(Page.Resleeves);
Engine.Display.resleeveContent.style.display = "block"; Engine.Display.content.style.display = "block";
ReactDOM.render(<ResleeveRoot player={Player} />, Engine.Display.resleeveContent); MainMenuLinks.City.classList.add("active");
ReactDOM.render(<ResleeveRoot player={Player} />, Engine.Display.content);
}, },
// Helper function that hides all content // Helper function that hides all content
hideAllContent: function () { hideAllContent: function () {
Engine.Display.terminalContent.style.display = "none"; Engine.Display.terminalContent.style.display = "none";
Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.scriptEditorContent);
Engine.Display.activeScriptsContent.style.display = "none"; Engine.Display.content.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent); ReactDOM.unmountComponentAtNode(Engine.Display.content);
Engine.Display.infiltrationContent.style.display = "none"; Engine.Display.infiltrationContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent); ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent);
Engine.Display.hacknetNodesContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.hacknetNodesContent);
Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionsContent);
Engine.Display.factionContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
Engine.Display.augmentationsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.augmentationsContent);
Engine.Display.milestonesContent.style.display = "none";
Engine.Display.tutorialContent.style.display = "none";
Engine.Display.locationContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.locationContent);
Engine.Display.gangContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.gangContent);
Engine.Display.bladeburnerContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.bladeburnerContent);
Engine.Display.resleeveContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.resleeveContent);
Engine.Display.sleevesContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.sleevesContent);
Engine.Display.corporationContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.corporationContent);
Engine.Display.workInProgressContent.style.display = "none"; Engine.Display.workInProgressContent.style.display = "none";
Engine.Display.redPillContent.style.display = "none"; Engine.Display.redPillContent.style.display = "none";
Engine.Display.cinematicTextContent.style.display = "none"; Engine.Display.cinematicTextContent.style.display = "none";
Engine.Display.stockMarketContent.style.display = "none";
Engine.Display.missionContent.style.display = "none"; Engine.Display.missionContent.style.display = "none";
// Make nav menu tabs inactive
Engine.inactivateMainMenuLinks();
// Close dev menu
closeDevMenu();
},
// Remove 'active' css class from all main menu links
inactivateMainMenuLinks: function () {
MainMenuLinks.Terminal.classList.remove("active");
MainMenuLinks.ScriptEditor.classList.remove("active");
MainMenuLinks.ActiveScripts.classList.remove("active");
MainMenuLinks.CreateProgram.classList.remove("active");
MainMenuLinks.Stats.classList.remove("active");
MainMenuLinks.Factions.classList.remove("active");
MainMenuLinks.Augmentations.classList.remove("active");
MainMenuLinks.HacknetNodes.classList.remove("active");
MainMenuLinks.Sleeves.classList.remove("active");
MainMenuLinks.City.classList.remove("active");
MainMenuLinks.Travel.classList.remove("active");
MainMenuLinks.Job.classList.remove("active");
MainMenuLinks.StockMarket.classList.remove("active");
MainMenuLinks.Gang.classList.remove("active");
MainMenuLinks.Bladeburner.classList.remove("active");
MainMenuLinks.Corporation.classList.remove("active");
MainMenuLinks.Gang.classList.remove("active");
MainMenuLinks.Milestones.classList.remove("active");
MainMenuLinks.Tutorial.classList.remove("active");
MainMenuLinks.Options.classList.remove("active");
MainMenuLinks.DevMenu.classList.remove("active");
}, },
displayCharacterOverviewInfo: function () { displayCharacterOverviewInfo: function () {
@ -540,11 +468,6 @@ const Engine = {
} }
}, },
/// Display character info
updateCharacterInfo: function () {
ReactDOM.render(CharacterInfo(Player), Engine.Display.characterInfo);
},
// Main Game Loop // Main Game Loop
idleTimer: function () { idleTimer: function () {
// Get time difference // Get time difference
@ -676,7 +599,6 @@ const Engine = {
autoSaveCounter: 300, autoSaveCounter: 300,
updateSkillLevelsCounter: 10, updateSkillLevelsCounter: 10,
updateDisplays: 3, updateDisplays: 3,
updateDisplaysMed: 9,
updateDisplaysLong: 15, updateDisplaysLong: 15,
updateActiveScriptsDisplay: 5, updateActiveScriptsDisplay: 5,
createProgramNotifications: 10, createProgramNotifications: 10,
@ -718,86 +640,11 @@ const Engine = {
Engine.Counters.updateSkillLevelsCounter = 10; Engine.Counters.updateSkillLevelsCounter = 10;
} }
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
if (routing.isOn(Page.ActiveScripts)) {
ReactDOM.render(
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
Engine.Display.activeScriptsContent,
);
}
Engine.Counters.updateActiveScriptsDisplay = 5;
}
if (Engine.Counters.updateDisplays <= 0) { if (Engine.Counters.updateDisplays <= 0) {
Engine.displayCharacterOverviewInfo(); Engine.displayCharacterOverviewInfo();
if (routing.isOn(Page.CreateProgram)) {
displayCreateProgramContent();
}
Engine.Counters.updateDisplays = 3; Engine.Counters.updateDisplays = 3;
} }
if (Engine.Counters.updateDisplaysMed <= 0) {
if (routing.isOn(Page.CharacterInfo)) {
Engine.updateCharacterInfo();
}
Engine.Counters.updateDisplaysMed = 9;
}
if (Engine.Counters.createProgramNotifications <= 0) {
var num = getNumAvailableCreateProgram();
var elem = document.getElementById("create-program-notification");
if (num > 0) {
elem.innerHTML = num;
elem.setAttribute("class", "notification-on");
} else {
elem.innerHTML = "";
elem.setAttribute("class", "notification-off");
}
Engine.Counters.createProgramNotifications = 10;
}
if (Engine.Counters.augmentationsNotifications <= 0) {
var num = Player.queuedAugmentations.length;
var elem = document.getElementById("augmentations-notification");
if (num > 0) {
elem.innerHTML = num;
elem.setAttribute("class", "notification-on");
} else {
elem.innerHTML = "";
elem.setAttribute("class", "notification-off");
}
Engine.Counters.augmentationsNotifications = 10;
}
if (Engine.Counters.checkFactionInvitations <= 0) {
var invitedFactions = Player.checkForFactionInvitations();
if (invitedFactions.length > 0) {
if (Player.firstFacInvRecvd === false) {
Player.firstFacInvRecvd = true;
document.getElementById("factions-tab").style.display = "list-item";
document.getElementById("character-menu-header").click();
document.getElementById("character-menu-header").click();
}
var randFaction = invitedFactions[Math.floor(Math.random() * invitedFactions.length)];
inviteToFaction(randFaction);
}
const num = Player.factionInvitations.length;
const elem = document.getElementById("factions-notification");
if (num > 0) {
elem.innerHTML = num;
elem.setAttribute("class", "notification-on");
} else {
elem.innerHTML = "";
elem.setAttribute("class", "notification-off");
}
Engine.Counters.checkFactionInvitations = 100;
}
if (Engine.Counters.passiveFactionGrowth <= 0) { if (Engine.Counters.passiveFactionGrowth <= 0) {
var adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth); var adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth);
processPassiveFactionRepGain(adjustedCycles); processPassiveFactionRepGain(adjustedCycles);
@ -872,29 +719,6 @@ const Engine = {
} }
}, },
/**
* Collapses a main menu header. Used when initializing the game.
* @param elems {HTMLElement[]} Elements under header
*/
closeMainMenuHeader: function (elems) {
for (var i = 0; i < elems.length; ++i) {
elems[i].style.maxHeight = null;
elems[i].style.opacity = 0;
elems[i].style.pointerEvents = "none";
}
},
/**
* Expands a main menu header. Used when initializing the game.
* @param elems {HTMLElement[]} Elements under header
*/
openMainMenuHeader: function (elems) {
for (var i = 0; i < elems.length; ++i) {
elems[i].style.maxHeight = elems[i].scrollHeight + "px";
elems[i].style.display = "block";
}
},
/** /**
* Used in game when clicking on a main menu header (NOT used for initialization) * Used in game when clicking on a main menu header (NOT used for initialization)
* @param open {boolean} Whether header is being opened or closed * @param open {boolean} Whether header is being opened or closed
@ -926,27 +750,6 @@ const Engine = {
}, },
load: function (saveString) { load: function (saveString) {
// Initialize main menu accordion panels to all start as "open"
const terminal = document.getElementById("terminal-tab");
const createScript = document.getElementById("create-script-tab");
const activeScripts = document.getElementById("active-scripts-tab");
const createProgram = document.getElementById("create-program-tab");
const stats = document.getElementById("stats-tab");
const factions = document.getElementById("factions-tab");
const augmentations = document.getElementById("augmentations-tab");
const hacknetnodes = document.getElementById("hacknet-nodes-tab");
const city = document.getElementById("city-tab");
const travel = document.getElementById("travel-tab");
const job = document.getElementById("job-tab");
const stockmarket = document.getElementById("stock-market-tab");
const bladeburner = document.getElementById("bladeburner-tab");
const corp = document.getElementById("corporation-tab");
const gang = document.getElementById("gang-tab");
const milestones = document.getElementById("milestones-tab");
const tutorial = document.getElementById("tutorial-tab");
const options = document.getElementById("options-tab");
const dev = document.getElementById("dev-tab");
// Load game from save or create new game // Load game from save or create new game
if (loadGame(saveString)) { if (loadGame(saveString)) {
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
@ -1084,66 +887,6 @@ const Engine = {
{Reputation(offlineReputation)} divided amongst your factions. {Reputation(offlineReputation)} divided amongst your factions.
</>, </>,
); );
// Close main menu accordions for loaded game
var visibleMenuTabs = [
terminal,
createScript,
activeScripts,
stats,
hacknetnodes,
city,
milestones,
tutorial,
options,
dev,
];
if (Player.firstFacInvRecvd) {
visibleMenuTabs.push(factions);
} else {
factions.style.display = "none";
}
if (Player.firstAugPurchased) {
visibleMenuTabs.push(augmentations);
} else {
augmentations.style.display = "none";
}
if (Player.companyName !== "") {
visibleMenuTabs.push(job);
} else {
job.style.display = "none";
}
if (Player.firstTimeTraveled) {
visibleMenuTabs.push(travel);
} else {
travel.style.display = "none";
}
if (Player.firstProgramAvailable) {
visibleMenuTabs.push(createProgram);
} else {
createProgram.style.display = "none";
}
if (Player.hasWseAccount) {
visibleMenuTabs.push(stockmarket);
} else {
stockmarket.style.display = "none";
}
if (Player.bladeburner instanceof Bladeburner) {
visibleMenuTabs.push(bladeburner);
} else {
bladeburner.style.display = "none";
}
if (Player.corporation instanceof Corporation) {
visibleMenuTabs.push(corp);
} else {
corp.style.display = "none";
}
if (Player.inGang()) {
visibleMenuTabs.push(gang);
} else {
gang.style.display = "none";
}
Engine.closeMainMenuHeader(visibleMenuTabs);
} else { } else {
// No save found, start new game // No save found, start new game
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
@ -1158,115 +901,27 @@ const Engine = {
initMessages(); initMessages();
updateSourceFileFlags(Player); updateSourceFileFlags(Player);
// Open main menu accordions for new game
const hackingHdr = document.getElementById("hacking-menu-header");
hackingHdr.classList.toggle("opened");
const characterHdr = document.getElementById("character-menu-header");
characterHdr.classList.toggle("opened");
const worldHdr = document.getElementById("world-menu-header");
worldHdr.classList.toggle("opened");
const helpHdr = document.getElementById("help-menu-header");
helpHdr.classList.toggle("opened");
// Hide tabs that wont be revealed until later
factions.style.display = "none";
augmentations.style.display = "none";
job.style.display = "none";
stockmarket.style.display = "none";
travel.style.display = "none";
createProgram.style.display = "none";
bladeburner.style.display = "none";
corp.style.display = "none";
gang.style.display = "none";
dev.style.display = "none";
Engine.openMainMenuHeader([
terminal,
createScript,
activeScripts,
stats,
hacknetnodes,
city,
milestones,
tutorial,
options,
]);
// Start interactive tutorial // Start interactive tutorial
iTutorialStart(); iTutorialStart();
removeLoadingScreen(); removeLoadingScreen();
} }
ReactDOM.render(<SidebarRoot engine={this} player={Player} />, document.getElementById("sidebar"));
// Initialize labels on game settings // Initialize labels on game settings
setSettingsLabels(); setSettingsLabels();
Terminal.resetTerminalInput(); Terminal.resetTerminalInput();
}, },
setDisplayElements: function () { setDisplayElements: function () {
Engine.Display.content = document.getElementById("generic-react-container");
Engine.Display.content.style.display = "none";
// Content elements // Content elements
Engine.Display.terminalContent = document.getElementById("terminal-container"); Engine.Display.terminalContent = document.getElementById("terminal-container");
routing.navigateTo(Page.Terminal); routing.navigateTo(Page.Terminal);
Engine.Display.characterContent = document.getElementById("character-container");
Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent = document.getElementById("script-editor-container");
Engine.Display.scriptEditorContent.style.display = "none";
Engine.Display.activeScriptsContent = document.getElementById("active-scripts-container");
Engine.Display.activeScriptsContent.style.display = "none";
Engine.Display.hacknetNodesContent = document.getElementById("hacknet-nodes-container");
Engine.Display.hacknetNodesContent.style.display = "none";
Engine.Display.createProgramContent = document.getElementById("create-program-container");
Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent = document.getElementById("factions-container");
Engine.Display.factionsContent.style.display = "none";
Engine.Display.factionContent = document.getElementById("faction-container");
Engine.Display.factionContent.style.display = "none";
Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
Engine.Display.augmentationsContent.style.display = "none";
Engine.Display.milestonesContent = document.getElementById("milestones-container");
Engine.Display.milestonesContent.style.display = "none";
Engine.Display.tutorialContent = document.getElementById("tutorial-container");
Engine.Display.tutorialContent.style.display = "none";
Engine.Display.infiltrationContent = document.getElementById("infiltration-container");
Engine.Display.infiltrationContent.style.display = "none";
Engine.Display.stockMarketContent = document.getElementById("stock-market-container");
Engine.Display.stockMarketContent.style.display = "none";
Engine.Display.gangContent = document.getElementById("gang-container");
Engine.Display.gangContent.style.display = "none";
Engine.Display.bladeburnerContent = document.getElementById("bladeburner-container");
Engine.Display.bladeburnerContent.style.display = "none";
Engine.Display.resleeveContent = document.getElementById("resleeve-container");
Engine.Display.resleeveContent.style.display = "none";
Engine.Display.sleevesContent = document.getElementById("sleeves-container");
Engine.Display.sleevesContent.style.display = "none";
Engine.Display.corporationContent = document.getElementById("corporation-container");
Engine.Display.corporationContent.style.display = "none";
Engine.Display.missionContent = document.getElementById("mission-container"); Engine.Display.missionContent = document.getElementById("mission-container");
Engine.Display.missionContent.style.display = "none"; Engine.Display.missionContent.style.display = "none";
// Character info
Engine.Display.characterInfo = document.getElementById("character-content");
// Location page (page that shows up when you visit a specific location in World)
Engine.Display.locationContent = document.getElementById("location-container");
Engine.Display.locationContent.style.display = "none";
// Work In Progress // Work In Progress
Engine.Display.workInProgressContent = document.getElementById("work-in-progress-container"); Engine.Display.workInProgressContent = document.getElementById("work-in-progress-container");
Engine.Display.workInProgressContent.style.display = "none"; Engine.Display.workInProgressContent.style.display = "none";
@ -1275,19 +930,12 @@ const Engine = {
Engine.Display.redPillContent = document.getElementById("red-pill-container"); Engine.Display.redPillContent = document.getElementById("red-pill-container");
Engine.Display.redPillContent.style.display = "none"; Engine.Display.redPillContent.style.display = "none";
Engine.Display.infiltrationContent = document.getElementById("infiltration-container");
Engine.Display.infiltrationContent.style.display = "none";
// Cinematic Text // Cinematic Text
Engine.Display.cinematicTextContent = document.getElementById("cinematic-text-container"); Engine.Display.cinematicTextContent = document.getElementById("cinematic-text-container");
Engine.Display.cinematicTextContent.style.display = "none"; Engine.Display.cinematicTextContent.style.display = "none";
// Initialize references to main menu links
if (!initializeMainMenuLinks()) {
const errorMsg =
"Failed to initialize Main Menu Links. Please try refreshing the page. " +
"If that doesn't work, report the issue to the developer";
exceptionAlert(new Error(errorMsg));
console.error(errorMsg);
return;
}
}, },
// Initialization // Initialization
@ -1297,120 +945,6 @@ const Engine = {
saveObject.importGame(); saveObject.importGame();
}; };
// Initialize Main Menu Headers (this must be done after initializing the links)
if (!initializeMainMenuHeaders(Player, process.env.NODE_ENV === "development")) {
const errorMsg =
"Failed to initialize Main Menu Headers. Please try refreshing the page. " +
"If that doesn't work, report the issue to the developer";
exceptionAlert(new Error(errorMsg));
console.error(errorMsg);
return;
}
MainMenuLinks.Terminal.addEventListener("click", function () {
Engine.loadTerminalContent();
return false;
});
MainMenuLinks.ScriptEditor.addEventListener("click", function () {
Engine.loadScriptEditorContent();
return false;
});
MainMenuLinks.ActiveScripts.addEventListener("click", function () {
Engine.loadActiveScriptsContent();
return false;
});
MainMenuLinks.CreateProgram.addEventListener("click", function () {
Engine.loadCreateProgramContent();
return false;
});
MainMenuLinks.Stats.addEventListener("click", function () {
Engine.loadCharacterContent();
return false;
});
MainMenuLinks.Factions.addEventListener("click", function () {
Engine.loadFactionsContent();
return false;
});
MainMenuLinks.Augmentations.addEventListener("click", function () {
Engine.loadAugmentationsContent();
return false;
});
MainMenuLinks.HacknetNodes.addEventListener("click", function () {
Engine.loadHacknetNodesContent();
return false;
});
MainMenuLinks.Sleeves.addEventListener("click", function () {
Engine.loadSleevesContent();
MainMenuLinks.Sleeves.classList.add("active");
return false;
});
MainMenuLinks.City.addEventListener("click", function () {
Engine.loadLocationContent();
return false;
});
MainMenuLinks.Travel.addEventListener("click", function () {
Engine.loadTravelContent();
return false;
});
MainMenuLinks.Job.addEventListener("click", function () {
Engine.loadJobContent();
return false;
});
MainMenuLinks.StockMarket.addEventListener("click", function () {
Engine.loadStockMarketContent();
MainMenuLinks.StockMarket.classList.add("active");
return false;
});
MainMenuLinks.Bladeburner.addEventListener("click", function () {
Engine.loadBladeburnerContent();
return false;
});
MainMenuLinks.Corporation.addEventListener("click", function () {
Engine.loadCorporationContent();
MainMenuLinks.Corporation.classList.add("active");
return false;
});
MainMenuLinks.Gang.addEventListener("click", function () {
Engine.loadGangContent();
MainMenuLinks.Gang.classList.add("active");
return false;
});
MainMenuLinks.Milestones.addEventListener("click", function () {
Engine.loadMilestonesContent();
return false;
});
MainMenuLinks.Tutorial.addEventListener("click", function () {
Engine.loadTutorialContent();
return false;
});
MainMenuLinks.DevMenu.addEventListener("click", function () {
if (process.env.NODE_ENV === "development") {
Engine.loadDevMenuContent();
}
return false;
});
// Active scripts list
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
// Save, Delete, Import/Export buttons // Save, Delete, Import/Export buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link"); Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function () { Engine.Clickables.saveMainMenuButton.addEventListener("click", function () {
@ -1440,11 +974,8 @@ const Engine = {
return false; return false;
}); });
// Create Program buttons
initCreateProgramButtons();
// Message at the top of terminal // Message at the top of terminal
postNetburnerText(); postVersion();
// Player was working cancel button // Player was working cancel button
if (Player.isWorking) { if (Player.isWorking) {
@ -1481,16 +1012,6 @@ const Engine = {
// Character overview screen // Character overview screen
document.getElementById("character-overview-container").style.display = "block"; document.getElementById("character-overview-container").style.display = "block";
// Remove classes from links (they might be set from tutorial)
document.getElementById("terminal-menu-link").removeAttribute("class");
document.getElementById("stats-menu-link").removeAttribute("class");
document.getElementById("create-script-menu-link").removeAttribute("class");
document.getElementById("active-scripts-menu-link").removeAttribute("class");
document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
document.getElementById("city-menu-link").removeAttribute("class");
document.getElementById("milestones-menu-link").removeAttribute("class");
document.getElementById("tutorial-menu-link").removeAttribute("class");
// Copy Save Data to Clipboard // Copy Save Data to Clipboard
document.getElementById("copy-save-to-clipboard-link").addEventListener("click", function () { document.getElementById("copy-save-to-clipboard-link").addEventListener("click", function () {
const saveString = saveObject.getSaveString(); const saveString = saveObject.getSaveString();
@ -1539,7 +1060,6 @@ const Engine = {
} }
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page."); dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
gameOptionsBoxClose(); gameOptionsBoxClose();
return false;
}); });
// DEBUG Soft Reset // DEBUG Soft Reset
@ -1547,13 +1067,11 @@ const Engine = {
dialogBoxCreate("Soft Reset!"); dialogBoxCreate("Soft Reset!");
prestigeAugmentation(); prestigeAugmentation();
gameOptionsBoxClose(); gameOptionsBoxClose();
return false;
}); });
// DEBUG File diagnostic // DEBUG File diagnostic
document.getElementById("debug-files").addEventListener("click", function () { document.getElementById("debug-files").addEventListener("click", function () {
createPopup("debug-files-diagnostic-popup", FileDiagnosticPopup, {}); createPopup("debug-files-diagnostic-popup", FileDiagnosticPopup, {});
return false;
}); });
}, },

@ -8,7 +8,6 @@ import "../css/mainmenu.scss";
import "../css/characteroverview.scss"; import "../css/characteroverview.scss";
import "../css/terminal.scss"; import "../css/terminal.scss";
import "../css/scripteditor.scss"; import "../css/scripteditor.scss";
import "../css/codemirror-overrides.scss";
import "../css/activescripts.scss"; import "../css/activescripts.scss";
import "../css/hacknetnodes.scss"; import "../css/hacknetnodes.scss";
import "../css/menupages.scss"; import "../css/menupages.scss";
@ -32,4 +31,3 @@ import "../css/dev-menu.css";
import "../css/casino.scss"; import "../css/casino.scss";
import "../css/milestones.scss"; import "../css/milestones.scss";
import "../css/infiltration.scss"; import "../css/infiltration.scss";
import "../css/corporation.scss";

@ -41,176 +41,7 @@
<div id="entire-game-container" style="visibility: hidden"> <div id="entire-game-container" style="visibility: hidden">
<div id="mainmenu-container"> <div id="mainmenu-container">
<!-- Main menu --> <!-- Main menu -->
<ul id="mainmenu" class="mainmenu noscrollbar"> <div id="sidebar"></div>
<!-- Hacking dropdown -->
<li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header noselect">Hacking</button>
</li>
<li id="terminal-tab" class="mainmenu-accordion-panel noselect">
<button id="terminal-menu-link">Terminal</button>
</li>
<li id="create-script-tab" class="mainmenu-accordion-panel noselect">
<button id="create-script-menu-link">Create Script</button>
</li>
<li id="active-scripts-tab" class="mainmenu-accordion-panel noselect">
<button id="active-scripts-menu-link">Active Scripts</button>
</li>
<li id="create-program-tab" class="mainmenu-accordion-panel noselect">
<button id="create-program-menu-link">Create Program</button>
<span id="create-program-notification" class="notification-off"> </span>
</li>
<!-- Character dropdown -->
<li id="character-menu-header-li">
<button id="character-menu-header" class="mainmenu-accordion-header noselect">Character</button>
</li>
<li id="stats-tab" class="mainmenu-accordion-panel noselect">
<button id="stats-menu-link">Stats</button>
</li>
<li id="factions-tab" class="mainmenu-accordion-panel noselect">
<button id="factions-menu-link">Factions</button>
<span id="factions-notification" class="notification-off"> </span>
</li>
<li id="augmentations-tab" class="mainmenu-accordion-panel noselect">
<button id="augmentations-menu-link" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
Augmentations
</button>
<span id="augmentations-notification" class="notification-off"> </span>
</li>
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel noselect">
<button id="hacknet-nodes-menu-link">Hacknet</button>
</li>
<li id="sleeves-tab" class="mainmenu-accordion-panel noselect">
<button id="sleeves-menu-link">Sleeves</button>
</li>
<!-- World dropdown -->
<li id="world-menu-header-li">
<button id="world-menu-header" class="mainmenu-accordion-header noselect">World</button>
</li>
<li id="city-tab" class="mainmenu-accordion-panel noselect">
<button id="city-menu-link">City</button>
</li>
<li id="travel-tab" class="mainmenu-accordion-panel noselect">
<button id="travel-menu-link">Travel</button>
</li>
<li id="job-tab" class="mainmenu-accordion-panel noselect">
<button id="job-menu-link">Job</button>
</li>
<li id="stock-market-tab" class="mainmenu-accordion-panel noselect">
<button id="stock-market-menu-link">Stock Market</button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel noselect">
<button id="bladeburner-menu-link">Bladeburner</button>
</li>
<li id="corporation-tab" class="mainmenu-accordion-panel noselect">
<button id="corporation-menu-link">Corp</button>
</li>
<li id="gang-tab" class="mainmenu-accordion-panel noselect">
<button id="gang-menu-link">Gang</button>
</li>
<li id="help-menu-header-li">
<button id="help-menu-header" class="mainmenu-accordion-header noselect">Help</button>
</li>
<li id="milestones-tab" class="mainmenu-accordion-panel noselect">
<button id="milestones-menu-link">Milestones</button>
</li>
<li id="tutorial-tab" class="mainmenu-accordion-panel noselect">
<button id="tutorial-menu-link">Tutorial</button>
</li>
<li id="options-tab" class="mainmenu-accordion-panel noselect">
<button id="options-menu-link">Options</button>
</li>
<li id="dev-tab" class="mainmenu-accordion-panel noselect">
<button id="dev-menu-link">Dev</button>
</li>
</ul>
</div>
<div id="script-editor-container" class="generic-menupage-container">
<div id="script-editor-wrapper">
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag">
<strong style="background-color: #555">Script name: </strong>
</p>
<input id="script-editor-filename" type="text" maxlength="100" tabindex="1" />
</div>
<div id="monaco-editor"></div>
<div id="script-editor-buttons-wrapper"></div>
<!-- Buttons below script editor -->
</div>
<!-- End wrapper -->
<div id="script-editor-options-panel">
<h1 style="color: white">Script Editor Options</h1>
<fieldset>
<label for="script-editor-option-editor">Editor</label>
<select id="script-editor-option-editor" class="dropdown">
<option value="Ace">Ace</option>
<option value="CodeMirror">CodeMirror</option>
</select>
</fieldset>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme" class="dropdown"></select>
</fieldset>
<fieldset>
<label for="script-editor-option-keybinding">Key Binding</label>
<select id="script-editor-option-keybinding" class="dropdown"></select>
</fieldset>
<fieldset>
<label for="script-editor-option-highlightactiveline">Highlight Active Line</label>
<input
type="checkbox"
class="optionCheckbox"
name="script-editor-option-highlightactiveline"
id="script-editor-option-highlightactiveline"
checked
/>
</fieldset>
<fieldset>
<label for="script-editor-option-showinvisibles">Show Invisibles</label>
<input
type="checkbox"
class="optionCheckbox"
name="script-editor-option-showinvisibles"
id="script-editor-option-showinvisibles"
/>
</fieldset>
<fieldset>
<label for="script-editor-option-usesofttab">Use Soft Tab</label>
<input
type="checkbox"
class="optionCheckbox"
name="script-editor-option-usesofttab"
id="script-editor-option-usesofttab"
checked
/>
</fieldset>
<fieldset id="script-editor-option-flex1-fieldset"></fieldset>
<fieldset id="script-editor-option-flex2-fieldset"></fieldset>
<fieldset id="script-editor-option-flex3-fieldset"></fieldset>
<fieldset id="script-editor-option-flex4-fieldset"></fieldset>
</div>
<!-- End script editor options panel -->
<!-- TODO(hydroflame): remove this once Monaco is implemented -->
<div id="ace-editor" style="display: none"></div>
<form id="codemirror-form-wrapper" style="display: none">
<textarea id="codemirror-editor"></textarea>
</form>
<div id="codemirror-vim-command-display-wrapper" style="display: none">
Key Buffer: <span id="codemirror-vim-command-display"></span>
</div>
</div> </div>
<!-- Terminal page --> <!-- Terminal page -->
@ -232,142 +63,10 @@
</table> </table>
</div> </div>
<!-- Character Info page --> <div class="generic-menupage-container">
<div id="character-container" class="generic-menupage-container"> <div id="generic-react-container"></div>
<div id="character-content"></div>
</div> </div>
<!-- Active scripts info page -->
<div id="active-scripts-container" class="generic-menupage-container">
<p id="active-scripts-text">
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the
servers on which they are running.
</p>
<p id="active-scripts-total-prod">
Total online production of Active scripts:
<span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br />
Total online production since last Aug installation:
<span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span class="money-gold"
><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span
>)
</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none"></ul>
</div>
<!-- Hacknet Nodes -->
<div id="hacknet-nodes-container" class="generic-menupage-container">
<!-- React Component -->
</div>
<!-- Create a program(executable) -->
<div id="create-program-container" class="generic-menupage-container">
<p id="create-program-page-text">
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating a program you can cancel at any
time. Your progress will be saved and you can continue later.
</p>
<ul id="create-program-list"></ul>
</div>
<!-- Factions -->
<div id="factions-container" class="generic-menupage-container"></div>
<!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div>
<!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div>
<!-- Milestones content -->
<div id="milestones-container" class="generic-menupage-container"></div>
<!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container">
<h1>Tutorial (AKA Links to Documentation)</h1>
<a
id="tutorial-getting-started-link"
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html"
>
Getting Started</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/servers.html"
>
Servers & Networking</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/hacking.html"
>
Hacking</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/scripts.html"
>
Scripts</a
><br /><br />
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/netscript.html">
Netscript Programming Language</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/world.html"
>
Traveling</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/companies.html"
>
Companies</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/infiltration.html"
>
Infiltration</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/factions.html"
>
Factions</a
><br /><br />
<a
class="a-link-button"
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/basicgameplay/augmentations.html"
>
Augmentations</a
><br /><br />
<a class="a-link-button" target="_blank" href="https://bitburner.readthedocs.io/en/latest/shortcuts.html">
Keyboard Shortcuts</a
>
</div>
<!-- Location (visiting a location in World) -->
<div id="location-container" class="generic-menupage-container"></div>
<div id="infiltration-container" class="generic-fullscreen-container"></div> <div id="infiltration-container" class="generic-fullscreen-container"></div>
<div id="stock-market-container" class="generic-menupage-container"></div>
<div id="bladeburner-container" class="generic-menupage-container"></div>
<div id="resleeve-container" class="generic-menupage-container"></div>
<div id="gang-container" class="generic-menupage-container"></div>
<div id="corporation-container" class="generic-menupage-container"></div>
<div id="sleeves-container" class="generic-menupage-container"></div>
<!-- Generic Yes/No Pop Up box --> <!-- Generic Yes/No Pop Up box -->
<div id="yes-no-box-container" class="popup-box-container"> <div id="yes-no-box-container" class="popup-box-container">
@ -403,19 +102,6 @@
</div> </div>
</div> </div>
<!-- End of Infiltration pop up box -->
<div id="infiltration-box-container" class="popup-box-container">
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"></p>
<button id="infiltration-box-sell" class="a-link-button">Sell on Black Market</button>
<br /><br />
<select id="infiltration-faction-select" class="dropdown"></select>
<br />
<button id="infiltration-box-faction" class="a-link-button">Give to Faction for Reputation</button>
</div>
</div>
<!-- Mission container --> <!-- Mission container -->
<div id="mission-container" class="generic-fullscreen-container"></div> <div id="mission-container" class="generic-fullscreen-container"></div>

@ -2,7 +2,7 @@
* Root React Component for the "Active Scripts" UI page. This page displays * Root React Component for the "Active Scripts" UI page. This page displays
* and provides information about all of the player's scripts that are currently running * and provides information about all of the player's scripts that are currently running
*/ */
import * as React from "react"; import React, { useState, useEffect } from "react";
import { ScriptProduction } from "./ScriptProduction"; import { ScriptProduction } from "./ScriptProduction";
import { ServerAccordions } from "./ServerAccordions"; import { ServerAccordions } from "./ServerAccordions";
@ -15,23 +15,28 @@ type IProps = {
workerScripts: Map<number, WorkerScript>; workerScripts: Map<number, WorkerScript>;
}; };
export class ActiveScriptsRoot extends React.Component<IProps> { export function ActiveScriptsRoot(props: IProps): React.ReactElement {
constructor(props: IProps) { const setRerender = useState(false)[1];
super(props); function rerender(): void {
setRerender((old) => !old);
} }
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => {
const id = setInterval(rerender, 20);
return () => clearInterval(id);
}, []);
render(): React.ReactNode {
return ( return (
<> <div className="active-scripts-container">
<p> <p>
This page displays a list of all of your scripts that are currently running across every machine. It also This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the provides information about each script's production. The scripts are categorized by the hostname of the servers
servers on which they are running. on which they are running.
</p> </p>
<ScriptProduction {...this.props} /> <ScriptProduction {...props} />
<ServerAccordions {...this.props} /> <ServerAccordions {...props} />
</> </div>
); );
}
} }

@ -1,4 +1,4 @@
import * as React from "react"; import React, { useState, useEffect } from "react";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodes } from "../BitNode/BitNode"; import { BitNodes } from "../BitNode/BitNode";
@ -13,12 +13,27 @@ import { HacknetServerConstants } from "../Hacknet/data/Constants";
import { StatsTable } from "./React/StatsTable"; import { StatsTable } from "./React/StatsTable";
import { Money } from "./React/Money"; import { Money } from "./React/Money";
export function CharacterInfo(p: IPlayer): React.ReactElement { interface IProps {
player: IPlayer;
}
export function CharacterInfo(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => {
const id = setInterval(rerender, 20);
return () => clearInterval(id);
}, []);
function LastEmployer(): React.ReactElement { function LastEmployer(): React.ReactElement {
if (p.companyName) { if (props.player.companyName) {
return ( return (
<> <>
<span>Employer at which you last worked: {p.companyName}</span> <span>Employer at which you last worked: {props.player.companyName}</span>
<br /> <br />
</> </>
); );
@ -26,10 +41,10 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
return <></>; return <></>;
} }
function LastJob(): React.ReactElement { function LastJob(): React.ReactElement {
if (p.companyName !== "") { if (props.player.companyName !== "") {
return ( return (
<> <>
<span>Job you last worked: {p.jobs[p.companyName]}</span> <span>Job you last worked: {props.player.jobs[props.player.companyName]}</span>
<br /> <br />
</> </>
); );
@ -37,13 +52,13 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
return <></>; return <></>;
} }
function Employers(): React.ReactElement { function Employers(): React.ReactElement {
if (p.jobs && Object.keys(p.jobs).length !== 0) if (props.player.jobs && Object.keys(props.player.jobs).length !== 0)
return ( return (
<> <>
<span>All Employers:</span> <span>All Employers:</span>
<br /> <br />
<ul> <ul>
{Object.keys(p.jobs).map((j) => ( {Object.keys(props.player.jobs).map((j) => (
<li key={j}> * {j}</li> <li key={j}> * {j}</li>
))} ))}
</ul> </ul>
@ -56,17 +71,17 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
function Hacknet(): React.ReactElement { function Hacknet(): React.ReactElement {
// Can't import HacknetHelpers for some reason. // Can't import HacknetHelpers for some reason.
if (!(p.bitNodeN === 9 || SourceFileFlags[9] > 0)) { if (!(props.player.bitNodeN === 9 || SourceFileFlags[9] > 0)) {
return ( return (
<> <>
<span>{`Hacknet Nodes owned: ${p.hacknetNodes.length}`}</span> <span>{`Hacknet Nodes owned: ${props.player.hacknetNodes.length}`}</span>
<br /> <br />
</> </>
); );
} else { } else {
return ( return (
<> <>
<span>{`Hacknet Servers owned: ${p.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`}</span> <span>{`Hacknet Servers owned: ${props.player.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`}</span>
<br /> <br />
</> </>
); );
@ -126,10 +141,10 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<> <>
<u>Money earned since you last installed Augmentations:</u> <u>Money earned since you last installed Augmentations:</u>
<br /> <br />
{convertMoneySourceTrackerToString(p.moneySourceA)} {convertMoneySourceTrackerToString(props.player.moneySourceA)}
</> </>
); );
if (p.sourceFiles.length !== 0) { if (props.player.sourceFiles.length !== 0) {
content = ( content = (
<> <>
{content} {content}
@ -137,7 +152,7 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<br /> <br />
<u>Money earned in this BitNode:</u> <u>Money earned in this BitNode:</u>
<br /> <br />
{convertMoneySourceTrackerToString(p.moneySourceB)} {convertMoneySourceTrackerToString(props.player.moneySourceB)}
</> </>
); );
} }
@ -146,11 +161,11 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
} }
function Intelligence(): React.ReactElement { function Intelligence(): React.ReactElement {
if (p.intelligence > 0 && (p.bitNodeN === 5 || SourceFileFlags[5] > 0)) { if (props.player.intelligence > 0 && (props.player.bitNodeN === 5 || SourceFileFlags[5] > 0)) {
return ( return (
<tr key="5"> <tr key="5">
<td>Intelligence:</td> <td>Intelligence:</td>
<td style={{ textAlign: "right" }}>{numeralWrapper.formatSkill(p.intelligence)}</td> <td style={{ textAlign: "right" }}>{numeralWrapper.formatSkill(props.player.intelligence)}</td>
</tr> </tr>
); );
} }
@ -189,15 +204,15 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
} }
function BladeburnerMults(): React.ReactElement { function BladeburnerMults(): React.ReactElement {
if (!p.canAccessBladeburner()) return <></>; if (!props.player.canAccessBladeburner()) return <></>;
return ( return (
<> <>
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Bladeburner Success Chance", p.bladeburner_max_stamina_mult], ["Bladeburner Success Chance", props.player.bladeburner_max_stamina_mult],
["Bladeburner Max Stamina", p.bladeburner_stamina_gain_mult], ["Bladeburner Max Stamina", props.player.bladeburner_stamina_gain_mult],
["Bladeburner Stamina Gain", p.bladeburner_analysis_mult], ["Bladeburner Stamina Gain", props.player.bladeburner_analysis_mult],
["Bladeburner Field Analysis", p.bladeburner_success_chance_mult], ["Bladeburner Field Analysis", props.player.bladeburner_success_chance_mult],
]} ]}
/> />
<br /> <br />
@ -206,12 +221,12 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
} }
function CurrentBitNode(): React.ReactElement { function CurrentBitNode(): React.ReactElement {
if (p.sourceFiles.length > 0) { if (props.player.sourceFiles.length > 0) {
const index = "BitNode" + p.bitNodeN; const index = "BitNode" + props.player.bitNodeN;
return ( return (
<> <>
<span> <span>
Current BitNode: {p.bitNodeN} ({BitNodes[index].name}) Current BitNode: {props.player.bitNodeN} ({BitNodes[index].name})
</span> </span>
<br /> <br />
<br /> <br />
@ -230,29 +245,36 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
return <></>; return <></>;
} }
const timeRows = [["Time played since last Augmentation:", convertTimeMsToTimeElapsedString(p.playtimeSinceLastAug)]]; const timeRows = [
if (p.sourceFiles.length > 0) { ["Time played since last Augmentation:", convertTimeMsToTimeElapsedString(props.player.playtimeSinceLastAug)],
];
if (props.player.sourceFiles.length > 0) {
timeRows.push([ timeRows.push([
"Time played since last Bitnode destroyed:", "Time played since last Bitnode destroyed:",
convertTimeMsToTimeElapsedString(p.playtimeSinceLastBitnode), convertTimeMsToTimeElapsedString(props.player.playtimeSinceLastBitnode),
]); ]);
} }
timeRows.push(["Total Time played:", convertTimeMsToTimeElapsedString(p.totalPlaytime)]); timeRows.push(["Total Time played:", convertTimeMsToTimeElapsedString(props.player.totalPlaytime)]);
return ( return (
<>
<pre> <pre>
<b>General</b> <b>General</b>
<br /> <br />
<br /> <br />
<span>Current City: {p.city}</span> <span>Current City: {props.player.city}</span>
<br /> <br />
<LastEmployer /> <LastEmployer />
<LastJob /> <LastJob />
<Employers /> <Employers />
<span> <span>
Money: <Money money={p.money.toNumber()} /> Money: <Money money={props.player.money.toNumber()} />
</span> </span>
<button className="popup-box-button" style={{ display: "inline-block", float: "none" }} onClick={openMoneyModal}> <button
className="popup-box-button"
style={{ display: "inline-block", float: "none" }}
onClick={openMoneyModal}
>
Money Statistics & Breakdown Money Statistics & Breakdown
</button> </button>
<br /> <br />
@ -263,55 +285,55 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<tr key="0"> <tr key="0">
<td key="0">Hacking:</td> <td key="0">Hacking:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.hacking_skill)} {numeralWrapper.formatSkill(props.player.hacking_skill)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.hacking_exp)} exp) ({numeralWrapper.formatExp(props.player.hacking_exp)} exp)
</td> </td>
</tr> </tr>
<tr key="1"> <tr key="1">
<td key="0">Strength:</td> <td key="0">Strength:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.strength)} {numeralWrapper.formatSkill(props.player.strength)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.strength_exp)} exp) ({numeralWrapper.formatExp(props.player.strength_exp)} exp)
</td> </td>
</tr> </tr>
<tr key="2"> <tr key="2">
<td key="0">Defense:</td> <td key="0">Defense:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.defense)} {numeralWrapper.formatSkill(props.player.defense)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.defense_exp)} exp) ({numeralWrapper.formatExp(props.player.defense_exp)} exp)
</td> </td>
</tr> </tr>
<tr key="3"> <tr key="3">
<td key="0">Dexterity:</td> <td key="0">Dexterity:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.dexterity)} {numeralWrapper.formatSkill(props.player.dexterity)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.dexterity_exp)} exp) ({numeralWrapper.formatExp(props.player.dexterity_exp)} exp)
</td> </td>
</tr> </tr>
<tr key="4"> <tr key="4">
<td key="0">Agility:</td> <td key="0">Agility:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.agility)} {numeralWrapper.formatSkill(props.player.agility)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.agility_exp)} exp) ({numeralWrapper.formatExp(props.player.agility_exp)} exp)
</td> </td>
</tr> </tr>
<tr key="5"> <tr key="5">
<td key="0">Charisma:</td> <td key="0">Charisma:</td>
<td key="1" style={{ textAlign: "right" }}> <td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatSkill(p.charisma)} {numeralWrapper.formatSkill(props.player.charisma)}
</td> </td>
<td key="2" style={{ textAlign: "right" }}> <td key="2" style={{ textAlign: "right" }}>
({numeralWrapper.formatExp(p.charisma_exp)} exp) ({numeralWrapper.formatExp(props.player.charisma_exp)} exp)
</td> </td>
</tr> </tr>
<Intelligence /> <Intelligence />
@ -320,57 +342,93 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Hacking Chance", p.hacking_chance_mult], ["Hacking Chance", props.player.hacking_chance_mult],
["Hacking Speed", p.hacking_speed_mult], ["Hacking Speed", props.player.hacking_speed_mult],
["Hacking Money", p.hacking_money_mult, p.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney], [
["Hacking Growth", p.hacking_grow_mult, p.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate], "Hacking Money",
props.player.hacking_money_mult,
props.player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
],
[
"Hacking Growth",
props.player.hacking_grow_mult,
props.player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Hacking Level", p.hacking_mult, p.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier], [
["Hacking Experience", p.hacking_exp_mult, p.hacking_exp_mult * BitNodeMultipliers.HackExpGain], "Hacking Level",
props.player.hacking_mult,
props.player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier,
],
[
"Hacking Experience",
props.player.hacking_exp_mult,
props.player.hacking_exp_mult * BitNodeMultipliers.HackExpGain,
],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Strength Level", p.strength_mult, p.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier], [
["Strength Experience", p.strength_exp_mult], "Strength Level",
props.player.strength_mult,
props.player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier,
],
["Strength Experience", props.player.strength_exp_mult],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Defense Level", p.defense_mult, p.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier], [
["Defense Experience", p.defense_exp_mult], "Defense Level",
props.player.defense_mult,
props.player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier,
],
["Defense Experience", props.player.defense_exp_mult],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Dexterity Level", p.dexterity_mult, p.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier], [
["Dexterity Experience", p.dexterity_exp_mult], "Dexterity Level",
props.player.dexterity_mult,
props.player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
],
["Dexterity Experience", props.player.dexterity_exp_mult],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Agility Level", p.agility_mult, p.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier], [
["Agility Experience", p.agility_exp_mult], "Agility Level",
props.player.agility_mult,
props.player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier,
],
["Agility Experience", props.player.agility_exp_mult],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Charisma Level", p.charisma_mult, p.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier], [
["Charisma Experience", p.charisma_exp_mult], "Charisma Level",
props.player.charisma_mult,
props.player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
],
["Charisma Experience", props.player.charisma_exp_mult],
]} ]}
/> />
<br /> <br />
@ -379,30 +437,42 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
rows={[ rows={[
[ [
"Hacknet Node production", "Hacknet Node production",
p.hacknet_node_money_mult, props.player.hacknet_node_money_mult,
p.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney, props.player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
], ],
["Hacknet Node purchase cost", p.hacknet_node_purchase_cost_mult], ["Hacknet Node purchase cost", props.player.hacknet_node_purchase_cost_mult],
["Hacknet Node RAM upgrade cost", p.hacknet_node_ram_cost_mult], ["Hacknet Node RAM upgrade cost", props.player.hacknet_node_ram_cost_mult],
["Hacknet Node Core purchase cost", p.hacknet_node_core_cost_mult], ["Hacknet Node Core purchase cost", props.player.hacknet_node_core_cost_mult],
["Hacknet Node level upgrade cost", p.hacknet_node_level_cost_mult], ["Hacknet Node level upgrade cost", props.player.hacknet_node_level_cost_mult],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Company reputation gain", p.company_rep_mult], ["Company reputation gain", props.player.company_rep_mult],
["Faction reputation gain", p.faction_rep_mult, p.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain], [
["Salary", p.work_money_mult, p.work_money_mult * BitNodeMultipliers.CompanyWorkMoney], "Faction reputation gain",
props.player.faction_rep_mult,
props.player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
],
[
"Salary",
props.player.work_money_mult,
props.player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney,
],
]} ]}
/> />
<br /> <br />
<MultiplierTable <MultiplierTable
rows={[ rows={[
["Crime success", p.crime_success_mult], ["Crime success", props.player.crime_success_mult],
["Crime money", p.crime_money_mult, p.crime_money_mult * BitNodeMultipliers.CrimeMoney], [
"Crime money",
props.player.crime_money_mult,
props.player.crime_money_mult * BitNodeMultipliers.CrimeMoney,
],
]} ]}
/> />
<br /> <br />
@ -413,15 +483,16 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<b>Misc.</b> <b>Misc.</b>
<br /> <br />
<br /> <br />
<span>{`Servers owned: ${p.purchasedServers.length} / ${getPurchaseServerLimit()}`}</span> <span>{`Servers owned: ${props.player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</span>
<br /> <br />
<Hacknet /> <Hacknet />
<span>{`Augmentations installed: ${p.augmentations.length}`}</span> <span>{`Augmentations installed: ${props.player.augmentations.length}`}</span>
<br /> <br />
<br /> <br />
{StatsTable(timeRows)} {StatsTable(timeRows)}
<br /> <br />
<CurrentBitNode /> <CurrentBitNode />
</pre> </pre>
</>
); );
} }

@ -3,6 +3,7 @@
import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners"; import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners";
interface IMainMenuLinks { interface IMainMenuLinks {
[key: string]: HTMLElement | undefined;
Terminal: HTMLElement; Terminal: HTMLElement;
ScriptEditor: HTMLElement; ScriptEditor: HTMLElement;
ActiveScripts: HTMLElement; ActiveScripts: HTMLElement;
@ -55,6 +56,7 @@ export const MainMenuLinks: IMainMenuLinks = {
}; };
export function initializeMainMenuLinks(): boolean { export function initializeMainMenuLinks(): boolean {
return true;
try { try {
function safeGetLink(id: string): HTMLElement { function safeGetLink(id: string): HTMLElement {
const elem: HTMLElement | null = clearEventListeners(id); const elem: HTMLElement | null = clearEventListeners(id);
@ -83,9 +85,9 @@ export function initializeMainMenuLinks(): boolean {
MainMenuLinks.Gang = safeGetLink("gang-menu-link"); MainMenuLinks.Gang = safeGetLink("gang-menu-link");
MainMenuLinks.Milestones = safeGetLink("milestones-menu-link"); MainMenuLinks.Milestones = safeGetLink("milestones-menu-link");
MainMenuLinks.Tutorial = safeGetLink("tutorial-menu-link"); MainMenuLinks.Tutorial = safeGetLink("tutorial-menu-link");
const op: HTMLElement | null = document.getElementById("options-menu-link"); // const op: HTMLElement | null = document.getElementById("options-menu-link");
if (op === null) throw new Error(`Could not find element with id: "options-menu-link"`); // if (op === null) throw new Error(`Could not find element with id: "options-menu-link"`);
MainMenuLinks.Options = op; // This click listener is already set, so don't clear it // MainMenuLinks.Options = op; // This click listener is already set, so don't clear it
MainMenuLinks.DevMenu = safeGetLink("dev-menu-link"); MainMenuLinks.DevMenu = safeGetLink("dev-menu-link");
return true; return true;

@ -6,9 +6,11 @@ import * as React from "react";
interface IStdButtonProps { interface IStdButtonProps {
addClasses?: string; addClasses?: string;
autoFocus?: boolean;
disabled?: boolean; disabled?: boolean;
id?: string; id?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any; onClick?: (e: React.MouseEvent<HTMLElement>) => any;
onKeyUp?: (e: React.KeyboardEvent<HTMLElement>) => any;
style?: any; style?: any;
text: string | JSX.Element; text: string | JSX.Element;
tooltip?: string | JSX.Element; tooltip?: string | JSX.Element;
@ -43,7 +45,7 @@ export function StdButton(props: IStdButtonProps): React.ReactElement {
} }
return ( return (
<button className={className} id={props.id} onClick={props.onClick} style={props.style}> <button className={className} id={props.id} onClick={props.onClick} onKeyUp={props.onKeyUp} style={props.style} autoFocus={props.autoFocus}>
{props.text} {props.text}
{hasTooltip && tooltip} {hasTooltip && tooltip}
</button> </button>

@ -9,7 +9,7 @@
"target": "es6", "target": "es6",
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
"types": ["cypress", "@testing-library/cypress"] "types": ["cypress", "@testing-library/cypress", "node"]
}, },
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

@ -12,6 +12,7 @@ $(document).click(function (event) {
var gameOptionsOpened = false; var gameOptionsOpened = false;
function gameOptionsBoxInit() { function gameOptionsBoxInit() {
return;
//Menu link button //Menu link button
document.getElementById("options-menu-link").addEventListener("click", function () { document.getElementById("options-menu-link").addEventListener("click", function () {
gameOptionsBoxOpen(); gameOptionsBoxOpen();