sidebar is react, fix a few bugs

This commit is contained in:
Olivier Gagnon 2021-09-11 01:54:19 -04:00
parent 4bedf05b1c
commit add19d353e
28 changed files with 458 additions and 798 deletions

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

@ -113,6 +113,10 @@ a:visited {
color: #fff;
border-radius: 2px;
padding: 1px 3px;
display: "list-item";
font-size: $defaultFontSize * 0.625;
top: 0;
right: 0;
}
.notification-off {
@ -120,6 +124,7 @@ a:visited {
color: #333;
border-radius: 0;
padding: 0;
display: "none";
}
/* 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(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([907,0]),o()}({907:function(n,t,o){"use strict";o.r(t);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),o(972)},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){},972:function(n,t,o){}});
//# sourceMappingURL=engineStyle.bundle.js.map

36
dist/engineStyle.css vendored

@ -105,13 +105,18 @@ a:visited {
background-color: #fa3e3e;
color: #fff;
border-radius: 2px;
padding: 1px 3px; }
padding: 1px 3px;
display: "list-item";
font-size: 10px;
top: 0;
right: 0; }
.notification-off {
background-color: #333;
color: #333;
border-radius: 0;
padding: 0; }
padding: 0;
display: "none"; }
/* help tip. Question mark that opens popup with info/details */
.help-tip {
@ -638,13 +643,18 @@ a:visited {
background-color: #fa3e3e;
color: #fff;
border-radius: 2px;
padding: 1px 3px; }
padding: 1px 3px;
display: "list-item";
font-size: 10px;
top: 0;
right: 0; }
.notification-off {
background-color: #333;
color: #333;
border-radius: 0;
padding: 0; }
padding: 0;
display: "none"; }
/* help tip. Question mark that opens popup with info/details */
.help-tip {
@ -1121,6 +1131,9 @@ button {
.mainmenu > li button.active:hover {
background-color: #aaa; }
.menu-header {
position: relative; }
#hacking-menu-header-li,
#character-menu-header-li,
#world-menu-header-li,
@ -1166,8 +1179,6 @@ button {
/* Slide down transition */
.mainmenu-accordion-panel {
max-height: 0;
opacity: 1;
transition: max-height 0.2s ease-out; }
/* COLORS */
@ -1652,6 +1663,10 @@ button {
width: 99%;
overflow-y: scroll; }
#generic-react-container {
position: fixed;
padding: 10px; }
/* Character Info */
#character-container {
padding-top: 10px;
@ -2052,13 +2067,18 @@ a:visited {
background-color: #fa3e3e;
color: #fff;
border-radius: 2px;
padding: 1px 3px; }
padding: 1px 3px;
display: "list-item";
font-size: 10px;
top: 0;
right: 0; }
.notification-off {
background-color: #333;
color: #333;
border-radius: 0;
padding: 0; }
padding: 0;
display: "none"; }
/* help tip. Question mark that opens popup with info/details */
.help-tip {

28
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="mainmenu-container">
<!-- Main menu -->
<ul id="mainmenu" class="mainmenu noscrollbar">
<!-- 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 id="sidebar"></div>
</div>
<!-- Terminal page -->
@ -208,92 +56,8 @@
</table>
</div>
<!-- Character Info page -->
<div id="character-container" class="generic-menupage-container">
<div id="character-content"></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="generic-react-container" class="generic-menupage-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 -->
<div id="yes-no-box-container" class="popup-box-container">
@ -329,19 +93,6 @@
</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 -->
<div id="mission-container" class="generic-fullscreen-container"></div>

13
package-lock.json generated

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

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

@ -51,7 +51,7 @@ export function CorporationRoot(props: IProps): React.ReactElement {
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => {
const id = setInterval(rerender, 20);
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);

@ -59,6 +59,14 @@ export function ExportPopup(props: IProps): React.ReactElement {
}
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 (
<>
@ -74,15 +82,14 @@ export function ExportPopup(props: IProps): React.ReactElement {
))}
</select>
<select className="dropdown" onChange={onCityChange} defaultValue={city}>
{currentDivision &&
Object.keys(currentDivision.warehouses).map((cityName: string) => {
if (currentDivision.warehouses[cityName] === 0) return;
return (
<option key={cityName} value={cityName}>
{cityName}
</option>
);
})}
{possibleCities.map((cityName: string) => {
if (currentDivision.warehouses[cityName] === 0) return;
return (
<option key={cityName} value={cityName}>
{cityName}
</option>
);
})}
</select>
<input className="text-input" placeholder="Export amount / s" onChange={onAmtChange} />
<button className="std-button" style={{ display: "inline-block" }} onClick={exportMaterial}>

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

@ -6,14 +6,27 @@ export interface IEngine {
indexedDb: any;
_lastUpdate: number;
hideAllContent: () => void;
loadBladeburnerContent: () => void;
loadFactionContent: () => void;
loadTerminalContent: () => void;
loadScriptEditorContent: () => void;
loadActiveScriptsContent: () => void;
loadCreateProgramContent: () => void;
loadCharacterContent: () => void;
loadFactionsContent: () => void;
loadGangContent: () => void;
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void;
loadAugmentationsContent: () => void;
loadHacknetNodesContent: () => void;
loadSleevesContent: () => 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;
loadResleevingContent: () => void;
loadStockMarketContent: () => void;
loadTerminalContent: () => void;
}

@ -104,18 +104,18 @@ function iTutorialEvaluateStep() {
}
// Disable and clear main menu
const terminalMainMenu = clearEventListeners("terminal-menu-link");
const statsMainMenu = clearEventListeners("stats-menu-link");
const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
const cityMainMenu = clearEventListeners("city-menu-link");
const tutorialMainMenu = clearEventListeners("tutorial-menu-link");
terminalMainMenu.removeAttribute("class");
statsMainMenu.removeAttribute("class");
activeScriptsMainMenu.removeAttribute("class");
hacknetMainMenu.removeAttribute("class");
cityMainMenu.removeAttribute("class");
tutorialMainMenu.removeAttribute("class");
// const terminalMainMenu = clearEventListeners("terminal-menu-link");
// const statsMainMenu = clearEventListeners("stats-menu-link");
// const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
// const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
// const cityMainMenu = clearEventListeners("city-menu-link");
// const tutorialMainMenu = clearEventListeners("tutorial-menu-link");
// terminalMainMenu.removeAttribute("class");
// statsMainMenu.removeAttribute("class");
// activeScriptsMainMenu.removeAttribute("class");
// hacknetMainMenu.removeAttribute("class");
// cityMainMenu.removeAttribute("class");
// tutorialMainMenu.removeAttribute("class");
// Interactive Tutorial Next button
const nextBtn = document.getElementById("interactive-tutorial-next");
@ -138,14 +138,6 @@ function iTutorialEvaluateStep() {
"the main navigation menu (left-hand side of the screen)",
);
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;
case iTutorialSteps.CharacterPage:
Engine.loadCharacterContent();
@ -162,14 +154,6 @@ function iTutorialEvaluateStep() {
"main navigation menu.",
);
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;
case iTutorialSteps.TerminalIntro:
Engine.loadTerminalContent();
@ -345,14 +329,6 @@ function iTutorialEvaluateStep() {
"<code class='interactive-tutorial-tab flashing-button'>Active Scripts</code> link in the main navigation menu.",
);
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;
case iTutorialSteps.ActiveScriptsPage:
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>",
);
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;
case iTutorialSteps.ActiveScriptsToTerminal:
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.",
);
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;
case iTutorialSteps.HacknetNodesIntroduction:
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.",
);
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;
case iTutorialSteps.WorldDescription:
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.",
);
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;
case iTutorialSteps.TutorialPageInfo:
Engine.loadTutorialContent();
@ -481,29 +425,6 @@ function iTutorialEvaluateStep() {
// Go to the next step and evaluate it
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;
if (ITutorial.currStep < iTutorialSteps.End) {
ITutorial.currStep += 1;
@ -527,17 +448,6 @@ function iTutorialEnd() {
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();
ITutorial.currStep = iTutorialSteps.End;

@ -3,7 +3,7 @@
*
* 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 { createPurchaseServerPopup } from "../LocationsHelpers";
@ -27,6 +27,10 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
const btnStyle = { display: "block" };
const purchaseServerButtons: React.ReactNode[] = [];

@ -875,12 +875,14 @@ export class Sleeve extends Person {
if (!factionInfo.offerHackingWork) {
return false;
}
console.log("Hacking");
this.factionWorkType = FactionWorkType.Hacking;
this.gainRatesForTask.hack = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) {
return false;
}
console.log("Field");
this.factionWorkType = FactionWorkType.Field;
this.gainRatesForTask.hack = 0.1 * this.hacking_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) {
return false;
}
console.log("Security");
this.factionWorkType = FactionWorkType.Security;
this.gainRatesForTask.hack = 0.1 * this.hacking_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.currentTask = SleeveTaskType.Faction;
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours;
console.log(this.currentTaskLocation);
return true;
}

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

@ -53,7 +53,10 @@ function possibleJobs(player: IPlayer, sleeve: Sleeve): string[] {
function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
// 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) {
if (sleeve === otherSleeve) {
continue;
@ -180,8 +183,10 @@ const canDo: {
"Work for Company": (player: IPlayer, sleeve: Sleeve) => possibleJobs(player, sleeve).length > 0,
"Work for Faction": (player: IPlayer, sleeve: Sleeve) => possibleFactions(player, sleeve).length > 0,
"Commit Crime": () => true,
"Take University Course": (player: IPlayer, sleeve: Sleeve) => [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),
"Take University Course": (player: IPlayer, sleeve: Sleeve) =>
[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,
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 [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];
@ -234,6 +240,15 @@ export function TaskSelector(props: IProps): React.ReactElement {
const details = detailsF(props.player, props.sleeve);
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 {
const n = event.target.value;
const detailsF = tasks[n];

@ -52,7 +52,7 @@ function prestigeAugmentation() {
Terminal.resetTerminalInput();
Engine.loadTerminalContent();
$("#terminal tr:not(:last)").remove();
postNetburnerText();
postVersion();
// Delete all Worker Scripts objects
prestigeWorkerScripts();
@ -233,7 +233,7 @@ function prestigeSourceFile(flume) {
// Clear terminal
$("#terminal tr:not(:last)").remove();
postNetburnerText();
postVersion();
// Messages
initMessages();

@ -1,36 +0,0 @@
import { Programs } from "./Programs";
import { Player } from "../Player";
import { createElement } from "../../utils/uiHelpers/createElement";
//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;
}
export { getNumAvailableCreateProgram };

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

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Programs } from "../Programs";
import { getAvailableCreatePrograms } from "../ProgramHelpers";
interface IProps {
player: IPlayer;
@ -11,10 +12,9 @@ export function ProgramsRoot(props: IProps): React.ReactElement {
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => {
const id = setInterval(rerender, 20);
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
@ -27,14 +27,13 @@ export function ProgramsRoot(props: IProps): React.ReactElement {
</p>
<ul id="create-program-list">
{Object.keys(Programs).map((programName) => {
const program = Programs[programName];
if (program == null) return <></>;
{getAvailableCreatePrograms(props.player).map((program) => {
const create = program.create;
if (create === null) return <></>;
return (
<a
key={programName}
key={program.name}
className="a-link-button tooltip"
onClick={() => props.player.startCreateProgramWork(program.name, create.time, create.level)}
>

@ -0,0 +1,270 @@
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.loadTerminalContent();
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;
const canCorporation = props.player.corporation != null;
const canGang = props.player.gang != null;
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 onClick={() => props.engine.loadCreateProgramContent()}>
Create Program{programCount > 0 ? ` (${programCount})` : ""}
</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 onClick={() => props.engine.loadFactionsContent()}>
Factions
{props.player.factionInvitations.length > 0 ? ` (${props.player.factionInvitations.length})` : ""}
</button>
<span id="factions-notification" className="notification-off">
{" "}
</span>
</li>
)}
{canOpenAugmentations && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadAugmentationsContent()}>
Augmentations
{props.player.queuedAugmentations.length > 0 ? ` (${props.player.queuedAugmentations.length})` : ""}
</button>
<span id="augmentations-notification" className="notification-off">
{" "}
</span>
</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>
{props.player.companyName !== "" && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadJobContent()}>Job</button>
</li>
)}
{props.player.hasWseAccount && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadStockMarketContent()}>Stock Market</button>
</li>
)}
{props.player.bladeburner && (
<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 && (
<li className="mainmenu-accordion-panel">
<button onClick={() => props.engine.loadDevMenuContent()}>Dev</button>
</li>
)}
</>
)}
</ul>
);
}

@ -27,6 +27,7 @@ import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/Faction
import { FactionList } from "./Faction/ui/FactionList";
import { Root as BladeburnerRoot } from "./Bladeburner/ui/Root";
import { Root as GangRoot } from "./Gang/ui/Root";
import { SidebarRoot } from "./Sidebar/ui/SidebarRoot";
import { CorporationRoot } from "./Corporation/ui/CorporationRoot";
import { ResleeveRoot } from "./PersonObjects/Resleeving/ui/ResleeveRoot";
import { SleeveRoot } from "./PersonObjects/Sleeve/ui/SleeveRoot";
@ -48,7 +49,6 @@ import { workerScripts } from "./Netscript/WorkerScripts";
import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker";
import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige";
import { getNumAvailableCreateProgram } from "./Programs/ProgramHelpers";
import { ProgramsRoot } from "./Programs/ui/ProgramsRoot";
import { redPillFlag } from "./RedPill";
import { saveObject, loadGame } from "./SaveObject";
@ -454,16 +454,6 @@ const Engine = {
Engine.Display.redPillContent.style.display = "none";
Engine.Display.cinematicTextContent.style.display = "none";
Engine.Display.missionContent.style.display = "none";
// Make nav menu tabs inactive
Engine.inactivateMainMenuLinks();
},
// Remove 'active' css class from all main menu links
inactivateMainMenuLinks: function () {
for (const link of Object.keys(MainMenuLinks)) {
MainMenuLinks[link].classList.remove("active");
}
},
displayCharacterOverviewInfo: function () {
@ -655,59 +645,6 @@ const Engine = {
Engine.Counters.updateDisplays = 3;
}
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) {
var adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth);
processPassiveFactionRepGain(adjustedCycles);
@ -782,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)
* @param open {boolean} Whether header is being opened or closed
@ -836,27 +750,6 @@ const Engine = {
},
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
if (loadGame(saveString)) {
initBitNodeMultipliers(Player);
@ -994,66 +887,6 @@ const Engine = {
{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 {
// No save found, start new game
initBitNodeMultipliers(Player);
@ -1068,44 +901,12 @@ const Engine = {
initMessages();
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
iTutorialStart();
removeLoadingScreen();
}
ReactDOM.render(<SidebarRoot engine={this} player={Player} />, document.getElementById("sidebar"));
// Initialize labels on game settings
setSettingsLabels();
Terminal.resetTerminalInput();
@ -1135,16 +936,6 @@ const Engine = {
// Cinematic Text
Engine.Display.cinematicTextContent = document.getElementById("cinematic-text-container");
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
@ -1154,40 +945,6 @@ const Engine = {
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", () => Engine.loadTerminalContent());
MainMenuLinks.ScriptEditor.addEventListener("click", () => Engine.loadScriptEditorContent());
MainMenuLinks.ActiveScripts.addEventListener("click", () => Engine.loadActiveScriptsContent());
MainMenuLinks.CreateProgram.addEventListener("click", () => Engine.loadCreateProgramContent());
MainMenuLinks.Stats.addEventListener("click", () => Engine.loadCharacterContent());
MainMenuLinks.Factions.addEventListener("click", () => Engine.loadFactionsContent());
MainMenuLinks.Augmentations.addEventListener("click", () => Engine.loadAugmentationsContent());
MainMenuLinks.HacknetNodes.addEventListener("click", () => Engine.loadHacknetNodesContent());
MainMenuLinks.Sleeves.addEventListener("click", () => Engine.loadSleevesContent());
MainMenuLinks.City.addEventListener("click", () => Engine.loadLocationContent());
MainMenuLinks.Travel.addEventListener("click", () => Engine.loadTravelContent());
MainMenuLinks.Job.addEventListener("click", () => Engine.loadJobContent());
MainMenuLinks.StockMarket.addEventListener("click", () => Engine.loadStockMarketContent());
MainMenuLinks.Bladeburner.addEventListener("click", () => Engine.loadBladeburnerContent());
MainMenuLinks.Corporation.addEventListener("click", () => Engine.loadCorporationContent());
MainMenuLinks.Gang.addEventListener("click", () => Engine.loadGangContent());
MainMenuLinks.Milestones.addEventListener("click", () => Engine.loadMilestonesContent());
MainMenuLinks.Tutorial.addEventListener("click", () => Engine.loadTutorialContent());
MainMenuLinks.DevMenu.addEventListener("click", function () {
if (process.env.NODE_ENV === "development") {
Engine.loadDevMenuContent();
}
});
// Save, Delete, Import/Export buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function () {
@ -1255,16 +1012,6 @@ const Engine = {
// Character overview screen
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
document.getElementById("copy-save-to-clipboard-link").addEventListener("click", function () {
const saveString = saveObject.getSaveString();

@ -41,91 +41,7 @@
<div id="entire-game-container" style="visibility: hidden">
<div id="mainmenu-container">
<!-- Main menu -->
<ul id="mainmenu" class="mainmenu noscrollbar noselect">
<!-- Hacking dropdown -->
<li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header">Hacking</button>
</li>
<li id="terminal-tab" class="mainmenu-accordion-panel">
<button id="terminal-menu-link">Terminal</button>
</li>
<li id="create-script-tab" class="mainmenu-accordion-panel">
<button id="create-script-menu-link">Create Script</button>
</li>
<li id="active-scripts-tab" class="mainmenu-accordion-panel">
<button id="active-scripts-menu-link">Active Scripts</button>
</li>
<li id="create-program-tab" class="mainmenu-accordion-panel">
<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">Character</button>
</li>
<li id="stats-tab" class="mainmenu-accordion-panel">
<button id="stats-menu-link">Stats</button>
</li>
<li id="factions-tab" class="mainmenu-accordion-panel">
<button id="factions-menu-link">Factions</button>
<span id="factions-notification" class="notification-off"> </span>
</li>
<li id="augmentations-tab" class="mainmenu-accordion-panel">
<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">
<button id="hacknet-nodes-menu-link">Hacknet</button>
</li>
<li id="sleeves-tab" class="mainmenu-accordion-panel">
<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">World</button>
</li>
<li id="city-tab" class="mainmenu-accordion-panel">
<button id="city-menu-link">City</button>
</li>
<li id="travel-tab" class="mainmenu-accordion-panel">
<button id="travel-menu-link">Travel</button>
</li>
<li id="job-tab" class="mainmenu-accordion-panel">
<button id="job-menu-link">Job</button>
</li>
<li id="stock-market-tab" class="mainmenu-accordion-panel">
<button id="stock-market-menu-link">Stock Market</button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel">
<button id="bladeburner-menu-link">Bladeburner</button>
</li>
<li id="corporation-tab" class="mainmenu-accordion-panel">
<button id="corporation-menu-link">Corp</button>
</li>
<li id="gang-tab" class="mainmenu-accordion-panel">
<button id="gang-menu-link">Gang</button>
</li>
<li id="help-menu-header-li">
<button id="help-menu-header" class="mainmenu-accordion-header">Help</button>
</li>
<li id="milestones-tab" class="mainmenu-accordion-panel">
<button id="milestones-menu-link">Milestones</button>
</li>
<li id="tutorial-tab" class="mainmenu-accordion-panel">
<button id="tutorial-menu-link">Tutorial</button>
</li>
<li id="options-tab" class="mainmenu-accordion-panel">
<button id="options-menu-link">Options</button>
</li>
<li id="dev-tab" class="mainmenu-accordion-panel">
<button id="dev-menu-link">Dev</button>
</li>
</ul>
<div id="sidebar"></div>
</div>
<!-- Terminal page -->

@ -56,6 +56,7 @@ export const MainMenuLinks: IMainMenuLinks = {
};
export function initializeMainMenuLinks(): boolean {
return true;
try {
function safeGetLink(id: string): HTMLElement {
const elem: HTMLElement | null = clearEventListeners(id);
@ -84,9 +85,9 @@ export function initializeMainMenuLinks(): boolean {
MainMenuLinks.Gang = safeGetLink("gang-menu-link");
MainMenuLinks.Milestones = safeGetLink("milestones-menu-link");
MainMenuLinks.Tutorial = safeGetLink("tutorial-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"`);
MainMenuLinks.Options = op; // This click listener is already set, so don't clear it
// const op: HTMLElement | null = document.getElementById("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.DevMenu = safeGetLink("dev-menu-link");
return true;

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

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