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-catch-shadow": ["error"],
"no-class-assign": ["error"], "no-class-assign": ["error"],
"no-compare-neg-zero": ["error"], "no-compare-neg-zero": ["error"],
"no-cond-assign": ["off", "except-parens"],
"no-confusing-arrow": ["error"], "no-confusing-arrow": ["error"],
"no-console": ["off"], "no-console": ["off"],
"no-const-assign": ["error"], "no-const-assign": ["error"],

@ -84,6 +84,28 @@ changes are okay to contribute:
- Changes that directly affect the game's balance - Changes that directly affect the game's balance
- New gameplay mechanics - 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 #### Submitting a Pull Request
When submitting a pull request with your code contributions, please abide by 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: .. _gameplay_sourcefiles:
.. warning:: This page contains spoilers regarding the game's story/plot-line. .. warning:: This page contains spoilers for the game
Source-Files Source-Files
============ ============

@ -3,6 +3,109 @@
Changelog 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) v0.55.0 - 2021-09-20 Material UI (hydroflame & community)
------------------------------------------- -------------------------------------------

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

@ -6,8 +6,6 @@ getServer() Netscript Function
:RAM cost: 2 GB :RAM cost: 2 GB
:param string hostname: Hostname of the server, defaults to host server. :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>`. 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:: Returns an object with the Server's stats. The object has the following properties::

@ -1,12 +1,13 @@
growthAnalyze() Netscript Function growthAnalyze() Netscript Function
================================== ==================================
.. js:function:: growthAnalyze(hostname, growthAmount) .. js:function:: growthAnalyze(hostname, growthAmount[, cores])
:RAM cost: 1 GB :RAM cost: 1 GB
:param string hostname: Hostname of server to analyze. :param string hostname: Hostname of server to analyze.
:param number growthAmount: Multiplicative factor by which the server is :param number growthAmount: Multiplicative factor by which the server is
grown. Decimal form. Must be >= 1. 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 :returns: The amount of :doc:`grow<grow>` threads needed to grow the specified
server by the specified amount. 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: Here are some good tutorials for learning programming/JavaScript as a beginner:
* `Learn-JS <http://www.learn-js.org/en/Welcome>`_ * `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>`_ * `Speaking JavaScript <http://speakingjs.com/es5/index.html>`_
This is a bit on the longer side. You can skip all of the historical This is a bit on the longer side. You can skip all of the historical
background stuff. Recommended chapters: 1, 7-18 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", "name": "bitburner",
"license": "SEE LICENSE IN license.txt", "license": "SEE LICENSE IN license.txt",
"version": "0.53.0", "version": "0.56.0",
"main": "electron-main.js", "main": "electron-main.js",
"author": { "author": {
"name": "Daniel Xie" "name": "Daniel Xie"

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

@ -8,7 +8,8 @@ const path = require("path");
const exec = require("child_process").exec; const exec = require("child_process").exec;
const semver = require("./semver"); const semver = require("./semver");
const getPackageJson = () => new Promise((resolve, reject) => { const getPackageJson = () =>
new Promise((resolve, reject) => {
try { try {
/* eslint-disable-next-line global-require */ /* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json"))); 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; let versions = null;
if (data.engines) { 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) => { exec("npm -v", (error, stdout, stderr) => {
if (error) { if (error) {
reject(`Unable to find NPM version\n${stderr}`); reject(`Unable to find NPM version\n${stderr}`);
@ -43,18 +46,23 @@ const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
if (semver.satisfies(npmVersion, engineVersion)) { if (semver.satisfies(npmVersion, engineVersion)) {
resolve(); resolve();
} else { } 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); const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) { if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines); resolve(engines);
} else { } 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 */ /* eslint-disable no-console, no-process-exit */
console.error(error); console.error(error);
process.exit(1); process.exit(1);
} },
); );

@ -444,7 +444,6 @@ function parseComparator(comp, loose) {
} }
class SemVer { class SemVer {
/** /**
* A semantic version. * A semantic version.
* @param {string} version The version. * @param {string} version The version.
@ -488,7 +487,7 @@ class SemVer {
// Numberify any prerelease numeric ids // Numberify any prerelease numeric ids
if (matches[4]) { if (matches[4]) {
this.prerelease = matches[4].split(".").map((id) => { this.prerelease = matches[4].split(".").map((id) => {
if ((/^[0-9]+$/).test(id)) { if (/^[0-9]+$/.test(id)) {
const num = Number(id); const num = Number(id);
if (num >= 0 && num < MAX_SAFE_INTEGER) { if (num >= 0 && num < MAX_SAFE_INTEGER) {
return num; return num;
@ -532,7 +531,9 @@ class SemVer {
} }
return ( 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 gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0;
const lt = (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; 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}`); console.warn(`Invalid Faction object in addToAllFactions(). Key value: ${fac}`);
continue; continue;
} }
if (facObj.getInfo().special) continue;
facObj.augmentations.push(this.name); facObj.augmentations.push(this.name);
} }
} }

@ -1,6 +1,118 @@
import { IMap } from "../../types"; export const AugmentationNames: {
Targeting1: string;
export const AugmentationNames: IMap<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", Targeting1: "Augmented Targeting I",
Targeting2: "Augmented Targeting II", Targeting2: "Augmented Targeting II",
Targeting3: "Augmented Targeting III", Targeting3: "Augmented Targeting III",
@ -87,7 +199,7 @@ export const AugmentationNames: IMap<string> = {
NutriGen: "NutriGen Implant", NutriGen: "NutriGen Implant",
INFRARet: "INFRARET Enhancement", INFRARet: "INFRARET Enhancement",
DermaForce: "DermaForce Particle Barrier", DermaForce: "DermaForce Particle Barrier",
GrapheneBrachiBlades: "Graphene BranchiBlades Upgrade", GrapheneBrachiBlades: "Graphene BrachiBlades Upgrade",
GrapheneBionicArms: "Graphene Bionic Arms Upgrade", GrapheneBionicArms: "Graphene Bionic Arms Upgrade",
BrachiBlades: "BrachiBlades", BrachiBlades: "BrachiBlades",
BionicArms: "Bionic Arms", BionicArms: "Bionic Arms",

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

@ -114,7 +114,7 @@ export const CONSTANTS: {
TotalNumBitNodes: number; TotalNumBitNodes: number;
LatestUpdate: string; LatestUpdate: string;
} = { } = {
Version: "0.55.0", Version: "0.56.0",
// Speed (in ms) at which the main loop is updated // Speed (in ms) at which the main loop is updated
_idleSpeed: 200, _idleSpeed: 200,
@ -281,31 +281,107 @@ export const CONSTANTS: {
TotalNumBitNodes: 24, TotalNumBitNodes: 24,
LatestUpdate: ` 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. ** ** Misc. **
* Corporations can no longer bribe special factions * Fixed loader incorrectly assuming some null values are incorrect.
* Infiltration can no longer lose focus of the keyboard. * installBackdoor trigger Bitverse sequence
* Fix terminal line limit * Some improvements to the theme editor
* Added theme editor * Improved documentation about where to learn javascript.
* Theme applies on game load (@Nolshine) * Added some instructions for contributors.
* Sleeves no longer consume all bonus time for some actions * Fixed typo in corporation sell shares modal (@Saynt_Garmo)
* Fix a bug where the autocomlete list would get duplicates * Fixed pagination being black on black in Active Scripts
* Fix tutorial not scaling properly on small screens * Create Script tab renamed to Script Editor
* Import should be more consistent * Fixed an issue where corp some textbox wouldn't update when changing city.
* Typo with 'help' command * Fixed an issue where hacknet online time was always 0.
* Fix infinite loop in casino * 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 * nerf noodle bar
`, `,
/*
*/
}; };

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

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

@ -185,11 +185,12 @@ export class Product {
0.05 * employeeProd[EmployeePositions.Business]); 0.05 * employeeProd[EmployeePositions.Business]);
this.calculateRating(industry); this.calculateRating(industry);
const advMult = 1 + Math.pow(this.advCost, 0.1) / 100; 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 actually don't understand well enough to know if this is right.
// I'm adding this to prevent a crash. // 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 = this.dmd =
industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness))); industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));

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

