merge v0.56.0

This commit is contained in:
Olivier Gagnon 2021-10-12 01:35:30 -04:00
commit 87c63cde59
74 changed files with 9894 additions and 2970 deletions

@ -99,7 +99,6 @@ module.exports = {
"no-catch-shadow": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": ["off", "except-parens"],
"no-confusing-arrow": ["error"],
"no-console": ["off"],
"no-const-assign": ["error"],

@ -84,6 +84,28 @@ changes are okay to contribute:
- Changes that directly affect the game's balance
- New gameplay mechanics
### How to setup fork properly
Fork and clone the repo
```
# This will add the game original code as a repo in your local copy
$ git remote add danielyxie git@github.com:danielyxie/bitburner.git
# You can verify you did this right by doing the following command
$ git remote show
danielyxie
origin
# Then download all the branches from the game. (there might be more branches)
$ git fetch danielyxie
From github.com:danielyxie/bitburner
* [new branch] dev -> danielyxie/dev
* [new branch] master -> danielyxie/master
# Makes sure you always start from `danielyxie/dev` to avoid merge conflicts.
```
#### Submitting a Pull Request
When submitting a pull request with your code contributions, please abide by

60
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
.. _gameplay_sourcefiles:
.. warning:: This page contains spoilers regarding the game's story/plot-line.
.. warning:: This page contains spoilers for the game
Source-Files
============

@ -3,6 +3,109 @@
Changelog
=========
v0.56.0 - 2021-10-11 Trimming the backlog (hydroflame & community)
-------------------------------------------
** BREAKING **
* The 'write' function is now async. This helps when making scripts that write scripts.
** Terminal **
* 'grow' and 'weaken' have been added as terminal command. This should help player transition
from commands to scripts. The tutorial also talks about it.
* 'cp' command added
* Improved performance by rate limiting refresh.
** IP vs Hostname **
* The game now uses hostname as primary key for it's servers (yeah believe it or not IPs were
used until then). This has caused some issues with purchased servers (they couldn't be sold).
You might need to soft reset for the game to fully convert itself.
** Sleeve **
* Fixed bug where they couldn't train at Volhaven.
* No longer consume all bonus time at once, making it look buggy.
** SF9 **
* Now boosts hacknet production by 8/12/14%
** Hacknet Servers **
* production nerfed by 10%
* Max money increase gets weaker above 10t max money
** Corporation **
* Warehouse tooltip now also displays the amount of space taken by products.
* Changed research box completely to avoid dependency on Treant (Treant is a pita)
* All textbox should accept MAX/MP case insensitive.
* Fixed export popup not refreshing dropdowns correctly.
* Fixed product mku becoming zero
* Increased scaling of Wilson to avoid feedback loop.
* Can no longer get in debt by buying real estate
* Bonus time is consumed faster.
** Netscript **
* isBusy takes bitverse and infiltration into account
* hospitalize can't be called when in infiltration.
* setToCommitCrime now accepts crime rough name instead of perfect name.
* disableLog All now works for bladeburner functions.
* Fixed netscript port for ns1.
** Augmentation **
* Added augmentation to Ti Di Hui that removes penalty for being unfocused.
* Neuroflux no longer appears in special factions.
** Script Editor **
* Ram check is debounced instead of refreshed every second.
* Added the vscode extension documentation to the game (it doesn't work well, thought)
* Fixed issue where autocomplete list would grow forever
* Added semi-monokai as theme.
* Fixed issue where modifying filename would mess it up.
* Font size can be changed now.
** Infiltration **
* Fixed issue where game controls would become unfocused.
** Misc. **
* Fixed loader incorrectly assuming some null values are incorrect.
* installBackdoor trigger Bitverse sequence
* Some improvements to the theme editor
* Improved documentation about where to learn javascript.
* Added some instructions for contributors.
* Fixed typo in corporation sell shares modal (@Saynt_Garmo)
* Fixed pagination being black on black in Active Scripts
* Create Script tab renamed to Script Editor
* Fixed an issue where corp some textbox wouldn't update when changing city.
* Fixed an issue where hacknet online time was always 0.
* Netscript function prompt fixed.
* Fixed miscalculation in growth.
* Script with syntax errors will try to be a tad more helpful.
* Corporations can no longer bribe bladeburners.
* Augmentation Graphene Branchiblade renamed to Brachi, like the rest of them.
* All ram is displayed in GB/TB/PB now.
* Game now saves when saving a file, this can be turned off.
* Several improvement to log window.
* Bladeburner current action returns General type instead of the name of the action.
* Bladeburner travel and Sleeve travel respect disable ASCII.
* Tutorial fits on small screens.
* Import is much slower but more consistent now.
* Fix intelligence not updating properly.
* Added SF -1: Time Compression
* ReadTheDoc theme now matches the game.
* Logbox should wrap text better
* Logbox behavior should feel better.
* Fix font for AutoLink.exe
* nerf noodle bar
v0.55.0 - 2021-09-20 Material UI (hydroflame & community)
-------------------------------------------

@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
# built documents.
#
# The short X.Y version.
version = '0.55'
version = '0.56'
# The full version, including alpha/beta/rc tags.
release = '0.55.0'
release = '0.56.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -189,3 +189,4 @@ intersphinx_mapping = {'https://docs.python.org/': None}
def setup(app):
print("Initializing (setup())");
app.add_stylesheet('maxwidthoverride.css')
app.add_stylesheet('dark_theme.css')

@ -6,8 +6,6 @@ getServer() Netscript Function
:RAM cost: 2 GB
:param string hostname: Hostname of the server, defaults to host server.
If you are not in BitNode-5, then you must have Source-File 5-1 in order to run this function.
This function is meant to be used in conjunction with the :doc:`formulas API<../netscriptformulasapi>`.
Returns an object with the Server's stats. The object has the following properties::

@ -1,12 +1,13 @@
growthAnalyze() Netscript Function
==================================
.. js:function:: growthAnalyze(hostname, growthAmount)
.. js:function:: growthAnalyze(hostname, growthAmount[, cores])
:RAM cost: 1 GB
:param string hostname: Hostname of server to analyze.
:param number growthAmount: Multiplicative factor by which the server is
grown. Decimal form. Must be >= 1.
:param number cores: Amount of cores on the server that would run the growth, defaults to 1
:returns: The amount of :doc:`grow<grow>` threads needed to grow the specified
server by the specified amount.

@ -15,6 +15,7 @@ help you learn some basic programming concepts.
Here are some good tutorials for learning programming/JavaScript as a beginner:
* `Learn-JS <http://www.learn-js.org/en/Welcome>`_
* `programiz <https://www.programiz.com/javascript/get-started>`_
* `Speaking JavaScript <http://speakingjs.com/es5/index.html>`_
This is a bit on the longer side. You can skip all of the historical
background stuff. Recommended chapters: 1, 7-18

@ -0,0 +1,453 @@
:root {
--dark-text-color: #0c0;
--dark-link-color: #090;
}
body {
color: #000;
}
.wy-nav-content-wrap {
background-color: #000;
}
.wy-nav-content {
background-color: #000;
}
.section {
color: var(--dark-text-color);
}
.rst-content .highlighted {
background: #333;
box-shadow: none;
}
.highlight {
background-color: #17181c;
}
.highlight .nn {
color: var(--dark-text-color);
}
.highlight .nb {
color: #8bb8df;
}
.highlight .kn,
.highlight .kc,
.highlight .k {
color: #41c2ea;
}
.highlight .s1,
.highlight .s2 {
color: #b3e87f;
}
.highlight .nt {
color: #ccb350;
}
.highlight .c1 {
color: #686868;
}
.rst-content div[class^="highlight"] {
border-color: #1a1a1a;
}
.icon,
.icon-home {
color: var(--dark-link-color);
}
.wy-nav-content a,
.wy-nav-content a:visited {
color: var(--dark-link-color) !important;
text-decoration: underline;
}
.btn-neutral {
background-color: #17181c !important;
}
.btn-neutral:hover {
background-color: #101114 !important;
}
.btn-neutral:visited {
color: #c1c1c1 !important;
}
.btn {
box-shadow: none;
}
footer {
color: #bdbdbd;
}
.wy-nav-side {
background-color: #000;
border: 1px solid #333;
}
.wy-menu-vertical > a {
color: var(--dark-text-color);
}
.wy-menu-vertical li.current {
background-color: #000;
}
.wy-menu-vertical li.current > a,
.wy-menu-vertical li.on a {
background-color: #000;
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l1.current > a,
.wy-menu-vertical li.current a {
border-color: #000;
}
.wy-menu-vertical header,
.wy-menu-vertical p.caption {
color: var(--dark-text-color);
}
html.writer-html4 .rst-content dl:not(.docutils) > dt,
html.writer-html5
.rst-content
dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
> dt {
background-color: #333;
}
.wy-menu-vertical li.current a {
color: #090;
}
.wy-menu-vertical a {
color: var(--dark-text-color);
}
.wy-menu-vertical li.current a:hover {
background-color: #222;
}
.wy-menu-vertical a:hover,
.wy-menu-vertical li.current > a:hover,
.wy-menu-vertical li.on a:hover {
background-color: #000;
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l2.current > a,
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
background-color: #000;
}
.wy-side-nav-search {
background-color: #000;
color: var(--dark-text-color);
}
.wy-side-nav-search .wy-dropdown > a,
.wy-side-nav-search > a {
color: var(--dark-text-color);
}
.wy-side-nav-search input[type="text"] {
border-left: 0px;
border-right: 0px;
border-top: 0px;
border-radius: 0px;
box-shadow: none;
border-bottom-color: #0c0;
background-color: #333;
color: var(--dark-text-color);
}
.theme-switcher {
background-color: #0b0c0d;
color: var(--dark-text-color);
}
writer-html4 .rst-content dl:not(.docutils) > dt,
writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) > dt {
background-color: #0b0b0b;
color: #007dce;
border-color: #282828;
}
.rst-content code,
.rst-content tt {
color: var(--dark-text-color);
}
writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list) > dt,
writer-html5
.rst-content
dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
dl:not(.field-list)
> dt {
background-color: #0f0f0f;
color: #959595;
border-color: #2b2b2b;
}
.rst-content code,
.rst-content tt,
code {
background-color: #2d2d2d;
border-color: #1c1c1c;
}
.rst-content code.xref,
.rst-content tt.xref,
a .rst-content code,
a .rst-content tt {
color: #cecece;
}
.rst-content .hint,
.rst-content .important,
.rst-content .tip,
.rst-content .wy-alert-success.admonition,
.rst-content .wy-alert-success.admonition-todo,
.rst-content .wy-alert-success.attention,
.rst-content .wy-alert-success.caution,
.rst-content .wy-alert-success.danger,
.rst-content .wy-alert-success.error,
.rst-content .wy-alert-success.note,
.rst-content .wy-alert-success.seealso,
.rst-content .wy-alert-success.warning,
.wy-alert.wy-alert-success {
background-color: #00392e;
}
.rst-content .hint .admonition-title,
.rst-content .hint .wy-alert-title,
.rst-content .important .admonition-title,
.rst-content .important .wy-alert-title,
.rst-content .tip .admonition-title,
.rst-content .tip .wy-alert-title,
.rst-content .wy-alert-success.admonition-todo .admonition-title,
.rst-content .wy-alert-success.admonition-todo .wy-alert-title,
.rst-content .wy-alert-success.admonition .admonition-title,
.rst-content .wy-alert-success.admonition .wy-alert-title,
.rst-content .wy-alert-success.attention .admonition-title,
.rst-content .wy-alert-success.attention .wy-alert-title,
.rst-content .wy-alert-success.caution .admonition-title,
.rst-content .wy-alert-success.caution .wy-alert-title,
.rst-content .wy-alert-success.danger .admonition-title,
.rst-content .wy-alert-success.danger .wy-alert-title,
.rst-content .wy-alert-success.error .admonition-title,
.rst-content .wy-alert-success.error .wy-alert-title,
.rst-content .wy-alert-success.note .admonition-title,
.rst-content .wy-alert-success.note .wy-alert-title,
.rst-content .wy-alert-success.seealso .admonition-title,
.rst-content .wy-alert-success.seealso .wy-alert-title,
.rst-content .wy-alert-success.warning .admonition-title,
.rst-content .wy-alert-success.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-success .admonition-title,
.wy-alert.wy-alert-success .rst-content .admonition-title,
.wy-alert.wy-alert-success .wy-alert-title {
background-color: #006a56;
}
.rst-content .note,
.rst-content .seealso,
.rst-content .wy-alert-info.admonition,
.rst-content .wy-alert-info.admonition-todo,
.rst-content .wy-alert-info.attention,
.rst-content .wy-alert-info.caution,
.rst-content .wy-alert-info.danger,
.rst-content .wy-alert-info.error,
.rst-content .wy-alert-info.hint,
.rst-content .wy-alert-info.important,
.rst-content .wy-alert-info.tip,
.rst-content .wy-alert-info.warning,
.wy-alert.wy-alert-info {
background-color: #002c4d;
}
.rst-content .note .admonition-title,
.rst-content .note .wy-alert-title,
.rst-content .seealso .admonition-title,
.rst-content .seealso .wy-alert-title,
.rst-content .wy-alert-info.admonition-todo .admonition-title,
.rst-content .wy-alert-info.admonition-todo .wy-alert-title,
.rst-content .wy-alert-info.admonition .admonition-title,
.rst-content .wy-alert-info.admonition .wy-alert-title,
.rst-content .wy-alert-info.attention .admonition-title,
.rst-content .wy-alert-info.attention .wy-alert-title,
.rst-content .wy-alert-info.caution .admonition-title,
.rst-content .wy-alert-info.caution .wy-alert-title,
.rst-content .wy-alert-info.danger .admonition-title,
.rst-content .wy-alert-info.danger .wy-alert-title,
.rst-content .wy-alert-info.error .admonition-title,
.rst-content .wy-alert-info.error .wy-alert-title,
.rst-content .wy-alert-info.hint .admonition-title,
.rst-content .wy-alert-info.hint .wy-alert-title,
.rst-content .wy-alert-info.important .admonition-title,
.rst-content .wy-alert-info.important .wy-alert-title,
.rst-content .wy-alert-info.tip .admonition-title,
.rst-content .wy-alert-info.tip .wy-alert-title,
.rst-content .wy-alert-info.warning .admonition-title,
.rst-content .wy-alert-info.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-info .admonition-title,
.wy-alert.wy-alert-info .rst-content .admonition-title,
.wy-alert.wy-alert-info .wy-alert-title {
background-color: #004a7b;
}
.rst-content dl:not(.docutils) dt {
background-color: #333;
}
.rst-content {
color: var(--dark-text-color);
}
.rst-content .admonition-todo,
.rst-content .attention,
.rst-content .caution,
.rst-content .warning,
.rst-content .wy-alert-warning.admonition,
.rst-content .wy-alert-warning.danger,
.rst-content .wy-alert-warning.error,
.rst-content .wy-alert-warning.hint,
.rst-content .wy-alert-warning.important,
.rst-content .wy-alert-warning.note,
.rst-content .wy-alert-warning.seealso,
.rst-content .wy-alert-warning.tip,
.wy-alert.wy-alert-warning {
background-color: #533500;
}
.rst-content .admonition-todo .admonition-title,
.rst-content .admonition-todo .wy-alert-title,
.rst-content .attention .admonition-title,
.rst-content .attention .wy-alert-title,
.rst-content .caution .admonition-title,
.rst-content .caution .wy-alert-title,
.rst-content .warning .admonition-title,
.rst-content .warning .wy-alert-title,
.rst-content .wy-alert-warning.admonition .admonition-title,
.rst-content .wy-alert-warning.admonition .wy-alert-title,
.rst-content .wy-alert-warning.danger .admonition-title,
.rst-content .wy-alert-warning.danger .wy-alert-title,
.rst-content .wy-alert-warning.error .admonition-title,
.rst-content .wy-alert-warning.error .wy-alert-title,
.rst-content .wy-alert-warning.hint .admonition-title,
.rst-content .wy-alert-warning.hint .wy-alert-title,
.rst-content .wy-alert-warning.important .admonition-title,
.rst-content .wy-alert-warning.important .wy-alert-title,
.rst-content .wy-alert-warning.note .admonition-title,
.rst-content .wy-alert-warning.note .wy-alert-title,
.rst-content .wy-alert-warning.seealso .admonition-title,
.rst-content .wy-alert-warning.seealso .wy-alert-title,
.rst-content .wy-alert-warning.tip .admonition-title,
.rst-content .wy-alert-warning.tip .wy-alert-title,
.rst-content .wy-alert.wy-alert-warning .admonition-title,
.wy-alert.wy-alert-warning .rst-content .admonition-title,
.wy-alert.wy-alert-warning .wy-alert-title {
background-color: #803b00;
}
.rst-content .danger,
.rst-content .error,
.rst-content .wy-alert-danger.admonition,
.rst-content .wy-alert-danger.admonition-todo,
.rst-content .wy-alert-danger.attention,
.rst-content .wy-alert-danger.caution,
.rst-content .wy-alert-danger.hint,
.rst-content .wy-alert-danger.important,
.rst-content .wy-alert-danger.note,
.rst-content .wy-alert-danger.seealso,
.rst-content .wy-alert-danger.tip,
.rst-content .wy-alert-danger.warning,
.wy-alert.wy-alert-danger {
background-color: #82231a;
}
.rst-content .danger .admonition-title,
.rst-content .danger .wy-alert-title,
.rst-content .error .admonition-title,
.rst-content .error .wy-alert-title,
.rst-content .wy-alert-danger.admonition-todo .admonition-title,
.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,
.rst-content .wy-alert-danger.admonition .admonition-title,
.rst-content .wy-alert-danger.admonition .wy-alert-title,
.rst-content .wy-alert-danger.attention .admonition-title,
.rst-content .wy-alert-danger.attention .wy-alert-title,
.rst-content .wy-alert-danger.caution .admonition-title,
.rst-content .wy-alert-danger.caution .wy-alert-title,
.rst-content .wy-alert-danger.hint .admonition-title,
.rst-content .wy-alert-danger.hint .wy-alert-title,
.rst-content .wy-alert-danger.important .admonition-title,
.rst-content .wy-alert-danger.important .wy-alert-title,
.rst-content .wy-alert-danger.note .admonition-title,
.rst-content .wy-alert-danger.note .wy-alert-title,
.rst-content .wy-alert-danger.seealso .admonition-title,
.rst-content .wy-alert-danger.seealso .wy-alert-title,
.rst-content .wy-alert-danger.tip .admonition-title,
.rst-content .wy-alert-danger.tip .wy-alert-title,
.rst-content .wy-alert-danger.warning .admonition-title,
.rst-content .wy-alert-danger.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-danger .admonition-title,
.wy-alert.wy-alert-danger .rst-content .admonition-title,
.wy-alert.wy-alert-danger .wy-alert-title {
background-color: #b9372b;
}
.wy-nav-top {
background-color: #0b152d;
}
.rst-content table.docutils thead,
.rst-content table.field-list thead,
.wy-table thead {
color: var(--dark-text-color);
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
.wy-table-backed,
.wy-table-odd td,
.wy-table-striped tr:nth-child(2n-1) td {
background-color: #333;
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n) td,
.wy-table-backed,
.wy-table-odd td,
.wy-table-striped tr:nth-child(2n) td {
background-color: #444;
}
.rst-content table.docutils td,
.wy-table-bordered-all td,
writer-html5 .rst-content table.docutils th,
.rst-content table.docutils,
.wy-table-bordered-all {
border-color: #262626;
}
.rst-content table.docutils caption,
.rst-content table.field-list caption,
.wy-table caption {
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l3.current > a,
.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a {
background-color: #000;
}
.wy-side-nav-search > div.version {
color: var(--dark-text-color);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4217
main.css

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "0.53.0",
"version": "0.56.0",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie"

@ -2,25 +2,25 @@ const numSpaces = 4;
const maxLineLength = 160;
module.exports = {
"env": {
"es6": true,
"node": true
env: {
es6: true,
node: true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
extends: "eslint:recommended",
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
},
"ecmaVersion": 8,
"sourceType": "module"
ecmaVersion: 8,
sourceType: "module",
},
"rules": {
rules: {
"accessor-pairs": [
"error",
{
"getWithoutSet": false,
"setWithoutGet": true
}
getWithoutSet: false,
setWithoutGet: true,
},
],
"array-bracket-newline": ["error"],
"array-bracket-spacing": ["error"],
@ -33,50 +33,35 @@ module.exports = {
"block-spacing": ["error"],
"brace-style": ["error"],
"callback-return": ["error"],
"camelcase": ["error"],
camelcase: ["error"],
"capitalized-comments": ["error"],
"class-methods-use-this": ["error"],
"comma-dangle": ["error"],
"comma-spacing": ["error"],
"comma-style": [
"error",
"last"
],
"complexity": ["error"],
"computed-property-spacing": [
"error",
"never"
],
"comma-style": ["error", "last"],
complexity: ["error"],
"computed-property-spacing": ["error", "never"],
"consistent-return": ["error"],
"consistent-this": ["error"],
"constructor-super": ["error"],
"curly": ["error"],
curly: ["error"],
"default-case": ["error"],
"dot-location": [
"error",
"property"
],
"dot-location": ["error", "property"],
"dot-notation": ["error"],
"eol-last": ["error"],
"eqeqeq": ["error"],
eqeqeq: ["error"],
"for-direction": ["error"],
"func-call-spacing": ["error"],
"func-name-matching": ["error"],
"func-names": [
"error",
"never"
],
"func-names": ["error", "never"],
"func-style": ["error"],
"function-paren-newline": ["error"],
"generator-star-spacing": [
"error",
"before"
],
"generator-star-spacing": ["error", "before"],
"getter-return": [
"error",
{
"allowImplicit": false
}
allowImplicit: false,
},
],
"global-require": ["error"],
"guard-for-in": ["error"],
@ -84,52 +69,37 @@ module.exports = {
"id-blacklist": ["error"],
"id-length": ["error"],
"id-match": ["error"],
"implicit-arrow-linebreak": [
"error",
"beside"
],
"indent": [
"implicit-arrow-linebreak": ["error", "beside"],
indent: [
"error",
numSpaces,
{
"SwitchCase": 1
}
SwitchCase: 1,
},
],
"init-declarations": ["error"],
"jsx-quotes": ["error"],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"line-comment-position": ["error"],
"linebreak-style": [
"error",
"windows"
],
"linebreak-style": ["error", "windows"],
"lines-around-comment": ["error"],
"lines-between-class-members": ["error"],
"max-depth": ["error"],
"max-len": [
"error",
maxLineLength
],
"max-len": ["error", maxLineLength],
"max-lines": [
"error",
{
"skipBlankLines": true,
"skipComments": true
}
skipBlankLines: true,
skipComments: true,
},
],
"max-nested-callbacks": ["error"],
"max-params": ["error"],
"max-statements": ["error"],
"max-statements-per-line": ["error"],
"multiline-comment-style": [
"off",
"starred-block"
],
"multiline-ternary": [
"error",
"never"
],
"multiline-comment-style": ["off", "starred-block"],
"multiline-ternary": ["error", "never"],
"new-cap": ["error"],
"new-parens": ["error"],
// TODO: configure this...
@ -145,18 +115,15 @@ module.exports = {
"no-catch-shadow": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": [
"error",
"except-parens"
],
"no-cond-assign": ["error", "except-parens"],
"no-confusing-arrow": ["error"],
"no-console": ["error"],
"no-const-assign": ["error"],
"no-constant-condition": [
"error",
{
"checkLoops": false
}
checkLoops: false,
},
],
"no-continue": ["off"],
"no-control-regex": ["error"],
@ -170,15 +137,15 @@ module.exports = {
"no-duplicate-imports": [
"error",
{
"includeExports": true
}
includeExports: true,
},
],
"no-else-return": ["error"],
"no-empty": [
"error",
{
"allowEmptyCatch": false
}
allowEmptyCatch: false,
},
],
"no-empty-character-class": ["error"],
"no-empty-function": ["error"],
@ -194,8 +161,8 @@ module.exports = {
"error",
"all",
{
"conditionalAssign": false
}
conditionalAssign: false,
},
],
"no-extra-semi": ["error"],
"no-fallthrough": ["error"],
@ -206,20 +173,17 @@ module.exports = {
"no-implicit-globals": ["error"],
"no-implied-eval": ["error"],
"no-inline-comments": ["error"],
"no-inner-declarations": [
"error",
"both"
],
"no-inner-declarations": ["error", "both"],
"no-invalid-regexp": ["error"],
"no-invalid-this": ["error"],
"no-irregular-whitespace": [
"error",
{
"skipComments": false,
"skipRegExps": false,
"skipStrings": false,
"skipTemplates": false
}
skipComments: false,
skipRegExps: false,
skipStrings: false,
skipTemplates: false,
},
],
"no-iterator": ["error"],
"no-label-var": ["error"],
@ -230,13 +194,9 @@ module.exports = {
"no-magic-numbers": [
"error",
{
"ignore": [
-1,
0,
1
],
"ignoreArrayIndexes": true
}
ignore: [-1, 0, 1],
ignoreArrayIndexes: true,
},
],
"no-mixed-operators": ["error"],
"no-mixed-requires": ["error"],
@ -247,8 +207,8 @@ module.exports = {
"no-multiple-empty-lines": [
"error",
{
"max": 1
}
max: 1,
},
],
"no-native-reassign": ["error"],
"no-negated-condition": ["error"],
@ -268,8 +228,8 @@ module.exports = {
"no-plusplus": [
"error",
{
"allowForLoopAfterthoughts": true
}
allowForLoopAfterthoughts: true,
},
],
"no-process-env": ["error"],
"no-process-exit": ["error"],
@ -283,10 +243,10 @@ module.exports = {
"no-restricted-properties": [
"error",
{
"message": "'log' is too general, use an appropriate level when logging.",
"object": "console",
"property": "log"
}
message: "'log' is too general, use an appropriate level when logging.",
object: "console",
property: "log",
},
],
"no-restricted-syntax": ["error"],
"no-return-assign": ["error"],
@ -295,8 +255,8 @@ module.exports = {
"no-self-assign": [
"error",
{
"props": false
}
props: false,
},
],
"no-self-compare": ["error"],
"no-sequences": ["error"],
@ -333,10 +293,10 @@ module.exports = {
"no-useless-rename": [
"error",
{
"ignoreDestructuring": false,
"ignoreExport": false,
"ignoreImport": false
}
ignoreDestructuring: false,
ignoreExport: false,
ignoreImport: false,
},
],
"no-useless-return": ["error"],
"no-var": ["error"],
@ -344,10 +304,7 @@ module.exports = {
"no-warning-comments": ["error"],
"no-whitespace-before-property": ["error"],
"no-with": ["error"],
"nonblock-statement-body-position": [
"error",
"below"
],
"nonblock-statement-body-position": ["error", "below"],
"object-curly-newline": ["error"],
"object-curly-spacing": ["error"],
"object-property-newline": ["error"],
@ -355,10 +312,7 @@ module.exports = {
"one-var": ["off"],
"one-var-declaration-per-line": ["error"],
"operator-assignment": ["error"],
"operator-linebreak": [
"error",
"none"
],
"operator-linebreak": ["error", "none"],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"],
@ -371,24 +325,15 @@ module.exports = {
"prefer-spread": ["error"],
"prefer-template": ["error"],
"quote-props": ["error"],
"quotes": ["error"],
"radix": [
"error",
"as-needed"
],
quotes: ["error"],
radix: ["error", "as-needed"],
"require-await": ["error"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": [
"error",
"never"
],
"semi": ["error"],
"rest-spread-spacing": ["error", "never"],
semi: ["error"],
"semi-spacing": ["error"],
"semi-style": [
"error",
"last"
],
"semi-style": ["error", "last"],
"sort-imports": ["error"],
"sort-keys": ["error"],
"sort-vars": ["error"],
@ -398,37 +343,25 @@ module.exports = {
"space-infix-ops": ["error"],
"space-unary-ops": ["error"],
"spaced-comment": ["error"],
"strict": ["error"],
strict: ["error"],
"switch-colon-spacing": [
"error",
{
"after": true,
"before": false
}
after: true,
before: false,
},
],
"symbol-description": ["error"],
"template-curly-spacing": ["error"],
"template-tag-spacing": ["error"],
"unicode-bom": [
"error",
"never"
],
"unicode-bom": ["error", "never"],
"use-isnan": ["error"],
"valid-jsdoc": ["error"],
"valid-typeof": ["error"],
"vars-on-top": ["error"],
"wrap-iife": [
"error",
"any"
],
"wrap-iife": ["error", "any"],
"wrap-regex": ["error"],
"yield-star-spacing": [
"error",
"before"
],
"yoda": [
"error",
"never"
]
}
"yield-star-spacing": ["error", "before"],
yoda: ["error", "never"],
},
};

@ -8,7 +8,8 @@ const path = require("path");
const exec = require("child_process").exec;
const semver = require("./semver");
const getPackageJson = () => new Promise((resolve, reject) => {
const getPackageJson = () =>
new Promise((resolve, reject) => {
try {
/* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json")));
@ -17,7 +18,8 @@ const getPackageJson = () => new Promise((resolve, reject) => {
}
});
const getEngines = (data) => new Promise((resolve, reject) => {
const getEngines = (data) =>
new Promise((resolve, reject) => {
let versions = null;
if (data.engines) {
@ -31,7 +33,8 @@ const getEngines = (data) => new Promise((resolve, reject) => {
}
});
const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
const checkNpmVersion = (engines) =>
new Promise((resolve, reject) => {
exec("npm -v", (error, stdout, stderr) => {
if (error) {
reject(`Unable to find NPM version\n${stderr}`);
@ -43,18 +46,23 @@ const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
if (semver.satisfies(npmVersion, engineVersion)) {
resolve();
} else {
reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`);
reject(
`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`,
);
}
});
});
const checkNodeVersion = (engines) => new Promise((resolve, reject) => {
const checkNodeVersion = (engines) =>
new Promise((resolve, reject) => {
const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines);
} else {
reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`);
reject(
`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`,
);
}
});
@ -69,5 +77,5 @@ getPackageJson()
/* eslint-disable no-console, no-process-exit */
console.error(error);
process.exit(1);
}
},
);

@ -444,7 +444,6 @@ function parseComparator(comp, loose) {
}
class SemVer {
/**
* A semantic version.
* @param {string} version The version.
@ -488,7 +487,7 @@ class SemVer {
// Numberify any prerelease numeric ids
if (matches[4]) {
this.prerelease = matches[4].split(".").map((id) => {
if ((/^[0-9]+$/).test(id)) {
if (/^[0-9]+$/.test(id)) {
const num = Number(id);
if (num >= 0 && num < MAX_SAFE_INTEGER) {
return num;
@ -532,7 +531,9 @@ class SemVer {
}
return (
compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch)
compareIdentifiers(this.major, other.major) ||
compareIdentifiers(this.minor, other.minor) ||
compareIdentifiers(this.patch, other.patch)
);
}
@ -572,7 +573,8 @@ class SemVer {
}
}
const compare = (leftVersion, rightVersion, loose) => new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
const compare = (leftVersion, rightVersion, loose) =>
new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
const gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0;
const lt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) < 0;
const eq = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) === 0;

@ -533,6 +533,7 @@ export class Augmentation {
console.warn(`Invalid Faction object in addToAllFactions(). Key value: ${fac}`);
continue;
}
if (facObj.getInfo().special) continue;
facObj.augmentations.push(this.name);
}
}

@ -1,6 +1,118 @@
import { IMap } from "../../types";
export const AugmentationNames: IMap<string> = {
export const AugmentationNames: {
Targeting1: string;
Targeting2: string;
Targeting3: string;
SyntheticHeart: string;
SynfibrilMuscle: string;
CombatRib1: string;
CombatRib2: string;
CombatRib3: string;
NanofiberWeave: string;
SubdermalArmor: string;
WiredReflexes: string;
GrapheneBoneLacings: string;
BionicSpine: string;
GrapheneBionicSpine: string;
BionicLegs: string;
GrapheneBionicLegs: string;
SpeechProcessor: string;
TITN41Injection: string;
EnhancedSocialInteractionImplant: string;
BitWire: string;
ArtificialBioNeuralNetwork: string;
ArtificialSynapticPotentiation: string;
EnhancedMyelinSheathing: string;
SynapticEnhancement: string;
NeuralRetentionEnhancement: string;
DataJack: string;
ENM: string;
ENMCore: string;
ENMCoreV2: string;
ENMCoreV3: string;
ENMAnalyzeEngine: string;
ENMDMA: string;
Neuralstimulator: string;
NeuralAccelerator: string;
CranialSignalProcessorsG1: string;
CranialSignalProcessorsG2: string;
CranialSignalProcessorsG3: string;
CranialSignalProcessorsG4: string;
CranialSignalProcessorsG5: string;
NeuronalDensification: string;
NeuroreceptorManager: string;
NuoptimalInjectorImplant: string;
SpeechEnhancement: string;
FocusWire: string;
PCDNI: string;
PCDNIOptimizer: string;
PCDNINeuralNetwork: string;
PCMatrix: string;
ADRPheromone1: string;
ADRPheromone2: string;
ShadowsSimulacrum: string;
HacknetNodeCPUUpload: string;
HacknetNodeCacheUpload: string;
HacknetNodeNICUpload: string;
HacknetNodeKernelDNI: string;
HacknetNodeCoreDNI: string;
NeuroFluxGovernor: string;
Neurotrainer1: string;
Neurotrainer2: string;
Neurotrainer3: string;
Hypersight: string;
LuminCloaking1: string;
LuminCloaking2: string;
HemoRecirculator: string;
SmartSonar: string;
PowerRecirculator: string;
QLink: string;
TheRedPill: string;
SPTN97: string;
HiveMind: string;
CordiARCReactor: string;
SmartJaw: string;
Neotra: string;
Xanipher: string;
nextSENS: string;
OmniTekInfoLoad: string;
PhotosyntheticCells: string;
Neurolink: string;
TheBlackHand: string;
UnstableCircadianModulator: string;
CRTX42AA: string;
Neuregen: string;
CashRoot: string;
NutriGen: string;
INFRARet: string;
DermaForce: string;
GrapheneBrachiBlades: string;
GrapheneBionicArms: string;
BrachiBlades: string;
BionicArms: string;
SNA: string;
HydroflameLeftArm: string;
EsperEyewear: string;
EMS4Recombination: string;
OrionShoulder: string;
HyperionV1: string;
HyperionV2: string;
GolemSerum: string;
VangelisVirus: string;
VangelisVirus3: string;
INTERLINKED: string;
BladeRunner: string;
BladeArmor: string;
BladeArmorPowerCells: string;
BladeArmorEnergyShielding: string;
BladeArmorUnibeam: string;
BladeArmorOmnibeam: string;
BladeArmorIPU: string;
BladesSimulacrum: string;
StaneksGift1: string;
StaneksGift2: string;
StaneksGift3: string;
StaneksGift4: string;
} = {
Targeting1: "Augmented Targeting I",
Targeting2: "Augmented Targeting II",
Targeting3: "Augmented Targeting III",
@ -87,7 +199,7 @@ export const AugmentationNames: IMap<string> = {
NutriGen: "NutriGen Implant",
INFRARet: "INFRARET Enhancement",
DermaForce: "DermaForce Particle Barrier",
GrapheneBrachiBlades: "Graphene BranchiBlades Upgrade",
GrapheneBrachiBlades: "Graphene BrachiBlades Upgrade",
GrapheneBionicArms: "Graphene Bionic Arms Upgrade",
BrachiBlades: "BrachiBlades",
BionicArms: "Bionic Arms",

@ -1960,6 +1960,18 @@ export class Bladeburner implements IBladeburner {
break;
}
}
const gen = [
"Training",
"Recruitment",
"FieldAnalysis",
"Field Analysis",
"Diplomacy",
"Hyperbolic Regeneration Chamber",
];
if (gen.includes(res.type)) {
res.type = "General";
}
if (res.type == null) {
res.type = "Idle";
}

@ -114,7 +114,7 @@ export const CONSTANTS: {
TotalNumBitNodes: number;
LatestUpdate: string;
} = {
Version: "0.55.0",
Version: "0.56.0",
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@ -281,31 +281,107 @@ export const CONSTANTS: {
TotalNumBitNodes: 24,
LatestUpdate: `
v0.55.0 - 2021-09-20 Material UI (hydroflame & community)
v0.56.0 - 2021-10-11 Trimming the backlog (hydroflame & community)
-------------------------------------------
** Global **
** BREAKING **
* The game is now 100% in typescript, react, and Material-UI
* The 'write' function is now async. This helps when making scripts that write scripts.
** Terminal **
* 'grow' and 'weaken' have been added as terminal command. This should help player transition
from commands to scripts. The tutorial also talks about it.
* 'cp' command added
* Improved performance by rate limiting refresh.
** IP vs Hostname **
* The game now uses hostname as primary key for it's servers (yeah believe it or not IPs were
used until then). This has caused some issues with purchased servers (they couldn't be sold).
You might need to soft reset for the game to fully convert itself.
** Sleeve **
* Fixed bug where they couldn't train at Volhaven.
* No longer consume all bonus time at once, making it look buggy.
** SF9 **
* Now boosts hacknet production by 8/12/14%
** Hacknet Servers **
* production nerfed by 10%
* Max money increase gets weaker above 10t max money
** Corporation **
* Warehouse tooltip now also displays the amount of space taken by products.
* Changed research box completely to avoid dependency on Treant (Treant is a pita)
* All textbox should accept MAX/MP case insensitive.
* Fixed export popup not refreshing dropdowns correctly.
* Fixed product mku becoming zero
* Increased scaling of Wilson to avoid feedback loop.
* Can no longer get in debt by buying real estate
* Bonus time is consumed faster.
** Netscript **
* isBusy takes bitverse and infiltration into account
* hospitalize can't be called when in infiltration.
* setToCommitCrime now accepts crime rough name instead of perfect name.
* disableLog All now works for bladeburner functions.
* Fixed netscript port for ns1.
** Augmentation **
* Added augmentation to Ti Di Hui that removes penalty for being unfocused.
* Neuroflux no longer appears in special factions.
** Script Editor **
* Ram check is debounced instead of refreshed every second.
* Added the vscode extension documentation to the game (it doesn't work well, thought)
* Fixed issue where autocomplete list would grow forever
* Added semi-monokai as theme.
* Fixed issue where modifying filename would mess it up.
* Font size can be changed now.
** Infiltration **
* Fixed issue where game controls would become unfocused.
** Misc. **
* Corporations can no longer bribe special factions
* Infiltration can no longer lose focus of the keyboard.
* Fix terminal line limit
* Added theme editor
* Theme applies on game load (@Nolshine)
* Sleeves no longer consume all bonus time for some actions
* Fix a bug where the autocomlete list would get duplicates
* Fix tutorial not scaling properly on small screens
* Import should be more consistent
* Typo with 'help' command
* Fix infinite loop in casino
* Fixed loader incorrectly assuming some null values are incorrect.
* installBackdoor trigger Bitverse sequence
* Some improvements to the theme editor
* Improved documentation about where to learn javascript.
* Added some instructions for contributors.
* Fixed typo in corporation sell shares modal (@Saynt_Garmo)
* Fixed pagination being black on black in Active Scripts
* Create Script tab renamed to Script Editor
* Fixed an issue where corp some textbox wouldn't update when changing city.
* Fixed an issue where hacknet online time was always 0.
* Netscript function prompt fixed.
* Fixed miscalculation in growth.
* Script with syntax errors will try to be a tad more helpful.
* Corporations can no longer bribe bladeburners.
* Augmentation Graphene Branchiblade renamed to Brachi, like the rest of them.
* All ram is displayed in GB/TB/PB now.
* Game now saves when saving a file, this can be turned off.
* Several improvement to log window.
* Bladeburner current action returns General type instead of the name of the action.
* Bladeburner travel and Sleeve travel respect disable ASCII.
* Tutorial fits on small screens.
* Import is much slower but more consistent now.
* Fix intelligence not updating properly.
* Added SF -1: Time Compression
* ReadTheDoc theme now matches the game.
* Logbox should wrap text better
* Logbox behavior should feel better.
* Fix font for AutoLink.exe
* nerf noodle bar
`,
/*
*/
};

@ -105,6 +105,7 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
}
//Parse quantity
amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD")) {
let q = amt.replace(/\s+/g, "");
q = q.replace(/[^-()\d/*+.MAXPROD]/g, "");
@ -168,6 +169,7 @@ export function SellProduct(product: Product, city: string, amt: string, price:
const cities = Object.keys(Cities);
// Parse quantity
amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD")) {
//Dynamically evaluated quantity. First test to make sure its valid
let qty = amt.replace(/\s+/g, "");
@ -372,7 +374,7 @@ export function Research(division: IIndustry, researchName: string): void {
export function ExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void {
// Sanitize amt
let sanitizedAmt = amt.replace(/\s+/g, "");
let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase();
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, "");
let temp = sanitizedAmt.replace(/MAX/g, "1");
try {

@ -564,7 +564,7 @@ export class Industry implements IIndustry {
buyAmt = mat.buy * CorporationConstants.SecsPerMarketCycle * marketCycles;
if (matName == "RealEstate") {
maxAmt = buyAmt;
maxAmt = corporation.funds.toNumber() / mat.bCost;
} else {
maxAmt = Math.floor((warehouse.size - warehouse.sizeUsed) / MaterialSizes[matName]);
}
@ -840,7 +840,7 @@ export class Industry implements IIndustry {
let sellAmt;
if (isString(mat.sllman[1])) {
//Dynamically evaluated
let tmp = (mat.sllman[1] as string).replace(/MAX/g, maxSell + "");
let tmp = (mat.sllman[1] as string).replace(/MAX/g, (maxSell + "").toUpperCase());
tmp = tmp.replace(/PROD/g, mat.prd + "");
try {
sellAmt = eval(tmp);
@ -893,7 +893,7 @@ export class Industry implements IIndustry {
const exp = mat.exp[expI];
const amtStr = exp.amt.replace(
/MAX/g,
mat.qty / (CorporationConstants.SecsPerMarketCycle * marketCycles) + "",
(mat.qty / (CorporationConstants.SecsPerMarketCycle * marketCycles) + "").toUpperCase(),
);
let amt = 0;
try {
@ -1201,7 +1201,7 @@ export class Industry implements IIndustry {
let sellAmt;
if (product.sllman[city][0] && isString(product.sllman[city][1])) {
//Sell amount is dynamically evaluated
let tmp = product.sllman[city][1].replace(/MAX/g, maxSell);
let tmp = product.sllman[city][1].replace(/MAX/g, (maxSell + "").toUpperCase());
tmp = tmp.replace(/PROD/g, product.data[city][1]);
try {
tmp = eval(tmp);

@ -185,11 +185,12 @@ export class Product {
0.05 * employeeProd[EmployeePositions.Business]);
this.calculateRating(industry);
const advMult = 1 + Math.pow(this.advCost, 0.1) / 100;
this.mku = 100 / (advMult * Math.pow(this.qlt + 0.001, 0.65) * (busRatio + mgmtRatio));
const busmgtgRatio = Math.max(busRatio + mgmtRatio, 1 / employeeProd["total"]);
this.mku = 100 / (advMult * Math.pow(this.qlt + 0.001, 0.65) * busmgtgRatio);
// I actually don't understand well enough to know if this is right.
// I'm adding this to prevent a crash.
if (this.mku === 0) this.mku = 1;
if (this.mku === 0 || !isFinite(this.mku)) this.mku = 1;
this.dmd =
industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));

@ -47,7 +47,7 @@ export const CorporationUpgrades: IMap<CorporationUpgrade> = {
"3": [
3,
4e9,
1.12,
1.5,
0.005,
"Wilson Analytics",
"Purchase data and analysis from Wilson, a marketing research " +

@ -80,10 +80,20 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
<Typography>Division name:</Typography>
<Box display="flex" alignItems="center">
<TextField autoFocus={true} value={name} onChange={onNameChange} onKeyDown={onKeyDown} type="text" />
<TextField
autoFocus={true}
value={name}
onChange={onNameChange}
onKeyDown={onKeyDown}
type="text"
InputProps={{
endAdornment: (
<Button disabled={disabled} sx={{ mx: 1 }} onClick={newIndustry}>
Create Division
Expand
</Button>
),
}}
/>
</Box>
</>
);

@ -43,16 +43,21 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
Would you like to expand into a new city by opening an office? This would cost{" "}
<MoneyCost money={CorporationConstants.OfficeInitialCost} corp={corp} />
</Typography>
<Select value={city} onChange={onCityChange}>
<Select
endAdornment={
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
}
value={city}
onChange={onCityChange}
>
{possibleCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}>
{cityName}
</MenuItem>
))}
</Select>
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
</>
);
}

@ -39,7 +39,9 @@ export function ExportModal(props: IProps): React.ReactElement {
}
function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(event.target.value);
const div = event.target.value;
setIndustry(div);
setCity(Object.keys(corp.divisions[0].warehouses)[0]);
}
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {

@ -30,8 +30,7 @@ export function Augmentations(props: IProps): React.ReactElement {
}
function queueAllAugs(): void {
for (const i in AugmentationNames) {
const augName = AugmentationNames[i];
for (const augName of Object.keys(AugmentationNames)) {
props.player.queueAugmentation(augName);
}
}

@ -2,13 +2,14 @@ import React from "react";
import { use } from "../ui/Context";
import { Exploit } from "./Exploit";
const getComputedStyle = window.getComputedStyle;
export function Unclickable(): React.ReactElement {
const player = use.Player();
function unclickable(event: React.MouseEvent<HTMLDivElement>): void {
if (!event.target || !(event.target instanceof Element)) return;
const display = window.getComputedStyle(event.target as Element).display;
const visibility = window.getComputedStyle(event.target as Element).visibility;
const display = getComputedStyle(event.target as Element).display;
const visibility = getComputedStyle(event.target as Element).visibility;
if (display === "none" && visibility === "hidden" && event.isTrusted) player.giveExploit(Exploit.Unclickable);
}

@ -24,9 +24,9 @@ function timeCompression(): void {
return;
}
last = now;
setTimeout(minute, 1000);
window.setTimeout(minute, 1000);
}
setTimeout(minute, 1000);
window.setTimeout(minute, 1000);
}
export function startExploits(): void {

@ -50,6 +50,11 @@ export class FactionInfo {
*/
keep: boolean;
/**
* Special faction
*/
special: boolean;
constructor(
infoText: JSX.Element,
enemies: string[],
@ -57,6 +62,7 @@ export class FactionInfo {
offerHackingWork: boolean,
offerFieldWork: boolean,
offerSecurityWork: boolean,
special: boolean,
keep: boolean,
) {
this.infoText = infoText;
@ -70,6 +76,7 @@ export class FactionInfo {
this.augmentationPriceMult = 1;
this.augmentationRepRequirementMult = 1;
this.keep = keep;
this.special = special;
}
offersWork(): boolean {
@ -96,6 +103,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
Daedalus: new FactionInfo(
@ -106,6 +114,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
"The Covenant": new FactionInfo(
@ -124,6 +133,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
// Megacorporations, each forms its own faction
@ -139,6 +149,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@ -158,6 +169,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@ -175,10 +187,11 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"Blade Industries": new FactionInfo(<>Augmentation is Salvation.</>, [], true, true, true, true, true),
"Blade Industries": new FactionInfo(<>Augmentation is Salvation.</>, [], true, true, true, true, false, true),
NWO: new FactionInfo(
(
@ -193,10 +206,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"Clarke Incorporated": new FactionInfo(<>The Power of the Genome - Unlocked.</>, [], true, true, true, true, true),
"Clarke Incorporated": new FactionInfo(
<>The Power of the Genome - Unlocked.</>,
[],
true,
true,
true,
true,
false,
true,
),
"OmniTek Incorporated": new FactionInfo(
<>Simply put, our mission is to design and build robots that make a difference.</>,
@ -205,6 +228,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@ -220,10 +244,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"KuaiGong International": new FactionInfo(<>Dream big. Work hard. Make history.</>, [], true, true, true, true, true),
"KuaiGong International": new FactionInfo(
<>Dream big. Work hard. Make history.</>,
[],
true,
true,
true,
true,
false,
true,
),
// Other Corporations
"Fulcrum Secret Technologies": new FactionInfo(
@ -238,6 +272,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
true,
false,
true,
),
@ -261,6 +296,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
"The Black Hand": new FactionInfo(
@ -280,6 +316,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
// prettier-ignore
@ -325,6 +362,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
// City factions, essentially governments
@ -336,8 +374,18 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
Chongqing: new FactionInfo(
<>Serve the People.</>,
["Sector-12", "Aevum", "Volhaven"],
true,
true,
true,
true,
false,
false,
),
Chongqing: new FactionInfo(<>Serve the People.</>, ["Sector-12", "Aevum", "Volhaven"], true, true, true, true, false),
Ishima: new FactionInfo(
<>The East Asian Order of the Future.</>,
["Sector-12", "Aevum", "Volhaven"],
@ -346,6 +394,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"New Tokyo": new FactionInfo(
<>Asia's World City.</>,
@ -355,6 +404,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"Sector-12": new FactionInfo(
<>The City of the Future.</>,
@ -364,6 +414,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
Volhaven: new FactionInfo(
<>Benefit, Honor, and Glory.</>,
@ -373,6 +424,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
// Criminal Organizations/Gangs
@ -384,6 +436,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"The Dark Army": new FactionInfo(
@ -394,9 +447,10 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
"The Syndicate": new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false),
"The Syndicate": new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false, false),
Silhouette: new FactionInfo(
(
@ -415,6 +469,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
Tetrads: new FactionInfo(
@ -425,14 +480,15 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"Slum Snakes": new FactionInfo(<>Slum Snakes rule!</>, [], false, false, true, true, false),
"Slum Snakes": new FactionInfo(<>Slum Snakes rule!</>, [], false, false, true, true, false, false),
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
Netburners: new FactionInfo(<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>, [], true, true, false, false, false),
Netburners: new FactionInfo(<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>, [], true, true, false, false, false, false),
"Tian Di Hui": new FactionInfo(<>Obey Heaven and work righteously.</>, [], true, true, false, true, false),
"Tian Di Hui": new FactionInfo(<>Obey Heaven and work righteously.</>, [], true, true, false, true, false, false),
CyberSec: new FactionInfo(
(
@ -448,6 +504,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
// Special Factions
@ -466,6 +523,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
true,
false,
),
@ -509,5 +567,6 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
true,
true,
),
};

@ -41,6 +41,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
if (isPlayersGang) {
const augs: string[] = [];
for (const augName in Augmentations) {
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
const aug = Augmentations[augName];
if (!aug.isSpecial) {
augs.push(augName);

@ -510,7 +510,9 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
}
if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`);
target.changeMaximumMoney(upg.value, true);
const old = target.moneyMax;
target.changeMaximumMoney(upg.value);
console.log(target.moneyMax / old);
} catch (e) {
player.hashManager.refundUpgrade(upgName);
return false;

@ -28,6 +28,7 @@ import { TableCell } from "../../ui/React/Table";
import TableBody from "@mui/material/TableBody";
import Table from "@mui/material/Table";
import TableRow from "@mui/material/TableRow";
import { numeralWrapper } from "../../ui/numeralFormat";
interface IProps {
node: HacknetNode;
@ -163,7 +164,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
<Typography>RAM:</Typography>
</TableCell>
<TableCell>
<Typography>{node.ram}GB</Typography>
<Typography>{numeralWrapper.formatRAM(node.ram)}</Typography>
</TableCell>
<TableCell>
<Button onClick={upgradeRamOnClick}>{upgradeRamContent}</Button>

@ -31,6 +31,7 @@ import { TableCell } from "../../ui/React/Table";
import TableBody from "@mui/material/TableBody";
import Table from "@mui/material/Table";
import TableRow from "@mui/material/TableRow";
import { numeralWrapper } from "../../ui/numeralFormat";
interface IProps {
node: HacknetServer;
@ -213,7 +214,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
<Typography>RAM:</Typography>
</TableCell>
<TableCell>
<Typography>{node.maxRam}GB</Typography>
<Typography>{numeralWrapper.formatRAM(node.maxRam)}</Typography>
</TableCell>
<TableCell>
<Button onClick={upgradeRamOnClick}>{upgradeRamContent}</Button>

@ -64,13 +64,21 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement {
</Typography>
<Typography>{upg.desc}</Typography>
{!upg.hasTargetServer && (
<Button onClick={purchase} disabled={!canPurchase}>
Purchase
Buy
</Button>
{level > 0 && effect && <Typography>{effect}</Typography>}
{upg.hasTargetServer && (
<ServerDropdown value={selectedServer} serverType={ServerType.Foreign} onChange={changeTargetServer} />
)}
{upg.hasTargetServer && (
<ServerDropdown
purchase={purchase}
canPurchase={canPurchase}
value={selectedServer}
serverType={ServerType.Foreign}
onChange={changeTargetServer}
/>
)}
{level > 0 && effect && <Typography>{effect}</Typography>}
</Paper>
);
}

@ -8,6 +8,7 @@ import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react";
import { numeralWrapper } from "../../ui/numeralFormat";
type IProps = {
p: IPlayer;
@ -31,7 +32,8 @@ export function RamButton(props: IProps): React.ReactElement {
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 3.2 \times 10^3 \times 1.58^{log_2{(ram)}}}`} />}>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({homeComputer.maxRam}GB -&gt;&nbsp;{homeComputer.maxRam * 2}GB) -&nbsp;
Upgrade 'home' RAM ({numeralWrapper.formatRAM(homeComputer.maxRam)} -&gt;&nbsp;
{numeralWrapper.formatRAM(homeComputer.maxRam * 2)}) -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</span>

@ -17,6 +17,7 @@ import { getPurchaseServerCost } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money";
import { use } from "../../ui/Context";
import { PurchaseServerModal } from "./PurchaseServerModal";
import { numeralWrapper } from "../../ui/numeralFormat";
interface IServerProps {
ram: number;
@ -30,7 +31,7 @@ function ServerButton(props: IServerProps): React.ReactElement {
return (
<>
<Button onClick={() => setOpen(true)} disabled={!player.canAfford(cost)}>
Purchase {props.ram}GB Server&nbsp;-&nbsp;
Purchase {numeralWrapper.formatRAM(props.ram)} Server&nbsp;-&nbsp;
<Money money={cost} player={player} />
</Button>
<PurchaseServerModal

@ -10,7 +10,6 @@ import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers";
import { compareArrays } from "../utils/helpers/compareArrays";
import { roundToTwo } from "../utils/helpers/roundToTwo";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean;
@ -90,15 +89,6 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
return;
}
// Recalculate ram used on that server
server.ramUsed = roundToTwo(server.ramUsed - workerScript.ramUsage);
if (server.ramUsed < 0) {
console.warn(
`Server (${server.hostname}) RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`,
);
server.ramUsed = 0;
}
// Delete the RunningScript object from that server
for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScript = server.runningScripts[i];
@ -108,6 +98,10 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
}
}
// Recalculate ram used on that server
server.ramUsed = 0;
for (const rs of server.runningScripts) server.ramUsed += rs.ramUsage * rs.threads;
// Delete script from global pool (workerScripts)
const res = workerScripts.delete(workerScript.pid);
if (!res) {

@ -13,13 +13,12 @@ export function netscriptDelay(time: number, workerScript: WorkerScript): Promis
}
export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string {
const lineNum = "";
const server = GetServer(workerScript.hostname);
if (server == null) {
throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.hostname}`);
}
return "|" + server.hostname + "|" + workerScript.name + "|" + msg + lineNum;
return "|" + server.hostname + "|" + workerScript.name + "|" + msg;
}
export function resolveNetscriptRequestedThreads(

@ -122,6 +122,7 @@ import { Router } from "./ui/GameRoot";
import { numeralWrapper } from "./ui/numeralFormat";
import { is2DArray } from "./utils/helpers/is2DArray";
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { SpecialServers } from "./Server/data/SpecialServers";
import { LogBoxEvents } from "./ui/React/LogBoxManager";
import { arrayToString } from "./utils/helpers/arrayToString";
@ -136,6 +137,7 @@ import { IIndustry } from "./Corporation/IIndustry";
import { Faction } from "./Faction/Faction";
import { Augmentation } from "./Augmentation/Augmentation";
import { Page } from "./ui/Router";
import { CodingContract } from "./CodingContracts";
import { Stock } from "./StockMarket/Stock";
@ -724,7 +726,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
const gang = NetscriptGang(Player, workerScript, helper);
const sleeve = NetscriptSleeve(Player, workerScript, helper);
const extra = NetscriptExtra(Player, workerScript, helper);
const extra = NetscriptExtra(Player, workerScript);
const hacknet = NetscriptHacknet(Player, workerScript, helper);
const stanek = NetscriptStanek(Player, workerScript, helper);
@ -732,7 +734,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
hacknet: hacknet,
sprintf: sprintf,
vsprintf: vsprintf,
scan: function (ip: any = workerScript.hostname, hostnames: any = true): any {
scan: function (ip: any = workerScript.hostname): any {
updateDynamicRam("scan", getRamCost("scan"));
const server = GetServer(ip);
if (server == null) {
@ -740,14 +742,9 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
const out = [];
for (let i = 0; i < server.serversOnNetwork.length; i++) {
let entry;
const s = getServerOnNetwork(server, i);
if (s === null) continue;
if (hostnames) {
entry = s.hostname;
} else {
entry = s.hostname;
}
const entry = s.hostname;
if (entry == null) {
continue;
}
@ -874,7 +871,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return Promise.resolve(moneyAfter / moneyBefore);
});
},
growthAnalyze: function (ip: any, growth: any): any {
growthAnalyze: function (ip: any, growth: any, cores: any = 1): any {
updateDynamicRam("growthAnalyze", getRamCost("growthAnalyze"));
// Check argument validity
@ -887,7 +884,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
throw makeRuntimeErrorMsg("growthAnalyze", `Invalid argument: growth must be numeric and >= 1, is ${growth}.`);
}
return numCycleForGrowth(server, Number(growth), Player, 1);
return numCycleForGrowth(server, Number(growth), Player, cores);
},
weaken: function (ip: any, { threads: requestedThreads }: any = {}): any {
updateDynamicRam("weaken", getRamCost("weaken"));
@ -1596,9 +1593,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
},
getServer: function (ip: any): any {
updateDynamicRam("getServer", getRamCost("getServer"));
if (SourceFileFlags[5] <= 0 && Player.bitNodeN !== 5) {
throw makeRuntimeErrorMsg("getServer", "Requires Source-File 5 to run.");
}
const server = safeGetServer(ip, "getServer");
const copy = Object.assign({}, server);
// These fields should be hidden.
@ -2333,17 +2327,16 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
// Create a new script
script = new Script(fn, data, server.hostname, server.scripts);
server.scripts.push(script);
return true;
return script.updateRamUsage(server.scripts);
}
mode === "w" ? (script.code = data) : (script.code += data);
script.updateRamUsage(server.scripts);
script.markUpdated();
return script.updateRamUsage(server.scripts);
} else {
// Write to text file
const txtFile = getTextFile(fn, server);
if (txtFile == null) {
createTextFile(fn, data, server);
return true;
return Promise.resolve();
}
if (mode === "w") {
txtFile.write(data);
@ -2351,7 +2344,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
txtFile.append(data);
}
}
return true;
return Promise.resolve();
} else {
throw makeRuntimeErrorMsg("write", `Invalid argument: ${port}`);
}
@ -3097,6 +3090,10 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
workerScript.log("installBackdoor", `Successfully installed backdoor on '${server.hostname}'`);
server.backdoorInstalled = true;
if (SpecialServers.WorldDaemon === server.hostname) {
Router.toBitVerse(false, false);
}
return Promise.resolve();
});
},
@ -3260,12 +3257,16 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
hospitalize: function (): any {
updateDynamicRam("hospitalize", getRamCost("hospitalize"));
checkSingularityAccess("hospitalize", 1);
if (Player.isWorking || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse) {
workerScript.log("hospitalize", "Cannot go to the hospital because the player is busy.");
return;
}
return Player.hospitalize();
},
isBusy: function (): any {
updateDynamicRam("isBusy", getRamCost("isBusy"));
checkSingularityAccess("isBusy", 1);
return Player.isWorking;
return Player.isWorking || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse;
},
stopAction: function (): any {
updateDynamicRam("stopAction", getRamCost("stopAction"));
@ -3301,7 +3302,9 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
workerScript.log(
"upgradeHomeRam",
`Purchased additional RAM for home computer! It now has ${homeComputer.maxRam}GB of RAM.`,
`Purchased additional RAM for home computer! It now has ${numeralWrapper.formatRAM(
homeComputer.maxRam,
)} of RAM.`,
);
return true;
},
@ -4191,9 +4194,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
},
joinBladeburnerDivision: function (): any {
updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision"));
checkBladeburnerAccess("joinBladeburnerDivision", true);
const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
if (Player.bitNodeN === 7 || SourceFileFlags[7] > 0) {
if (Player.bitNodeN === 8) {
return false;
@ -4209,12 +4209,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
Player.bladeburner = new Bladeburner(Player);
workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division");
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
return true;
} else {
workerScript.log(
@ -4634,19 +4628,19 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
...extra,
};
function getFunctionNames(obj: NS): string[] {
function getFunctionNames(obj: NS, prefix: string): string[] {
const functionNames: string[] = [];
for (const [key, value] of Object.entries(obj)) {
if (typeof value == "function") {
functionNames.push(key);
functionNames.push(prefix + key);
} else if (typeof value == "object") {
functionNames.push(...getFunctionNames(value));
functionNames.push(...getFunctionNames(value, key + "."));
}
}
return functionNames;
}
const possibleLogs = Object.fromEntries([...getFunctionNames(functions)].map((a) => [a, true]));
const possibleLogs = Object.fromEntries([...getFunctionNames(functions, "")].map((a) => [a, true]));
return functions;
} // End NetscriptFunction()

@ -1,4 +1,3 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Exploit } from "../Exploits/Exploit";
@ -11,7 +10,7 @@ export interface INetscriptExtra {
bypass(doc: Document): void;
}
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): INetscriptExtra {
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INetscriptExtra {
return {
heart: {
// Easter egg function

@ -8,6 +8,7 @@ import { WorkerScript } from "../Netscript/WorkerScript";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
import { Augmentations } from "../Augmentation/Augmentations";
import { CityName } from "../Locations/data/CityNames";
import { findCrime } from "../Crime/CrimeHelpers";
export interface INetscriptSleeve {
getNumSleeves(): number;
@ -87,13 +88,17 @@ export function NetscriptSleeve(
checkSleeveNumber("setToSynchronize", sleeveNumber);
return player.sleeves[sleeveNumber].synchronize(player);
},
setToCommitCrime: function (asleeveNumber: any = 0, acrimeName: any = ""): boolean {
setToCommitCrime: function (asleeveNumber: any = 0, aCrimeRoughName: any = ""): boolean {
const sleeveNumber = helper.number("setToCommitCrime", "sleeveNumber", asleeveNumber);
const crimeName = helper.string("setToUniversityCourse", "crimeName", acrimeName);
const crimeRoughName = helper.string("setToCommitCrime", "crimeName", aCrimeRoughName);
helper.updateDynamicRam("setToCommitCrime", getRamCost("sleeve", "setToCommitCrime"));
checkSleeveAPIAccess("setToCommitCrime");
checkSleeveNumber("setToCommitCrime", sleeveNumber);
return player.sleeves[sleeveNumber].commitCrime(player, crimeName);
const crime = findCrime(crimeRoughName);
if (crime === null) {
return false;
}
return player.sleeves[sleeveNumber].commitCrime(player, crime.name);
},
setToUniversityCourse: function (asleeveNumber: any = 0, auniversityName: any = "", aclassName: any = ""): boolean {
const sleeveNumber = helper.number("setToUniversityCourse", "sleeveNumber", asleeveNumber);

@ -125,10 +125,14 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
.catch((e) => reject(e));
}).catch((e) => {
if (e instanceof Error) {
if (e instanceof SyntaxError) {
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e.message + " (sorry we can't be more helpful)");
} else {
workerScript.errorMessage = makeRuntimeRejectMsg(
workerScript,
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
);
}
throw workerScript;
} else if (isScriptErrorMessage(e)) {
workerScript.errorMessage = e;
@ -190,7 +194,8 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
cb(res);
})
.catch(function (err: any) {
console.error(err);
// workerscript is when you cancel a delay
if (!(err instanceof WorkerScript)) console.error(err);
});
};
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));

@ -201,6 +201,7 @@ export interface IPlayer {
inGang(): boolean;
isQualified(company: Company, position: CompanyPosition): boolean;
loseMoney(money: number): void;
process(router: IRouter, numCycles?: number): void;
reapplyAllAugmentations(resetMultipliers?: boolean): void;
reapplyAllSourceFiles(): void;
regenerateHp(amt: number): void;

@ -245,6 +245,7 @@ export class PlayerObject implements IPlayer {
getIntelligenceBonus: (weight: number) => number;
getCasinoWinnings: () => number;
quitJob: (company: string) => void;
process: (router: IRouter, numCycles?: number) => void;
createHacknetServer: () => HacknetServer;
startCreateProgramWork: (router: IRouter, programName: string, time: number, reqLevel: number) => void;
queueAugmentation: (augmentationName: string) => void;
@ -507,6 +508,7 @@ export class PlayerObject implements IPlayer {
this.getWorkAgiExpGain = generalMethods.getWorkAgiExpGain;
this.getWorkChaExpGain = generalMethods.getWorkChaExpGain;
this.getWorkRepGain = generalMethods.getWorkRepGain;
this.process = generalMethods.process;
this.startCreateProgramWork = generalMethods.startCreateProgramWork;
this.createProgramWork = generalMethods.createProgramWork;
this.finishCreateProgramWork = generalMethods.finishCreateProgramWork;

@ -77,7 +77,6 @@ export function init(this: IPlayer): void {
}
export function prestigeAugmentation(this: IPlayer): void {
const homeComp = this.getHomeComputer();
this.currentServer = SpecialServers.Home;
this.numPeopleKilled = 0;
@ -453,6 +452,8 @@ export function gainIntelligenceExp(this: IPlayer, exp: number): void {
if (SourceFileFlags[5] > 0 || this.intelligence > 0) {
this.intelligence_exp += exp;
}
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
}
//Given a string expression like "str" or "strength", returns the given stat
@ -574,6 +575,37 @@ export function startWork(this: IPlayer, router: IRouter, companyName: string):
router.toWork();
}
export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
// Working
if (this.isWorking) {
if (this.workType == CONSTANTS.WorkTypeFaction) {
if (this.workForFaction(numCycles)) {
router.toFaction();
}
} else if (this.workType == CONSTANTS.WorkTypeCreateProgram) {
if (this.createProgramWork(numCycles)) {
router.toTerminal();
}
} else if (this.workType == CONSTANTS.WorkTypeStudyClass) {
if (this.takeClass(numCycles)) {
router.toCity();
}
} else if (this.workType == CONSTANTS.WorkTypeCrime) {
if (this.commitCrime(numCycles)) {
router.toLocation(Locations[LocationName.Slums]);
}
} else if (this.workType == CONSTANTS.WorkTypeCompanyPartTime) {
if (this.workPartTime(numCycles)) {
router.toCity();
}
} else {
if (this.work(numCycles)) {
router.toCity();
}
}
}
}
export function cancelationPenalty(this: IPlayer): number {
const server = GetServer(this.companyName);
if (server instanceof Server) {

@ -476,9 +476,9 @@ export class Sleeve extends Person {
return null;
}
let time = this.storedCycles * CONSTANTS.MilliPerCycle;
let cyclesUsed = this.storedCycles;
cyclesUsed = Math.min(cyclesUsed, 15);
let time = cyclesUsed * CONSTANTS.MilliPerCycle;
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
time = this.currentTaskMaxTime - this.currentTaskTime;
cyclesUsed = Math.floor(time / CONSTANTS.MilliPerCycle);
@ -489,6 +489,7 @@ export class Sleeve extends Person {
cyclesUsed = 0;
}
}
this.currentTaskTime += time;
// Shock gradually goes towards 100
@ -846,7 +847,6 @@ export class Sleeve extends Person {
this.currentTaskLocation = companyName;
this.currentTask = SleeveTaskType.Company;
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer8Hours;
return true;
}
@ -906,7 +906,6 @@ export class Sleeve extends Person {
this.currentTaskLocation = factionName;
this.currentTask = SleeveTaskType.Faction;
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours;
return true;
}

@ -29,4 +29,5 @@ export function loadPlayer(saveString: string): void {
}
Player.exploits = sanitizeExploits(Player.exploits);
console.log(Player.bladeburner);
}

@ -137,7 +137,7 @@ function prestigeAugmentation(): void {
}
}
if (augmentationExists(AugmentationNames.StaneksGift) && Augmentations[AugmentationNames.StaneksGift].owned) {
if (augmentationExists(AugmentationNames.StaneksGift1) && Augmentations[AugmentationNames.StaneksGift1].owned) {
// TODO(hydroflame): refactor faction names so we don't have to hard
// code strings.
joinFaction(Factions["Church of the Machine God"]);

@ -144,6 +144,18 @@ function evaluateVersionCompatibility(ver: string): void {
delete anyPlayer.companyPosition;
}
if (ver < "0.56.0") {
for (const q of anyPlayer.queuedAugmentations) {
if (q.name === "Graphene BranchiBlades Upgrade") {
q.name = "Graphene BrachiBlades Upgrade";
}
}
for (const q of anyPlayer.augmentations) {
if (q.name === "Graphene BranchiBlades Upgrade") {
q.name = "Graphene BrachiBlades Upgrade";
}
}
}
}
function loadGame(saveString: string): boolean {

@ -109,6 +109,7 @@ export class Script {
if (res > 0) {
this.ramUsage = roundToTwo(res);
}
this.markUpdated();
}
// Serialize the current object to a JSON save state

File diff suppressed because it is too large Load Diff

@ -44,6 +44,7 @@ export function OptionsModal(props: IProps): React.ReactElement {
<Select onChange={(event) => setTheme(event.target.value)} defaultValue={props.options.theme}>
<MenuItem value="vs-dark">dark</MenuItem>
<MenuItem value="light">light</MenuItem>
<MenuItem value="monokai">monokai</MenuItem>
</Select>
</Box>

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from "react";
import React, { useState, useEffect, useRef, useMemo } from "react";
import Editor from "@monaco-editor/react";
import * as monaco from "monaco-editor";
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
@ -21,6 +21,8 @@ import { NetscriptFunctions } from "../../NetscriptFunctions";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
import { debounce } from "lodash";
import { saveObject } from "../../SaveObject";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
@ -30,6 +32,7 @@ import TextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";
import SettingsIcon from "@mui/icons-material/Settings";
let symbolsLoaded = false;
let symbols: string[] = [];
export function SetupTextEditor(): void {
const ns = NetscriptFunctions({} as WorkerScript);
@ -83,6 +86,7 @@ export function Root(props: IProps): React.ReactElement {
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
const [code, setCode] = useState<string>(props.filename ? props.code : lastCode);
const [ram, setRAM] = useState("RAM: ???");
const [updatingRam, setUpdatingRam] = useState(false);
const [optionsOpen, setOptionsOpen] = useState(false);
const [options, setOptions] = useState<Options>({
theme: Settings.MonacoTheme,
@ -90,6 +94,15 @@ export function Root(props: IProps): React.ReactElement {
fontSize: Settings.MonacoFontSize,
});
const debouncedSetRAM = useMemo(
() =>
debounce((s) => {
setRAM(s);
setUpdatingRam(false);
}, 300),
[],
);
// store the last known state in case we need to restart without nano.
useEffect(() => {
if (props.filename === undefined) return;
@ -164,6 +177,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(filename, code, props.player.currentServer, server.scripts);
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
return;
}
@ -177,6 +191,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === filename) {
server.textFiles[i].write(code);
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
return;
}
@ -187,6 +202,8 @@ export function Root(props: IProps): React.ReactElement {
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
return;
}
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
}
@ -212,38 +229,40 @@ export function Root(props: IProps): React.ReactElement {
lastPosition = editorRef.current.getPosition();
}
setCode(newCode);
updateRAM(newCode);
}
async function updateRAM(): Promise<void> {
const codeCopy = code + "";
// calculate it once the first time the file is loaded.
useEffect(() => {
updateRAM(code);
}, []);
async function updateRAM(newCode: string): Promise<void> {
setUpdatingRam(true);
const codeCopy = newCode + "";
const ramUsage = await calculateRamUsage(codeCopy, props.player.getCurrentServer().scripts);
if (ramUsage > 0) {
setRAM("RAM: " + numeralWrapper.formatRAM(ramUsage));
debouncedSetRAM("RAM: " + numeralWrapper.formatRAM(ramUsage));
return;
}
switch (ramUsage) {
case RamCalculationErrorCode.ImportError: {
setRAM("RAM: Import Error");
debouncedSetRAM("RAM: Import Error");
break;
}
case RamCalculationErrorCode.URLImportError: {
setRAM("RAM: HTTP Import Error");
debouncedSetRAM("RAM: HTTP Import Error");
break;
}
case RamCalculationErrorCode.SyntaxError:
default: {
setRAM("RAM: Syntax Error");
debouncedSetRAM("RAM: Syntax Error");
break;
}
}
return new Promise<void>(() => undefined);
}
useEffect(() => {
const id = setInterval(updateRAM, 1000);
return () => clearInterval(id);
}, [code]);
useEffect(() => {
function maybeSave(event: KeyboardEvent): void {
if (Settings.DisableHotkeys) return;
@ -275,6 +294,8 @@ export function Root(props: IProps): React.ReactElement {
}
function beforeMount(monaco: any): void {
if (symbolsLoaded) return;
symbolsLoaded = true;
monaco.languages.registerCompletionItemProvider("javascript", {
provideCompletionItems: () => {
const suggestions = [];
@ -323,7 +344,9 @@ export function Root(props: IProps): React.ReactElement {
/>
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
<Button onClick={beautify}>Beautify</Button>
<Typography sx={{ mx: 1 }}>{ram}</Typography>
<Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}>
{ram}
</Typography>
<Button onClick={save}>Save & Close (Ctrl/Cmd + b)</Button>
<Link sx={{ mx: 1 }} target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
<Typography> Netscript Documentation</Typography>

@ -0,0 +1,51 @@
export async function loadThemes(monaco: { editor: any }): Promise<void> {
monaco.editor.defineTheme("monokai", {
base: "vs-dark",
inherit: true,
rules: [
{
background: "272822",
token: "",
},
{
foreground: "75715e",
token: "comment",
},
{
foreground: "e6db74",
token: "string",
},
{
token: "number",
foreground: "ae81ff",
},
{
foreground: "ae81ff",
token: "function",
},
{
foreground: "f92672",
token: "keyword",
},
{
token: "storage.type.function.js",
foreground: "ae81ff",
},
// {
// foreground: "ae81ff",
// token: "entity.name.function",
// },
],
colors: {
"editor.foreground": "#F8F8F2",
"editor.background": "#272822",
"editor.selectionBackground": "#49483E",
"editor.lineHighlightBackground": "#3E3D32",
"editorCursor.foreground": "#F8F8F0",
"editorWhitespace.foreground": "#3B3A32",
"editorIndentGuide.activeBackground": "#9D550FB0",
"editor.selectionHighlightBorder": "#222218",
},
});
}

@ -125,14 +125,16 @@ export class Server extends BaseServer {
/**
* Change this server's maximum money
* @param n - Value by which to change the server's maximum money
* @param perc - Whether it should be changed by a percentage, or a flat value
*/
changeMaximumMoney(n: number, perc = false): void {
if (perc) {
this.moneyMax *= n;
} else {
this.moneyMax += n;
changeMaximumMoney(n: number): void {
const softCap = 10e12;
if (this.moneyMax > softCap) {
const aboveCap = this.moneyMax - softCap;
n = 1 + (n - 1) / Math.log(aboveCap) / Math.log(8);
}
console.log(n);
this.moneyMax *= n;
}
/**

@ -68,6 +68,11 @@ interface IDefaultSettings {
*/
MaxTerminalCapacity: number;
/**
* Save the game when you save any file.
*/
SaveGameOnFileSave: boolean;
/**
* Whether the player should be asked to confirm purchasing each and every augmentation.
*/
@ -168,6 +173,7 @@ export const defaultSettings: IDefaultSettings = {
MaxLogCapacity: 50,
MaxPortCapacity: 50,
MaxTerminalCapacity: 200,
SaveGameOnFileSave: true,
SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false,
SuppressHospitalizationPopup: false,
@ -226,6 +232,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity,
OwnedAugmentationsOrder: OwnedAugmentationsOrderSetting.AcquirementTime,
PurchaseAugmentationsOrder: PurchaseAugmentationsOrderSetting.Default,
SaveGameOnFileSave: defaultSettings.SaveGameOnFileSave,
SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation,
SuppressFactionInvites: defaultSettings.SuppressFactionInvites,
SuppressHospitalizationPopup: defaultSettings.SuppressHospitalizationPopup,
@ -234,7 +241,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
SuppressBladeburnerPopup: defaultSettings.SuppressBladeburnerPopup,
MonacoTheme: "vs-dark",
MonacoInsertSpaces: false,
MonacoFontSize: 10,
MonacoFontSize: 20,
theme: {
primarylight: defaultSettings.theme.primarylight,

@ -14,6 +14,7 @@ export const TerminalHelpText: string[] = [
"clear Clear all text on the terminal ",
"cls See 'clear' command ",
"connect [hostname] Connects to a remote server",
"cp [src] [dst]: Copy a file",
"download [script/text file] Downloads scripts or text files to your computer",
"expr [math expression] Evaluate a mathematical expression",
"free Check the machine's memory (RAM) usage",
@ -158,6 +159,7 @@ export const HelpTexts: IMap<string[]> = {
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
"see which servers can be connected to, use the 'scan' command.",
],
cp: ["cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp."],
download: [
"download [script/text file]",
" ",

@ -41,6 +41,7 @@ import { cat } from "./commands/cat";
import { cd } from "./commands/cd";
import { check } from "./commands/check";
import { connect } from "./commands/connect";
import { cp } from "./commands/cp";
import { download } from "./commands/download";
import { expr } from "./commands/expr";
import { free } from "./commands/free";
@ -729,6 +730,7 @@ export class Terminal implements ITerminal {
clear: () => this.clear(),
cls: () => this.clear(),
connect: connect,
cp: cp,
download: download,
expr: expr,
free: free,

@ -0,0 +1,92 @@
import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/isScriptFilename";
export function cp(
terminal: ITerminal,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],
): void {
try {
if (args.length !== 2) {
terminal.error("Incorrect usage of cp command. Usage: cp [src] [dst]");
return;
}
const src = args[0] + "";
const dst = args[1] + "";
if (src === dst) {
terminal.error("src and dst cannot be the same");
return;
}
const srcExt = src.slice(src.lastIndexOf("."));
const dstExt = dst.slice(dst.lastIndexOf("."));
if (srcExt !== dstExt) {
terminal.error("src and dst must have the same extension.");
return;
}
const filename = terminal.getFilepath(src);
if (!isScriptFilename(filename) && !filename.endsWith(".txt")) {
terminal.error("cp only works for scripts and .txt files");
return;
}
// Scp for txt files
if (filename.endsWith(".txt")) {
let txtFile = null;
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === filename) {
txtFile = server.textFiles[i];
break;
}
}
if (txtFile === null) {
return terminal.error("No such file exists!");
}
const tRes = server.writeToTextFile(dst, txtFile.text);
if (!tRes.success) {
terminal.error("scp failed");
return;
}
if (tRes.overwritten) {
terminal.print(`WARNING: ${dst} already exists and will be overwriten`);
terminal.print(`${dst} overwritten`);
return;
}
terminal.print(`${dst} copied`);
return;
}
// Get the current script
let sourceScript = null;
for (let i = 0; i < server.scripts.length; ++i) {
if (filename == server.scripts[i].filename) {
sourceScript = server.scripts[i];
break;
}
}
if (sourceScript == null) {
terminal.error("cp() failed. No such script exists");
return;
}
const sRes = server.writeToScriptFile(dst, sourceScript.code);
if (!sRes.success) {
terminal.error(`scp failed`);
return;
}
if (sRes.overwritten) {
terminal.print(`WARNING: ${dst} already exists and will be overwritten`);
terminal.print(`${dst} overwritten`);
return;
}
terminal.print(`${dst} copied`);
} catch (e) {
terminal.error(e + "");
}
}

@ -24,7 +24,10 @@ export function nano(
if (script == null) {
let code = "";
if (filename.endsWith(".ns") || filename.endsWith(".js")) {
code = `export async function main(ns) {
code = `/**
* @param {NS} ns
**/
export async function main(ns) {
}`;
}

@ -7,6 +7,7 @@ import { startWorkerScript } from "../../NetscriptWorker";
import { RunningScript } from "../../Script/RunningScript";
import { findRunningScript } from "../../Script/ScriptHelpers";
import * as libarg from "arg";
import { numeralWrapper } from "../../ui/numeralFormat";
export function runScript(
terminal: ITerminal,
@ -64,8 +65,8 @@ export function runScript(
"This machine does not have enough RAM to run this script with " +
numThreads +
" threads. Script requires " +
ramUsage +
"GB of RAM",
numeralWrapper.formatRAM(ramUsage) +
" of RAM",
);
return;
}

@ -18,6 +18,7 @@ const commands = [
"clear",
"cls",
"connect",
"cp",
"download",
"expr",
"free",
@ -239,6 +240,13 @@ export function determineAllPossibilitiesForTabCompletion(
return allPos;
}
if (isCommand("cp") && index === 0) {
addAllScripts();
addAllTextFiles();
addAllDirectories();
return allPos;
}
if (isCommand("connect")) {
// All network connections
for (let i = 0; i < currServ.serversOnNetwork.length; ++i) {

@ -35,8 +35,6 @@ import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { Locations } from "./Locations/Locations";
import { LocationName } from "./Locations/data/LocationNames";
import { Money } from "./ui/React/Money";
import { Hashes } from "./ui/React/Hashes";
@ -92,34 +90,7 @@ const Engine: {
Terminal.process(Router, Player, numCycles);
// Working
if (Player.isWorking) {
if (Player.workType == CONSTANTS.WorkTypeFaction) {
if (Player.workForFaction(numCycles)) {
Router.toFaction();
}
} else if (Player.workType == CONSTANTS.WorkTypeCreateProgram) {
if (Player.createProgramWork(numCycles)) {
Router.toTerminal();
}
} else if (Player.workType == CONSTANTS.WorkTypeStudyClass) {
if (Player.takeClass(numCycles)) {
Router.toCity();
}
} else if (Player.workType == CONSTANTS.WorkTypeCrime) {
if (Player.commitCrime(numCycles)) {
Router.toLocation(Locations[LocationName.Slums]);
}
} else if (Player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
if (Player.workPartTime(numCycles)) {
Router.toCity();
}
} else {
if (Player.work(numCycles)) {
Router.toCity();
}
}
}
Player.process(Router, numCycles);
// Update stock prices
if (Player.hasWseAccount) {

@ -77,6 +77,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
const [disableTextEffects, setDisableTextEffects] = useState(Settings.DisableTextEffects);
const [enableBashHotkeys, setEnableBashHotkeys] = useState(Settings.EnableBashHotkeys);
const [enableTimestamps, setEnableTimestamps] = useState(Settings.EnableTimestamps);
const [saveGameOnFileSave, setSaveGameOnFileSave] = useState(Settings.SaveGameOnFileSave);
const [locale, setLocale] = useState(Settings.Locale);
const [diagnosticOpen, setDiagnosticOpen] = useState(false);
@ -165,6 +166,10 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
setEnableTimestamps(event.target.checked);
Settings.EnableTimestamps = event.target.checked;
}
function handleSaveGameOnFile(event: React.ChangeEvent<HTMLInputElement>): void {
setSaveGameOnFileSave(event.target.checked);
Settings.SaveGameOnFileSave = event.target.checked;
}
function startImport(): void {
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
@ -505,6 +510,19 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
/>
</ListItem>
<ListItem>
<FormControlLabel
control={<Switch checked={saveGameOnFileSave} onChange={handleSaveGameOnFile} />}
label={
<Tooltip
title={<Typography>Save your game any time a file is saved in the script editor.</Typography>}
>
<Typography>Save game on file save</Typography>
</Tooltip>
}
/>
</ListItem>
<ListItem>
<Tooltip title={<Typography>Sets the locale for displaying numbers.</Typography>}>
<Typography>Locale&nbsp;</Typography>

@ -11,6 +11,7 @@ import { BaseServer } from "../../Server/BaseServer";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Button from "@mui/material/Button";
// TODO make this an enum when this gets converted to TypeScript
export const ServerType = {
@ -21,6 +22,8 @@ export const ServerType = {
};
interface IProps {
purchase: () => void;
canPurchase: boolean;
serverType: number;
onChange: (event: SelectChangeEvent<string>) => void;
value: string;
@ -61,7 +64,16 @@ export function ServerDropdown(props: IProps): React.ReactElement {
}
return (
<Select sx={{ mx: 1 }} value={props.value} onChange={props.onChange}>
<Select
startAdornment={
<Button onClick={props.purchase} disabled={!props.canPurchase}>
Buy
</Button>
}
sx={{ mx: 1 }}
value={props.value}
onChange={props.onChange}
>
{servers}
</Select>
);

@ -36,24 +36,16 @@ export function TablePaginationActionsAll(props: TablePaginationActionsProps): R
return (
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
<IconButton onClick={handleFirstPageButtonClick} disabled={page === 0} aria-label="first page">
<IconButton onClick={handleFirstPageButtonClick} disabled={page === 0}>
{theme.direction === "rtl" ? <LastPageIcon /> : <FirstPageIcon />}
</IconButton>
<IconButton onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
<IconButton onClick={handleBackButtonClick} disabled={page === 0}>
{theme.direction === "rtl" ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="next page"
>
<IconButton onClick={handleNextButtonClick} disabled={page >= Math.ceil(count / rowsPerPage) - 1}>
{theme.direction === "rtl" ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</IconButton>
<IconButton
onClick={handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="last page"
>
<IconButton onClick={handleLastPageButtonClick} disabled={page >= Math.ceil(count / rowsPerPage) - 1}>
{theme.direction === "rtl" ? <FirstPageIcon /> : <LastPageIcon />}
</IconButton>
</Box>

@ -276,6 +276,12 @@ export function refreshTheme(): void {
select: {
color: Settings.theme.primary,
},
selectLabel: {
color: Settings.theme.primary,
},
displayedRows: {
color: Settings.theme.primary,
},
},
},
MuiTab: {

@ -2,6 +2,8 @@ import React, { useState } from "react";
import { Modal } from "./Modal";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import TextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";
import ReplyIcon from "@mui/icons-material/Reply";
@ -87,17 +89,22 @@ export function ThemeEditorModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Button color="primary">primary</Button>
<Button color="secondary">secondary</Button>
<Button color="warning">warning</Button>
<Button color="info">info</Button>
<Button color="error">error</Button>
<Typography color="primary">primary</Typography>
<Typography color="secondary">secondary</Typography>
<Typography color="warning">warning</Typography>
<Typography color="info">info</Typography>
<Typography color="error">error</Typography>
<Paper>
<Tooltip open={true} placement={"top"} title={<Typography>Example tooltip</Typography>}>
<Button color="primary">primary button</Button>
</Tooltip>
<Button color="secondary">secondary button</Button>
<Button color="warning">warning button</Button>
<Button color="info">info button</Button>
<Button color="error">error button</Button>
<Button disabled>disabled button</Button>
<Typography color="primary">text with primary color</Typography>
<Typography color="secondary">text with secondary color</Typography>
<Typography color="error">text with error color</Typography>
<TextField value={"Text field"} />
</Paper>
<br />
<Typography>Warning: Editing the theme is very slow.</Typography>
<ColorEditor
name="primarylight"
onColorChange={onColorChange}

@ -106,6 +106,10 @@ class NumeralFormatter {
}
formatRAM(n: number): string {
if (n < 1e3) return this.format(n, "0.00") + "GB";
if (n < 1e6) return this.format(n / 1e3, "0.00") + "TB";
if (n < 1e9) return this.format(n / 1e6, "0.00") + "PB";
if (n < 1e12) return this.format(n / 1e9, "0.00") + "EB";
return this.format(n, "0.00") + "GB";
}

@ -12,8 +12,7 @@ export interface IReviverValue {
// off to that `fromJSON` fuunction, passing in the value.
export function Reviver(key: string, value: IReviverValue | null): any {
if (value == null) {
console.log("Reviver WRONGLY called with key: " + key + ", and value: " + value);
return 0;
return null;
}
if (typeof value === "object" && typeof value.ctor === "string" && typeof value.data !== "undefined") {