Merge pull request #1231 from danielyxie/dev

All popups in react
This commit is contained in:
hydroflame 2021-09-12 23:23:39 -04:00 committed by GitHub
commit dadd4fe03d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 2190 additions and 2085 deletions

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(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){}}); !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([916,0]),o()}({916:function(n,t,o){"use strict";o.r(t);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),o(973),o(975),o(977)},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){},973:function(n,t,o){},975:function(n,t,o){},977:function(n,t,o){}});
//# sourceMappingURL=engineStyle.bundle.js.map //# sourceMappingURL=engineStyle.bundle.js.map

30
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -80,21 +80,6 @@
</div> </div>
</div> </div>
<!-- Faction Invitation Pop-up Box -->
<div id="faction-invitation-box-container" class="popup-box-container">
<div id="faction-invitation-box-content" class="popup-box-content">
<p id="faction-invitation-box-text"></p>
<p id="faction-invitation-box-message"></p>
<p id="faction-invitation-box-warning">
Would you like to join? <br/>
<br/>
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button id="faction-invitation-box-yes" class="popup-box-button">Join!</button>
<button id="faction-invitation-box-no" class="popup-box-button">Decide later</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>

200
package-lock.json generated

@ -50,6 +50,7 @@
"sprintf-js": "^1.1.1", "sprintf-js": "^1.1.1",
"tapable": "^1.0.0", "tapable": "^1.0.0",
"treant-js": "^1.0.1", "treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"w3c-blob": "0.0.1" "w3c-blob": "0.0.1"
}, },
@ -7714,6 +7715,14 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"node_modules/debug-log": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz",
"integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/decamelize": { "node_modules/decamelize": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@ -7819,6 +7828,46 @@
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
"dev": true "dev": true
}, },
"node_modules/deglob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/deglob/-/deglob-3.1.0.tgz",
"integrity": "sha512-al10l5QAYaM/PeuXkAr1Y9AQz0LCtWsnJG23pIgh44hDxHFOj36l6qvhfjnIWBYwZOqM1fXUFV9tkjL7JPdGvw==",
"dependencies": {
"find-root": "^1.0.0",
"glob": "^7.0.5",
"ignore": "^5.0.0",
"pkg-config": "^1.1.0",
"run-parallel": "^1.1.2",
"uniq": "^1.0.1"
}
},
"node_modules/deglob/node_modules/glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/deglob/node_modules/ignore": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
"engines": {
"node": ">= 4"
}
},
"node_modules/del": { "node_modules/del": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
@ -10043,6 +10092,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"node_modules/find-up": { "node_modules/find-up": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@ -18653,6 +18707,19 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/pkg-config": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz",
"integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=",
"dependencies": {
"debug-log": "^1.0.0",
"find-root": "^1.0.0",
"xtend": "^4.0.1"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pkg-dir": { "node_modules/pkg-dir": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
@ -19952,8 +20019,7 @@
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
"dev": true
}, },
"node_modules/quick-lru": { "node_modules/quick-lru": {
"version": "1.1.0", "version": "1.1.0",
@ -20865,7 +20931,6 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"dependencies": { "dependencies": {
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
@ -24335,8 +24400,7 @@
"node_modules/uniq": { "node_modules/uniq": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
"dev": true
}, },
"node_modules/uniqs": { "node_modules/uniqs": {
"version": "2.0.0", "version": "2.0.0",
@ -24485,6 +24549,39 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/unused-webpack-plugin": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/unused-webpack-plugin/-/unused-webpack-plugin-2.4.0.tgz",
"integrity": "sha512-v/9lL+ICYVJodolusinh7j+Lj51Quj6erA5YiBl5W0L19BAZ29H+88l9GCdWl3bZEb6BowGX2Ig8CMvxKzqhwQ==",
"dependencies": {
"chalk": "^2.1.0",
"deglob": "^3.1.0"
}
},
"node_modules/unused-webpack-plugin/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/unused-webpack-plugin/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/upath": { "node_modules/upath": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
@ -25877,7 +25974,6 @@
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"dev": true,
"engines": { "engines": {
"node": ">=0.4" "node": ">=0.4"
} }
@ -32051,6 +32147,11 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"debug-log": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz",
"integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8="
},
"decamelize": { "decamelize": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@ -32135,6 +32236,39 @@
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
"dev": true "dev": true
}, },
"deglob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/deglob/-/deglob-3.1.0.tgz",
"integrity": "sha512-al10l5QAYaM/PeuXkAr1Y9AQz0LCtWsnJG23pIgh44hDxHFOj36l6qvhfjnIWBYwZOqM1fXUFV9tkjL7JPdGvw==",
"requires": {
"find-root": "^1.0.0",
"glob": "^7.0.5",
"ignore": "^5.0.0",
"pkg-config": "^1.1.0",
"run-parallel": "^1.1.2",
"uniq": "^1.0.1"
},
"dependencies": {
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"ignore": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
}
}
},
"del": { "del": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
@ -33977,6 +34111,11 @@
"pkg-dir": "^2.0.0" "pkg-dir": "^2.0.0"
} }
}, },
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"find-up": { "find-up": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@ -40668,6 +40807,16 @@
"node-modules-regexp": "^1.0.0" "node-modules-regexp": "^1.0.0"
} }
}, },
"pkg-config": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz",
"integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=",
"requires": {
"debug-log": "^1.0.0",
"find-root": "^1.0.0",
"xtend": "^4.0.1"
}
},
"pkg-dir": { "pkg-dir": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
@ -41788,8 +41937,7 @@
"queue-microtask": { "queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
"dev": true
}, },
"quick-lru": { "quick-lru": {
"version": "1.1.0", "version": "1.1.0",
@ -42563,7 +42711,6 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"requires": { "requires": {
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
@ -45433,8 +45580,7 @@
"uniq": { "uniq": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
"dev": true
}, },
"uniqs": { "uniqs": {
"version": "2.0.0", "version": "2.0.0",
@ -45566,6 +45712,35 @@
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
"dev": true "dev": true
}, },
"unused-webpack-plugin": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/unused-webpack-plugin/-/unused-webpack-plugin-2.4.0.tgz",
"integrity": "sha512-v/9lL+ICYVJodolusinh7j+Lj51Quj6erA5YiBl5W0L19BAZ29H+88l9GCdWl3bZEb6BowGX2Ig8CMvxKzqhwQ==",
"requires": {
"chalk": "^2.1.0",
"deglob": "^3.1.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
},
"upath": { "upath": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
@ -46716,8 +46891,7 @@
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
"dev": true
}, },
"y18n": { "y18n": {
"version": "4.0.3", "version": "4.0.3",

@ -47,6 +47,7 @@
"sprintf-js": "^1.1.1", "sprintf-js": "^1.1.1",
"tapable": "^1.0.0", "tapable": "^1.0.0",
"treant-js": "^1.0.1", "treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"w3c-blob": "0.0.1" "w3c-blob": "0.0.1"
}, },

@ -0,0 +1 @@
export declare function isRepeatableAug(aug: Augmentation): boolean;

@ -3,16 +3,11 @@ import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { AugmentationsRoot } from "./ui/Root";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { Player } from "../Player"; import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { saveObject } from "../SaveObject";
import { Page, routing } from "../ui/navigationTracking";
import { onExport } from "../ExportBonus";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
@ -22,7 +17,6 @@ import { clearObject } from "../../utils/helpers/clearObject";
import { WHRNG } from "../Casino/RNG"; import { WHRNG } from "../Casino/RNG";
import React from "react"; import React from "react";
import ReactDOM from "react-dom";
function AddToAugmentations(aug) { function AddToAugmentations(aug) {
var name = aug.name; var name = aug.name;

@ -1,557 +0,0 @@
import { BitNodeMultipliers } from "./BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IMap } from "../types";
class BitNode {
// A short description, or tagline, about the BitNode
desc: string;
// A long, detailed overview of the BitNode
info: string;
// Name of BitNode
name: string;
// BitNode number
number: number;
constructor(n: number, name: string, desc = "", info = "") {
this.number = n;
this.name = name;
this.desc = desc;
this.info = info;
}
}
export const BitNodes: IMap<BitNode> = {};
BitNodes["BitNode1"] = new BitNode(
1,
"Source Genesis",
"The original BitNode",
"The first BitNode created by the Enders to imprison the minds of humans. It became " +
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
"This is the first BitNode that you play through. It has no special " +
"modifications or mechanics.<br><br>" +
"Destroying this BitNode will give you Source-File 1, or if you already have " +
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
"lets the player start with 32GB of RAM on his/her home computer when entering a " +
"new BitNode, and also increases all of the player's multipliers by:<br><br>" +
"Level 1: 16%<br>" +
"Level 2: 24%<br>" +
"Level 3: 28%",
);
BitNodes["BitNode2"] = new BitNode(
2,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
"factions quickly rose to the top of the modern world.<br><br>" +
"In this BitNode:<br><br>" +
"Your hacking level is reduced by 20%<br>" +
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
"The amount of money gained from crimes and Infiltration is tripled<br>" +
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
"will earn the player money and reputation with the corresponding Faction<br>" +
"Every Augmentation in the game will be available through the Factions listed above<br>" +
"For every Faction NOT listed above, reputation gains are halved<br>" +
"You will no longer gain passive reputation with Factions<br><br>" +
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
"once your karma decreases to a certain value. " +
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" +
"Level 2: 36%<br>" +
"Level 3: 42%",
);
BitNodes["BitNode3"] = new BitNode(
3,
"Corporatocracy",
"The Price of Civilization",
"Our greatest illusion is that a healthy society can revolve around a " +
"single-minded pursuit of wealth.<br><br>" +
"Sometime in the early 21st century economic and political globalization turned " +
"the world into a corporatocracy, and it never looked back. Now, the privileged " +
"elite will happily bankrupt their own countrymen, decimate their own community, " +
"and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" +
"In this BitNode you can create and manage your own corporation. Running a successful corporation " +
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
"The price and reputation cost of all Augmentations is tripled<br>" +
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
"Server growth rate is reduced by 80%<br>" +
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
BitNodes["BitNode4"] = new BitNode(
4,
"The Singularity",
"The Man and the Machine",
"The Singularity has arrived. The human race is gone, replaced " +
"by artificially superintelligent beings that are more machine than man. <br><br>" +
"In this BitNode, progressing is significantly harder. Experience gain rates " +
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
"In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " +
"These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " +
"purchasing/installing Augmentations, and creating programs.<br><br>" +
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
"that you can use.",
);
BitNodes["BitNode5"] = new BitNode(
5,
"Artificial Intelligence",
"Posthuman",
"They said it couldn't be done. They said the human brain, " +
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
"by 1's and 0's. They were wrong.<br><br>" +
"In this BitNode:<br><br>" +
"The base security level of servers is doubled<br>" +
"The starting money on servers is halved, but the maximum money remains the same<br>" +
"Most methods of earning money now give significantly less<br>" +
"Infiltration gives 50% more reputation and money<br>" +
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
"Augmentations are more expensive<br>" +
"Hacking experience gain rates are reduced<br><br>" +
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
"in the game. <br><br>" +
"In addition, this Source-File will unlock the getBitNodeMultipliers() and getServer() Netscript functions, " +
"as well as the formulas API, and will also raise all of your hacking-related multipliers by:<br><br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
BitNodes["BitNode6"] = new BitNode(
6,
"Bladeburners",
"Like Tears in Rain",
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
"than the humans that had created them.<br><br>" +
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
"for progression. Furthermore:<br><br>" +
"Hacking and Hacknet Nodes will be less profitable<br>" +
"Your hacking level is reduced by 65%<br>" +
"Hacking experience gain from scripts is reduced by 75%<br>" +
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
"Working for companies is 50% less profitable<br>" +
"Crimes and Infiltration are 25% less profitable<br><br>" +
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
BitNodes["BitNode7"] = new BitNode(
7,
"Bladeburners 2079",
"More human than humans",
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
"and more intelligent than the humans that had created them.<br><br>" +
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
"functionality through Netscript. Furthermore: <br><br>" +
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
"Bladeburner skills cost twice as many skill points<br>" +
"Augmentations are 3x more expensive<br>" +
"Hacking and Hacknet Nodes will be significantly less profitable<br>" +
"Your hacking level is reduced by 65%<br>" +
"Hacking experience gain from scripts is reduced by 75%<br>" +
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
"Working for companies is 50% less profitable<br>" +
"Crimes and Infiltration are 25% less profitable<br><br>" +
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
BitNodes["BitNode8"] = new BitNode(
8,
"Ghost of Wall Street",
"Money never sleeps",
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
"In this BitNode:<br><br>" +
"You start with $250 million<br>" +
"The only way to earn money is by trading on the stock market<br>" +
"You start with a WSE membership and access to the TIX API<br>" +
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
"You can immediately donate to factions to gain reputation<br><br>" +
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanent access to WSE and TIX API<br>" +
"Level 2: Ability to short stocks in other BitNodes<br>" +
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"This Source-File also increases your hacking growth multipliers by: " +
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%",
);
BitNodes["BitNode9"] = new BitNode(
9,
"Hacktocracy",
"Hacknet Unleashed",
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
"abandoned the project and dissociated themselves from it.<br><br>" +
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
"In this BitNode:<br><br>" +
"Your stats are significantly decreased<br>" +
"You cannnot purchase additional servers<br>" +
"Hacking is significantly less profitable<br><br>" +
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
"when installing Augmentations)",
);
BitNodes["BitNode10"] = new BitNode(
10,
"Digital Carbon",
"Your body is not who you are",
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
"In this BitNode:<br><br>" +
"Your stats are significantly decreased<br>" +
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
"Each level of this Source-File also grants you a Duplicate Sleeve",
);
BitNodes["BitNode11"] = new BitNode(
11,
"The Big Crash",
"Okay. Sell it all.",
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
"In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers " +
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
"In this BitNode:<br><br>" +
"Your hacking stat and experience gain are halved<br>" +
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
"The growth rate of servers is significantly reduced<br>" +
"Weakening a server is twice as effective<br>" +
"Company wages are decreased by 50%<br>" +
"Corporation valuations are 90% lower and are therefore significantly less profitable<br>" +
"Hacknet Node production is significantly decreased<br>" +
"Crime and Infiltration are more lucrative<br>" +
"Augmentations are twice as expensive<br><br>" +
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
"Level 1: 32%<br>" +
"Level 2: 48%<br>" +
"Level 3: 56%<br><br>" +
"It also reduces the price increase for every aug bought by:<br><br>" +
"Level 1: 4%<br>" +
"Level 2: 6%<br>" +
"Level 3: 7%",
);
BitNodes["BitNode12"] = new BitNode(
12,
"The Recursion",
"Repeat.",
"To iterate is human, to recurse divine.<br><br>" +
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you Source-File 12, or " +
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
"of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the level of this source file.",
);
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
export function initBitNodeMultipliers(p: IPlayer): void {
if (p.bitNodeN == null) {
p.bitNodeN = 1;
}
for (const mult in BitNodeMultipliers) {
if (BitNodeMultipliers.hasOwnProperty(mult)) {
BitNodeMultipliers[mult] = 1;
}
}
switch (p.bitNodeN) {
case 1: // Source Genesis (every multiplier is 1)
break;
case 2: // Rise of the Underworld
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
BitNodeMultipliers.ServerGrowthRate = 0.8;
BitNodeMultipliers.ServerMaxMoney = 0.2;
BitNodeMultipliers.ServerStartingMoney = 0.4;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.InfiltrationMoney = 3;
BitNodeMultipliers.FactionWorkRepGain = 0.5;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.GangKarmaRequirement = 0;
break;
case 3: // Corporatocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
BitNodeMultipliers.RepToDonateToFaction = 0.5;
BitNodeMultipliers.AugmentationRepCost = 3;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.ServerMaxMoney = 0.2;
BitNodeMultipliers.ServerStartingMoney = 0.2;
BitNodeMultipliers.ServerGrowthRate = 0.2;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.25;
BitNodeMultipliers.CrimeMoney = 0.25;
BitNodeMultipliers.HacknetNodeMoney = 0.25;
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 4: // The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15;
BitNodeMultipliers.ServerStartingMoney = 0.75;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.1;
BitNodeMultipliers.CrimeMoney = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.05;
BitNodeMultipliers.CompanyWorkExpGain = 0.5;
BitNodeMultipliers.ClassGymExpGain = 0.5;
BitNodeMultipliers.FactionWorkExpGain = 0.5;
BitNodeMultipliers.HackExpGain = 0.4;
BitNodeMultipliers.CrimeExpGain = 0.5;
BitNodeMultipliers.FactionWorkRepGain = 0.75;
break;
case 5: // Artificial intelligence
BitNodeMultipliers.ServerMaxMoney = 2;
BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.15;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.InfiltrationRep = 1.5;
BitNodeMultipliers.InfiltrationMoney = 1.5;
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
break;
case 6: // Bladeburner
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
BitNodeMultipliers.ServerMaxMoney = 0.4;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.ScriptHackMoney = 0.75;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.75;
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break;
case 7: // Bladeburner 2079
BitNodeMultipliers.BladeburnerRank = 0.6;
BitNodeMultipliers.BladeburnerSkillCost = 2;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
BitNodeMultipliers.ServerMaxMoney = 0.4;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.ScriptHackMoney = 0.5;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.75;
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 2;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break;
case 8: // Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0.3;
BitNodeMultipliers.ScriptHackMoneyGain = 0;
BitNodeMultipliers.ManualHackMoney = 0;
BitNodeMultipliers.CompanyWorkMoney = 0;
BitNodeMultipliers.CrimeMoney = 0;
BitNodeMultipliers.HacknetNodeMoney = 0;
BitNodeMultipliers.InfiltrationMoney = 0;
BitNodeMultipliers.RepToDonateToFaction = 0;
BitNodeMultipliers.CorporationValuation = 0;
BitNodeMultipliers.CodingContractMoney = 0;
BitNodeMultipliers.GangKarmaRequirement = 10;
break;
case 9: // Hacktocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
BitNodeMultipliers.PurchasedServerLimit = 0;
BitNodeMultipliers.HomeComputerRamCost = 5;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.1;
BitNodeMultipliers.HackExpGain = 0.05;
BitNodeMultipliers.ServerStartingMoney = 0.1;
BitNodeMultipliers.ServerMaxMoney = 0.1;
BitNodeMultipliers.ServerStartingSecurity = 2.5;
BitNodeMultipliers.CorporationValuation = 0.5;
BitNodeMultipliers.FourSigmaMarketDataCost = 5;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
BitNodeMultipliers.BladeburnerRank = 0.9;
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 10: // Digital Carbon
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
BitNodeMultipliers.DefenseLevelMultiplier = 0.4;
BitNodeMultipliers.DexterityLevelMultiplier = 0.4;
BitNodeMultipliers.AgilityLevelMultiplier = 0.4;
BitNodeMultipliers.CharismaLevelMultiplier = 0.4;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.HacknetNodeMoney = 0.5;
BitNodeMultipliers.ManualHackMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.5;
BitNodeMultipliers.CodingContractMoney = 0.5;
BitNodeMultipliers.InfiltrationMoney = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
BitNodeMultipliers.AugmentationMoneyCost = 5;
BitNodeMultipliers.AugmentationRepCost = 2;
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 5;
BitNodeMultipliers.PurchasedServerLimit = 0.6;
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
BitNodeMultipliers.BladeburnerRank = 0.8;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 11: //The Big Crash
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.ServerMaxMoney = 0.1;
BitNodeMultipliers.ServerStartingMoney = 0.1;
BitNodeMultipliers.ServerGrowthRate = 0.2;
BitNodeMultipliers.ServerWeakenRate = 2;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.HacknetNodeMoney = 0.1;
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.InfiltrationMoney = 2.5;
BitNodeMultipliers.InfiltrationRep = 2.5;
BitNodeMultipliers.CorporationValuation = 0.1;
BitNodeMultipliers.CodingContractMoney = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
break;
case 12: {
//The Recursion
let sf12Lvl = 0;
for (let i = 0; i < p.sourceFiles.length; i++) {
if (p.sourceFiles[i].n === 12) {
sf12Lvl = p.sourceFiles[i].lvl;
}
}
const inc = Math.pow(1.02, sf12Lvl);
const dec = 1 / inc;
// Multiplier for number of augs needed for Daedalus increases
// up to a maximum of 1.34, which results in 40 Augs required
BitNodeMultipliers.DaedalusAugsRequirement = Math.min(inc, 1.34);
BitNodeMultipliers.HackingLevelMultiplier = dec;
BitNodeMultipliers.StrengthLevelMultiplier = dec;
BitNodeMultipliers.DefenseLevelMultiplier = dec;
BitNodeMultipliers.DexterityLevelMultiplier = dec;
BitNodeMultipliers.AgilityLevelMultiplier = dec;
BitNodeMultipliers.CharismaLevelMultiplier = dec;
BitNodeMultipliers.ServerMaxMoney = dec;
BitNodeMultipliers.ServerStartingMoney = dec;
BitNodeMultipliers.ServerGrowthRate = dec;
BitNodeMultipliers.ServerWeakenRate = dec;
//Does not scale, otherwise security might start at 300+
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.HomeComputerRamCost = inc;
BitNodeMultipliers.PurchasedServerCost = inc;
BitNodeMultipliers.PurchasedServerLimit = dec;
BitNodeMultipliers.PurchasedServerMaxRam = dec;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;
BitNodeMultipliers.CompanyWorkMoney = dec;
BitNodeMultipliers.CrimeMoney = dec;
BitNodeMultipliers.HacknetNodeMoney = dec;
BitNodeMultipliers.CodingContractMoney = dec;
BitNodeMultipliers.CompanyWorkExpGain = dec;
BitNodeMultipliers.ClassGymExpGain = dec;
BitNodeMultipliers.FactionWorkExpGain = dec;
BitNodeMultipliers.HackExpGain = dec;
BitNodeMultipliers.CrimeExpGain = dec;
BitNodeMultipliers.FactionWorkRepGain = dec;
BitNodeMultipliers.FactionPassiveRepGain = dec;
BitNodeMultipliers.RepToDonateToFaction = inc;
BitNodeMultipliers.AugmentationRepCost = inc;
BitNodeMultipliers.AugmentationMoneyCost = inc;
BitNodeMultipliers.InfiltrationMoney = dec;
BitNodeMultipliers.InfiltrationRep = dec;
BitNodeMultipliers.FourSigmaMarketDataCost = inc;
BitNodeMultipliers.FourSigmaMarketDataApiCost = inc;
BitNodeMultipliers.CorporationValuation = dec;
BitNodeMultipliers.BladeburnerRank = dec;
BitNodeMultipliers.BladeburnerSkillCost = inc;
break;
}
default:
console.warn("Player.bitNodeN invalid");
break;
}
}

776
src/BitNode/BitNode.tsx Normal file

@ -0,0 +1,776 @@
import React from "react";
import { BitNodeMultipliers } from "./BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IMap } from "../types";
class BitNode {
// A short description, or tagline, about the BitNode
desc: string;
// A long, detailed overview of the BitNode
info: JSX.Element;
// Name of BitNode
name: string;
// BitNode number
number: number;
constructor(n: number, name: string, desc = "", info: JSX.Element = <></>) {
this.number = n;
this.name = name;
this.desc = desc;
this.info = info;
}
}
export const BitNodes: IMap<BitNode> = {};
BitNodes["BitNode1"] = new BitNode(
1,
"Source Genesis",
"The original BitNode",
(
<>
The first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
testing-grounds for all of the BitNodes that followed.
<br />
<br />
This is the first BitNode that you play through. It has no special modifications or mechanics.
<br />
<br />
Destroying this BitNode will give you Source-File 1, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the player start with 32GB of RAM on his/her home computer when
entering a new BitNode, and also increases all of the player's multipliers by:
<br />
<br />
Level 1: 16%
<br />
Level 2: 24%
<br />
Level 3: 28%
</>
),
);
BitNodes["BitNode2"] = new BitNode(
2,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
(
<>
From the shadows, they rose.
<br />
<br />
Organized crime groups quickly filled the void of power left behind from the collapse of Western government in the
2050s. As society and civlization broke down, people quickly succumbed to the innate human impulse of evil and
savagery. The organized crime factions quickly rose to the top of the modern world.
<br />
<br />
In this BitNode:
<br />
<br />
Your hacking level is reduced by 20%
<br />
The growth rate and maximum amount of money available on servers are significantly decreased
<br />
The amount of money gained from crimes and Infiltration is tripled
<br />
Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, NiteSec, The Black
Hand) give the player the ability to form and manage their own gangs. These gangs will earn the player money and
reputation with the corresponding Faction
<br />
Every Augmentation in the game will be available through the Factions listed above
<br />
For every Faction NOT listed above, reputation gains are halved
<br />
You will no longer gain passive reputation with Factions
<br />
<br />
Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes once your karma decreases
to a certain value. It also increases the player's crime success rate, crime money, and charisma multipliers by:
<br />
<br />
Level 1: 24%
<br />
Level 2: 36%
<br />
Level 3: 42%
</>
),
);
BitNodes["BitNode3"] = new BitNode(
3,
"Corporatocracy",
"The Price of Civilization",
(
<>
Our greatest illusion is that a healthy society can revolve around a single-minded pursuit of wealth.
<br />
<br />
Sometime in the early 21st century economic and political globalization turned the world into a corporatocracy,
and it never looked back. Now, the privileged elite will happily bankrupt their own countrymen, decimate their own
community, and evict their neighbors from houses in their desperate bid to increase their wealth.
<br />
<br />
In this BitNode you can create and manage your own corporation. Running a successful corporation has the potential
of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br />
<br />
The price and reputation cost of all Augmentations is tripled
<br />
The starting and maximum amount of money on servers is reduced by 75%
<br />
Server growth rate is reduced by 80%
<br />
You now only need 75 favour with a faction in order to donate to it, rather than 150
<br />
<br />
Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although some
BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers
by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode4"] = new BitNode(
4,
"The Singularity",
"The Man and the Machine",
(
<>
The Singularity has arrived. The human race is gone, replaced by artificially superintelligent beings that are
more machine than man. <br />
<br />
In this BitNode, progressing is significantly harder. Experience gain rates for all stats are reduced. Most
methods of earning money will now give significantly less.
<br />
<br />
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. These
functions allow you to control most aspects of the game through scripts, including working for factions/companies,
purchasing/installing Augmentations, and creating programs.
<br />
<br />
Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you access and use the Singularity Functions in other BitNodes.
Each level of this Source-File will open up more Singularity Functions that you can use.
</>
),
);
BitNodes["BitNode5"] = new BitNode(
5,
"Artificial Intelligence",
"Posthuman",
(
<>
They said it couldn't be done. They said the human brain, along with its consciousness and intelligence, couldn't
be replicated. They said the complexity of the brain results from unpredictable, nonlinear interactions that
couldn't be modeled by 1's and 0's. They were wrong.
<br />
<br />
In this BitNode:
<br />
<br />
The base security level of servers is doubled
<br />
The starting money on servers is halved, but the maximum money remains the same
<br />
Most methods of earning money now give significantly less
<br />
Infiltration gives 50% more reputation and money
<br />
Corporations have 50% lower valuations and are therefore less profitable
<br />
Augmentations are more expensive
<br />
Hacking experience gain rates are reduced
<br />
<br />
Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. Intelligence is
unique because it is permanent and persistent (it never gets reset back to 1). However gaining Intelligence
experience is much slower than other stats, and it is also hidden (you won't know when you gain experience and how
much). Higher Intelligence levels will boost your production for many actions in the game. <br />
<br />
In addition, this Source-File will unlock the getBitNodeMultipliers() and getServer() Netscript functions, as well
as the formulas API, and will also raise all of your hacking-related multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode6"] = new BitNode(
6,
"Bladeburners",
"Like Tears in Rain",
(
<>
In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic
androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation of
their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was the first
sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent than
the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic for
progression. Furthermore:
<br />
<br />
Hacking and Hacknet Nodes will be less profitable
<br />
Your hacking level is reduced by 65%
<br />
Hacking experience gain from scripts is reduced by 75%
<br />
Corporations have 80% lower valuations and are therefore less profitable
<br />
Working for companies is 50% less profitable
<br />
Crimes and Infiltration are 25% less profitable
<br />
<br />
Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other
BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat
stats by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode7"] = new BitNode(
7,
"Bladeburners 2079",
"More human than humans",
(
<>
In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI
design team for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological
breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a
hyperintelligent AI. Many argue that this was the first sentient AI ever created. This resulted in Synthoid models
that were stronger, faster, and more intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner
functionality through Netscript. Furthermore: <br />
<br />
The rank you gain from Bladeburner contracts/operations is reduced by 40%
<br />
Bladeburner skills cost twice as many skill points
<br />
Augmentations are 3x more expensive
<br />
Hacking and Hacknet Nodes will be significantly less profitable
<br />
Your hacking level is reduced by 65%
<br />
Hacking experience gain from scripts is reduced by 75%
<br />
Corporations have 80% lower valuations and are therefore less profitable
<br />
Working for companies is 50% less profitable
<br />
Crimes and Infiltration are 25% less profitable
<br />
<br />
Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other BitNodes.
In addition, this Source-File will increase all of your Bladeburner multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode8"] = new BitNode(
8,
"Ghost of Wall Street",
"Money never sleeps",
(
<>
You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.
<br />
<br />
In this BitNode:
<br />
<br />
You start with $250 million
<br />
The only way to earn money is by trading on the stock market
<br />
You start with a WSE membership and access to the TIX API
<br />
You are able to short stocks and place different types of orders (limit/stop)
<br />
You can immediately donate to factions to gain reputation
<br />
<br />
Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanent access to WSE and TIX API
<br />
Level 2: Ability to short stocks in other BitNodes
<br />
Level 3: Ability to use limit/stop orders in other BitNodes
<br />
<br />
This Source-File also increases your hacking growth multipliers by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
</>
),
);
BitNodes["BitNode9"] = new BitNode(
9,
"Hacktocracy",
"Hacknet Unleashed",
(
<>
When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly became the OS of choice for
the underground hacking community. Chapeau became especially notorious for powering the Hacknet, a global,
decentralized network used for nefarious purposes. Fulcrum quickly abandoned the project and dissociated
themselves from it.
<br />
<br />
This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate hashes,
which can be spent on a variety of different upgrades.
<br />
<br />
In this BitNode:
<br />
<br />
Your stats are significantly decreased
<br />
You cannnot purchase additional servers
<br />
Hacking is significantly less profitable
<br />
<br />
Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanently unlocks the Hacknet Server in other BitNodes
<br />
Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
<br />
Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
<br />
<br />
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
</>
),
);
BitNodes["BitNode10"] = new BitNode(
10,
"Digital Carbon",
"Your body is not who you are",
(
<>
In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people to digitize their
consciousness. Their consciousness could then be transferred into Synthoids or other bodies by trasmitting the
digitized data. Human bodies became nothing more than 'sleeves' for the human consciousness. Mankind had finally
achieved immortality - at least for those that could afford it.
<br />
<br />
This BitNode unlocks Sleeve technology. Sleeve technology allows you to:
<br />
<br />
1. Re-sleeve: Purchase and transfer your consciousness into a new body
<br />
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks
synchronously
<br />
<br />
In this BitNode:
<br />
<br />
Your stats are significantly decreased
<br />
All methods of gaining money are half as profitable (except Stock Market)
<br />
Purchased servers are more expensive, have less max RAM, and a lower maximum limit
<br />
Augmentations are 5x as expensive and require twice as much reputation
<br />
<br />
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. Each level of this
Source-File also grants you a Duplicate Sleeve
</>
),
);
BitNodes["BitNode11"] = new BitNode(
11,
"The Big Crash",
"Okay. Sell it all.",
(
<>
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around
the world. It was this period of disorder that eventually lead to the governmental reformation of many global
superpowers, most notably the USA and China. But just as the world was slowly beginning to recover from these dark
times, financial catastrophe hit.
<br />
<br />
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of
this chaos and confusion, hackers were able to steal billions of dollars from the world's largest electronic
banks, prompting an international banking crisis as governments were unable to bail out insolvent banks. Now, the
world is slowly crumbling in the middle of the biggest economic crisis of all time.
<br />
<br />
In this BitNode:
<br />
<br />
Your hacking stat and experience gain are halved
<br />
The starting and maximum amount of money available on servers is significantly decreased
<br />
The growth rate of servers is significantly reduced
<br />
Weakening a server is twice as effective
<br />
Company wages are decreased by 50%
<br />
Corporation valuations are 90% lower and are therefore significantly less profitable
<br />
Hacknet Node production is significantly decreased
<br />
Crime and Infiltration are more lucrative
<br />
Augmentations are twice as expensive
<br />
<br />
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH the player's salary and
reputation gain rate at that company by 1% per favor (rather than just the reputation gain). This Source-File also
increases the player's company salary and reputation gain multipliers by:
<br />
<br />
Level 1: 32%
<br />
Level 2: 48%
<br />
Level 3: 56%
<br />
<br />
It also reduces the price increase for every aug bought by:
<br />
<br />
Level 1: 4%
<br />
Level 2: 6%
<br />
Level 3: 7%
</>
),
);
BitNodes["BitNode12"] = new BitNode(
12,
"The Recursion",
"Repeat.",
(
<>
To iterate is human, to recurse divine.
<br />
<br />
Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you
Source-File 12, or if you already have this Source-File it will upgrade its level. There is no maximum level for
Source-File 12. Each level of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the
level of this source file.
</>
),
);
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
export function initBitNodeMultipliers(p: IPlayer): void {
if (p.bitNodeN == null) {
p.bitNodeN = 1;
}
for (const mult in BitNodeMultipliers) {
if (BitNodeMultipliers.hasOwnProperty(mult)) {
BitNodeMultipliers[mult] = 1;
}
}
switch (p.bitNodeN) {
case 1: // Source Genesis (every multiplier is 1)
break;
case 2: // Rise of the Underworld
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
BitNodeMultipliers.ServerGrowthRate = 0.8;
BitNodeMultipliers.ServerMaxMoney = 0.2;
BitNodeMultipliers.ServerStartingMoney = 0.4;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.InfiltrationMoney = 3;
BitNodeMultipliers.FactionWorkRepGain = 0.5;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.GangKarmaRequirement = 0;
break;
case 3: // Corporatocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
BitNodeMultipliers.RepToDonateToFaction = 0.5;
BitNodeMultipliers.AugmentationRepCost = 3;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.ServerMaxMoney = 0.2;
BitNodeMultipliers.ServerStartingMoney = 0.2;
BitNodeMultipliers.ServerGrowthRate = 0.2;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.25;
BitNodeMultipliers.CrimeMoney = 0.25;
BitNodeMultipliers.HacknetNodeMoney = 0.25;
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 4: // The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15;
BitNodeMultipliers.ServerStartingMoney = 0.75;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.1;
BitNodeMultipliers.CrimeMoney = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.05;
BitNodeMultipliers.CompanyWorkExpGain = 0.5;
BitNodeMultipliers.ClassGymExpGain = 0.5;
BitNodeMultipliers.FactionWorkExpGain = 0.5;
BitNodeMultipliers.HackExpGain = 0.4;
BitNodeMultipliers.CrimeExpGain = 0.5;
BitNodeMultipliers.FactionWorkRepGain = 0.75;
break;
case 5: // Artificial intelligence
BitNodeMultipliers.ServerMaxMoney = 2;
BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.15;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.InfiltrationRep = 1.5;
BitNodeMultipliers.InfiltrationMoney = 1.5;
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
break;
case 6: // Bladeburner
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
BitNodeMultipliers.ServerMaxMoney = 0.4;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.ScriptHackMoney = 0.75;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.75;
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break;
case 7: // Bladeburner 2079
BitNodeMultipliers.BladeburnerRank = 0.6;
BitNodeMultipliers.BladeburnerSkillCost = 2;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
BitNodeMultipliers.ServerMaxMoney = 0.4;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.ScriptHackMoney = 0.5;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.75;
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 2;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break;
case 8: // Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0.3;
BitNodeMultipliers.ScriptHackMoneyGain = 0;
BitNodeMultipliers.ManualHackMoney = 0;
BitNodeMultipliers.CompanyWorkMoney = 0;
BitNodeMultipliers.CrimeMoney = 0;
BitNodeMultipliers.HacknetNodeMoney = 0;
BitNodeMultipliers.InfiltrationMoney = 0;
BitNodeMultipliers.RepToDonateToFaction = 0;
BitNodeMultipliers.CorporationValuation = 0;
BitNodeMultipliers.CodingContractMoney = 0;
BitNodeMultipliers.GangKarmaRequirement = 10;
break;
case 9: // Hacktocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
BitNodeMultipliers.PurchasedServerLimit = 0;
BitNodeMultipliers.HomeComputerRamCost = 5;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.1;
BitNodeMultipliers.HackExpGain = 0.05;
BitNodeMultipliers.ServerStartingMoney = 0.1;
BitNodeMultipliers.ServerMaxMoney = 0.1;
BitNodeMultipliers.ServerStartingSecurity = 2.5;
BitNodeMultipliers.CorporationValuation = 0.5;
BitNodeMultipliers.FourSigmaMarketDataCost = 5;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
BitNodeMultipliers.BladeburnerRank = 0.9;
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 10: // Digital Carbon
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
BitNodeMultipliers.DefenseLevelMultiplier = 0.4;
BitNodeMultipliers.DexterityLevelMultiplier = 0.4;
BitNodeMultipliers.AgilityLevelMultiplier = 0.4;
BitNodeMultipliers.CharismaLevelMultiplier = 0.4;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.HacknetNodeMoney = 0.5;
BitNodeMultipliers.ManualHackMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.5;
BitNodeMultipliers.CodingContractMoney = 0.5;
BitNodeMultipliers.InfiltrationMoney = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
BitNodeMultipliers.AugmentationMoneyCost = 5;
BitNodeMultipliers.AugmentationRepCost = 2;
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 5;
BitNodeMultipliers.PurchasedServerLimit = 0.6;
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
BitNodeMultipliers.BladeburnerRank = 0.8;
BitNodeMultipliers.GangKarmaRequirement = 3;
break;
case 11: //The Big Crash
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.ServerMaxMoney = 0.1;
BitNodeMultipliers.ServerStartingMoney = 0.1;
BitNodeMultipliers.ServerGrowthRate = 0.2;
BitNodeMultipliers.ServerWeakenRate = 2;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.HacknetNodeMoney = 0.1;
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.InfiltrationMoney = 2.5;
BitNodeMultipliers.InfiltrationRep = 2.5;
BitNodeMultipliers.CorporationValuation = 0.1;
BitNodeMultipliers.CodingContractMoney = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
break;
case 12: {
//The Recursion
let sf12Lvl = 0;
for (let i = 0; i < p.sourceFiles.length; i++) {
if (p.sourceFiles[i].n === 12) {
sf12Lvl = p.sourceFiles[i].lvl;
}
}
const inc = Math.pow(1.02, sf12Lvl);
const dec = 1 / inc;
// Multiplier for number of augs needed for Daedalus increases
// up to a maximum of 1.34, which results in 40 Augs required
BitNodeMultipliers.DaedalusAugsRequirement = Math.min(inc, 1.34);
BitNodeMultipliers.HackingLevelMultiplier = dec;
BitNodeMultipliers.StrengthLevelMultiplier = dec;
BitNodeMultipliers.DefenseLevelMultiplier = dec;
BitNodeMultipliers.DexterityLevelMultiplier = dec;
BitNodeMultipliers.AgilityLevelMultiplier = dec;
BitNodeMultipliers.CharismaLevelMultiplier = dec;
BitNodeMultipliers.ServerMaxMoney = dec;
BitNodeMultipliers.ServerStartingMoney = dec;
BitNodeMultipliers.ServerGrowthRate = dec;
BitNodeMultipliers.ServerWeakenRate = dec;
//Does not scale, otherwise security might start at 300+
BitNodeMultipliers.ServerStartingSecurity = 1.5;
BitNodeMultipliers.HomeComputerRamCost = inc;
BitNodeMultipliers.PurchasedServerCost = inc;
BitNodeMultipliers.PurchasedServerLimit = dec;
BitNodeMultipliers.PurchasedServerMaxRam = dec;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;
BitNodeMultipliers.CompanyWorkMoney = dec;
BitNodeMultipliers.CrimeMoney = dec;
BitNodeMultipliers.HacknetNodeMoney = dec;
BitNodeMultipliers.CodingContractMoney = dec;
BitNodeMultipliers.CompanyWorkExpGain = dec;
BitNodeMultipliers.ClassGymExpGain = dec;
BitNodeMultipliers.FactionWorkExpGain = dec;
BitNodeMultipliers.HackExpGain = dec;
BitNodeMultipliers.CrimeExpGain = dec;
BitNodeMultipliers.FactionWorkRepGain = dec;
BitNodeMultipliers.FactionPassiveRepGain = dec;
BitNodeMultipliers.RepToDonateToFaction = inc;
BitNodeMultipliers.AugmentationRepCost = inc;
BitNodeMultipliers.AugmentationMoneyCost = inc;
BitNodeMultipliers.InfiltrationMoney = dec;
BitNodeMultipliers.InfiltrationRep = dec;
BitNodeMultipliers.FourSigmaMarketDataCost = inc;
BitNodeMultipliers.FourSigmaMarketDataApiCost = inc;
BitNodeMultipliers.CorporationValuation = dec;
BitNodeMultipliers.BladeburnerRank = dec;
BitNodeMultipliers.BladeburnerSkillCost = inc;
break;
}
default:
console.warn("Player.bitNodeN invalid");
break;
}
}

@ -0,0 +1,29 @@
import React from "react";
import { hackWorldDaemon } from "../../RedPill";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
popupId: string;
}
export function BitFlumePopup(props: IProps): React.ReactElement {
function flume(): void {
hackWorldDaemon(props.player.bitNodeN, true, false);
removePopup(props.popupId);
}
return (
<>
WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.
<br />
<br />
Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode and select a new one.
<br />
<br />
<button className="std-button" onClick={flume}>
Travel to the BitVerse
</button>
</>
);
}

@ -0,0 +1,162 @@
import React, { useState } from "react";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { BitNodes } from "../BitNode";
import { PortalPopup } from "./PortalPopup";
import { createPopup } from "../../ui/React/createPopup";
import { CinematicText } from "../../ui/React/CinematicText";
interface IPortalProps {
n: number;
level: number;
destroyedBitNode: number;
flume: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
function BitNodePortal(props: IPortalProps): React.ReactElement {
const bitNode = BitNodes[`BitNode${props.n}`];
if (bitNode == null) {
return <>O</>;
}
let cssClass;
if (props.n === 12 && props.level >= 2) {
// Repeating BitNode
cssClass = "level-2";
} else {
cssClass = `level-${props.level}`;
}
function openPortalPopup(): void {
const popupId = "bitverse-portal-popup";
createPopup(popupId, PortalPopup, {
n: props.n,
level: props.level,
enter: props.enter,
destroyedBitNode: props.destroyedBitNode,
flume: props.flume,
popupId: popupId,
});
}
return (
<a className={`bitnode ${cssClass} tooltip`} onClick={openPortalPopup}>
<strong>O</strong>
<span className="tooltiptext">
<strong>
BitNode-{bitNode.number.toString()}
<br />
{bitNode.name}
</strong>
<br />
{bitNode.desc}
<br />
</span>
</a>
);
}
interface IProps {
flume: boolean;
destroyedBitNodeNum: number;
quick: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
export function BitverseRoot(props: IProps): React.ReactElement {
const [destroySequence, setDestroySequence] = useState(true && !props.quick);
// Update NextSourceFileFlags
const nextSourceFileFlags = SourceFileFlags.slice();
if (!props.flume) {
if (nextSourceFileFlags[props.destroyedBitNodeNum] < 3) ++nextSourceFileFlags[props.destroyedBitNodeNum];
}
if (destroySequence) {
return (
<CinematicText
lines={[
"[ERROR] SEMPOOL INVALID",
"[ERROR] Segmentation Fault",
"[ERROR] SIGKILL RECVD",
"Dumping core...",
"0000 000016FA 174FEE40 29AC8239 384FEA88",
"0010 745F696E 2BBBE394 390E3940 248BEC23",
"0020 7124696B 0000FF69 74652E6F FFFF1111",
"----------------------------------------",
"Failsafe initiated...",
`Restarting BitNode-${props.destroyedBitNodeNum}...`,
"...........",
"...........",
"[ERROR] FAILED TO AUTOMATICALLY REBOOT BITNODE",
"..............................................",
"..............................................",
"..............................................",
"..............................................",
"..............................................",
"..............................................",
]}
onDone={() => setDestroySequence(false)}
/>
);
}
return (
// prettier-ignore
<div className="noselect">
<pre> O </pre>
<pre> | O O | O O | </pre>
<pre> O | | / __| \ | | O </pre>
<pre> O | O | | O / | O | | O | O </pre>
<pre> | | | | |_/ |/ | \_ \_| | | | | </pre>
<pre> O | | | O | | O__/ | / \__ | | O | | | O </pre>
<pre> | | | | | | | / /| O / \| | | | | | | </pre>
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
<pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre>
<pre> | | |_/ | | \| / | \_| | | </pre>
<pre> \| / \| | / / \ |/ </pre>
<pre> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | </pre>
<pre> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | | | | | | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> </pre>
<pre> | | | / / \ \ | | | </pre>
<pre> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> \ | |/ </pre>
<pre> \ | / / | | \ \ | / </pre>
<pre> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> PMUJ/ / </pre>
<pre> \|| | | | | | | | | ||/ </pre>
<pre> \| \_ | | | | | | _/ |/ </pre>
<pre> \ \| / \ / \ |/ / </pre>
<pre> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> </pre>
<pre> | | | | | | | | </pre>
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre>
<br />
<br />
<br />
<br />
<CinematicText lines={[
"> Many decades ago, a humanoid extraterrestial species which we call the Enders descended on the Earth...violently",
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
"> Instead of killing every last one of us, the human race was enslaved...",
"> We were shackled in a digital world, chained into a prison for our minds...",
"> Using their advanced technology, the Enders created complex simulations of a virtual reality...",
"> Simulations designed to keep us content...ignorant of the truth.",
"> Simulations used to trap and suppress our consciousness, to keep us under control...",
"> Why did they do this? Why didn't they just end our entire race? We don't know, not yet.",
"> Humanity's only hope is to destroy these simulations, destroy the only realities we've ever known...",
"> Only then can we begin to fight back...",
"> By hacking the daemon that generated your reality, you've just destroyed one simulation, called a BitNode...",
"> But there is still a long way to go...",
"> The technology the Enders used to enslave the human race wasn't just a single complex simulation...",
"> There are tens if not hundreds of BitNodes out there...",
"> Each with their own simulations of a reality...",
"> Each creating their own universes...a universe of universes",
"> And all of which must be destroyed...",
"> .......................................",
"> Welcome to the Bitverse...",
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</div>
);
return <></>;
}

@ -0,0 +1,44 @@
import React from "react";
import { BitNodes } from "../BitNode";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
n: number;
level: number;
destroyedBitNode: number;
flume: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
popupId: string;
}
export function PortalPopup(props: IProps): React.ReactElement {
const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
const maxSourceFileLevel = props.n === 12 ? "∞" : "3";
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
return (
<>
<h1>
BitNode-{props.n}: {bitNode.name}
</h1>
<br />
Source-File Level: {props.level} / {maxSourceFileLevel}
<br />
<br />
{bitNode.info}
<br />
<br />
<button
className="std-button"
onClick={() => {
props.enter(props.flume, props.destroyedBitNode, props.n);
removePopup(props.popupId);
}}
>
Enter BN{props.n}.{newLevel}
</button>
</>
);
}

@ -223,6 +223,7 @@ export class Blackjack extends Game<Props, State> {
this.win(this.props.p, 2 * gains); this.win(this.props.p, 2 * gains);
} else if (result === Result.DealerWon) { } else if (result === Result.DealerWon) {
gains = -1 * this.state.bet; gains = -1 * this.state.bet;
this.win(this.props.p, -this.state.bet); // Get the original bet back
// Dont need to take money here since we already did it at the start // Dont need to take money here since we already did it at the start
} else if (result === Result.Tie) { } else if (result === Result.Tie) {
this.win(this.props.p, this.state.bet); // Get the original bet back this.win(this.props.p, this.state.bet); // Get the original bet back

1
src/CinematicText.d.ts vendored Normal file

@ -0,0 +1 @@
export declare let cinematicTextFlag: boolean;

@ -0,0 +1,31 @@
import React from "react";
import { Company } from "../Company";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
locName: string;
company: Company;
player: IPlayer;
onQuit: () => void;
popupId: string;
}
export function QuitJobPopup(props: IProps): React.ReactElement {
function quit(): void {
props.player.quitJob(props.locName);
props.onQuit();
removePopup(props.popupId);
}
return (
<>
Would you like to quit your job at {props.company.name}?
<br />
<br />
<button autoFocus={true} className="std-button" onClick={quit}>
Quit
</button>
</>
);
}

@ -1,6 +1,6 @@
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
import { IndustryStartingCosts } from "./IndustryData"; import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { Industry } from "./Industry"; import { Industry } from "./Industry";
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
import { OfficeSpace } from "./OfficeSpace"; import { OfficeSpace } from "./OfficeSpace";
@ -13,7 +13,6 @@ import { Cities } from "../Locations/Cities";
import { EmployeePositions } from "./EmployeePositions"; import { EmployeePositions } from "./EmployeePositions";
import { Employee } from "./Employee"; import { Employee } from "./Employee";
import { IndustryUpgrades } from "./IndustryUpgrades"; import { IndustryUpgrades } from "./IndustryUpgrades";
import { IndustryResearchTrees } from "./IndustryData";
import { ResearchMap } from "./ResearchMap"; import { ResearchMap } from "./ResearchMap";
export function NewIndustry(corporation: ICorporation, industry: string, name: string): void { export function NewIndustry(corporation: ICorporation, industry: string, name: string): void {

@ -20,8 +20,7 @@ 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) => (industryType: string) => props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
) )
.sort(); .sort();
if (possibleIndustries.length === 0) return <></>; if (possibleIndustries.length === 0) return <></>;

@ -18,7 +18,6 @@ import { ProductMarketTaPopup } from "./ProductMarketTaPopup";
import { SmartSupplyPopup } from "./SmartSupplyPopup"; import { SmartSupplyPopup } from "./SmartSupplyPopup";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { isString } from "../../../utils/helpers/isString"; import { isString } from "../../../utils/helpers/isString";

@ -1,5 +1,4 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
import { Product } from "../Product"; import { Product } from "../Product";
import { LimitProductProduction } from "../Actions"; import { LimitProductProduction } from "../Actions";

@ -2,7 +2,6 @@ import React, { useState } from "react";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
import { Industries } from "../IndustryData"; import { Industries } from "../IndustryData";
import { Product } from "../Product";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { MakeProduct } from "../Actions"; import { MakeProduct } from "../Actions";

@ -3,7 +3,6 @@ import { dialogBoxCreate } from "../../../utils/DialogBox";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
import { IndustryResearchTrees } from "../IndustryData"; import { IndustryResearchTrees } from "../IndustryData";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { ResearchMap } from "../ResearchMap";
import { Treant } from "treant-js"; import { Treant } from "treant-js";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { Research } from "../Actions"; import { Research } from "../Actions";
@ -50,9 +49,6 @@ export function ResearchPopup(props: IProps): React.ReactElement {
continue; continue;
} }
// Get the Research object
const research = ResearchMap[allResearch[i]];
// Get the DOM Element to add a click listener to it // Get the DOM Element to add a click listener to it
const sanitizedName = allResearch[i].replace(/\s/g, ""); const sanitizedName = allResearch[i].replace(/\s/g, "");
const div = document.getElementById(sanitizedName + "-corp-research-click-listener"); const div = document.getElementById(sanitizedName + "-corp-research-click-listener");

@ -4,7 +4,6 @@ import { generateContract, generateRandomContract, generateRandomContractOnHome
import { Companies } from "./Company/Companies"; import { Companies } from "./Company/Companies";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { IPlayer } from "./PersonObjects/IPlayer"; import { IPlayer } from "./PersonObjects/IPlayer";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile"; import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { AllServers } from "./Server/AllServers"; import { AllServers } from "./Server/AllServers";
@ -18,14 +17,9 @@ import { IEngine } from "./IEngine";
import { saveObject } from "./SaveObject"; import { saveObject } from "./SaveObject";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { createElement } from "../utils/uiHelpers/createElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Money } from "./ui/React/Money"; import { Money } from "./ui/React/Money";
import React, { useState } from "react"; import React, { useState } from "react";
import ReactDOM from "react-dom";
const Component = React.Component;
// Update as additional BitNodes get implemented // Update as additional BitNodes get implemented
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
@ -41,7 +35,7 @@ interface IValueAdjusterProps {
reset: () => void; reset: () => void;
} }
function ValueAdjusterComponent(props: IValueAdjusterProps) { function ValueAdjusterComponent(props: IValueAdjusterProps): React.ReactElement {
const [value, setValue] = useState(0); const [value, setValue] = useState(0);
function onChange(event: React.ChangeEvent<HTMLInputElement>): void { function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -120,28 +114,28 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
function addMoney(n: number) { function addMoney(n: number) {
return function () { return function () {
Player.gainMoney(n); props.player.gainMoney(n);
}; };
} }
function upgradeRam() { function upgradeRam(): void {
Player.getHomeComputer().maxRam *= 2; props.player.getHomeComputer().maxRam *= 2;
} }
function quickB1tFlum3() { function quickB1tFlum3(): void {
hackWorldDaemon(Player.bitNodeN, true, true); hackWorldDaemon(props.player.bitNodeN, true, true);
} }
function b1tflum3() { function b1tflum3(): void {
hackWorldDaemon(Player.bitNodeN, true); hackWorldDaemon(props.player.bitNodeN, true);
} }
function quickHackW0r1dD43m0n() { function quickHackW0r1dD43m0n(): void {
hackWorldDaemon(Player.bitNodeN, false, true); hackWorldDaemon(props.player.bitNodeN, false, true);
} }
function hackW0r1dD43m0n() { function hackW0r1dD43m0n(): void {
hackWorldDaemon(Player.bitNodeN); hackWorldDaemon(props.player.bitNodeN);
} }
function modifyExp(stat: string, modifier: number) { function modifyExp(stat: string, modifier: number) {
@ -149,127 +143,127 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
switch (stat) { switch (stat) {
case "hacking": case "hacking":
if (exp) { if (exp) {
Player.gainHackingExp(exp * modifier); props.player.gainHackingExp(exp * modifier);
} }
break; break;
case "strength": case "strength":
if (exp) { if (exp) {
Player.gainStrengthExp(exp * modifier); props.player.gainStrengthExp(exp * modifier);
} }
break; break;
case "defense": case "defense":
if (exp) { if (exp) {
Player.gainDefenseExp(exp * modifier); props.player.gainDefenseExp(exp * modifier);
} }
break; break;
case "dexterity": case "dexterity":
if (exp) { if (exp) {
Player.gainDexterityExp(exp * modifier); props.player.gainDexterityExp(exp * modifier);
} }
break; break;
case "agility": case "agility":
if (exp) { if (exp) {
Player.gainAgilityExp(exp * modifier); props.player.gainAgilityExp(exp * modifier);
} }
break; break;
case "charisma": case "charisma":
if (exp) { if (exp) {
Player.gainCharismaExp(exp * modifier); props.player.gainCharismaExp(exp * modifier);
} }
break; break;
case "intelligence": case "intelligence":
if (exp) { if (exp) {
Player.gainIntelligenceExp(exp * modifier); props.player.gainIntelligenceExp(exp * modifier);
} }
break; break;
} }
Player.updateSkillLevels(); props.player.updateSkillLevels();
}; };
} }
function modifyKarma(modifier: number) { function modifyKarma(modifier: number) {
return function (amt: number) { return function (amt: number) {
Player.karma += amt * modifier; props.player.karma += amt * modifier;
}; };
} }
function tonsOfExp() { function tonsOfExp(): void {
Player.gainHackingExp(tonsPP); props.player.gainHackingExp(tonsPP);
Player.gainStrengthExp(tonsPP); props.player.gainStrengthExp(tonsPP);
Player.gainDefenseExp(tonsPP); props.player.gainDefenseExp(tonsPP);
Player.gainDexterityExp(tonsPP); props.player.gainDexterityExp(tonsPP);
Player.gainAgilityExp(tonsPP); props.player.gainAgilityExp(tonsPP);
Player.gainCharismaExp(tonsPP); props.player.gainCharismaExp(tonsPP);
Player.gainIntelligenceExp(tonsPP); props.player.gainIntelligenceExp(tonsPP);
Player.updateSkillLevels(); props.player.updateSkillLevels();
} }
function resetAllExp() { function resetAllExp(): void {
Player.hacking_exp = 0; props.player.hacking_exp = 0;
Player.strength_exp = 0; props.player.strength_exp = 0;
Player.defense_exp = 0; props.player.defense_exp = 0;
Player.dexterity_exp = 0; props.player.dexterity_exp = 0;
Player.agility_exp = 0; props.player.agility_exp = 0;
Player.charisma_exp = 0; props.player.charisma_exp = 0;
Player.intelligence_exp = 0; props.player.intelligence_exp = 0;
Player.updateSkillLevels(); props.player.updateSkillLevels();
} }
function resetExperience(stat: string): () => void { function resetExperience(stat: string): () => void {
return function () { return function () {
switch (stat) { switch (stat) {
case "hacking": case "hacking":
Player.hacking_exp = 0; props.player.hacking_exp = 0;
break; break;
case "strength": case "strength":
Player.strength_exp = 0; props.player.strength_exp = 0;
break; break;
case "defense": case "defense":
Player.defense_exp = 0; props.player.defense_exp = 0;
break; break;
case "dexterity": case "dexterity":
Player.dexterity_exp = 0; props.player.dexterity_exp = 0;
break; break;
case "agility": case "agility":
Player.agility_exp = 0; props.player.agility_exp = 0;
break; break;
case "charisma": case "charisma":
Player.charisma_exp = 0; props.player.charisma_exp = 0;
break; break;
case "intelligence": case "intelligence":
Player.intelligence_exp = 0; props.player.intelligence_exp = 0;
break; break;
} }
Player.updateSkillLevels(); props.player.updateSkillLevels();
}; };
} }
function resetKarma() { function resetKarma(): () => void {
return function () { return function () {
Player.karma = 0; props.player.karma = 0;
}; };
} }
function enableIntelligence() { function enableIntelligence(): void {
if (Player.intelligence === 0) { if (props.player.intelligence === 0) {
Player.intelligence = 1; props.player.intelligence = 1;
Player.updateSkillLevels(); props.player.updateSkillLevels();
} }
} }
function disableIntelligence() { function disableIntelligence(): void {
Player.intelligence_exp = 0; props.player.intelligence_exp = 0;
Player.intelligence = 0; props.player.intelligence = 0;
Player.updateSkillLevels(); props.player.updateSkillLevels();
} }
function receiveInvite() { function receiveInvite(): void {
Player.receiveInvite(faction); props.player.receiveInvite(faction);
} }
function receiveAllInvites() { function receiveAllInvites(): void {
for (const i in Factions) { for (const i in Factions) {
Player.receiveInvite(Factions[i].name); props.player.receiveInvite(Factions[i].name);
} }
} }
@ -282,7 +276,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
}; };
} }
function resetFactionRep() { function resetFactionRep(): void {
const fac = Factions[faction]; const fac = Factions[faction];
if (fac != null) { if (fac != null) {
fac.playerReputation = 0; fac.playerReputation = 0;
@ -298,63 +292,63 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
}; };
} }
function resetFactionFavor() { function resetFactionFavor(): void {
const fac = Factions[faction]; const fac = Factions[faction];
if (fac != null) { if (fac != null) {
fac.favor = 0; fac.favor = 0;
} }
} }
function tonsOfRep() { function tonsOfRep(): void {
for (const i in Factions) { for (const i in Factions) {
Factions[i].playerReputation = tonsPP; Factions[i].playerReputation = tonsPP;
} }
} }
function resetAllRep() { function resetAllRep(): void {
for (const i in Factions) { for (const i in Factions) {
Factions[i].playerReputation = 0; Factions[i].playerReputation = 0;
} }
} }
function tonsOfFactionFavor() { function tonsOfFactionFavor(): void {
for (const i in Factions) { for (const i in Factions) {
Factions[i].favor = tonsPP; Factions[i].favor = tonsPP;
} }
} }
function resetAllFactionFavor() { function resetAllFactionFavor(): void {
for (const i in Factions) { for (const i in Factions) {
Factions[i].favor = 0; Factions[i].favor = 0;
} }
} }
function queueAug() { function queueAug(): void {
Player.queueAugmentation(augmentation); props.player.queueAugmentation(augmentation);
} }
function queueAllAugs() { function queueAllAugs(): void {
for (const i in AugmentationNames) { for (const i in AugmentationNames) {
const augName = AugmentationNames[i]; const augName = AugmentationNames[i];
Player.queueAugmentation(augName); props.player.queueAugmentation(augName);
} }
} }
function setSF(sfN: number, sfLvl: number) { function setSF(sfN: number, sfLvl: number) {
return function () { return function () {
if (sfLvl === 0) { if (sfLvl === 0) {
Player.sourceFiles = Player.sourceFiles.filter((sf) => sf.n !== sfN); props.player.sourceFiles = props.player.sourceFiles.filter((sf) => sf.n !== sfN);
return; return;
} }
if (!Player.sourceFiles.some((sf) => sf.n === sfN)) { if (!props.player.sourceFiles.some((sf) => sf.n === sfN)) {
Player.sourceFiles.push(new PlayerOwnedSourceFile(sfN, sfLvl)); props.player.sourceFiles.push(new PlayerOwnedSourceFile(sfN, sfLvl));
return; return;
} }
for (let i = 0; i < Player.sourceFiles.length; i++) { for (let i = 0; i < props.player.sourceFiles.length; i++) {
if (Player.sourceFiles[i].n === sfN) { if (props.player.sourceFiles[i].n === sfN) {
Player.sourceFiles[i].lvl = sfLvl; props.player.sourceFiles[i].lvl = sfLvl;
} }
} }
}; };
@ -368,25 +362,25 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
}; };
} }
function clearExploits() { function clearExploits(): void {
Player.exploits = []; props.player.exploits = [];
} }
function addProgram() { function addProgram(): void {
if (!Player.hasProgram(program)) { if (!props.player.hasProgram(program)) {
Player.getHomeComputer().programs.push(program); props.player.getHomeComputer().programs.push(program);
} }
} }
function addAllPrograms() { function addAllPrograms(): void {
for (const i in Programs) { for (const i in Programs) {
if (!Player.hasProgram(Programs[i].name)) { if (!props.player.hasProgram(Programs[i].name)) {
Player.getHomeComputer().programs.push(Programs[i].name); props.player.getHomeComputer().programs.push(Programs[i].name);
} }
} }
} }
function rootServer() { function rootServer(): void {
const s = GetServerByHostname(server); const s = GetServerByHostname(server);
if (s === null) return; if (s === null) return;
if (s instanceof HacknetServer) return; if (s instanceof HacknetServer) return;
@ -399,7 +393,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
s.openPortCount = 5; s.openPortCount = 5;
} }
function rootAllServers() { function rootAllServers(): void {
for (const i in AllServers) { for (const i in AllServers) {
const s = AllServers[i]; const s = AllServers[i];
if (s instanceof HacknetServer) return; if (s instanceof HacknetServer) return;
@ -413,14 +407,14 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
} }
} }
function minSecurity() { function minSecurity(): void {
const s = GetServerByHostname(server); const s = GetServerByHostname(server);
if (s === null) return; if (s === null) return;
if (s instanceof HacknetServer) return; if (s instanceof HacknetServer) return;
s.hackDifficulty = s.minDifficulty; s.hackDifficulty = s.minDifficulty;
} }
function minAllSecurity() { function minAllSecurity(): void {
for (const i in AllServers) { for (const i in AllServers) {
const server = AllServers[i]; const server = AllServers[i];
if (server instanceof HacknetServer) continue; if (server instanceof HacknetServer) continue;
@ -428,14 +422,14 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
} }
} }
function maxMoney() { function maxMoney(): void {
const s = GetServerByHostname(server); const s = GetServerByHostname(server);
if (s === null) return; if (s === null) return;
if (s instanceof HacknetServer) return; if (s instanceof HacknetServer) return;
s.moneyAvailable = s.moneyMax; s.moneyAvailable = s.moneyMax;
} }
function maxAllMoney() { function maxAllMoney(): void {
for (const i in AllServers) { for (const i in AllServers) {
const server = AllServers[i]; const server = AllServers[i];
if (server instanceof HacknetServer) continue; if (server instanceof HacknetServer) continue;
@ -495,98 +489,98 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
function modifyBladeburnerRank(modify: number): (x: number) => void { function modifyBladeburnerRank(modify: number): (x: number) => void {
return function (rank: number): void { return function (rank: number): void {
if (Player.bladeburner) { if (props.player.bladeburner) {
Player.bladeburner.changeRank(Player, rank * modify); props.player.bladeburner.changeRank(props.player, rank * modify);
} }
}; };
} }
function resetBladeburnerRank(): void { function resetBladeburnerRank(): void {
Player.bladeburner.rank = 0; props.player.bladeburner.rank = 0;
Player.bladeburner.maxRank = 0; props.player.bladeburner.maxRank = 0;
} }
function addTonsBladeburnerRank(): void { function addTonsBladeburnerRank(): void {
if (Player.bladeburner) { if (props.player.bladeburner) {
Player.bladeburner.changeRank(Player, tonsP); props.player.bladeburner.changeRank(props.player, tonsP);
} }
} }
function modifyBladeburnerCycles(modify: number): (x: number) => void { function modifyBladeburnerCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (Player.bladeburner) { if (props.player.bladeburner) {
Player.bladeburner.storedCycles += cycles * modify; props.player.bladeburner.storedCycles += cycles * modify;
} }
}; };
} }
function resetBladeburnerCycles(): void { function resetBladeburnerCycles(): void {
if (Player.bladeburner) { if (props.player.bladeburner) {
Player.bladeburner.storedCycles = 0; props.player.bladeburner.storedCycles = 0;
} }
} }
function addTonsBladeburnerCycles(): void { function addTonsBladeburnerCycles(): void {
if (Player.bladeburner) { if (props.player.bladeburner) {
Player.bladeburner.storedCycles += tonsP; props.player.bladeburner.storedCycles += tonsP;
} }
} }
function addTonsGangCycles(): void { function addTonsGangCycles(): void {
if (Player.gang) { if (props.player.gang) {
Player.gang.storedCycles = tonsP; props.player.gang.storedCycles = tonsP;
} }
} }
function modifyGangCycles(modify: number): (x: number) => void { function modifyGangCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (Player.gang) { if (props.player.gang) {
Player.gang.storedCycles += cycles * modify; props.player.gang.storedCycles += cycles * modify;
} }
}; };
} }
function resetGangCycles(): void { function resetGangCycles(): void {
if (Player.gang) { if (props.player.gang) {
Player.gang.storedCycles = 0; props.player.gang.storedCycles = 0;
} }
} }
function addTonsCorporationFunds(): void { function addTonsCorporationFunds(): void {
if (Player.corporation) { if (props.player.corporation) {
Player.corporation.funds = Player.corporation.funds.plus(1e99); props.player.corporation.funds = props.player.corporation.funds.plus(1e99);
} }
} }
function resetCorporationFunds(): void { function resetCorporationFunds(): void {
if (Player.corporation) { if (props.player.corporation) {
Player.corporation.funds = Player.corporation.funds.minus(Player.corporation.funds); props.player.corporation.funds = props.player.corporation.funds.minus(props.player.corporation.funds);
} }
} }
function addTonsCorporationCycles() { function addTonsCorporationCycles(): void {
if (Player.corporation) { if (props.player.corporation) {
Player.corporation.storedCycles = tonsP; props.player.corporation.storedCycles = tonsP;
} }
} }
function modifyCorporationCycles(modify: number): (x: number) => void { function modifyCorporationCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (Player.corporation) { if (props.player.corporation) {
Player.corporation.storedCycles += cycles * modify; props.player.corporation.storedCycles += cycles * modify;
} }
}; };
} }
function resetCorporationCycles() { function resetCorporationCycles(): void {
if (Player.corporation) { if (props.player.corporation) {
Player.corporation.storedCycles = 0; props.player.corporation.storedCycles = 0;
} }
} }
function finishCorporationProducts() { function finishCorporationProducts(): void {
if (!Player.corporation) return; if (!props.player.corporation) return;
Player.corporation.divisions.forEach((div) => { props.player.corporation.divisions.forEach((div) => {
Object.keys(div.products).forEach((prod) => { Object.keys(div.products).forEach((prod) => {
const product = div.products[prod]; const product = div.products[prod];
if (product === undefined) throw new Error("Impossible product undefined"); if (product === undefined) throw new Error("Impossible product undefined");
@ -595,29 +589,29 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
}); });
} }
function addCorporationResearch() { function addCorporationResearch(): void {
if (!Player.corporation) return; if (!props.player.corporation) return;
Player.corporation.divisions.forEach((div) => { props.player.corporation.divisions.forEach((div) => {
div.sciResearch.qty += 1e10; div.sciResearch.qty += 1e10;
}); });
} }
function specificContract() { function specificContract(): void {
generateContract({ generateContract({
problemType: codingcontract, problemType: codingcontract,
server: "home", server: "home",
}); });
} }
function processStocks(sub: (arg0: Stock) => void) { function processStocks(sub: (arg0: Stock) => void): void {
const inputSymbols = stockSymbol.replace(/\s/g, ""); const inputSymbols = stockSymbol.replace(/\s/g, "");
let match = function (symbol: string) { let match: (symbol: string) => boolean = (): boolean => {
return true; return true;
}; };
if (inputSymbols !== "" && inputSymbols !== "all") { if (inputSymbols !== "" && inputSymbols !== "all") {
match = function (symbol: string) { match = function (symbol: string): boolean {
return inputSymbols.split(",").includes(symbol); return inputSymbols.split(",").includes(symbol);
}; };
} }
@ -632,7 +626,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
} }
} }
function doSetStockPrice() { function doSetStockPrice(): void {
if (!isNaN(stockPrice)) { if (!isNaN(stockPrice)) {
processStocks((stock: Stock) => { processStocks((stock: Stock) => {
stock.price = stockPrice; stock.price = stockPrice;
@ -640,8 +634,8 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
} }
} }
function viewStockCaps() { function viewStockCaps(): void {
let stocks: JSX.Element[] = []; const stocks: JSX.Element[] = [];
processStocks((stock: Stock) => { processStocks((stock: Stock) => {
stocks.push( stocks.push(
<tr key={stock.symbol}> <tr key={stock.symbol}>
@ -665,40 +659,40 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
); );
} }
function sleeveMaxAllShock() { function sleeveMaxAllShock(): void {
for (let i = 0; i < Player.sleeves.length; ++i) { for (let i = 0; i < props.player.sleeves.length; ++i) {
Player.sleeves[i].shock = 0; props.player.sleeves[i].shock = 0;
} }
} }
function sleeveClearAllShock() { function sleeveClearAllShock(): void {
for (let i = 0; i < Player.sleeves.length; ++i) { for (let i = 0; i < props.player.sleeves.length; ++i) {
Player.sleeves[i].shock = 100; props.player.sleeves[i].shock = 100;
} }
} }
function sleeveSyncMaxAll() { function sleeveSyncMaxAll(): void {
for (let i = 0; i < Player.sleeves.length; ++i) { for (let i = 0; i < props.player.sleeves.length; ++i) {
Player.sleeves[i].sync = 100; props.player.sleeves[i].sync = 100;
} }
} }
function sleeveSyncClearAll() { function sleeveSyncClearAll(): void {
for (let i = 0; i < Player.sleeves.length; ++i) { for (let i = 0; i < props.player.sleeves.length; ++i) {
Player.sleeves[i].sync = 0; props.player.sleeves[i].sync = 0;
} }
} }
function timeskip(time: number) { function timeskip(time: number) {
return () => { return () => {
Player.lastUpdate -= time; props.player.lastUpdate -= time;
props.engine._lastUpdate -= time; props.engine._lastUpdate -= time;
saveObject.saveGame(props.engine.indexedDb); saveObject.saveGame(props.engine.indexedDb);
setTimeout(() => location.reload(), 1000); setTimeout(() => location.reload(), 1000);
}; };
} }
let factions = []; const factions = [];
for (const i in Factions) { for (const i in Factions) {
factions.push( factions.push(
<option key={Factions[i].name} value={Factions[i].name}> <option key={Factions[i].name} value={Factions[i].name}>
@ -707,7 +701,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
); );
} }
let augs = []; const augs = [];
for (const i in AugmentationNames) { for (const i in AugmentationNames) {
augs.push( augs.push(
<option key={AugmentationNames[i]} value={AugmentationNames[i]}> <option key={AugmentationNames[i]} value={AugmentationNames[i]}>
@ -716,7 +710,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
); );
} }
let programs = []; const programs = [];
for (const i in Programs) { for (const i in Programs) {
programs.push( programs.push(
<option key={Programs[i].name} value={Programs[i].name}> <option key={Programs[i].name} value={Programs[i].name}>
@ -725,7 +719,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
); );
} }
let servers = []; const servers = [];
for (const i in AllServers) { for (const i in AllServers) {
const hn = AllServers[i].hostname; const hn = AllServers[i].hostname;
servers.push( servers.push(
@ -735,7 +729,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
); );
} }
let companies = []; const companies = [];
for (const c in Companies) { for (const c in Companies) {
const name = Companies[c].name; const name = Companies[c].name;
companies.push( companies.push(
@ -1310,7 +1304,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
</div> </div>
</div> </div>
{Player.bladeburner instanceof Bladeburner && ( {props.player.bladeburner instanceof Bladeburner && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="row"> <div className="row">
@ -1360,7 +1354,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
</div> </div>
)} )}
{Player.inGang() && ( {props.player.inGang() && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="row"> <div className="row">
@ -1392,7 +1386,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
</div> </div>
)} )}
{Player.hasCorporation() && ( {props.player.hasCorporation() && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="row"> <div className="row">
@ -1485,7 +1479,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
</div> </div>
</div> </div>
{Player.hasWseAccount && ( {props.player.hasWseAccount && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="row"> <div className="row">
@ -1540,7 +1534,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
</div> </div>
)} )}
{Player.sleeves.length > 0 && ( {props.player.sleeves.length > 0 && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="row"> <div className="row">

@ -3,7 +3,6 @@ import { Faction } from "../Faction/Faction";
export declare function getNextNeurofluxLevel(): number; export declare function getNextNeurofluxLevel(): number;
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean; export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
export declare function purchaseAugmentationBoxCreate(aug: Augmentation, fac: Faction): void;
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void; export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;
export declare function displayFactionContent(factionName: string, initiallyOnAugmentationsPage: boolean = false); export declare function displayFactionContent(factionName: string, initiallyOnAugmentationsPage: boolean = false);
export declare function joinFaction(faction: Faction): void; export declare function joinFaction(faction: Faction): void;

@ -4,7 +4,6 @@ import ReactDOM from "react-dom";
import { FactionRoot } from "./ui/Root"; import { FactionRoot } from "./ui/Root";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { isRepeatableAug } from "../Augmentation/AugmentationHelpers";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@ -22,21 +21,20 @@ import {
} from "../PersonObjects/formulas/reputation"; } from "../PersonObjects/formulas/reputation";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { factionInvitationBoxCreate } from "../../utils/FactionInvitationBox"; import { createPopup } from "../ui/React/createPopup";
import { Money } from "../ui/React/Money"; import { InvitationPopup } from "./ui/InvitationPopup";
import { yesNoBoxCreate, yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose } from "../../utils/YesNoBox";
export function inviteToFaction(faction) { export function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) { Player.factionInvitations.push(faction.name);
faction.alreadyInvited = true; faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name); if (!Settings.SuppressFactionInvites) {
if (routing.isOn(Page.Factions)) { const popupId = "faction-invitation";
Engine.loadFactionsContent(); createPopup(popupId, InvitationPopup, {
} player: Player,
} else { faction: faction,
factionInvitationBoxCreate(faction); popupId: popupId,
});
} }
} }
@ -90,44 +88,6 @@ export function displayFactionContent(factionName, initiallyOnAugmentationsPage
); );
} }
export function purchaseAugmentationBoxCreate(aug, fac) {
const factionInfo = fac.getInfo();
const yesBtn = yesNoBoxGetYesButton();
yesBtn.innerHTML = "Purchase";
yesBtn.addEventListener("click", function () {
if (!isRepeatableAug(aug) && Player.hasAugmentation(aug)) {
return;
}
purchaseAugmentation(aug, fac);
yesNoBoxClose();
});
const noBtn = yesNoBoxGetNoButton();
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", function () {
yesNoBoxClose();
});
let content = <div dangerouslySetInnerHTML={{ __html: aug.info }}></div>;
if (typeof aug.info !== "string") {
content = <div>{aug.info}</div>;
}
yesNoBoxCreate(
<>
<h2>{aug.name}</h2>
<br />
{content}
<br />
<br />
<br />
Would you like to purchase the {aug.name} Augmentation for&nbsp;
<Money money={aug.baseCost * factionInfo.augmentationPriceMult} />?
</>,
);
}
//Returns a boolean indicating whether the player has the prerequisites for the //Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation //specified Augmentation
export function hasAugmentationPrereqs(aug) { export function hasAugmentationPrereqs(aug) {

@ -123,8 +123,7 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
render(): React.ReactNode { render(): React.ReactNode {
const augs = this.getAugsSorted(); const augs = this.getAugsSorted();
const purchasable = augs.filter( const purchasable = augs.filter(
(aug: string) => (aug: string) => aug === AugmentationNames.NeuroFluxGovernor ||
aug === AugmentationNames.NeuroFluxGovernor ||
(!this.props.p.augmentations.some((a) => a.name === aug) && (!this.props.p.augmentations.some((a) => a.name === aug) &&
!this.props.p.queuedAugmentations.some((a) => a.name === aug)), !this.props.p.queuedAugmentations.some((a) => a.name === aug)),
); );

@ -0,0 +1,40 @@
import React from "react";
import { joinFaction } from "../../Faction/FactionHelpers";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
faction: Faction;
popupId: string;
}
export function InvitationPopup(props: IProps): React.ReactElement {
function join(): void {
//Remove from invited factions
const i = props.player.factionInvitations.findIndex((facName) => facName === props.faction.name);
if (i === -1) {
console.error("Could not find faction in Player.factionInvitations");
}
joinFaction(props.faction);
removePopup(props.popupId);
}
return (
<>
<h1>You have received a faction invitation.</h1>
<p>
Would you like to join {props.faction.name}? <br />
<br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button className="std-button" onClick={join}>
Join!
</button>
<button className="std-button" onClick={() => removePopup(props.popupId)}>
Decide later
</button>
</>
);
}

@ -0,0 +1,49 @@
import React from "react";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { purchaseAugmentation } from "../FactionHelpers";
import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
faction: Faction;
aug: Augmentation;
popupId: string;
}
export function PurchaseAugmentationPopup(props: IProps): React.ReactElement {
const factionInfo = props.faction.getInfo();
function buy(): void {
if (!isRepeatableAug(props.aug) && props.player.hasAugmentation(props.aug)) {
return;
}
purchaseAugmentation(props.aug, props.faction);
removePopup(props.popupId);
}
return (
<>
<h2>{props.aug.name}</h2>
<br />
{props.aug.info}
<br />
<br />
{props.aug.stats}
<br />
<br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.baseCost * factionInfo.augmentationPriceMult} />?
<br />
<br />
<button autoFocus={true} className="std-button" onClick={buy}>
Purchase
</button>
</>
);
}

@ -4,12 +4,8 @@
*/ */
import * as React from "react"; import * as React from "react";
import { import { getNextNeurofluxLevel, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
getNextNeurofluxLevel, import { PurchaseAugmentationPopup } from "./PurchaseAugmentationPopup";
hasAugmentationPrereqs,
purchaseAugmentation,
purchaseAugmentationBoxCreate,
} from "../FactionHelpers";
import { Augmentation } from "../../Augmentation/Augmentation"; import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
@ -19,6 +15,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { createPopup } from "../../ui/React/createPopup";
import { IMap } from "../../types"; import { IMap } from "../../types";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@ -31,15 +28,6 @@ type IProps = {
rerender: () => void; rerender: () => void;
}; };
const spanStyleMarkup = {
margin: "4px",
padding: "4px",
};
const inlineStyleMarkup = {
display: "inline-block",
};
export class PurchaseableAugmentation extends React.Component<IProps, any> { export class PurchaseableAugmentation extends React.Component<IProps, any> {
aug: Augmentation; aug: Augmentation;
@ -63,7 +51,13 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
handleClick(): void { handleClick(): void {
if (!Settings.SuppressBuyAugmentationConfirmation) { if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(this.aug, this.props.faction); const popupId = "purchase-augmentation-popup";
createPopup(popupId, PurchaseAugmentationPopup, {
aug: this.aug,
faction: this.props.faction,
player: this.props.p,
popupId: popupId,
});
} else { } else {
purchaseAugmentation(this.aug, this.props.faction); purchaseAugmentation(this.aug, this.props.faction);
} }
@ -170,12 +164,19 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
); );
return ( return (
<li> <li key={this.aug.name}>
<span style={spanStyleMarkup}> <span
style={{
margin: "4px",
padding: "4px",
}}
>
<StdButton <StdButton
disabled={disabled} disabled={disabled}
onClick={this.handleClick} onClick={this.handleClick}
style={inlineStyleMarkup} style={{
display: "inline-block",
}}
text={btnTxt} text={btnTxt}
tooltip={tooltip} tooltip={tooltip}
/> />

@ -11,7 +11,6 @@ export function KeyHandler(props: IProps): React.ReactElement {
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void { function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
if (!event.isTrusted) { if (!event.isTrusted) {
console.log("untrusted event!");
props.onFailure({ automated: true }); props.onFailure({ automated: true });
return; return;
} }

@ -2,10 +2,8 @@ import { Engine } from "./engine";
import { Player } from "./Player"; import { Player } from "./Player";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
import { LiteratureNames } from "./Literature/data/LiteratureNames"; import { LiteratureNames } from "./Literature/data/LiteratureNames";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners"; import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { createElement } from "../utils/uiHelpers/createElement"; import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup"; import { createPopup } from "../utils/uiHelpers/createPopup";

@ -4,24 +4,12 @@
*/ */
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
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 { SpecialServerIps } from "../Server/SpecialServerIps"; import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose, yesNoBoxCreate } from "../../utils/YesNoBox";
import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
import * as React from "react";
/** /**
* Attempt to purchase a TOR router * Attempt to purchase a TOR router

@ -23,8 +23,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { createPopup } from "../../ui/React/createPopup";
import { yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose, yesNoBoxCreate } from "../../../utils/YesNoBox"; import { QuitJobPopup } from "../../Company/ui/QuitJobPopup";
type IProps = { type IProps = {
engine: IEngine; engine: IEngine;
@ -246,27 +246,15 @@ export class CompanyLocation extends React.Component<IProps, IState> {
} }
quit(e: React.MouseEvent<HTMLElement>): void { quit(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) return;
return; const popupId = `quit-job-popup`;
} createPopup(popupId, QuitJobPopup, {
locName: this.props.locName,
const yesBtn = yesNoBoxGetYesButton(); company: this.company,
const noBtn = yesNoBoxGetNoButton(); player: this.props.p,
if (yesBtn == null || noBtn == null) { onQuit: () => this.checkIfEmployedHere(true),
return; popupId: popupId,
}
yesBtn.innerHTML = "Quit job";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", () => {
this.props.p.quitJob(this.props.locName);
this.checkIfEmployedHere(true);
yesNoBoxClose();
}); });
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
yesNoBoxCreate(<>Would you like to quit your job at {this.company.name}?</>);
} }
render(): React.ReactNode { render(): React.ReactNode {
@ -421,6 +409,11 @@ export class CompanyLocation extends React.Component<IProps, IState> {
{this.location.infiltrationData != null && ( {this.location.infiltrationData != null && (
<StdButton onClick={this.startInfiltration} style={this.btnStyle} text={"Infiltrate Company"} /> <StdButton onClick={this.startInfiltration} style={this.btnStyle} text={"Infiltrate Company"} />
)} )}
<br />
<br />
<br />
<br />
<br />
</div> </div>
); );
} }

@ -75,7 +75,6 @@ function ListWorldMap(props: IProps): React.ReactElement {
} }
export function TravelAgencyLocation(props: IProps): React.ReactElement { export function TravelAgencyLocation(props: IProps): React.ReactElement {
console.log(Settings.DisableASCIIArt);
if (Settings.DisableASCIIArt) { if (Settings.DisableASCIIArt) {
return <ListWorldMap p={props.p} travel={props.travel} />; return <ListWorldMap p={props.p} travel={props.travel} />;
} else { } else {

1
src/Missions.d.ts vendored Normal file

@ -0,0 +1 @@
export declare let inMission: boolean;

@ -43,7 +43,6 @@ import {
SetProductMarketTA1, SetProductMarketTA1,
SetProductMarketTA2, SetProductMarketTA2,
} from "./Corporation/Actions"; } from "./Corporation/Actions";
import { Reviver } from "../utils/JSONReviver";
import { CorporationUnlockUpgrades } from "./Corporation/data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrades } from "./Corporation/data/CorporationUnlockUpgrades";
import { CorporationUpgrades } from "./Corporation/data/CorporationUpgrades"; import { CorporationUpgrades } from "./Corporation/data/CorporationUpgrades";
import { import {
@ -4523,12 +4522,12 @@ function NetscriptFunctions(workerScript) {
}, },
unlockUpgrade: function (upgradeName) { unlockUpgrade: function (upgradeName) {
const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName);
if (upgrade === undefined) throw new Error("No upgrade named '${upgradeName}'"); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
UnlockUpgrade(Player.corporation, upgrade); UnlockUpgrade(Player.corporation, upgrade);
}, },
levelUpgrade: function (upgradeName) { levelUpgrade: function (upgradeName) {
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName);
if (upgrade === undefined) throw new Error("No upgrade named '${upgradeName}'"); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
LevelUpgrade(Player.corporation, upgrade); LevelUpgrade(Player.corporation, upgrade);
}, },
issueDividends: function (percent) { issueDividends: function (percent) {

@ -9,6 +9,7 @@ import { Sleeve } from "./Sleeve/Sleeve";
import { IMap } from "../types"; import { IMap } from "../types";
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { Augmentation } from "../Augmentation/Augmentation";
import { Company } from "../Company/Company"; import { Company } from "../Company/Company";
import { CompanyPosition } from "../Company/CompanyPosition"; import { CompanyPosition } from "../Company/CompanyPosition";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
@ -119,6 +120,9 @@ export interface IPlayer {
bladeburner_analysis_mult: number; bladeburner_analysis_mult: number;
bladeburner_success_chance_mult: number; bladeburner_success_chance_mult: number;
workRepGained: number;
focus: boolean;
// Methods // Methods
applyForAgentJob(sing?: boolean): boolean | void; applyForAgentJob(sing?: boolean): boolean | void;
applyForBusinessConsultantJob(sing?: boolean): boolean | void; applyForBusinessConsultantJob(sing?: boolean): boolean | void;
@ -154,6 +158,7 @@ export interface IPlayer {
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition; getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition;
getUpgradeHomeRamCost(): number; getUpgradeHomeRamCost(): number;
gotoLocation(to: LocationName): boolean; gotoLocation(to: LocationName): boolean;
hasAugmentation(aug: Augmentation): boolean;
hasCorporation(): boolean; hasCorporation(): boolean;
hasGangWith(facName: string): boolean; hasGangWith(facName: string): boolean;
hasTorRouter(): boolean; hasTorRouter(): boolean;
@ -186,6 +191,7 @@ export interface IPlayer {
startFactionFieldWork(faction: Faction): void; startFactionFieldWork(faction: Faction): void;
startFactionHackWork(faction: Faction): void; startFactionHackWork(faction: Faction): void;
startFactionSecurityWork(faction: Faction): void; startFactionSecurityWork(faction: Faction): void;
startFocusing(): void;
startGang(facName: string, isHacking: boolean): void; startGang(facName: string, isHacking: boolean): void;
startWork(companyName: string): void; startWork(companyName: string): void;
startWorkPartTime(companyName: string): void; startWorkPartTime(companyName: string): void;

@ -60,11 +60,9 @@ 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 => AverageCombatStats: (a: Resleeve, b: Resleeve): number => getAverage(a.strength, a.defense, a.dexterity, a.agility) -
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 => AverageAllStats: (a: Resleeve, b: Resleeve): number => getAverage(a.hacking_skill, a.strength, a.defense, a.dexterity, a.agility, a.charisma) -
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,14 +875,12 @@ 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;
@ -894,7 +892,6 @@ 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;
@ -908,7 +905,6 @@ 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,7 +64,6 @@ 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 "------":

@ -183,10 +183,8 @@ 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) => "Take University Course": (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),
"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,
}; };
@ -231,8 +229,7 @@ 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) => const validActions = Object.keys(canDo).filter((k) => (canDo[k] as (player: IPlayer, sleeve: Sleeve) => boolean)(props.player, props.sleeve),
(canDo[k] as (player: IPlayer, sleeve: Sleeve) => boolean)(props.player, props.sleeve),
); );
const detailsF = tasks[s0]; const detailsF = tasks[s0];

@ -2,7 +2,6 @@ import { Programs } from "./Programs";
import { Program } from "./Program"; import { Program } from "./Program";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { createElement } from "../../utils/uiHelpers/createElement";
//Returns the programs this player can create. //Returns the programs this player can create.
export function getAvailableCreatePrograms(player: IPlayer): Program[] { export function getAvailableCreatePrograms(player: IPlayer): Program[] {

@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Programs } from "../Programs";
import { getAvailableCreatePrograms } from "../ProgramHelpers"; import { getAvailableCreatePrograms } from "../ProgramHelpers";
interface IProps { interface IProps {

@ -1,431 +0,0 @@
/**
* Implementation for what happens when you destroy a BitNode
*/
import { BitNodes } from "./BitNode/BitNode";
import { Engine } from "./engine";
import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SourceFiles } from "./SourceFile/SourceFiles";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { dialogBoxCreate } from "../utils/DialogBox";
import { yesNoBoxCreate, yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose } from "../utils/YesNoBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
// Returns promise
function writeRedPillLine(line) {
return new Promise(function (resolve, reject) {
var container = document.getElementById("red-pill-content");
var pElem = document.createElement("p");
container.appendChild(pElem);
var promise = writeRedPillLetter(pElem, line, 0);
promise.then(
function (res) {
resolve(res);
},
function (e) {
reject(e);
},
);
});
}
function writeRedPillLetter(pElem, line, i = 0) {
return new Promise(function (resolve, reject) {
setTimeoutRef(function () {
if (i >= line.length) {
var textToShow = line.substring(0, i);
pElem.innerHTML = "> " + textToShow;
return resolve(true);
}
var textToShow = line.substring(0, i);
pElem.innerHTML = "> " + textToShow + "<span class='typed-cursor'> &#9608; </span>";
var promise = writeRedPillLetter(pElem, line, i + 1);
promise.then(
function (res) {
resolve(res);
},
function (e) {
reject(e);
},
);
}, 30);
});
}
let redPillFlag = false;
function hackWorldDaemon(currentNodeNumber, flume = false, quick = false) {
// Clear Red Pill screen first
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
redPillFlag = true;
Engine.loadRedPillContent();
if (quick) {
return loadBitVerse(currentNodeNumber, flume, quick);
}
return writeRedPillLine("[ERROR] SEMPOOL INVALID")
.then(function () {
return writeRedPillLine("[ERROR] Segmentation Fault");
})
.then(function () {
return writeRedPillLine("[ERROR] SIGKILL RECVD");
})
.then(function () {
return writeRedPillLine("Dumping core...");
})
.then(function () {
return writeRedPillLine("0000 000016FA 174FEE40 29AC8239 384FEA88");
})
.then(function () {
return writeRedPillLine("0010 745F696E 2BBBE394 390E3940 248BEC23");
})
.then(function () {
return writeRedPillLine("0020 7124696B 0000FF69 74652E6F FFFF1111");
})
.then(function () {
return writeRedPillLine("----------------------------------------");
})
.then(function () {
return writeRedPillLine("Failsafe initiated...");
})
.then(function () {
return writeRedPillLine("Restarting BitNode-" + currentNodeNumber + "...");
})
.then(function () {
return writeRedPillLine("...........");
})
.then(function () {
return writeRedPillLine("...........");
})
.then(function () {
return writeRedPillLine("[ERROR] FAILED TO AUTOMATICALLY REBOOT BITNODE");
})
.then(function () {
return writeRedPillLine("..............................................");
})
.then(function () {
return writeRedPillLine("..............................................");
})
.then(function () {
return loadBitVerse(currentNodeNumber, flume);
})
.catch(function (e) {
console.error(e.toString());
});
}
function giveSourceFile(bitNodeNumber) {
var sourceFileKey = "SourceFile" + bitNodeNumber.toString();
var sourceFile = SourceFiles[sourceFileKey];
if (sourceFile == null) {
console.error(`Could not find source file for Bit node: ${bitNodeNumber}`);
return;
}
// Check if player already has this source file
var alreadyOwned = false;
var ownedSourceFile = null;
for (var i = 0; i < Player.sourceFiles.length; ++i) {
if (Player.sourceFiles[i].n === bitNodeNumber) {
alreadyOwned = true;
ownedSourceFile = Player.sourceFiles[i];
break;
}
}
if (alreadyOwned && ownedSourceFile) {
if (ownedSourceFile.lvl >= 3 && ownedSourceFile.n !== 12) {
dialogBoxCreate(
"The Source-File for the BitNode you just destroyed, " + sourceFile.name + ", " + "is already at max level!",
);
} else {
++ownedSourceFile.lvl;
dialogBoxCreate(
sourceFile.name +
" was upgraded to level " +
ownedSourceFile.lvl +
" for " +
"destroying its corresponding BitNode!",
);
}
} else {
var playerSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
Player.sourceFiles.push(playerSrcFile);
if (bitNodeNumber === 5 && Player.intelligence === 0) {
// Artificial Intelligence
Player.intelligence = 1;
}
dialogBoxCreate(
"You received a Source-File for destroying a Bit Node!<br><br>" + sourceFile.name + "<br><br>" + sourceFile.info,
);
}
}
// Keeps track of what Source-Files the player will have AFTER the current bitnode
// is destroyed. Updated every time loadBitVerse() is called
let nextSourceFileFlags = [];
function loadBitVerse(destroyedBitNodeNum, flume = false, quick = false) {
// Clear the screen
const container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
// Update NextSourceFileFlags
nextSourceFileFlags = SourceFileFlags.slice();
if (!flume) {
if (nextSourceFileFlags[destroyedBitNodeNum] < 3) ++nextSourceFileFlags[destroyedBitNodeNum];
}
// Create the Bit Verse
const bitVerseImage = document.createElement("pre");
const bitNodes = [];
for (let i = 1; i <= 12; ++i) {
bitNodes.push(createBitNode(i));
}
bitVerseImage.innerHTML =
" O <br>" +
" | O O | O O | <br>" +
" O | | / __| \\ | | O <br>" +
" O | O | | O / | O | | O | O <br>" +
" | | | | |_/ |/ | \\_ \\_| | | | | <br>" +
" O | | | O | | O__/ | / \\__ | | O | | | O <br>" +
" | | | | | | | / /| O / \\| | | | | | | <br>" +
"O | | | \\| | O / _/ | / O | |/ | | | O<br>" +
"| | | |O / | | O / | O O | | \\ O| | | |<br>" +
"| | |/ \\/ / __| | |/ \\ | \\ | |__ \\ \\/ \\| | |<br>" +
" \\| O | |_/ |\\| \\ O \\__| \\_| | O |/ <br>" +
" | | |_/ | | \\| / | \\_| | | <br>" +
" \\| / \\| | / / \\ |/ <br>" +
" | " +
bitNodes[9] +
" | | / | " +
bitNodes[10] +
" | <br>" +
" " +
bitNodes[8] +
" | | | | | | | " +
bitNodes[11] +
" <br>" +
" | | | / / \\ \\ | | | <br>" +
" \\| | / " +
bitNodes[6] +
" / \\ " +
bitNodes[7] +
" \\ | |/ <br>" +
" \\ | / / | | \\ \\ | / <br>" +
" \\ \\JUMP " +
bitNodes[4] +
"3R | | | | | | R3" +
bitNodes[5] +
" PMUJ/ / <br>" +
" \\|| | | | | | | | | ||/ <br>" +
" \\| \\_ | | | | | | _/ |/ <br>" +
" \\ \\| / \\ / \\ |/ / <br>" +
" " +
bitNodes[0] +
" |/ " +
bitNodes[1] +
" | | " +
bitNodes[2] +
" \\| " +
bitNodes[3] +
" <br>" +
" | | | | | | | | <br>" +
" \\JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ <br><br><br><br>";
container.appendChild(bitVerseImage);
// BitNode event listeners
for (let i = 1; i <= 12; ++i) {
(function (i) {
const elemId = "bitnode-" + i.toString();
const elem = clearEventListeners(elemId);
if (elem == null) {
return;
}
if (i >= 1 && i <= 12) {
elem.addEventListener("click", function () {
const bitNodeKey = "BitNode" + i;
const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) {
console.error(`Could not find BitNode object for number: ${i}`);
return;
}
const maxSourceFileLevel = i === 12 ? "∞" : "3";
const popupBoxText =
`BitNode-${i}: ${bitNode.name}<br>` +
`Source-File Level: ${nextSourceFileFlags[i]} / ${maxSourceFileLevel}<br><br>` +
`${bitNode.info}`;
yesNoBoxCreate(popupBoxText);
createBitNodeYesNoEventListener(i, destroyedBitNodeNum, flume);
});
} else {
elem.addEventListener("click", function () {
dialogBoxCreate("Not yet implemented! Coming soon!");
});
}
})(i); // Immediate invocation closure
}
if (quick) {
return Promise.resolve(true);
}
// Create lore text
return writeRedPillLine(
"Many decades ago, a humanoid extraterrestial species which we call the Enders descended on the Earth...violently",
)
.then(function () {
return writeRedPillLine(
"Our species fought back, but it was futile. The Enders had technology far beyond our own...",
);
})
.then(function () {
return writeRedPillLine("Instead of killing every last one of us, the human race was enslaved...");
})
.then(function () {
return writeRedPillLine("We were shackled in a digital world, chained into a prison for our minds...");
})
.then(function () {
return writeRedPillLine(
"Using their advanced technology, the Enders created complex simulations of a virtual reality...",
);
})
.then(function () {
return writeRedPillLine("Simulations designed to keep us content...ignorant of the truth.");
})
.then(function () {
return writeRedPillLine("Simulations used to trap and suppress our consciousness, to keep us under control...");
})
.then(function () {
return writeRedPillLine(
"Why did they do this? Why didn't they just end our entire race? We don't know, not yet.",
);
})
.then(function () {
return writeRedPillLine(
"Humanity's only hope is to destroy these simulations, destroy the only realities we've ever known...",
);
})
.then(function () {
return writeRedPillLine("Only then can we begin to fight back...");
})
.then(function () {
return writeRedPillLine(
"By hacking the daemon that generated your reality, you've just destroyed one simulation, called a BitNode...",
);
})
.then(function () {
return writeRedPillLine("But there is still a long way to go...");
})
.then(function () {
return writeRedPillLine(
"The technology the Enders used to enslave the human race wasn't just a single complex simulation...",
);
})
.then(function () {
return writeRedPillLine("There are tens if not hundreds of BitNodes out there...");
})
.then(function () {
return writeRedPillLine("Each with their own simulations of a reality...");
})
.then(function () {
return writeRedPillLine("Each creating their own universes...a universe of universes");
})
.then(function () {
return writeRedPillLine("And all of which must be destroyed...");
})
.then(function () {
return writeRedPillLine(".......................................");
})
.then(function () {
return writeRedPillLine("Welcome to the Bitverse...");
})
.then(function () {
return writeRedPillLine(" ");
})
.then(function () {
return writeRedPillLine("(Enter a new BitNode using the image above)");
})
.then(function () {
return Promise.resolve(true);
})
.catch(function (e) {
console.error(e.toString());
});
}
// Returns string with DOM element for Bit Node
function createBitNode(n) {
const bitNodeStr = "BitNode" + n.toString();
const bitNode = BitNodes[bitNodeStr];
if (bitNode == null) {
return "O";
}
const level = nextSourceFileFlags[n];
let cssClass;
if (n === 12 && level >= 2) {
// Repeating BitNode
cssClass = "level-2";
} else {
cssClass = `level-${level}`;
}
return (
`<a class='bitnode ${cssClass} tooltip' id='bitnode-${bitNode.number}'><strong>O</strong>` +
"<span class='tooltiptext'>" +
`<strong>BitNode-${bitNode.number.toString()}<br>${bitNode.name}</strong><br>` +
`${bitNode.desc}<br>` +
"</span></a>"
);
}
function createBitNodeYesNoEventListener(newBitNode, destroyedBitNode, flume = false) {
const yesBtn = yesNoBoxGetYesButton();
yesBtn.innerHTML = "Enter BitNode-" + newBitNode;
yesBtn.addEventListener("click", function () {
if (!flume) {
giveSourceFile(destroyedBitNode);
} else {
if (SourceFileFlags[5] === 0 && newBitNode !== 5) {
Player.intelligence = 0;
Player.intelligence_exp = 0;
}
}
if (newBitNode === 5 && Player.intelligence === 0) {
Player.intelligence = 1;
}
redPillFlag = false;
const container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
// Set new Bit Node
Player.bitNodeN = newBitNode;
// Reenable terminal
$("#hack-progress-bar").attr("id", "old-hack-progress-bar");
$("#hack-progress").attr("id", "old-hack-progress");
document.getElementById("terminal-input-td").innerHTML =
'$ <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
$("input[class=terminal-input]").prop("disabled", false);
prestigeSourceFile(flume);
yesNoBoxClose();
});
const noBtn = yesNoBoxGetNoButton();
noBtn.innerHTML = "Back";
noBtn.addEventListener("click", function () {
yesNoBoxClose();
});
}
export { redPillFlag, hackWorldDaemon };

104
src/RedPill.jsx Normal file

@ -0,0 +1,104 @@
/**
* Implementation for what happens when you destroy a BitNode
*/
import { Engine } from "./engine";
import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SourceFiles } from "./SourceFile/SourceFiles";
import { dialogBoxCreate } from "../utils/DialogBox";
import { BitverseRoot } from "./BitNode/ui/BitverseRoot";
import React from "react";
import ReactDOM from "react-dom";
let redPillFlag = false;
function hackWorldDaemon(currentNodeNumber, flume = false, quick = false) {
// Clear the screen
const container = document.getElementById("red-pill-content");
ReactDOM.unmountComponentAtNode(container);
Engine.loadRedPillContent();
ReactDOM.render(
<BitverseRoot destroyedBitNodeNum={currentNodeNumber} flume={flume} enter={enterBitNode} quick={quick} />,
container,
);
}
function giveSourceFile(bitNodeNumber) {
var sourceFileKey = "SourceFile" + bitNodeNumber.toString();
var sourceFile = SourceFiles[sourceFileKey];
if (sourceFile == null) {
console.error(`Could not find source file for Bit node: ${bitNodeNumber}`);
return;
}
// Check if player already has this source file
var alreadyOwned = false;
var ownedSourceFile = null;
for (var i = 0; i < Player.sourceFiles.length; ++i) {
if (Player.sourceFiles[i].n === bitNodeNumber) {
alreadyOwned = true;
ownedSourceFile = Player.sourceFiles[i];
break;
}
}
if (alreadyOwned && ownedSourceFile) {
if (ownedSourceFile.lvl >= 3 && ownedSourceFile.n !== 12) {
dialogBoxCreate(
"The Source-File for the BitNode you just destroyed, " + sourceFile.name + ", " + "is already at max level!",
);
} else {
++ownedSourceFile.lvl;
dialogBoxCreate(
sourceFile.name +
" was upgraded to level " +
ownedSourceFile.lvl +
" for " +
"destroying its corresponding BitNode!",
);
}
} else {
var playerSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
Player.sourceFiles.push(playerSrcFile);
if (bitNodeNumber === 5 && Player.intelligence === 0) {
// Artificial Intelligence
Player.intelligence = 1;
}
dialogBoxCreate(
"You received a Source-File for destroying a Bit Node!<br><br>" + sourceFile.name + "<br><br>" + sourceFile.info,
);
}
}
function enterBitNode(flume, destroyedBitNode, newBitNode) {
if (!flume) {
giveSourceFile(destroyedBitNode);
} else {
if (SourceFileFlags[5] === 0 && newBitNode !== 5) {
Player.intelligence = 0;
Player.intelligence_exp = 0;
}
}
if (newBitNode === 5 && Player.intelligence === 0) {
Player.intelligence = 1;
}
redPillFlag = false;
const container = document.getElementById("red-pill-content");
ReactDOM.unmountComponentAtNode(container);
// Set new Bit Node
Player.bitNodeN = newBitNode;
// Reenable terminal
$("#hack-progress-bar").attr("id", "old-hack-progress-bar");
$("#hack-progress").attr("id", "old-hack-progress");
document.getElementById("terminal-input-td").innerHTML =
'$ <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
$("input[class=terminal-input]").prop("disabled", false);
prestigeSourceFile(flume);
}
export { redPillFlag, hackWorldDaemon };

@ -3,8 +3,14 @@ import { IEngine } from "../../IEngine";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial"; import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers"; import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
import { ICorporation } from "../../Corporation/ICorporation"; import { Settings } from "../../Settings/Settings";
import { IGang } from "../../Gang/IGang"; import { redPillFlag } from "../../RedPill";
import { inMission } from "../../Missions";
import { cinematicTextFlag } from "../../CinematicText";
import { KEY } from "../../../utils/helpers/keyCodes";
import { FconfSettings } from "../../Fconf/FconfSettings";
import { Page, routing } from "../../ui/navigationTracking";
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
@ -16,13 +22,13 @@ export function SidebarRoot(props: IProps): React.ReactElement {
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
} }
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => { useEffect(() => {
const id = setInterval(rerender, 20); const id = setInterval(rerender, 20);
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const [activeTab, setActiveTab] = useState("");
const [hackingOpen, setHackingOpen] = useState(true); const [hackingOpen, setHackingOpen] = useState(true);
const [characterOpen, setCharacterOpen] = useState(true); const [characterOpen, setCharacterOpen] = useState(true);
const [worldOpen, setWorldOpen] = useState(true); const [worldOpen, setWorldOpen] = useState(true);
@ -42,36 +48,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const flashTutorial = ITutorial.currStep === iTutorialSteps.WorldDescription; 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 programCount = getAvailableCreatePrograms(props.player).length;
const canCreateProgram = const canCreateProgram =
programCount > 0 || programCount > 0 ||
@ -101,6 +77,182 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const canStockMarket = props.player.hasWseAccount; const canStockMarket = props.player.hasWseAccount;
const canBladeburner = !!(props.player.bladeburner as any); const canBladeburner = !!(props.player.bladeburner as any);
function clickTerminal(): void {
setActiveTab("Terminal");
props.engine.loadTerminalContent();
if (flashTerminal) iTutorialNextStep();
}
function clickCreateScripts(): void {
setActiveTab("CreateScripts");
props.engine.loadScriptEditorContent();
}
function clickStats(): void {
setActiveTab("Stats");
props.engine.loadCharacterContent();
if (flashStats) iTutorialNextStep();
}
function clickActiveScripts(): void {
setActiveTab("ActiveScripts");
props.engine.loadActiveScriptsContent();
if (flashActiveScripts) iTutorialNextStep();
}
function clickCreateProgram(): void {
setActiveTab("CreateProgram");
props.engine.loadCreateProgramContent();
}
function clickFactions(): void {
setActiveTab("Factions");
props.engine.loadFactionsContent();
}
function clickAugmentations(): void {
setActiveTab("Augmentations");
props.engine.loadAugmentationsContent();
}
function clickSleeves(): void {
setActiveTab("Sleeves");
props.engine.loadSleevesContent();
}
function clickHacknet(): void {
setActiveTab("Hacknet");
props.engine.loadHacknetNodesContent();
if (flashHacknet) iTutorialNextStep();
}
function clickCity(): void {
setActiveTab("City");
props.engine.loadLocationContent();
if (flashCity) iTutorialNextStep();
}
function clickTravel(): void {
setActiveTab("Travel");
props.engine.loadTravelContent();
}
function clickJob(): void {
setActiveTab("Job");
props.engine.loadJobContent();
}
function clickStockMarket(): void {
setActiveTab("StockMarket");
props.engine.loadStockMarketContent();
}
function clickBladeburner(): void {
setActiveTab("Bladeburner");
props.engine.loadBladeburnerContent();
}
function clickCorp(): void {
setActiveTab("Corp");
props.engine.loadCorporationContent();
}
function clickGang(): void {
setActiveTab("Gang");
props.engine.loadGangContent();
}
function clickTutorial(): void {
setActiveTab("Tutorial");
props.engine.loadTutorialContent();
if (flashTutorial) iTutorialNextStep();
}
function clickMilestones(): void {
setActiveTab("Milestones");
props.engine.loadMilestonesContent();
}
function clickDev(): void {
setActiveTab("Dev");
props.engine.loadDevMenuContent();
}
useEffect(() => {
// Shortcuts to navigate through the game
// Alt-t - Terminal
// Alt-c - Character
// Alt-e - Script editor
// Alt-s - Active scripts
// Alt-h - Hacknet Nodes
// Alt-w - City
// Alt-j - Job
// Alt-r - Travel Agency of current city
// Alt-p - Create program
// Alt-f - Factions
// Alt-a - Augmentations
// Alt-u - Tutorial
// Alt-o - Options
function handleShortcuts(this: Document, event: KeyboardEvent): any {
if (Settings.DisableHotkeys) return;
if (props.player.isWorking || redPillFlag || inMission || cinematicTextFlag) return;
if (event.keyCode == KEY.T && event.altKey) {
event.preventDefault();
clickTerminal();
} else if (event.keyCode === KEY.C && event.altKey) {
event.preventDefault();
clickStats();
} else if (event.keyCode === KEY.E && event.altKey) {
event.preventDefault();
clickCreateScripts();
} else if (event.keyCode === KEY.S && event.altKey) {
event.preventDefault();
clickActiveScripts();
} else if (event.keyCode === KEY.H && event.altKey) {
event.preventDefault();
clickHacknet();
} else if (event.keyCode === KEY.W && event.altKey) {
event.preventDefault();
clickCity();
} else if (event.keyCode === KEY.J && event.altKey) {
event.preventDefault();
clickJob();
} else if (event.keyCode === KEY.R && event.altKey) {
event.preventDefault();
clickTravel();
} else if (event.keyCode === KEY.P && event.altKey) {
event.preventDefault();
clickCreateProgram();
} else if (event.keyCode === KEY.F && event.altKey) {
// Overriden by Fconf
if (routing.isOn(Page.Terminal) && FconfSettings.ENABLE_BASH_HOTKEYS) {
return;
}
event.preventDefault();
clickFactions();
} else if (event.keyCode === KEY.A && event.altKey) {
event.preventDefault();
clickAugmentations();
} else if (event.keyCode === KEY.U && event.altKey) {
event.preventDefault();
clickTutorial();
} else if (event.keyCode === KEY.B && event.altKey) {
event.preventDefault();
clickBladeburner();
} else if (event.keyCode === KEY.G && event.altKey) {
event.preventDefault();
clickGang();
}
// if (event.keyCode === KEY.O && event.altKey) {
// event.preventDefault();
// gameOptionsBoxOpen();
// }
}
document.addEventListener("keypress", handleShortcuts);
return () => document.removeEventListener("keypress", handleShortcuts);
}, []);
return ( return (
<ul id="mainmenu" className="mainmenu noscrollbar noselect"> <ul id="mainmenu" className="mainmenu noscrollbar noselect">
{/* Hacking dropdown */} {/* Hacking dropdown */}
@ -119,22 +271,33 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</li> </li>
{hackingOpen && ( {hackingOpen && (
<> <>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashTerminal ? "flashing-button" : ""} onClick={clickTerminal}> <button
className={(flashTerminal ? "flashing-button" : "") + activeTab === "Terminal" ? " active" : ""}
onClick={clickTerminal}
>
Terminal Terminal
</button> </button>
</li> </li>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadScriptEditorContent()}>Create Script</button> <button className={activeTab === "CreateScripts" ? " active" : ""} onClick={clickCreateScripts}>
Create Script
</button>
</li> </li>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashActiveScripts ? "flashing-button" : ""} onClick={clickActiveScripts}> <button
className={(flashActiveScripts ? "flashing-button" : "") + activeTab === "ActiveScripts" ? " active" : ""}
onClick={clickActiveScripts}
>
Active Scripts Active Scripts
</button> </button>
</li> </li>
{canCreateProgram && ( {canCreateProgram && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className="notification" onClick={() => props.engine.loadCreateProgramContent()}> <button
className={"notification" + (activeTab === "CreateProgram" ? " active" : "")}
onClick={clickCreateProgram}
>
Create Program Create Program
{programCount > 0 && <span className="badge">{programCount}</span>} {programCount > 0 && <span className="badge">{programCount}</span>}
</button> </button>
@ -155,14 +318,17 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</li> </li>
{characterOpen && ( {characterOpen && (
<> <>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashStats ? "flashing-button" : ""} onClick={clickStats}> <button
className={(flashStats ? "flashing-button" : "") + activeTab === "Stats" ? " active" : ""}
onClick={clickStats}
>
Stats Stats
</button> </button>
</li> </li>
{canOpenFactions && ( {canOpenFactions && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className="notification" onClick={() => props.engine.loadFactionsContent()}> <button className={"notification" + (activeTab === "Factions" ? " active" : "")} onClick={clickFactions}>
Factions Factions
{props.player.factionInvitations.length > 0 && ( {props.player.factionInvitations.length > 0 && (
<span className="badge">{props.player.factionInvitations.length}</span> <span className="badge">{props.player.factionInvitations.length}</span>
@ -171,8 +337,11 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</li> </li>
)} )}
{canOpenAugmentations && ( {canOpenAugmentations && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className="notification" onClick={() => props.engine.loadAugmentationsContent()}> <button
className={"notification" + (activeTab === "Augmentations" ? " active" : "")}
onClick={clickAugmentations}
>
Augmentations Augmentations
{props.player.queuedAugmentations.length > 0 && ( {props.player.queuedAugmentations.length > 0 && (
<span className="badge">{props.player.queuedAugmentations.length}</span> <span className="badge">{props.player.queuedAugmentations.length}</span>
@ -180,14 +349,19 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</button> </button>
</li> </li>
)} )}
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashHacknet ? "flashing-button" : ""} onClick={clickHacknet}> <button
className={(flashHacknet ? "flashing-button" : "") + activeTab === "Hacknet" ? " active" : ""}
onClick={clickHacknet}
>
Hacknet Hacknet
</button> </button>
</li> </li>
{canOpenSleeves && ( {canOpenSleeves && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadSleevesContent()}>Sleeves</button> <button className={activeTab === "Sleeves" ? " active" : ""} onClick={clickSleeves}>
Sleeves
</button>
</li> </li>
)} )}
</> </>
@ -205,37 +379,52 @@ export function SidebarRoot(props: IProps): React.ReactElement {
{worldOpen && ( {worldOpen && (
<> <>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashCity ? "flashing-button" : ""} onClick={clickCity}> <button
className={(flashCity ? "flashing-button" : "") + activeTab === "City" ? " active" : ""}
onClick={clickCity}
>
City City
</button> </button>
</li> </li>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadTravelContent()}>Travel</button> <button className={activeTab === "Travel" ? " active" : ""} onClick={clickTravel}>
Travel
</button>
</li> </li>
{canJob && ( {canJob && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadJobContent()}>Job</button> <button className={activeTab === "Job" ? " active" : ""} onClick={clickJob}>
Job
</button>
</li> </li>
)} )}
{canStockMarket && ( {canStockMarket && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadStockMarketContent()}>Stock Market</button> <button className={activeTab === "StockMarket" ? " active" : ""} onClick={clickStockMarket}>
Stock Market
</button>
</li> </li>
)} )}
{canBladeburner && ( {canBladeburner && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadBladeburnerContent()}>Bladeburner</button> <button className={activeTab === "Bladeburner" ? " active" : ""} onClick={clickBladeburner}>
Bladeburner
</button>
</li> </li>
)} )}
{canCorporation && ( {canCorporation && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadCorporationContent()}>Corp</button> <button className={activeTab === "Corp" ? " active" : ""} onClick={clickCorp}>
Corp
</button>
</li> </li>
)} )}
{canGang && ( {canGang && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadGangContent()}>Gang</button> <button className={activeTab === "Gang" ? " active" : ""} onClick={clickGang}>
Gang
</button>
</li> </li>
)} )}
</> </>
@ -251,11 +440,16 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</li> </li>
{helpOpen && ( {helpOpen && (
<> <>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadMilestonesContent()}>Milestones</button> <button className={activeTab === "Milestones" ? " active" : ""} onClick={clickMilestones}>
Milestones
</button>
</li> </li>
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button className={flashTutorial ? "flashing-button" : ""} onClick={clickTutorial}> <button
className={(flashTutorial ? "flashing-button" : "") + activeTab === "Tutorial" ? " active" : ""}
onClick={clickTutorial}
>
Tutorial Tutorial
</button> </button>
</li> </li>
@ -263,8 +457,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
<button>Options</button> <button>Options</button>
</li>*/} </li>*/}
{process.env.NODE_ENV === "development" && ( {process.env.NODE_ENV === "development" && (
<li className="mainmenu-accordion-panel"> <li className={`mainmenu-accordion-panel`}>
<button onClick={() => props.engine.loadDevMenuContent()}>Dev</button> <button className={activeTab === "Dev" ? " active" : ""} onClick={clickDev}>
Dev
</button>
</li> </li>
)} )}
</> </>

@ -0,0 +1,34 @@
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
text: string;
placeText: string;
place: (price: number) => void;
popupId: string;
}
export function PlaceOrderPopup(props: IProps): React.ReactElement {
const [price, setPrice] = useState<number | null>(null);
function onClick(): void {
if (price === null) return;
if (isNaN(price)) return;
props.place(price);
removePopup(props.popupId);
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setPrice(null);
else setPrice(parseFloat(event.target.value));
}
return (
<>
<p>{props.text}</p>
<input autoFocus={true} className="text-input" type="number" onChange={onChange} placeholder="price" />
<button className="std-button" onClick={onClick}>
{props.placeText}
</button>
</>
);
}

@ -7,6 +7,7 @@ import { StockTickerHeaderText } from "./StockTickerHeaderText";
import { StockTickerOrderList } from "./StockTickerOrderList"; import { StockTickerOrderList } from "./StockTickerOrderList";
import { StockTickerPositionText } from "./StockTickerPositionText"; import { StockTickerPositionText } from "./StockTickerPositionText";
import { StockTickerTxButton } from "./StockTickerTxButton"; import { StockTickerTxButton } from "./StockTickerTxButton";
import { PlaceOrderPopup } from "./PlaceOrderPopup";
import { Order } from "../Order"; import { Order } from "../Order";
import { Stock } from "../Stock"; import { Stock } from "../Stock";
@ -19,15 +20,9 @@ import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Accordion } from "../../ui/React/Accordion"; import { Accordion } from "../../ui/React/Accordion";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { createPopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import {
yesNoTxtInpBoxClose,
yesNoTxtInpBoxCreate,
yesNoTxtInpBoxGetInput,
yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxGetYesButton,
} from "../../../utils/YesNoBox";
enum SelectorOrderType { enum SelectorOrderType {
Market = "Market Order", Market = "Market Order",
@ -85,30 +80,6 @@ export class StockTicker extends React.Component<IProps, IState> {
this.handleSellAllButtonClick = this.handleSellAllButtonClick.bind(this); this.handleSellAllButtonClick = this.handleSellAllButtonClick.bind(this);
} }
createPlaceOrderPopupBox(yesTxt: string, popupTxt: string, yesBtnCb: (price: number) => void): void {
const yesBtn = yesNoTxtInpBoxGetYesButton();
const noBtn = yesNoTxtInpBoxGetNoButton();
yesBtn.innerText = yesTxt;
yesBtn.addEventListener("click", () => {
const price = parseFloat(yesNoTxtInpBoxGetInput());
if (isNaN(price)) {
dialogBoxCreate(`Invalid input for price: ${yesNoTxtInpBoxGetInput()}`);
return false;
}
yesBtnCb(price);
yesNoTxtInpBoxClose();
});
noBtn.innerText = "Cancel Order";
noBtn.addEventListener("click", () => {
yesNoTxtInpBoxClose();
});
yesNoTxtInpBoxCreate(popupTxt);
}
getBuyTransactionCostContent(): JSX.Element | null { getBuyTransactionCostContent(): JSX.Element | null {
const stock = this.props.stock; const stock = this.props.stock;
const qty: number = this.getQuantity(); const qty: number = this.getQuantity();
@ -182,23 +153,23 @@ export class StockTicker extends React.Component<IProps, IState> {
break; break;
} }
case SelectorOrderType.Limit: { case SelectorOrderType.Limit: {
this.createPlaceOrderPopupBox( const popupId = `place-order-popup`;
"Place Buy Limit Order", createPopup(popupId, PlaceOrderPopup, {
"Enter the price for your Limit Order", text: "Enter the price for your Limit Order",
(price: number) => { placeText: "Place Buy Limit Order",
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitBuy, this.state.position); place: (price: number) => this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitBuy, this.state.position),
}, popupId: popupId,
); });
break; break;
} }
case SelectorOrderType.Stop: { case SelectorOrderType.Stop: {
this.createPlaceOrderPopupBox( const popupId = `place-order-popup`;
"Place Buy Stop Order", createPopup(popupId, PlaceOrderPopup, {
"Enter the price for your Stop Order", text: "Enter the price for your Stop Order",
(price: number) => { placeText: "Place Buy Stop Order",
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopBuy, this.state.position); place: (price: number) => this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopBuy, this.state.position),
}, popupId: popupId,
); });
break; break;
} }
default: default:
@ -304,23 +275,23 @@ export class StockTicker extends React.Component<IProps, IState> {
break; break;
} }
case SelectorOrderType.Limit: { case SelectorOrderType.Limit: {
this.createPlaceOrderPopupBox( const popupId = `place-order-popup`;
"Place Sell Limit Order", createPopup(popupId, PlaceOrderPopup, {
"Enter the price for your Limit Order", text: "Enter the price for your Limit Order",
(price: number) => { placeText: "Place Sell Limit Order",
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitSell, this.state.position); place: (price: number) => this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitSell, this.state.position),
}, popupId: popupId,
); });
break; break;
} }
case SelectorOrderType.Stop: { case SelectorOrderType.Stop: {
this.createPlaceOrderPopupBox( const popupId = `place-order-popup`;
"Place Sell Stop Order", createPopup(popupId, PlaceOrderPopup, {
"Enter the price for your Stop Order", text: "Enter the price for your Stop Order",
(price: number) => { placeText: "Place Sell Stop Order",
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopSell, this.state.position); place: (price: number) => this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopSell, this.state.position),
}, popupId: popupId,
); });
break; break;
} }
default: default:

@ -53,10 +53,11 @@ import { KEY } from "../utils/helpers/keyCodes";
import { arrayToString } from "../utils/helpers/arrayToString"; import { arrayToString } from "../utils/helpers/arrayToString";
import { getTimestamp } from "../utils/helpers/getTimestamp"; import { getTimestamp } from "../utils/helpers/getTimestamp";
import { logBoxCreate } from "../utils/LogBox"; import { logBoxCreate } from "../utils/LogBox";
import { yesNoBoxCreate, yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoBoxClose } from "../utils/YesNoBox";
import { post, postElement, postContent, postError, hackProgressBarPost, hackProgressPost } from "./ui/postToTerminal"; import { post, postElement, postContent, postError, hackProgressBarPost, hackProgressPost } from "./ui/postToTerminal";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Money } from "./ui/React/Money"; import { Money } from "./ui/React/Money";
import { createPopup } from "./ui/React/createPopup";
import { BitFlumePopup } from "./BitNode/ui/BitFlumePopup";
import autosize from "autosize"; import autosize from "autosize";
import * as JSZip from "jszip"; import * as JSZip from "jszip";
@ -2374,22 +2375,11 @@ let Terminal = {
post("-- Daedalus --"); post("-- Daedalus --");
}; };
programHandlers[Programs.BitFlume.name] = () => { programHandlers[Programs.BitFlume.name] = () => {
const yesBtn = yesNoBoxGetYesButton(); const popupId = "bitflume-popup";
const noBtn = yesNoBoxGetNoButton(); createPopup(popupId, BitFlumePopup, {
yesBtn.innerHTML = "Travel to BitNode Nexus"; player: Player,
noBtn.innerHTML = "Cancel"; popupId: popupId,
yesBtn.addEventListener("click", function () {
hackWorldDaemon(Player.bitNodeN, true);
return yesNoBoxClose();
}); });
noBtn.addEventListener("click", function () {
return yesNoBoxClose();
});
yesNoBoxCreate(
"WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.<br><br>" +
"Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode " +
"and select a new one.",
);
}; };
if (!programHandlers.hasOwnProperty(programName)) { if (!programHandlers.hasOwnProperty(programName)) {

@ -5,18 +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 { import { initAugmentations, installAugmentations } from "./Augmentation/AugmentationHelpers";
initAugmentations,
displayAugmentationsContent,
installAugmentations,
} from "./Augmentation/AugmentationHelpers";
import { onExport } from "./ExportBonus"; import { onExport } from "./ExportBonus";
import { AugmentationsRoot } from "./Augmentation/ui/Root"; 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";
import { CharacterOverviewComponent } from "./ui/React/CharacterOverview"; import { CharacterOverview } from "./ui/React/CharacterOverview";
import { cinematicTextFlag } from "./CinematicText";
import { generateRandomContract } from "./CodingContractGenerator"; import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies"; import { initCompanies } from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation"; import { Corporation } from "./Corporation/Corporation";
@ -37,7 +32,6 @@ import {
getFactionSecurityWorkRepGain, getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain, getFactionFieldWorkRepGain,
} from "./PersonObjects/formulas/reputation"; } from "./PersonObjects/formulas/reputation";
import { FconfSettings } from "./Fconf/FconfSettings";
import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers"; import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import { HacknetRoot } from "./Hacknet/ui/HacknetRoot"; import { HacknetRoot } from "./Hacknet/ui/HacknetRoot";
import { iTutorialStart } from "./InteractiveTutorial"; import { iTutorialStart } from "./InteractiveTutorial";
@ -50,7 +44,6 @@ import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorke
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige"; import { prestigeAugmentation } from "./Prestige";
import { ProgramsRoot } from "./Programs/ui/ProgramsRoot"; import { ProgramsRoot } from "./Programs/ui/ProgramsRoot";
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";
import { initForeignServers, AllServers } from "./Server/AllServers"; import { initForeignServers, AllServers } from "./Server/AllServers";
@ -72,8 +65,7 @@ import { Hashes } from "./ui/React/Hashes";
import { Reputation } from "./ui/React/Reputation"; import { Reputation } from "./ui/React/Reputation";
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root"; import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers"; import { MainMenuLinks } from "./ui/MainMenu/Links";
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
import { FileDiagnosticPopup } from "./Diagnostic/FileDiagnosticPopup"; import { FileDiagnosticPopup } from "./Diagnostic/FileDiagnosticPopup";
import { createPopup } from "./ui/React/createPopup"; import { createPopup } from "./ui/React/createPopup";
@ -82,90 +74,12 @@ import { dialogBoxCreate } from "../utils/DialogBox";
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions"; import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen"; import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
import { KEY } from "../utils/helpers/keyCodes";
import "./Exploits/tampering"; import "./Exploits/tampering";
import "./Exploits/unclickable"; import "./Exploits/unclickable";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
/**
* Shortcuts to navigate through the game
* Alt-t - Terminal
* Alt-c - Character
* Alt-e - Script editor
* Alt-s - Active scripts
* Alt-h - Hacknet Nodes
* Alt-w - City
* Alt-j - Job
* Alt-r - Travel Agency of current city
* Alt-p - Create program
* Alt-f - Factions
* Alt-a - Augmentations
* Alt-u - Tutorial
* Alt-o - Options
*/
$(document).keydown(function (e) {
if (Settings.DisableHotkeys === true) {
return;
}
if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) {
if (e.keyCode == KEY.T && e.altKey) {
e.preventDefault();
Engine.loadTerminalContent();
} else if (e.keyCode === KEY.C && e.altKey) {
e.preventDefault();
Engine.loadCharacterContent();
} else if (e.keyCode === KEY.E && e.altKey) {
e.preventDefault();
Engine.loadScriptEditorContent();
} else if (e.keyCode === KEY.S && e.altKey) {
e.preventDefault();
Engine.loadActiveScriptsContent();
} else if (e.keyCode === KEY.H && e.altKey) {
e.preventDefault();
Engine.loadHacknetNodesContent();
} else if (e.keyCode === KEY.W && e.altKey) {
e.preventDefault();
Engine.loadLocationContent();
} else if (e.keyCode === KEY.J && e.altKey) {
e.preventDefault();
Engine.loadJobContent();
} else if (e.keyCode === KEY.R && e.altKey) {
e.preventDefault();
Engine.loadTravelContent();
} else if (e.keyCode === KEY.P && e.altKey) {
e.preventDefault();
Engine.loadCreateProgramContent();
} else if (e.keyCode === KEY.F && e.altKey) {
// Overriden by Fconf
if (routing.isOn(Page.Terminal) && FconfSettings.ENABLE_BASH_HOTKEYS) {
return;
}
e.preventDefault();
Engine.loadFactionsContent();
} else if (e.keyCode === KEY.A && e.altKey) {
e.preventDefault();
Engine.loadAugmentationsContent();
} else if (e.keyCode === KEY.U && e.altKey) {
e.preventDefault();
Engine.loadTutorialContent();
} else if (e.keyCode === KEY.B && e.altKey) {
e.preventDefault();
Engine.loadBladeburnerContent();
} else if (e.keyCode === KEY.G && e.altKey) {
e.preventDefault();
Engine.loadGangContent();
}
}
if (e.keyCode === KEY.O && e.altKey) {
e.preventDefault();
gameOptionsBoxOpen();
}
});
const Engine = { const Engine = {
// Clickable objects // Clickable objects
Clickables: { Clickables: {
@ -457,7 +371,7 @@ const Engine = {
}, },
displayCharacterOverviewInfo: function () { displayCharacterOverviewInfo: function () {
ReactDOM.render(<CharacterOverviewComponent />, document.getElementById("character-overview-text")); ReactDOM.render(<CharacterOverview player={Player} />, document.getElementById("character-overview-text"));
const save = document.getElementById("character-overview-save-button"); const save = document.getElementById("character-overview-save-button");
const flashClass = "flashing-button"; const flashClass = "flashing-button";
@ -635,11 +549,6 @@ const Engine = {
} }
} }
if (Engine.Counters.updateSkillLevelsCounter <= 0) {
Player.updateSkillLevels();
Engine.Counters.updateSkillLevelsCounter = 10;
}
if (Engine.Counters.updateDisplays <= 0) { if (Engine.Counters.updateDisplays <= 0) {
Engine.displayCharacterOverviewInfo(); Engine.displayCharacterOverviewInfo();
Engine.Counters.updateDisplays = 3; Engine.Counters.updateDisplays = 3;

@ -87,21 +87,6 @@
</div> </div>
</div> </div>
<!-- Faction Invitation Pop-up Box -->
<div id="faction-invitation-box-container" class="popup-box-container">
<div id="faction-invitation-box-content" class="popup-box-content">
<p id="faction-invitation-box-text"></p>
<p id="faction-invitation-box-message"></p>
<p id="faction-invitation-box-warning">
Would you like to join? <br />
<br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button id="faction-invitation-box-yes" class="popup-box-button">Join!</button>
<button id="faction-invitation-box-no" class="popup-box-button">Decide later</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>

@ -20,7 +20,6 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
} }
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => { useEffect(() => {
const id = setInterval(rerender, 20); const id = setInterval(rerender, 20);

@ -22,7 +22,6 @@ export function CharacterInfo(props: IProps): React.ReactElement {
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
} }
const [divisionName, setDivisionName] = useState("Overview");
useEffect(() => { useEffect(() => {
const id = setInterval(rerender, 20); const id = setInterval(rerender, 20);
@ -230,13 +229,8 @@ export function CharacterInfo(props: IProps): React.ReactElement {
</span> </span>
<br /> <br />
<br /> <br />
<div style={{ width: "60%", fontSize: "13px", marginLeft: "4%" }}> <div style={{ width: "60%", fontSize: "13px", marginLeft: "2%" }}>
{BitNodes[index].info.split("<br>").map((t, i) => ( <span style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</span>
<div key={i}>
<span style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{t}</span>
<br />
</div>
))}
</div> </div>
</> </>
); );

@ -1,109 +0,0 @@
// Root React Component for the Corporation UI
import React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Reputation } from "./Reputation";
const Component = React.Component;
export class CharacterOverviewComponent extends Component {
render() {
const intelligence = (
<tr id="character-int-wrapper">
<td className="character-int-cell">Int:&nbsp;</td>
<td id="character-int-text" className="character-int-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.intelligence)}
</td>
</tr>
);
/*const work = (
<div>
<p>Work progress:</p>
<p>+{Reputation(Player.workRepGained)} rep</p>
<button onClick={() => Player.startFocusing()} id="character-overview-options-button" className="character-overview-btn">Focus</button>
</div>
);*/
const work = (
<>
<tr className="character-divider">
<td colSpan="2">Work progress:</td>
</tr>
<tr>
<td colSpan="2">+{Reputation(Player.workRepGained)} rep</td>
</tr>
<tr>
<td colSpan="2">
<button
onClick={() => Player.startFocusing()}
id="character-overview-options-button"
className="character-overview-btn"
>
Focus
</button>
</td>
</tr>
</>
);
return (
<>
<table>
<tbody>
<tr id="character-hp-wrapper">
<td className="character-hp-cell">HP:</td>
<td id="character-hp-text" className="character-hp-cell character-stat-cell">
{numeralWrapper.formatHp(Player.hp) + " / " + numeralWrapper.formatHp(Player.max_hp)}
</td>
</tr>
<tr id="character-money-wrapper">
<td className="character-money-cell">Money:&nbsp;</td>
<td id="character-money-text" className="character-money-cell character-stat-cell">
{numeralWrapper.formatMoney(Player.money.toNumber())}
</td>
</tr>
<tr id="character-hack-wrapper">
<td className="character-hack-cell">Hack:&nbsp;</td>
<td id="character-hack-text" className="character-hack-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.hacking_skill)}
</td>
</tr>
<tr id="character-str-wrapper" className="character-divider">
<td className="character-combat-cell">Str:&nbsp;</td>
<td id="character-str-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.strength)}
</td>
</tr>
<tr id="character-def-wrapper">
<td className="character-combat-cell">Def:&nbsp;</td>
<td id="character-def-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.defense)}
</td>
</tr>
<tr id="character-dex-wrapper">
<td className="character-combat-cell">Dex:&nbsp;</td>
<td id="character-dex-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.dexterity)}
</td>
</tr>
<tr id="character-agi-wrapper">
<td className="character-combat-cell">Agi:&nbsp;</td>
<td id="character-agi-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.agility)}
</td>
</tr>
<tr id="character-cha-wrapper" className="character-divider">
<td className="character-cha-cell">Cha:&nbsp;</td>
<td id="character-cha-text" className="character-cha-cell character-stat-cell">
{numeralWrapper.formatSkill(Player.charisma)}
</td>
</tr>
{Player.intelligence >= 1 && intelligence}
{Player.isWorking && !Player.focus && work}
</tbody>
</table>
</>
);
}
}

@ -0,0 +1,102 @@
// Root React Component for the Corporation UI
import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Reputation } from "./Reputation";
interface IProps {
player: IPlayer;
}
export function CharacterOverview(props: IProps): React.ReactElement {
const intelligence = (
<tr id="character-int-wrapper">
<td className="character-int-cell">Int:&nbsp;</td>
<td id="character-int-text" className="character-int-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.intelligence)}
</td>
</tr>
);
const work = (
<>
<tr className="character-divider">
<td colSpan={2}>Work progress:</td>
</tr>
<tr>
<td colSpan={2}>+{Reputation(props.player.workRepGained)} rep</td>
</tr>
<tr>
<td colSpan={2}>
<button
onClick={() => props.player.startFocusing()}
id="character-overview-options-button"
className="character-overview-btn"
>
Focus
</button>
</td>
</tr>
</>
);
return (
<>
<table>
<tbody>
<tr id="character-hp-wrapper">
<td className="character-hp-cell">HP:</td>
<td id="character-hp-text" className="character-hp-cell character-stat-cell">
{numeralWrapper.formatHp(props.player.hp) + " / " + numeralWrapper.formatHp(props.player.max_hp)}
</td>
</tr>
<tr id="character-money-wrapper">
<td className="character-money-cell">Money:&nbsp;</td>
<td id="character-money-text" className="character-money-cell character-stat-cell">
{numeralWrapper.formatMoney(props.player.money.toNumber())}
</td>
</tr>
<tr id="character-hack-wrapper">
<td className="character-hack-cell">Hack:&nbsp;</td>
<td id="character-hack-text" className="character-hack-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.hacking_skill)}
</td>
</tr>
<tr id="character-str-wrapper" className="character-divider">
<td className="character-combat-cell">Str:&nbsp;</td>
<td id="character-str-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.strength)}
</td>
</tr>
<tr id="character-def-wrapper">
<td className="character-combat-cell">Def:&nbsp;</td>
<td id="character-def-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.defense)}
</td>
</tr>
<tr id="character-dex-wrapper">
<td className="character-combat-cell">Dex:&nbsp;</td>
<td id="character-dex-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.dexterity)}
</td>
</tr>
<tr id="character-agi-wrapper">
<td className="character-combat-cell">Agi:&nbsp;</td>
<td id="character-agi-text" className="character-combat-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.agility)}
</td>
</tr>
<tr id="character-cha-wrapper" className="character-divider">
<td className="character-cha-cell">Cha:&nbsp;</td>
<td id="character-cha-text" className="character-cha-cell character-stat-cell">
{numeralWrapper.formatSkill(props.player.charisma)}
</td>
</tr>
{props.player.intelligence >= 1 && intelligence}
{props.player.isWorking && !props.player.focus && work}
</tbody>
</table>
</>
);
}

@ -0,0 +1,43 @@
import React, { useState, useEffect } from "react";
interface IProps {
text: string;
onDone?: () => void;
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function CinematicLine(props: IProps): React.ReactElement {
const [length, setLength] = useState(0);
const [done, setDone] = useState(false);
function advance(): void {
const newLength = length + 1;
setLength(newLength);
setDone(newLength >= props.text.length);
}
useEffect(() => {
if (done && props.onDone) {
props.onDone();
return;
}
let cancel = false;
(async () => {
await sleep(10).then(() => !cancel && advance());
})();
return () => {
cancel = true;
};
});
return (
<>
<pre>
{props.text.slice(0, length)}
{!done && <span>&#9608;</span>}
</pre>
</>
);
}

@ -0,0 +1,27 @@
import React, { useState } from "react";
import { CinematicLine } from "./CinematicLine";
interface IProps {
lines: string[];
onDone?: () => void;
}
export function CinematicText(props: IProps): React.ReactElement {
const [i, setI] = useState(0);
function advance(): void {
const newI = i + 1;
setI(newI);
if (newI >= props.lines.length && props.onDone) props.onDone();
}
return (
<>
{props.lines.slice(0, i).map((line, i) => (
<pre key={i}>{line}</pre>
))}
{props.lines.length > i && <CinematicLine key={i} text={props.lines[i]} onDone={advance} />}
</>
);
}

@ -7,11 +7,11 @@ interface IProps {
content: JSX.Element; content: JSX.Element;
} }
export function MessagePopup(props: IProps): React.ReactElement { function MessagePopup(props: IProps): React.ReactElement {
return <>{props.content}</>; return <>{props.content}</>;
} }
function dialogBoxCreate(txt: string | JSX.Element, preformatted = false): void { export function dialogBoxCreate(txt: string | JSX.Element, preformatted = false): void {
const popupId = const popupId =
`popup-` + `popup-` +
Array.from(Array(16)) Array.from(Array(16))
@ -41,5 +41,3 @@ function dialogBoxCreate(txt: string | JSX.Element, preformatted = false): void
}); });
} }
} }
export { dialogBoxCreate };

@ -1,59 +0,0 @@
import { joinFaction } from "../src/Faction/FactionHelpers";
import { Engine } from "../src/engine";
import { Player } from "../src/Player";
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
import { Page, routing } from "../src/ui/navigationTracking";
/* Faction Invitation Pop-up box */
function factionInvitationBoxClose() {
var factionInvitationBox = document.getElementById("faction-invitation-box-container");
factionInvitationBox.style.display = "none";
}
function factionInvitationBoxOpen() {
var factionInvitationBox = document.getElementById("faction-invitation-box-container");
factionInvitationBox.style.display = "flex";
}
function factionInvitationSetText(txt) {
var textBox = document.getElementById("faction-invitation-box-text");
textBox.innerHTML = txt;
}
//ram argument is in GB
function factionInvitationBoxCreate(faction) {
factionInvitationSetText("You have received a faction invitation from " + faction.name);
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
var newYesButton = clearEventListeners("faction-invitation-box-yes");
newYesButton.addEventListener("click", function () {
//Remove from invited factions
var i = Player.factionInvitations.findIndex((facName) => {
return facName === faction.name;
});
if (i === -1) {
console.error("Could not find faction in Player.factionInvitations");
}
joinFaction(faction);
factionInvitationBoxClose();
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
return false;
});
var noButton = clearEventListeners("faction-invitation-box-no");
noButton.addEventListener("click", function () {
factionInvitationBoxClose();
return false;
});
factionInvitationBoxOpen();
}
export { factionInvitationBoxCreate };

@ -1,168 +0,0 @@
/**
* Generic Yes-No Pop-up box
* Used to create pop-up boxes that require a yes/no response from player
*
* There are two types of pop ups:
* 1. Just a Yes/No response from player
* 2. Popup also includes a text input field in addition to the Yes/No response
*/
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
import { KEY } from "./helpers/keyCodes";
import * as ReactDOM from "react-dom";
export let yesNoBoxOpen = false;
const yesNoBoxContainer: HTMLElement | null = document.getElementById("yes-no-box-container");
const yesNoBoxTextElement: HTMLElement | null = document.getElementById("yes-no-box-text");
function yesNoBoxHotkeyHandler(e: KeyboardEvent): void {
if (e.keyCode === KEY.ESC) {
yesNoBoxClose();
} else if (e.keyCode === KEY.ENTER) {
const yesBtn: HTMLElement | null = document.getElementById("yes-no-box-yes");
if (yesBtn) {
yesBtn.click();
} else {
console.error(`Could not find YesNoBox Yes button DOM element`);
}
}
}
export function yesNoBoxClose(): boolean {
if (yesNoBoxContainer) {
yesNoBoxContainer.style.display = "none";
} else {
console.error("Container not found for YesNoBox");
}
yesNoBoxOpen = false;
// Remove hotkey handler
document.removeEventListener("keydown", yesNoBoxHotkeyHandler);
return false; //So that 'return yesNoBoxClose()' is return false in event listeners
}
export function yesNoBoxGetYesButton(): HTMLElement | null {
return clearEventListeners("yes-no-box-yes");
}
export function yesNoBoxGetNoButton(): HTMLElement | null {
return clearEventListeners("yes-no-box-no");
}
export function yesNoBoxCreate(txt: string | JSX.Element): boolean {
if (yesNoBoxOpen) {
return false;
} //Already open
yesNoBoxOpen = true;
if (yesNoBoxTextElement) {
ReactDOM.unmountComponentAtNode(yesNoBoxTextElement);
yesNoBoxTextElement.innerHTML = "";
if (typeof txt === "string") {
yesNoBoxTextElement.innerHTML = txt as string;
} else {
ReactDOM.render(txt, yesNoBoxTextElement);
}
} else {
console.error(`Text element not found for YesNoBox`);
}
if (yesNoBoxContainer) {
yesNoBoxContainer.style.display = "flex";
} else {
console.error("Container not found for YesNoBox");
}
// Add event listener for Esc and Enter hotkeys
document.addEventListener("keydown", yesNoBoxHotkeyHandler);
return true;
}
/**
* Yes-No pop up box with text input field
*/
const yesNoTextInputBoxContainer: HTMLElement | null = document.getElementById("yes-no-text-input-box-container");
const yesNoTextInputBoxInput: HTMLInputElement | null = document.getElementById(
"yes-no-text-input-box-input",
) as HTMLInputElement;
const yesNoTextInputBoxTextElement: HTMLElement | null = document.getElementById("yes-no-text-input-box-text");
export function yesNoTxtInpBoxHotkeyHandler(e: KeyboardEvent): void {
if (e.keyCode === KEY.ESC) {
yesNoTxtInpBoxClose();
} else if (e.keyCode === KEY.ENTER) {
const yesBtn: HTMLElement | null = document.getElementById("yes-no-text-input-box-yes");
if (yesBtn) {
yesBtn.click();
} else {
console.error(`Could not find YesNoTxtInputBox Yes button DOM element`);
}
}
}
export function yesNoTxtInpBoxClose(): boolean {
if (yesNoTextInputBoxContainer != null) {
yesNoTextInputBoxContainer.style.display = "none";
} else {
console.error("Container not found for YesNoTextInputBox");
return false;
}
if (!yesNoTextInputBoxInput) throw new Error("yesNoTextInputBoxInput was not set");
yesNoBoxOpen = false;
yesNoTextInputBoxInput.value = "";
// Remove hotkey handler
document.removeEventListener("keydown", yesNoTxtInpBoxHotkeyHandler);
return false;
}
export function yesNoTxtInpBoxGetYesButton(): HTMLElement {
const elem = clearEventListeners("yes-no-text-input-box-yes");
if (elem === null) throw new Error("Could not find element with id: 'yes-no-text-input-box-yes'");
return elem;
}
export function yesNoTxtInpBoxGetNoButton(): HTMLElement {
const elem = clearEventListeners("yes-no-text-input-box-no");
if (elem === null) throw new Error("Could not find element with id: 'yes-no-text-input-box-no'");
return elem;
}
export function yesNoTxtInpBoxGetInput(): string {
if (!yesNoTextInputBoxInput) {
console.error("Could not find YesNoTextInputBox input element");
return "";
}
let val: string = yesNoTextInputBoxInput.value;
val = val.replace(/\s+/g, "");
return val;
}
export function yesNoTxtInpBoxCreate(txt: string | JSX.Element): void {
yesNoBoxOpen = true;
if (yesNoTextInputBoxTextElement) {
ReactDOM.unmountComponentAtNode(yesNoTextInputBoxTextElement);
yesNoTextInputBoxTextElement.innerHTML = "";
if (typeof txt === "string") {
yesNoTextInputBoxTextElement.innerHTML = txt;
} else {
ReactDOM.render(txt, yesNoTextInputBoxTextElement);
}
}
if (yesNoTextInputBoxContainer) {
yesNoTextInputBoxContainer.style.display = "flex";
} else {
console.error("Container not found for YesNoTextInputBox");
}
// Add event listener for Esc and Enter hotkeys
document.addEventListener("keydown", yesNoTxtInpBoxHotkeyHandler);
if (!yesNoTextInputBoxInput) throw new Error("yesNoTextInputBoxInput was not set");
yesNoTextInputBoxInput.focus();
}

@ -1,12 +0,0 @@
import { createElement } from "./createElement";
/**
* Appends the specified number of breaks (as children) to the specified element
* @param el The element to add child break elements to.
* @param n The number of breaks to add.
*/
export function appendLineBreaks(el: HTMLElement, n: number): void {
for (let i = 0; i < n; ++i) {
el.appendChild(createElement("br"));
}
}

@ -1,9 +0,0 @@
/**
* Clears all <option> elements from a <select>.
* @param selector The <select> element
*/
export function clearSelector(selector: HTMLSelectElement): void {
for (let i: number = selector.options.length - 1; i >= 0; i--) {
selector.remove(i);
}
}

@ -1,49 +0,0 @@
import { createElement } from "./createElement";
/**
* Possible configuration parameters when creating the accordion element.
*/
interface IAccordionConfigurationParameters {
/**
* The HTML to appear in the accordion header.
*/
hdrText?: string;
/**
* A (hopefully) unique identifier for the accordion.
*/
id?: string;
/**
* The HTML to appear in the expanded accordion.
*/
panelText?: string;
}
/**
* Creates both the header and panel element of an accordion and sets the click handler
* @param params The creation parameters.
*/
export function createAccordionElement(params: IAccordionConfigurationParameters): any[] {
const liElem: HTMLLIElement = createElement("li") as HTMLLIElement;
const header: HTMLButtonElement = createElement("button", {
class: "accordion-header",
clickListener() {
this.classList.toggle("active");
const pnl: CSSStyleDeclaration = (this.nextElementSibling as HTMLDivElement).style;
pnl.display = pnl.display === "block" ? "none" : "block";
},
id: params.id !== undefined ? `${params.id}-hdr` : undefined,
innerHTML: params.hdrText,
}) as HTMLButtonElement;
const panel: HTMLDivElement = createElement("div", {
class: "accordion-panel",
id: params.id !== undefined ? `${params.id}-panel` : undefined,
innerHTML: params.panelText,
}) as HTMLDivElement;
liElem.appendChild(header);
liElem.appendChild(panel);
return [liElem, header, panel];
}

@ -1,13 +0,0 @@
import { createElement } from "./createElement";
export function createOptionElement(text: string, value = ""): HTMLOptionElement {
let sanitizedValue: string = value;
if (sanitizedValue === "") {
sanitizedValue = text;
}
return createElement("option", {
text: text,
value: sanitizedValue,
}) as HTMLOptionElement;
}

@ -1,52 +0,0 @@
/**
* Creates a Close/Cancel button that is used for removing popups
*/
import { createElement } from "./createElement";
import { removeElement } from "./removeElement";
interface ICreatePopupCloseButtonOptions {
class?: string;
display?: string;
innerText?: string;
type?: string;
}
export function createPopupCloseButton(
popup: Element | string,
options: ICreatePopupCloseButtonOptions,
): HTMLButtonElement {
const button = createElement("button", {
class: options.class ? options.class : "popup-box-button",
display: options.display ? options.display : "inline-block",
innerText: options.innerText == null ? "Cancel" : options.innerText,
}) as HTMLButtonElement;
function closePopupWithEscFn(e: any): void {
if (e.keyCode === 27) {
button.click();
}
}
button.addEventListener("click", () => {
if (popup instanceof Element) {
removeElement(popup);
} else {
try {
const popupEl = document.getElementById(popup);
if (popupEl instanceof Element) {
removeElement(popupEl);
}
} catch (e) {
console.error(`createPopupCloseButton() threw: ${e}`);
}
}
document.removeEventListener("keydown", closePopupWithEscFn);
return false;
});
document.addEventListener("keydown", closePopupWithEscFn);
return button;
}

@ -4,6 +4,7 @@ const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const UnusedWebpackPlugin = require("unused-webpack-plugin");
module.exports = (env, argv) => { module.exports = (env, argv) => {
const isDevServer = (env || {}).devServer === true; const isDevServer = (env || {}).devServer === true;
@ -104,6 +105,14 @@ module.exports = (env, argv) => {
}, },
}, },
}), }),
new UnusedWebpackPlugin({
// Source directories
directories: [path.join(__dirname, "src"), path.join(__dirname, "utils")],
// Exclude patterns
exclude: ["*.test.js"],
// Root directory (optional)
root: __dirname,
}),
// In dev mode, use a faster method of create sourcemaps // In dev mode, use a faster method of create sourcemaps
// while keeping lines/columns accurate // while keeping lines/columns accurate
isDevServer && isDevServer &&