@ -80,10 +80,20 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
<Typography>Division name:</Typography> <Typography>Division name:</Typography>
<Box display="flex" alignItems="center"> <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}> <Button disabled={disabled} sx={{ mx: 1 }} onClick={newIndustry}>
Create Division Expand
</Button> </Button>
),
}}
/>
</Box> </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{" "} Would you like to expand into a new city by opening an office? This would cost{" "}
<MoneyCost money={CorporationConstants.OfficeInitialCost} corp={corp} /> <MoneyCost money={CorporationConstants.OfficeInitialCost} corp={corp} />
</Typography> </Typography>
<Select value={city} onChange={onCityChange}> <Select
endAdornment={
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
}
value={city}
onChange={onCityChange}
>
{possibleCities.map((cityName: string) => ( {possibleCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}> <MenuItem key={cityName} value={cityName}>
{cityName} {cityName}
</MenuItem> </MenuItem>
))} ))}
</Select> </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 { 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 { function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {

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

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

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

@ -50,6 +50,11 @@ export class FactionInfo {
*/ */
keep: boolean; keep: boolean;
/**
* Special faction
*/
special: boolean;
constructor( constructor(
infoText: JSX.Element, infoText: JSX.Element,
enemies: string[], enemies: string[],
@ -57,6 +62,7 @@ export class FactionInfo {
offerHackingWork: boolean, offerHackingWork: boolean,
offerFieldWork: boolean, offerFieldWork: boolean,
offerSecurityWork: boolean, offerSecurityWork: boolean,
special: boolean,
keep: boolean, keep: boolean,
) { ) {
this.infoText = infoText; this.infoText = infoText;
@ -70,6 +76,7 @@ export class FactionInfo {
this.augmentationPriceMult = 1; this.augmentationPriceMult = 1;
this.augmentationRepRequirementMult = 1; this.augmentationRepRequirementMult = 1;
this.keep = keep; this.keep = keep;
this.special = special;
} }
offersWork(): boolean { offersWork(): boolean {
@ -96,6 +103,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
false, false,
false,
), ),
Daedalus: new FactionInfo( Daedalus: new FactionInfo(
@ -106,6 +114,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
false, false,
false,
), ),
"The Covenant": new FactionInfo( "The Covenant": new FactionInfo(
@ -124,6 +133,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
false, false,
false,
), ),
// Megacorporations, each forms its own faction // Megacorporations, each forms its own faction
@ -139,6 +149,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
true, true,
false,
true, true,
), ),
@ -158,6 +169,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
true, true,
false,
true, true,
), ),
@ -175,10 +187,11 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
true, true,
false,
true, 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( NWO: new FactionInfo(
( (
@ -193,10 +206,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
true, true,
false,
true, 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( "OmniTek Incorporated": new FactionInfo(
<>Simply put, our mission is to design and build robots that make a difference.</>, <>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, true,
true, true,
false,
true, true,
), ),
@ -220,10 +244,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
true, true,
false,
true, 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 // Other Corporations
"Fulcrum Secret Technologies": new FactionInfo( "Fulcrum Secret Technologies": new FactionInfo(
@ -238,6 +272,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
true, true,
false,
true, true,
), ),
@ -261,6 +296,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false, false,
false, false,
false, false,
false,
), ),
"The Black Hand": new FactionInfo( "The Black Hand": new FactionInfo(
@ -280,6 +316,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
false, false,
false,
), ),
// prettier-ignore // prettier-ignore
@ -325,6 +362,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false, false,
false, false,
false, false,
false,
), ),
// City factions, essentially governments // City factions, essentially governments
@ -336,8 +374,18 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, 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( Ishima: new FactionInfo(
<>The East Asian Order of the Future.</>, <>The East Asian Order of the Future.</>,
["Sector-12", "Aevum", "Volhaven"], ["Sector-12", "Aevum", "Volhaven"],
@ -346,6 +394,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, false,
false,
), ),
"New Tokyo": new FactionInfo( "New Tokyo": new FactionInfo(
<>Asia's World City.</>, <>Asia's World City.</>,
@ -355,6 +404,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, false,
false,
), ),
"Sector-12": new FactionInfo( "Sector-12": new FactionInfo(
<>The City of the Future.</>, <>The City of the Future.</>,
@ -364,6 +414,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, false,
false,
), ),
Volhaven: new FactionInfo( Volhaven: new FactionInfo(
<>Benefit, Honor, and Glory.</>, <>Benefit, Honor, and Glory.</>,
@ -373,6 +424,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, false,
false,
), ),
// Criminal Organizations/Gangs // Criminal Organizations/Gangs
@ -384,6 +436,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, false,
false,
), ),
"The Dark Army": new FactionInfo( "The Dark Army": new FactionInfo(
@ -394,9 +447,10 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
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( Silhouette: new FactionInfo(
( (
@ -415,6 +469,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
false, false,
false, false,
false,
), ),
Tetrads: new FactionInfo( Tetrads: new FactionInfo(
@ -425,14 +480,15 @@ export const FactionInfos: IMap<FactionInfo> = {
true, true,
true, true,
false, 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. // 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( CyberSec: new FactionInfo(
( (
@ -448,6 +504,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false, false,
false, false,
false, false,
false,
), ),
// Special Factions // Special Factions
@ -466,6 +523,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false, false,
false, false,
false, false,
true,
false, false,
), ),
@ -509,5 +567,6 @@ export const FactionInfos: IMap<FactionInfo> = {
false, false,
false, false,
true, true,
true,
), ),
}; };

@ -41,6 +41,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
if (isPlayersGang) { if (isPlayersGang) {
const augs: string[] = []; const augs: string[] = [];
for (const augName in Augmentations) { for (const augName in Augmentations) {
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
const aug = Augmentations[augName]; const aug = Augmentations[augName];
if (!aug.isSpecial) { if (!aug.isSpecial) {
augs.push(augName); 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.`); 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) { } catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;

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

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

@ -64,13 +64,21 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement {
</Typography> </Typography>
<Typography>{upg.desc}</Typography> <Typography>{upg.desc}</Typography>
{!upg.hasTargetServer && (
<Button onClick={purchase} disabled={!canPurchase}> <Button onClick={purchase} disabled={!canPurchase}>
Purchase Buy
</Button> </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> </Paper>
); );
} }

@ -8,6 +8,7 @@ import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react"; import { MathComponent } from "mathjax-react";
import { numeralWrapper } from "../../ui/numeralFormat";
type IProps = { type IProps = {
p: IPlayer; 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)}}}`} />}> <Tooltip title={<MathComponent tex={String.raw`\large{cost = 3.2 \times 10^3 \times 1.58^{log_2{(ram)}}}`} />}>
<span> <span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}> <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} /> <Money money={cost} player={props.p} />
</Button> </Button>
</span> </span>

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

@ -10,7 +10,6 @@ import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { compareArrays } from "../utils/helpers/compareArrays"; 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(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean; export function killWorkerScript(workerScript: WorkerScript): boolean;
@ -90,15 +89,6 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
return; 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 // Delete the RunningScript object from that server
for (let i = 0; i < server.runningScripts.length; ++i) { for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScript = server.runningScripts[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) // Delete script from global pool (workerScripts)
const res = workerScripts.delete(workerScript.pid); const res = workerScripts.delete(workerScript.pid);
if (!res) { if (!res) {

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

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

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

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

@ -125,10 +125,14 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
.catch((e) => reject(e)); .catch((e) => reject(e));
}).catch((e) => { }).catch((e) => {
if (e instanceof Error) { 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.errorMessage = makeRuntimeRejectMsg(
workerScript, workerScript,
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""), e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
); );
}
throw workerScript; throw workerScript;
} else if (isScriptErrorMessage(e)) { } else if (isScriptErrorMessage(e)) {
workerScript.errorMessage = e; workerScript.errorMessage = e;
@ -190,7 +194,8 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
cb(res); cb(res);
}) })
.catch(function (err: any) { .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)); int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));

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

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

@ -77,7 +77,6 @@ export function init(this: IPlayer): void {
} }
export function prestigeAugmentation(this: IPlayer): void { export function prestigeAugmentation(this: IPlayer): void {
const homeComp = this.getHomeComputer();
this.currentServer = SpecialServers.Home; this.currentServer = SpecialServers.Home;
this.numPeopleKilled = 0; this.numPeopleKilled = 0;
@ -453,6 +452,8 @@ export function gainIntelligenceExp(this: IPlayer, exp: number): void {
if (SourceFileFlags[5] > 0 || this.intelligence > 0) { if (SourceFileFlags[5] > 0 || this.intelligence > 0) {
this.intelligence_exp += exp; 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 //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(); 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 { export function cancelationPenalty(this: IPlayer): number {
const server = GetServer(this.companyName); const server = GetServer(this.companyName);
if (server instanceof Server) { if (server instanceof Server) {

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

@ -29,4 +29,5 @@ export function loadPlayer(saveString: string): void {
} }
Player.exploits = sanitizeExploits(Player.exploits); 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 // TODO(hydroflame): refactor faction names so we don't have to hard
// code strings. // code strings.
joinFaction(Factions["Church of the Machine God"]); joinFaction(Factions["Church of the Machine God"]);

@ -144,6 +144,18 @@ function evaluateVersionCompatibility(ver: string): void {
delete anyPlayer.companyPosition; 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 { function loadGame(saveString: string): boolean {

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

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

@ -14,6 +14,7 @@ export const TerminalHelpText: string[] = [
"clear Clear all text on the terminal ", "clear Clear all text on the terminal ",
"cls See 'clear' command ", "cls See 'clear' command ",
"connect [hostname] Connects to a remote server", "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", "download [script/text file] Downloads scripts or text files to your computer",
"expr [math expression] Evaluate a mathematical expression", "expr [math expression] Evaluate a mathematical expression",
"free Check the machine's memory (RAM) usage", "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 ", "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.", "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: [
"download [script/text file]", "download [script/text file]",
" ", " ",

@ -41,6 +41,7 @@ import { cat } from "./commands/cat";
import { cd } from "./commands/cd"; import { cd } from "./commands/cd";
import { check } from "./commands/check"; import { check } from "./commands/check";
import { connect } from "./commands/connect"; import { connect } from "./commands/connect";
import { cp } from "./commands/cp";
import { download } from "./commands/download"; import { download } from "./commands/download";
import { expr } from "./commands/expr"; import { expr } from "./commands/expr";
import { free } from "./commands/free"; import { free } from "./commands/free";
@ -729,6 +730,7 @@ export class Terminal implements ITerminal {
clear: () => this.clear(), clear: () => this.clear(),
cls: () => this.clear(), cls: () => this.clear(),
connect: connect, connect: connect,
cp: cp,
download: download, download: download,
expr: expr, expr: expr,
free: free, 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) { if (script == null) {
let code = ""; let code = "";
if (filename.endsWith(".ns") || filename.endsWith(".js")) { 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 { RunningScript } from "../../Script/RunningScript";
import { findRunningScript } from "../../Script/ScriptHelpers"; import { findRunningScript } from "../../Script/ScriptHelpers";
import * as libarg from "arg"; import * as libarg from "arg";
import { numeralWrapper } from "../../ui/numeralFormat";
export function runScript( export function runScript(
terminal: ITerminal, terminal: ITerminal,
@ -64,8 +65,8 @@ export function runScript(
"This machine does not have enough RAM to run this script with " + "This machine does not have enough RAM to run this script with " +
numThreads + numThreads +
" threads. Script requires " + " threads. Script requires " +
ramUsage + numeralWrapper.formatRAM(ramUsage) +
"GB of RAM", " of RAM",
); );
return; return;
} }

@ -18,6 +18,7 @@ const commands = [
"clear", "clear",
"cls", "cls",
"connect", "connect",
"cp",
"download", "download",
"expr", "expr",
"free", "free",
@ -239,6 +240,13 @@ export function determineAllPossibilitiesForTabCompletion(
return allPos; return allPos;
} }
if (isCommand("cp") && index === 0) {
addAllScripts();
addAllTextFiles();
addAllDirectories();
return allPos;
}
if (isCommand("connect")) { if (isCommand("connect")) {
// All network connections // All network connections
for (let i = 0; i < currServ.serversOnNetwork.length; ++i) { 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 { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { Locations } from "./Locations/Locations";
import { LocationName } from "./Locations/data/LocationNames";
import { Money } from "./ui/React/Money"; import { Money } from "./ui/React/Money";
import { Hashes } from "./ui/React/Hashes"; import { Hashes } from "./ui/React/Hashes";
@ -92,34 +90,7 @@ const Engine: {
Terminal.process(Router, Player, numCycles); Terminal.process(Router, Player, numCycles);
// Working Player.process(Router, numCycles);
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();
}
}
}
// Update stock prices // Update stock prices
if (Player.hasWseAccount) { if (Player.hasWseAccount) {

@ -77,6 +77,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
const [disableTextEffects, setDisableTextEffects] = useState(Settings.DisableTextEffects); const [disableTextEffects, setDisableTextEffects] = useState(Settings.DisableTextEffects);
const [enableBashHotkeys, setEnableBashHotkeys] = useState(Settings.EnableBashHotkeys); const [enableBashHotkeys, setEnableBashHotkeys] = useState(Settings.EnableBashHotkeys);
const [enableTimestamps, setEnableTimestamps] = useState(Settings.EnableTimestamps); const [enableTimestamps, setEnableTimestamps] = useState(Settings.EnableTimestamps);
const [saveGameOnFileSave, setSaveGameOnFileSave] = useState(Settings.SaveGameOnFileSave);
const [locale, setLocale] = useState(Settings.Locale); const [locale, setLocale] = useState(Settings.Locale);
const [diagnosticOpen, setDiagnosticOpen] = useState(false); const [diagnosticOpen, setDiagnosticOpen] = useState(false);
@ -165,6 +166,10 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
setEnableTimestamps(event.target.checked); setEnableTimestamps(event.target.checked);
Settings.EnableTimestamps = 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 { function startImport(): void {
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return; if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
@ -505,6 +510,19 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
/> />
</ListItem> </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> <ListItem>
<Tooltip title={<Typography>Sets the locale for displaying numbers.</Typography>}> <Tooltip title={<Typography>Sets the locale for displaying numbers.</Typography>}>
<Typography>Locale&nbsp;</Typography> <Typography>Locale&nbsp;</Typography>

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

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

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

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

@ -106,6 +106,10 @@ class NumeralFormatter {
} }
formatRAM(n: number): string { 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"; return this.format(n, "0.00") + "GB";
} }

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