Compare commits

..

109 Commits

Author SHA1 Message Date
Olivier Gagnon
87c63cde59 merge v0.56.0 2021-10-12 01:35:30 -04:00
Olivier Gagnon
83137a2364 v0.56.0 2021-10-12 00:29:16 -04:00
Olivier Gagnon
78d9c25671 fix error message with wrong reviver, refactor part of player 2021-10-11 23:14:15 -04:00
Olivier Gagnon
56c8a23631 corp MAXMPPROD is now case insensitive 2021-10-11 22:54:28 -04:00
Olivier Gagnon
1d4cf45a92 fix issue with corp export popup 2021-10-11 22:48:34 -04:00
Olivier Gagnon
b1e37acaa1 added autocomplete for scp 2021-10-11 22:35:00 -04:00
Olivier Gagnon
65ee49fb92 Added cp command 2021-10-11 22:34:04 -04:00
Olivier Gagnon
6a795a7c50 neuroflux doesnt appear in gangs 2021-10-11 19:00:14 -04:00
Olivier Gagnon
ae33a23db4 remove Neuroflux from specila factions 2021-10-11 18:56:51 -04:00
Olivier Gagnon
faad0ae8a7 Try to make an error message more helpful 2021-10-11 18:47:05 -04:00
Olivier Gagnon
1ff7f4bcd4 table pagination text no longer black 2021-10-11 18:30:46 -04:00
Olivier Gagnon
1335ca8e01 Impossible to buy real estate with negative money 2021-10-11 18:22:57 -04:00
Olivier Gagnon
8e07cc999d change GB to TBPBEB 2021-10-11 18:14:10 -04:00
Olivier Gagnon
06775b20fa saving file now saves game 2021-10-11 17:57:17 -04:00
Olivier Gagnon
30554560da softcap hacknet max moneyt upgrade 2021-10-11 17:43:48 -04:00
Olivier Gagnon
ac3a6b9a6f setToCommitCrime sleeve works with rough crime name 2021-10-11 17:12:08 -04:00
Olivier Gagnon
b126bd01ee ram check is debounced 2021-10-11 16:59:37 -04:00
Olivier Gagnon
8f13363466 prettier 2021-10-11 16:38:50 -04:00
Olivier Gagnon
828c9c2de6 improve contributing.md 2021-10-11 16:28:01 -04:00
Olivier Gagnon
dd61fd6efb document learn js and make getServer not require sf5 2021-10-11 16:18:05 -04:00
hydroflame
383b02fdbb Merge pull request #1457 from Snarling/patch-3
Change effect replacer text to x% instead of +x%
2021-10-11 14:27:01 -04:00
Olivier Gagnon
010f43e5d4 few bugfix 2021-10-11 14:26:44 -04:00
Olivier Gagnon
3d36982a56 fix disableLog not disabling blade functions 2021-10-11 13:31:12 -04:00
Olivier Gagnon
3fd26bea9b fix int not calculating 2021-10-11 13:18:37 -04:00
Snarling
41de3102c7 Change effect replacer text to x% instead of +x%
This will make replacement work for hacknet cost (which is -x% so currently is not seen for replacement) and will keep the + or - for effectiveness (e.g. +12.3% hacking skill instead of just 12.3% hacking skill)
2021-10-10 23:00:11 -04:00
Olivier Gagnon
e9ba4ae9a2 added vscode module def in netscript 2021-10-10 13:52:56 -04:00
Olivier Gagnon
12b192ab43 prerawloader 2021-10-10 13:23:36 -04:00
Olivier Gagnon
cdbbc657e2 nano new ns2 file starts with param 2021-10-09 23:21:22 -04:00
Olivier Gagnon
995a0b11d9 build fix for duplicate autocomplete 2021-10-09 23:07:18 -04:00
Olivier Gagnon
06df10d2f9 build fix for duplicate autocomplete 2021-10-09 22:59:06 -04:00
Olivier Gagnon
01d15176ac Added kindof monokai to monaco 2021-10-09 19:00:27 -04:00
Olivier Gagnon
f9afff57b2 fix ram miscalc 2021-10-09 15:07:42 -04:00
Olivier Gagnon
2bf47c60df fix sleeve consuming too much time at once. 2021-10-09 14:31:06 -04:00
Olivier Gagnon
783750051e build w dev 2021-10-09 12:51:44 -04:00
Olivier Gagnon
752534bc4d fix joinBladeburner 2021-10-09 12:49:53 -04:00
Olivier Gagnon
3346f3539c increase the price of Wilson to stop feedback loop 2021-10-09 02:24:31 -04:00
Olivier Gagnon
0aa26df9d7 build sg 2021-10-09 01:09:27 -04:00
Olivier Gagnon
bbf3a1d19a fix server ram recalc 2021-10-08 23:45:54 -04:00
Olivier Gagnon
a2599f19d7 patch time compression 2021-10-08 15:31:32 -04:00
Olivier Gagnon
8e4722c5e7 write can be awaited in order to wait for the ram calculation to go through 2021-10-08 14:05:47 -04:00
Olivier Gagnon
e91c183d37 blade getCurrentAction says type general 2021-10-08 13:17:27 -04:00
Olivier Gagnon
2ed29e10b3 build cores 2021-10-08 12:10:28 -04:00
Olivier Gagnon
effa9f15af growthanalyze has core argument 2021-10-08 12:09:44 -04:00
Olivier Gagnon
4355420349 made staneks gift work with prestiges 2021-10-08 03:16:51 -04:00
Olivier Gagnon
35ebb06761 change highlight color 2021-10-08 02:05:53 -04:00
Olivier Gagnon
5d21bd7840 more theme 2021-10-08 02:04:12 -04:00
Olivier Gagnon
815b04037c more theme 2021-10-08 01:49:12 -04:00
Olivier Gagnon
3cd0ae51e7 Change rdt theme to match game sorta 2021-10-08 01:39:40 -04:00
Olivier Gagnon
ed57a8c4f4 trying dark theme 2 2021-10-08 00:59:36 -04:00
Olivier Gagnon
0f7ad063ca thing to know if rrdt has reloaded 2021-10-08 00:55:55 -04:00
Olivier Gagnon
809f9117b8 thing to know if rrdt has reloaded 2021-10-08 00:55:40 -04:00
Olivier Gagnon
38e165100f Tried to change the theme 2021-10-08 00:55:06 -04:00
Olivier Gagnon
f4ecbd9b48 merge dev 2021-10-08 00:26:35 -04:00
Olivier Gagnon
7db1164a1a build script editor filename issue 2021-10-08 00:22:32 -04:00
Olivier Gagnon
3ca7c49ce8 Fixed Script Editor last filename not keeping proper track 2021-10-08 00:21:30 -04:00
Olivier Gagnon
528a8f30db more elements around 2021-10-08 00:13:18 -04:00
Olivier Gagnon
500063e87e Merge branch 'dev' into sg 2021-10-07 23:53:49 -04:00
Olivier Gagnon
ec3037f8c6 readded font 2021-10-07 23:53:44 -04:00
Olivier Gagnon
8d7f0488f8 merge dev 2021-10-07 17:58:32 -04:00
Olivier Gagnon
7d0536a4d2 finish convert to hostname 2021-10-07 17:55:49 -04:00
Olivier Gagnon
2958034ad4 more ip conversion 2021-10-07 17:04:32 -04:00
Olivier Gagnon
a7dfb1a537 more convertion from ip to hostname 2021-10-07 16:56:01 -04:00
Olivier Gagnon
be29481689 unexport AllServers 2021-10-07 16:04:04 -04:00
Olivier Gagnon
1d488565c6 build logbox 2021-10-07 15:11:58 -04:00
Olivier Gagnon
42890843fb Merge branch 'dev' of github.com:danielyxie/bitburner into dev 2021-10-07 15:08:27 -04:00
Olivier Gagnon
c06aff3437 Improve logbox behavior 2021-10-07 15:02:54 -04:00
Olivier Gagnon
62bdfb1875 notes 2021-10-07 14:49:40 -04:00
hydroflame
123f071c12 Merge pull request #1427 from Tyasuh/dev
Corpo Typo Fixes
2021-10-07 13:55:15 -04:00
tyasuh.taeragan@gmail.com
0edd4ffdf1 Corpo Typo Fixes 2021-10-07 13:46:33 -04:00
Olivier Gagnon
8e5c10cc2f enable dev for beta 2021-10-07 01:47:13 -04:00
Olivier Gagnon
15a03dd532 enable dev for beta 2021-10-07 01:42:06 -04:00
Olivier Gagnon
8e58482db0 sg 2021-10-07 01:36:59 -04:00
Olivier Gagnon
b1d1de9118 revert growthAnalyze stuff because it causes errors 2021-10-06 19:41:36 -04:00
Olivier Gagnon
83a84c6d38 corp bonus time consumes faster 2021-10-06 01:59:41 -04:00
Olivier Gagnon
75a2742911 fix netscript port read not correctly converting ns1 objects 2021-10-05 16:37:54 -04:00
Olivier Gagnon
a420a87eba fix bug with hacknet servers and grow 2021-10-05 15:23:30 -04:00
Olivier Gagnon
f579ee398b made log box resize a tad better 2021-10-05 01:44:46 -04:00
Olivier Gagnon
4901c84d34 remove some unused css 2021-10-05 01:30:37 -04:00
Olivier Gagnon
227fbd7060 made log box resizable 2021-10-05 01:23:20 -04:00
Olivier Gagnon
70796e7674 use react-draggable 2021-10-05 00:59:40 -04:00
Olivier Gagnon
da746a63c3 add incremental game plaza stuff 2021-10-04 23:56:24 -04:00
Olivier Gagnon
2f677c7ec8 more work 2021-10-04 23:51:39 -04:00
Olivier Gagnon
c5e29dafc4 fix mc 2021-10-04 22:31:07 -04:00
Olivier Gagnon
bb0bdb776b extracted some of the Netscript functions into their own file. 2021-10-04 22:25:21 -04:00
Olivier Gagnon
48b839d68c Added font size to text editor 2021-10-04 21:06:55 -04:00
Olivier Gagnon
c47a5bc8cc added grow, weaken, and time compression 2021-10-04 19:58:34 -04:00
Olivier Gagnon
33ea31be87 convert autocomplete to tooltip 2021-10-04 17:52:20 -04:00
Olivier Gagnon
27fc90c87a fix corp ui text fields now clearing on city change 2021-10-04 13:49:27 -04:00
Olivier Gagnon
d58e2df9c7 cat now accepts newline 2021-10-04 13:15:04 -04:00
Olivier Gagnon
c989e6713f fix logbox newlines and corp researching multiple times 2021-10-04 12:37:19 -04:00
Olivier Gagnon
880654c222 ui work 2021-10-04 12:28:57 -04:00
Olivier Gagnon
4fc6d393e4 fix mc 2021-10-03 21:44:15 -04:00
Olivier Gagnon
d21382e96e remove debug log 2021-10-03 21:37:05 -04:00
Olivier Gagnon
81fd2c1236 remove log 2021-10-03 21:34:56 -04:00
hydroflame
bdb10217db Merge pull request #1409 from BartKoppelmans/patch-3
Removed console warning for Sleeves
2021-10-03 21:34:21 -04:00
Olivier Gagnon
ab2ffb112f fix some bugs 2021-10-03 21:33:48 -04:00
Olivier Gagnon
7304e5379f sg 2021-10-03 20:34:36 -04:00
Bart Koppelmans
ee0532eba7 Removed console warning for Sleeves 2021-10-03 20:29:20 +02:00
Olivier Gagnon
1a749505e7 build research tree 2021-10-02 00:25:50 -04:00
Olivier Gagnon
fae6e6d22f fix research tree, kinda 2021-10-02 00:24:50 -04:00
Olivier Gagnon
826357e8b8 change SF9 to seem more appealing 2021-10-01 23:20:44 -04:00
Olivier Gagnon
94550dbaee forgot about grwothAnalyze 2021-10-01 23:03:37 -04:00
Olivier Gagnon
83c159e901 fix miscalculation in growth formulas 2021-10-01 23:02:09 -04:00
Olivier Gagnon
3f5b412547 fixed prompt 2021-10-01 22:53:23 -04:00
Olivier Gagnon
1fdb5c33c7 fix sleeves not being able to work at volhaven 2021-10-01 22:42:31 -04:00
Olivier Gagnon
665d25650a fixed log boxes 2021-10-01 22:31:58 -04:00
Olivier Gagnon
447731c5f3 fix infil 2021-10-01 22:27:32 -04:00
Olivier Gagnon
cc02701e97 fix autolink wrong font 2021-10-01 16:42:07 -04:00
Olivier Gagnon
793d9b34ce update BN13 for new UI 2021-09-25 17:21:50 -04:00
199 changed files with 14159 additions and 10373 deletions

View File

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

View File

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

23
css/staneksgift.scss Normal file
View File

@@ -0,0 +1,23 @@
.staneksgift_row {
padding: 0;
margin: 0;
}
.staneksgift_cell {
width: 25px;
height: 25px;
background-color: #808080;
font-color: white;
padding: 0px;
margin: 0px;
border: 1px solid black;
float: left;
}
.staneksgift_cell:first-child {
clear: left;
}
.staneksgift_container {
position: fixed;
}

98
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@@ -30,5 +30,6 @@ to reach out to the developer!
Gang API <netscript/netscriptgangapi>
Coding Contract API <netscript/netscriptcodingcontractapi>
Sleeve API <netscript/netscriptsleeveapi>
Stanek API <netscript/netscriptstanekapi>
Formulas API <netscript/netscriptformulasapi>
Miscellaneous <netscript/netscriptmisc>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
.. _netscriptstanek:
Netscript Stanek Functions
============================
.. warning:: This page contains spoilers for the game.
The Stanek API allow you to control Stanek's Gift.
All these function require Source-File 13-1 or to be in BitNode 13.
.. toctree::
charge() <stanekapi/charge>
fragmentDefinitions() <stanekapi/fragmentDefinitions>
placedFragments() <stanekapi/placedFragments>
clear() <stanekapi/clear>
canPlace() <stanekapi/canPlace>
place() <stanekapi/place>
fragmentAt() <stanekapi/fragmentAt>
deleteAt() <stanekapi/deleteAt>

View File

@@ -0,0 +1,15 @@
canPlace() Netscript Function
=======================================
.. js:function:: canPlace(worldX, worldY, fragmentId)
:RAM cost: 0.5 GB
:param int worldX: World X against which to align the top left of the fragment.
:param int worldY: World Y against which to align the top left of the fragment.
:param int fragmentId: ID of the fragment to place.
:returns: `true` if the fragment can be placed at that position. `false` otherwise.
Example:
.. code-block:: javascript
canPlace(0, 4, 17); // returns true

View File

@@ -0,0 +1,21 @@
charge() Netscript Function
=======================================
.. js:function:: charge(worldX, worldY)
:RAM cost: 0.4 GB
:param int worldX: World X of the fragment to charge.
:param int worldY: World Y of the fragment to charge.
Charge a fragment, increasing it's power but also it's heat. The
effectiveness of the charge depends on the amount of ram the running script
consumes as well as the fragments current heat. This operation takes time to
complete.
Example:
.. code-block:: javascript
charge(0, 4); // Finishes 5 seconds later.
.. warning::
Netscript JS users: This function is `async`

View File

@@ -0,0 +1,13 @@
clear() Netscript Function
=======================================
.. js:function:: clear()
:RAM cost: 0 GB
Completely clear Stanek's Gift.
Example:
.. code-block:: javascript
clear(); // No more fragments.

View File

@@ -0,0 +1,16 @@
deleteAt() Netscript Function
=======================================
.. js:function:: deleteAt(worldX, worldY)
:RAM cost: 0.15 GB
:param int worldX: World X coordinate of the fragment to delete.
:param int worldY: World Y coordinate of the fragment to delete.
:returns: `true` if the fragment was deleted. `false` otherwise.
Delete the fragment located at `[worldX, worldY]`.
Example:
.. code-block:: javascript
deleteAt(0, 4); // returns true

View File

@@ -0,0 +1,28 @@
fragmentAt() Netscript Function
=======================================
.. js:function:: fragmentAt(worldX, worldY)
:RAM cost: 2 GB
:param int worldX: World X coordinate of the fragment.
:param int worldY: World Y coordinate of the fragment.
:returns: The fragment located at `[worldX, worldY]` in Stanek's Gift, or null.
.. code-block:: typescript
{
// In world coordinates
x: number;
y: number;
heat: number;
charge: number;
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
Example:
.. code-block:: javascript
var fragment = fragmentAt(0, 4);
print(fragment); // {'heat': 50, 'charge': 98}

View File

@@ -0,0 +1,23 @@
fragmentDefinitions() Netscript Function
=======================================
.. js:function:: fragmentDefinitions()
:RAM cost: 0 GB
:returns: The list of all fragment that can be embedded in Stanek's Gift.
.. code-block:: typescript
[
{
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
]
Example:
.. code-block:: javascript
var fragments = fragmentDefinitions();
print(fragment); // prints all possible fragments

View File

@@ -0,0 +1,15 @@
place() Netscript Function
=======================================
.. js:function:: place(worldX, worldY, fragmentId)
:RAM cost: 5 GB
:param int worldX: World X against which to align the top left of the fragment.
:param int worldY: World Y against which to align the top left of the fragment.
:param int fragmentId: ID of the fragment to place.
:returns: `true` if the fragment has been placed at that position. `false` otherwise.
Example:
.. code-block:: javascript
place(0, 4, 17); // returns true

View File

@@ -0,0 +1,27 @@
placedFragments() Netscript Function
=======================================
.. js:function:: placedFragments()
:RAM cost: 5 GB
:returns: The list of all fragment that are embedded in Stanek's Gift.
.. code-block:: typescript
[
{
// In world coordinates
x: number;
y: number;
heat: number;
charge: number;
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
]
Example:
.. code-block:: javascript
var myFragments = placedFragments();

View File

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

View File

@@ -39,11 +39,13 @@
<style>
body {
background-color: black;
}
* {
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
}
body::-webkit-scrollbar {
*::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}
</style>
@@ -51,5 +53,5 @@
<body>
<div id="root"/>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
<script src="src/ThirdParty/raphael.min.js"></script>
<!-- http://plaza.dsolver.ca/m/hydroflame4418 -->
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4737
main.css

File diff suppressed because it is too large Load Diff

62
package-lock.json generated
View File

@@ -21,6 +21,7 @@
"@types/numeral": "0.0.25",
"@types/react": "^17.0.21",
"@types/react-dom": "^17.0.9",
"@types/react-resizable": "^1.7.3",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"ajv": "^5.1.5",
@@ -53,7 +54,9 @@
"numeral": "2.0.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draggable": "^4.4.4",
"react-modal": "^3.12.1",
"react-resizable": "^3.0.4",
"sprintf-js": "^1.1.1",
"tapable": "^1.0.0",
"treant-js": "^1.0.1",
@@ -4673,6 +4676,14 @@
"@types/react": "*"
}
},
"node_modules/@types/react-resizable": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-1.7.3.tgz",
"integrity": "sha512-DAx+hdnHFMJHgl8geiKo3jLt1GCT838SwQixjCtbRRfqCBawAKriVLCZ1nvp7B/2Pxd94MWod8NyJEnAAmNHNA==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz",
@@ -22097,6 +22108,19 @@
"react": "17.0.2"
}
},
"node_modules/react-draggable": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz",
"integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==",
"dependencies": {
"clsx": "^1.1.1",
"prop-types": "^15.6.0"
},
"peerDependencies": {
"react": ">= 16.3.0",
"react-dom": ">= 16.3.0"
}
},
"node_modules/react-is": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
@@ -22130,6 +22154,18 @@
"node": ">=0.10.0"
}
},
"node_modules/react-resizable": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
"integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
"dependencies": {
"prop-types": "15.x",
"react-draggable": "^4.0.3"
},
"peerDependencies": {
"react": ">= 16.3"
}
},
"node_modules/react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
@@ -31573,6 +31609,14 @@
"@types/react": "*"
}
},
"@types/react-resizable": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-1.7.3.tgz",
"integrity": "sha512-DAx+hdnHFMJHgl8geiKo3jLt1GCT838SwQixjCtbRRfqCBawAKriVLCZ1nvp7B/2Pxd94MWod8NyJEnAAmNHNA==",
"requires": {
"@types/react": "*"
}
},
"@types/react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz",
@@ -45494,6 +45538,15 @@
"scheduler": "^0.20.2"
}
},
"react-draggable": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz",
"integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==",
"requires": {
"clsx": "^1.1.1",
"prop-types": "^15.6.0"
}
},
"react-is": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
@@ -45521,6 +45574,15 @@
"integrity": "sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==",
"dev": true
},
"react-resizable": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
"integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
"requires": {
"prop-types": "15.x",
"react-draggable": "^4.0.3"
}
},
"react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",

View File

@@ -1,7 +1,7 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "0.53.0",
"version": "0.56.0",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie"
@@ -22,6 +22,7 @@
"@types/numeral": "0.0.25",
"@types/react": "^17.0.21",
"@types/react-dom": "^17.0.9",
"@types/react-resizable": "^1.7.3",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"ajv": "^5.1.5",
@@ -54,7 +55,9 @@
"numeral": "2.0.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draggable": "^4.4.4",
"react-modal": "^3.12.1",
"react-resizable": "^3.0.4",
"sprintf-js": "^1.1.1",
"tapable": "^1.0.0",
"treant-js": "^1.0.1",

View File

@@ -2,433 +2,366 @@ const numSpaces = 4;
const maxLineLength = 160;
module.exports = {
"env": {
"es6": true,
"node": true
env: {
es6: true,
node: true,
},
extends: "eslint:recommended",
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
},
"ecmaVersion": 8,
"sourceType": "module"
},
"rules": {
"accessor-pairs": [
"error",
{
"getWithoutSet": false,
"setWithoutGet": true
}
],
"array-bracket-newline": ["error"],
"array-bracket-spacing": ["error"],
"array-callback-return": ["error"],
"array-element-newline": ["error"],
"arrow-body-style": ["error"],
"arrow-parens": ["error"],
"arrow-spacing": ["error"],
"block-scoped-var": ["error"],
"block-spacing": ["error"],
"brace-style": ["error"],
"callback-return": ["error"],
"camelcase": ["error"],
"capitalized-comments": ["error"],
"class-methods-use-this": ["error"],
"comma-dangle": ["error"],
"comma-spacing": ["error"],
"comma-style": [
"error",
"last"
],
"complexity": ["error"],
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": ["error"],
"consistent-this": ["error"],
"constructor-super": ["error"],
"curly": ["error"],
"default-case": ["error"],
"dot-location": [
"error",
"property"
],
"dot-notation": ["error"],
"eol-last": ["error"],
"eqeqeq": ["error"],
"for-direction": ["error"],
"func-call-spacing": ["error"],
"func-name-matching": ["error"],
"func-names": [
"error",
"never"
],
"func-style": ["error"],
"function-paren-newline": ["error"],
"generator-star-spacing": [
"error",
"before"
],
"getter-return": [
"error",
{
"allowImplicit": false
}
],
"global-require": ["error"],
"guard-for-in": ["error"],
"handle-callback-err": ["error"],
"id-blacklist": ["error"],
"id-length": ["error"],
"id-match": ["error"],
"implicit-arrow-linebreak": [
"error",
"beside"
],
"indent": [
"error",
numSpaces,
{
"SwitchCase": 1
}
],
"init-declarations": ["error"],
"jsx-quotes": ["error"],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"line-comment-position": ["error"],
"linebreak-style": [
"error",
"windows"
],
"lines-around-comment": ["error"],
"lines-between-class-members": ["error"],
"max-depth": ["error"],
"max-len": [
"error",
maxLineLength
],
"max-lines": [
"error",
{
"skipBlankLines": true,
"skipComments": true
}
],
"max-nested-callbacks": ["error"],
"max-params": ["error"],
"max-statements": ["error"],
"max-statements-per-line": ["error"],
"multiline-comment-style": [
"off",
"starred-block"
],
"multiline-ternary": [
"error",
"never"
],
"new-cap": ["error"],
"new-parens": ["error"],
// TODO: configure this...
"newline-before-return": ["error"],
"newline-per-chained-call": ["error"],
"no-alert": ["error"],
"no-array-constructor": ["error"],
"no-await-in-loop": ["error"],
"no-bitwise": ["error"],
"no-buffer-constructor": ["error"],
"no-caller": ["error"],
"no-case-declarations": ["error"],
"no-catch-shadow": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": [
"error",
"except-parens"
],
"no-confusing-arrow": ["error"],
"no-console": ["error"],
"no-const-assign": ["error"],
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-continue": ["off"],
"no-control-regex": ["error"],
"no-debugger": ["error"],
"no-delete-var": ["error"],
"no-div-regex": ["error"],
"no-dupe-args": ["error"],
"no-dupe-class-members": ["error"],
"no-dupe-keys": ["error"],
"no-duplicate-case": ["error"],
"no-duplicate-imports": [
"error",
{
"includeExports": true
}
],
"no-else-return": ["error"],
"no-empty": [
"error",
{
"allowEmptyCatch": false
}
],
"no-empty-character-class": ["error"],
"no-empty-function": ["error"],
"no-empty-pattern": ["error"],
"no-eq-null": ["error"],
"no-eval": ["error"],
"no-ex-assign": ["error"],
"no-extend-native": ["error"],
"no-extra-bind": ["error"],
"no-extra-boolean-cast": ["error"],
"no-extra-label": ["error"],
"no-extra-parens": [
"error",
"all",
{
"conditionalAssign": false
}
],
"no-extra-semi": ["error"],
"no-fallthrough": ["error"],
"no-floating-decimal": ["error"],
"no-func-assign": ["error"],
"no-global-assign": ["error"],
"no-implicit-coercion": ["error"],
"no-implicit-globals": ["error"],
"no-implied-eval": ["error"],
"no-inline-comments": ["error"],
"no-inner-declarations": [
"error",
"both"
],
"no-invalid-regexp": ["error"],
"no-invalid-this": ["error"],
"no-irregular-whitespace": [
"error",
{
"skipComments": false,
"skipRegExps": false,
"skipStrings": false,
"skipTemplates": false
}
],
"no-iterator": ["error"],
"no-label-var": ["error"],
"no-labels": ["error"],
"no-lone-blocks": ["error"],
"no-lonely-if": ["error"],
"no-loop-func": ["error"],
"no-magic-numbers": [
"error",
{
"ignore": [
-1,
0,
1
],
"ignoreArrayIndexes": true
}
],
"no-mixed-operators": ["error"],
"no-mixed-requires": ["error"],
"no-mixed-spaces-and-tabs": ["error"],
"no-multi-assign": ["error"],
"no-multi-spaces": ["error"],
"no-multi-str": ["error"],
"no-multiple-empty-lines": [
"error",
{
"max": 1
}
],
"no-native-reassign": ["error"],
"no-negated-condition": ["error"],
"no-negated-in-lhs": ["error"],
"no-nested-ternary": ["error"],
"no-new": ["error"],
"no-new-func": ["error"],
"no-new-object": ["error"],
"no-new-require": ["error"],
"no-new-symbol": ["error"],
"no-new-wrappers": ["error"],
"no-obj-calls": ["error"],
"no-octal": ["error"],
"no-octal-escape": ["error"],
"no-param-reassign": ["error"],
"no-path-concat": ["error"],
"no-plusplus": [
"error",
{
"allowForLoopAfterthoughts": true
}
],
"no-process-env": ["error"],
"no-process-exit": ["error"],
"no-proto": ["error"],
"no-prototype-builtins": ["error"],
"no-redeclare": ["error"],
"no-regex-spaces": ["error"],
"no-restricted-globals": ["error"],
"no-restricted-imports": ["error"],
"no-restricted-modules": ["error"],
"no-restricted-properties": [
"error",
{
"message": "'log' is too general, use an appropriate level when logging.",
"object": "console",
"property": "log"
}
],
"no-restricted-syntax": ["error"],
"no-return-assign": ["error"],
"no-return-await": ["error"],
"no-script-url": ["error"],
"no-self-assign": [
"error",
{
"props": false
}
],
"no-self-compare": ["error"],
"no-sequences": ["error"],
"no-shadow": ["error"],
"no-shadow-restricted-names": ["error"],
"no-spaced-func": ["error"],
"no-sparse-arrays": ["error"],
"no-sync": ["error"],
"no-tabs": ["error"],
"no-template-curly-in-string": ["error"],
"no-ternary": ["off"],
"no-this-before-super": ["error"],
"no-throw-literal": ["error"],
"no-trailing-spaces": ["error"],
"no-undef": ["error"],
"no-undef-init": ["error"],
"no-undefined": ["error"],
"no-underscore-dangle": ["error"],
"no-unexpected-multiline": ["error"],
"no-unmodified-loop-condition": ["error"],
"no-unneeded-ternary": ["error"],
"no-unreachable": ["error"],
"no-unsafe-finally": ["error"],
"no-unsafe-negation": ["error"],
"no-unused-expressions": ["error"],
"no-unused-labels": ["error"],
"no-unused-vars": ["error"],
"no-use-before-define": ["error"],
"no-useless-call": ["error"],
"no-useless-computed-key": ["error"],
"no-useless-concat": ["error"],
"no-useless-constructor": ["error"],
"no-useless-escape": ["error"],
"no-useless-rename": [
"error",
{
"ignoreDestructuring": false,
"ignoreExport": false,
"ignoreImport": false
}
],
"no-useless-return": ["error"],
"no-var": ["error"],
"no-void": ["error"],
"no-warning-comments": ["error"],
"no-whitespace-before-property": ["error"],
"no-with": ["error"],
"nonblock-statement-body-position": [
"error",
"below"
],
"object-curly-newline": ["error"],
"object-curly-spacing": ["error"],
"object-property-newline": ["error"],
"object-shorthand": ["error"],
"one-var": ["off"],
"one-var-declaration-per-line": ["error"],
"operator-assignment": ["error"],
"operator-linebreak": [
"error",
"none"
],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"],
"prefer-const": ["error"],
"prefer-destructuring": ["off"],
"prefer-numeric-literals": ["error"],
"prefer-promise-reject-errors": ["off"],
"prefer-reflect": ["error"],
"prefer-rest-params": ["error"],
"prefer-spread": ["error"],
"prefer-template": ["error"],
"quote-props": ["error"],
"quotes": ["error"],
"radix": [
"error",
"as-needed"
],
"require-await": ["error"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": [
"error",
"never"
],
"semi": ["error"],
"semi-spacing": ["error"],
"semi-style": [
"error",
"last"
],
"sort-imports": ["error"],
"sort-keys": ["error"],
"sort-vars": ["error"],
"space-before-blocks": ["error"],
"space-before-function-paren": ["off"],
"space-in-parens": ["error"],
"space-infix-ops": ["error"],
"space-unary-ops": ["error"],
"spaced-comment": ["error"],
"strict": ["error"],
"switch-colon-spacing": [
"error",
{
"after": true,
"before": false
}
],
"symbol-description": ["error"],
"template-curly-spacing": ["error"],
"template-tag-spacing": ["error"],
"unicode-bom": [
"error",
"never"
],
"use-isnan": ["error"],
"valid-jsdoc": ["error"],
"valid-typeof": ["error"],
"vars-on-top": ["error"],
"wrap-iife": [
"error",
"any"
],
"wrap-regex": ["error"],
"yield-star-spacing": [
"error",
"before"
],
"yoda": [
"error",
"never"
]
}
ecmaVersion: 8,
sourceType: "module",
},
rules: {
"accessor-pairs": [
"error",
{
getWithoutSet: false,
setWithoutGet: true,
},
],
"array-bracket-newline": ["error"],
"array-bracket-spacing": ["error"],
"array-callback-return": ["error"],
"array-element-newline": ["error"],
"arrow-body-style": ["error"],
"arrow-parens": ["error"],
"arrow-spacing": ["error"],
"block-scoped-var": ["error"],
"block-spacing": ["error"],
"brace-style": ["error"],
"callback-return": ["error"],
camelcase: ["error"],
"capitalized-comments": ["error"],
"class-methods-use-this": ["error"],
"comma-dangle": ["error"],
"comma-spacing": ["error"],
"comma-style": ["error", "last"],
complexity: ["error"],
"computed-property-spacing": ["error", "never"],
"consistent-return": ["error"],
"consistent-this": ["error"],
"constructor-super": ["error"],
curly: ["error"],
"default-case": ["error"],
"dot-location": ["error", "property"],
"dot-notation": ["error"],
"eol-last": ["error"],
eqeqeq: ["error"],
"for-direction": ["error"],
"func-call-spacing": ["error"],
"func-name-matching": ["error"],
"func-names": ["error", "never"],
"func-style": ["error"],
"function-paren-newline": ["error"],
"generator-star-spacing": ["error", "before"],
"getter-return": [
"error",
{
allowImplicit: false,
},
],
"global-require": ["error"],
"guard-for-in": ["error"],
"handle-callback-err": ["error"],
"id-blacklist": ["error"],
"id-length": ["error"],
"id-match": ["error"],
"implicit-arrow-linebreak": ["error", "beside"],
indent: [
"error",
numSpaces,
{
SwitchCase: 1,
},
],
"init-declarations": ["error"],
"jsx-quotes": ["error"],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"line-comment-position": ["error"],
"linebreak-style": ["error", "windows"],
"lines-around-comment": ["error"],
"lines-between-class-members": ["error"],
"max-depth": ["error"],
"max-len": ["error", maxLineLength],
"max-lines": [
"error",
{
skipBlankLines: true,
skipComments: true,
},
],
"max-nested-callbacks": ["error"],
"max-params": ["error"],
"max-statements": ["error"],
"max-statements-per-line": ["error"],
"multiline-comment-style": ["off", "starred-block"],
"multiline-ternary": ["error", "never"],
"new-cap": ["error"],
"new-parens": ["error"],
// TODO: configure this...
"newline-before-return": ["error"],
"newline-per-chained-call": ["error"],
"no-alert": ["error"],
"no-array-constructor": ["error"],
"no-await-in-loop": ["error"],
"no-bitwise": ["error"],
"no-buffer-constructor": ["error"],
"no-caller": ["error"],
"no-case-declarations": ["error"],
"no-catch-shadow": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": ["error", "except-parens"],
"no-confusing-arrow": ["error"],
"no-console": ["error"],
"no-const-assign": ["error"],
"no-constant-condition": [
"error",
{
checkLoops: false,
},
],
"no-continue": ["off"],
"no-control-regex": ["error"],
"no-debugger": ["error"],
"no-delete-var": ["error"],
"no-div-regex": ["error"],
"no-dupe-args": ["error"],
"no-dupe-class-members": ["error"],
"no-dupe-keys": ["error"],
"no-duplicate-case": ["error"],
"no-duplicate-imports": [
"error",
{
includeExports: true,
},
],
"no-else-return": ["error"],
"no-empty": [
"error",
{
allowEmptyCatch: false,
},
],
"no-empty-character-class": ["error"],
"no-empty-function": ["error"],
"no-empty-pattern": ["error"],
"no-eq-null": ["error"],
"no-eval": ["error"],
"no-ex-assign": ["error"],
"no-extend-native": ["error"],
"no-extra-bind": ["error"],
"no-extra-boolean-cast": ["error"],
"no-extra-label": ["error"],
"no-extra-parens": [
"error",
"all",
{
conditionalAssign: false,
},
],
"no-extra-semi": ["error"],
"no-fallthrough": ["error"],
"no-floating-decimal": ["error"],
"no-func-assign": ["error"],
"no-global-assign": ["error"],
"no-implicit-coercion": ["error"],
"no-implicit-globals": ["error"],
"no-implied-eval": ["error"],
"no-inline-comments": ["error"],
"no-inner-declarations": ["error", "both"],
"no-invalid-regexp": ["error"],
"no-invalid-this": ["error"],
"no-irregular-whitespace": [
"error",
{
skipComments: false,
skipRegExps: false,
skipStrings: false,
skipTemplates: false,
},
],
"no-iterator": ["error"],
"no-label-var": ["error"],
"no-labels": ["error"],
"no-lone-blocks": ["error"],
"no-lonely-if": ["error"],
"no-loop-func": ["error"],
"no-magic-numbers": [
"error",
{
ignore: [-1, 0, 1],
ignoreArrayIndexes: true,
},
],
"no-mixed-operators": ["error"],
"no-mixed-requires": ["error"],
"no-mixed-spaces-and-tabs": ["error"],
"no-multi-assign": ["error"],
"no-multi-spaces": ["error"],
"no-multi-str": ["error"],
"no-multiple-empty-lines": [
"error",
{
max: 1,
},
],
"no-native-reassign": ["error"],
"no-negated-condition": ["error"],
"no-negated-in-lhs": ["error"],
"no-nested-ternary": ["error"],
"no-new": ["error"],
"no-new-func": ["error"],
"no-new-object": ["error"],
"no-new-require": ["error"],
"no-new-symbol": ["error"],
"no-new-wrappers": ["error"],
"no-obj-calls": ["error"],
"no-octal": ["error"],
"no-octal-escape": ["error"],
"no-param-reassign": ["error"],
"no-path-concat": ["error"],
"no-plusplus": [
"error",
{
allowForLoopAfterthoughts: true,
},
],
"no-process-env": ["error"],
"no-process-exit": ["error"],
"no-proto": ["error"],
"no-prototype-builtins": ["error"],
"no-redeclare": ["error"],
"no-regex-spaces": ["error"],
"no-restricted-globals": ["error"],
"no-restricted-imports": ["error"],
"no-restricted-modules": ["error"],
"no-restricted-properties": [
"error",
{
message: "'log' is too general, use an appropriate level when logging.",
object: "console",
property: "log",
},
],
"no-restricted-syntax": ["error"],
"no-return-assign": ["error"],
"no-return-await": ["error"],
"no-script-url": ["error"],
"no-self-assign": [
"error",
{
props: false,
},
],
"no-self-compare": ["error"],
"no-sequences": ["error"],
"no-shadow": ["error"],
"no-shadow-restricted-names": ["error"],
"no-spaced-func": ["error"],
"no-sparse-arrays": ["error"],
"no-sync": ["error"],
"no-tabs": ["error"],
"no-template-curly-in-string": ["error"],
"no-ternary": ["off"],
"no-this-before-super": ["error"],
"no-throw-literal": ["error"],
"no-trailing-spaces": ["error"],
"no-undef": ["error"],
"no-undef-init": ["error"],
"no-undefined": ["error"],
"no-underscore-dangle": ["error"],
"no-unexpected-multiline": ["error"],
"no-unmodified-loop-condition": ["error"],
"no-unneeded-ternary": ["error"],
"no-unreachable": ["error"],
"no-unsafe-finally": ["error"],
"no-unsafe-negation": ["error"],
"no-unused-expressions": ["error"],
"no-unused-labels": ["error"],
"no-unused-vars": ["error"],
"no-use-before-define": ["error"],
"no-useless-call": ["error"],
"no-useless-computed-key": ["error"],
"no-useless-concat": ["error"],
"no-useless-constructor": ["error"],
"no-useless-escape": ["error"],
"no-useless-rename": [
"error",
{
ignoreDestructuring: false,
ignoreExport: false,
ignoreImport: false,
},
],
"no-useless-return": ["error"],
"no-var": ["error"],
"no-void": ["error"],
"no-warning-comments": ["error"],
"no-whitespace-before-property": ["error"],
"no-with": ["error"],
"nonblock-statement-body-position": ["error", "below"],
"object-curly-newline": ["error"],
"object-curly-spacing": ["error"],
"object-property-newline": ["error"],
"object-shorthand": ["error"],
"one-var": ["off"],
"one-var-declaration-per-line": ["error"],
"operator-assignment": ["error"],
"operator-linebreak": ["error", "none"],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"],
"prefer-const": ["error"],
"prefer-destructuring": ["off"],
"prefer-numeric-literals": ["error"],
"prefer-promise-reject-errors": ["off"],
"prefer-reflect": ["error"],
"prefer-rest-params": ["error"],
"prefer-spread": ["error"],
"prefer-template": ["error"],
"quote-props": ["error"],
quotes: ["error"],
radix: ["error", "as-needed"],
"require-await": ["error"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": ["error", "never"],
semi: ["error"],
"semi-spacing": ["error"],
"semi-style": ["error", "last"],
"sort-imports": ["error"],
"sort-keys": ["error"],
"sort-vars": ["error"],
"space-before-blocks": ["error"],
"space-before-function-paren": ["off"],
"space-in-parens": ["error"],
"space-infix-ops": ["error"],
"space-unary-ops": ["error"],
"spaced-comment": ["error"],
strict: ["error"],
"switch-colon-spacing": [
"error",
{
after: true,
before: false,
},
],
"symbol-description": ["error"],
"template-curly-spacing": ["error"],
"template-tag-spacing": ["error"],
"unicode-bom": ["error", "never"],
"use-isnan": ["error"],
"valid-jsdoc": ["error"],
"valid-typeof": ["error"],
"vars-on-top": ["error"],
"wrap-iife": ["error", "any"],
"wrap-regex": ["error"],
"yield-star-spacing": ["error", "before"],
yoda: ["error", "never"],
},
};

View File

@@ -8,66 +8,74 @@ const path = require("path");
const exec = require("child_process").exec;
const semver = require("./semver");
const getPackageJson = () => new Promise((resolve, reject) => {
const getPackageJson = () =>
new Promise((resolve, reject) => {
try {
/* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json")));
/* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json")));
} catch (error) {
reject(error);
reject(error);
}
});
});
const getEngines = (data) => new Promise((resolve, reject) => {
const getEngines = (data) =>
new Promise((resolve, reject) => {
let versions = null;
if (data.engines) {
versions = data.engines;
versions = data.engines;
}
if (versions) {
resolve(versions);
resolve(versions);
} else {
reject("Missing or improper 'engines' property in 'package.json'");
reject("Missing or improper 'engines' property in 'package.json'");
}
});
});
const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
const checkNpmVersion = (engines) =>
new Promise((resolve, reject) => {
exec("npm -v", (error, stdout, stderr) => {
if (error) {
reject(`Unable to find NPM version\n${stderr}`);
}
if (error) {
reject(`Unable to find NPM version\n${stderr}`);
}
const npmVersion = stdout.trim();
const engineVersion = engines.npm || ">=0";
const npmVersion = stdout.trim();
const engineVersion = engines.npm || ">=0";
if (semver.satisfies(npmVersion, engineVersion)) {
resolve();
} else {
reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`);
}
if (semver.satisfies(npmVersion, engineVersion)) {
resolve();
} else {
reject(
`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`,
);
}
});
});
});
const checkNodeVersion = (engines) => new Promise((resolve, reject) => {
const checkNodeVersion = (engines) =>
new Promise((resolve, reject) => {
const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines);
resolve(engines);
} else {
reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`);
reject(
`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`,
);
}
});
});
getPackageJson()
.then(getEngines)
.then(checkNodeVersion)
.then(checkNpmVersion)
.then(
() => true,
(error) => {
// Specifically disable these as the error message gets lost in the normal unhandled output.
/* eslint-disable no-console, no-process-exit */
console.error(error);
process.exit(1);
}
);
.then(getEngines)
.then(checkNodeVersion)
.then(checkNpmVersion)
.then(
() => true,
(error) => {
// Specifically disable these as the error message gets lost in the normal unhandled output.
/* eslint-disable no-console, no-process-exit */
console.error(error);
process.exit(1);
},
);

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -2366,6 +2366,145 @@ function initAugmentations(): void {
resetAugmentation(BladesSimulacrum);
}
// Special CotMG Augmentations
const ChurchOfTheMachineGodFactionName = "Church of the Machine God";
if (factionExists(ChurchOfTheMachineGodFactionName)) {
const StaneksGift1 = new Augmentation({
name: AugmentationNames.StaneksGift1,
repCost: 0,
moneyCost: 0,
info:
'Allison "Mother" Stanek imparts you with her gift. An ' +
"experimental Augmentation implanted at the base of the neck. " +
"It allows you to overclock your entire system by carefully " +
"changing the configuration.",
isSpecial: true,
hacking_chance_mult: 0.9,
hacking_speed_mult: 0.9,
hacking_money_mult: 0.9,
hacking_grow_mult: 0.9,
hacking_mult: 0.9,
strength_mult: 0.9,
defense_mult: 0.9,
dexterity_mult: 0.9,
agility_mult: 0.9,
charisma_mult: 0.9,
hacking_exp_mult: 0.9,
strength_exp_mult: 0.9,
defense_exp_mult: 0.9,
dexterity_exp_mult: 0.9,
agility_exp_mult: 0.9,
charisma_exp_mult: 0.9,
company_rep_mult: 0.9,
faction_rep_mult: 0.9,
crime_money_mult: 0.9,
crime_success_mult: 0.9,
hacknet_node_money_mult: 0.9,
hacknet_node_purchase_cost_mult: 1.1,
hacknet_node_ram_cost_mult: 1.1,
hacknet_node_core_cost_mult: 1.1,
hacknet_node_level_cost_mult: 1.1,
work_money_mult: 0.9,
stats: <>Its unstable nature decreases all your stats by 10%</>,
});
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift1);
const StaneksGift2 = new Augmentation({
name: AugmentationNames.StaneksGift2,
repCost: 1000,
moneyCost: 0,
info:
'TODO, something about Mother being bullshit and you get more control over her "gift"<br><br>' +
"The penalty for the gift is only 5%",
prereqs: [AugmentationNames.StaneksGift1],
isSpecial: true,
hacking_chance_mult: 0.95 / 0.9,
hacking_speed_mult: 0.95 / 0.9,
hacking_money_mult: 0.95 / 0.9,
hacking_grow_mult: 0.95 / 0.9,
hacking_mult: 0.95 / 0.9,
strength_mult: 0.95 / 0.9,
defense_mult: 0.95 / 0.9,
dexterity_mult: 0.95 / 0.9,
agility_mult: 0.95 / 0.9,
charisma_mult: 0.95 / 0.9,
hacking_exp_mult: 0.95 / 0.9,
strength_exp_mult: 0.95 / 0.9,
defense_exp_mult: 0.95 / 0.9,
dexterity_exp_mult: 0.95 / 0.9,
agility_exp_mult: 0.95 / 0.9,
charisma_exp_mult: 0.95 / 0.9,
company_rep_mult: 0.95 / 0.9,
faction_rep_mult: 0.95 / 0.9,
crime_money_mult: 0.95 / 0.9,
crime_success_mult: 0.95 / 0.9,
hacknet_node_money_mult: 0.95 / 0.9,
hacknet_node_purchase_cost_mult: 1.05 / 1.1,
hacknet_node_ram_cost_mult: 1.05 / 1.1,
hacknet_node_core_cost_mult: 1.05 / 1.1,
hacknet_node_level_cost_mult: 1.05 / 1.1,
work_money_mult: 0.95 / 0.9,
stats: null,
});
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift2);
const StaneksGift3 = new Augmentation({
name: AugmentationNames.StaneksGift3,
repCost: 10000,
moneyCost: 0,
info:
"TODO, learn more about Allisons scheme, gain full control over the gift.<br><br>" +
"Finally freed from the penalty of the gift.",
prereqs: [AugmentationNames.StaneksGift2],
isSpecial: true,
hacking_chance_mult: 1 / 0.95,
hacking_speed_mult: 1 / 0.95,
hacking_money_mult: 1 / 0.95,
hacking_grow_mult: 1 / 0.95,
hacking_mult: 1 / 0.95,
strength_mult: 1 / 0.95,
defense_mult: 1 / 0.95,
dexterity_mult: 1 / 0.95,
agility_mult: 1 / 0.95,
charisma_mult: 1 / 0.95,
hacking_exp_mult: 1 / 0.95,
strength_exp_mult: 1 / 0.95,
defense_exp_mult: 1 / 0.95,
dexterity_exp_mult: 1 / 0.95,
agility_exp_mult: 1 / 0.95,
charisma_exp_mult: 1 / 0.95,
company_rep_mult: 1 / 0.95,
faction_rep_mult: 1 / 0.95,
crime_money_mult: 1 / 0.95,
crime_success_mult: 1 / 0.95,
hacknet_node_money_mult: 1 / 0.95,
hacknet_node_purchase_cost_mult: 1 / 1.05,
hacknet_node_ram_cost_mult: 1 / 1.05,
hacknet_node_core_cost_mult: 1 / 1.05,
hacknet_node_level_cost_mult: 1 / 1.05,
work_money_mult: 1 / 0.95,
stats: null,
});
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift3);
const StaneksGiftAscension4 = new Augmentation({
name: AugmentationNames.StaneksGift4,
repCost: 500000000,
moneyCost: 0,
info:
"Allow Allison to install an Ascension port in her Gift. Allowing you to connect with the Machine God.<br><br>" +
"(hydro notes: Finishes the BN, eventually)",
prereqs: [AugmentationNames.StaneksGift3],
isSpecial: true,
stats: null,
});
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGiftAscension4);
}
// Update costs based on how many have been purchased
mult = Math.pow(
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],

View File

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

View File

@@ -396,6 +396,15 @@ BitNodes["BitNode9"] = new BitNode(
<br />
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
<br />
<br />
This Source-File also increases your hacknet multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
@@ -521,8 +530,38 @@ BitNodes["BitNode12"] = new BitNode(
</>
),
);
BitNodes["BitNode13"] = new BitNode(
13,
2,
"They're lunatics",
"1 step back, 2 steps forward",
(
<>
With the invention of Augmentations in the 2040s a religious group known as the Church of the Machine God has
rallied far more support than anyone would have hoped.
<br />
<br />
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
other. Find her in Chongquing and gain her trust.
<br />
<br />
In this BitNode:
<br />
<br />
Every is significantly reduced
<br />
Stanek's Gift power is significantly increased.
<br />
<br />
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the Church of the Machine God appear in other BitNodes.
<br />
<br />
Each level of this Source-File increases the size of Stanek's Gift.
</>
),
);
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
@@ -544,6 +583,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers[mult] = 1;
}
}
// Special case.
BitNodeMultipliers.StaneksGiftExtraSize = 0;
switch (p.bitNodeN) {
case 1: // Source Genesis (every multiplier is 1)
@@ -784,6 +825,47 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.BladeburnerSkillCost = inc;
break;
}
case 13: {
BitNodeMultipliers.DaedalusAugsRequirement = 100;
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
BitNodeMultipliers.StrengthLevelMultiplier = 0.2;
BitNodeMultipliers.DefenseLevelMultiplier = 0.2;
BitNodeMultipliers.DexterityLevelMultiplier = 0.2;
BitNodeMultipliers.AgilityLevelMultiplier = 0.2;
BitNodeMultipliers.CharismaLevelMultiplier = 0.2;
BitNodeMultipliers.ServerMaxMoney = 0.15;
BitNodeMultipliers.ServerStartingMoney = 0.75;
BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CodingContractMoney = 0.2;
BitNodeMultipliers.CompanyWorkExpGain = 0.1;
BitNodeMultipliers.ClassGymExpGain = 0.1;
BitNodeMultipliers.FactionWorkExpGain = 0.1;
BitNodeMultipliers.HackExpGain = 0.1;
BitNodeMultipliers.CrimeExpGain = 0.1;
BitNodeMultipliers.FactionWorkRepGain = 0.4;
BitNodeMultipliers.FourSigmaMarketDataCost = 10;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 10;
BitNodeMultipliers.CorporationValuation = 0.001;
BitNodeMultipliers.BladeburnerRank = 0.1;
BitNodeMultipliers.BladeburnerSkillCost = 5;
BitNodeMultipliers.GangKarmaRequirement = 20;
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
BitNodeMultipliers.StaneksGiftExtraSize = 1;
break;
}
default:
console.warn("Player.bitNodeN invalid");
break;

View File

@@ -212,6 +212,16 @@ interface IBitNodeMultipliers {
*/
StrengthLevelMultiplier: number;
/**
* Influences the power of the gift.
*/
StaneksGiftPowerMultiplier: number;
/**
* Influences the size of the gift.
*/
StaneksGiftExtraSize: number;
// Index signature
[key: string]: number;
}
@@ -274,4 +284,7 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
DaedalusAugsRequirement: 1,
GangKarmaRequirement: 1,
StaneksGiftPowerMultiplier: 1,
StaneksGiftExtraSize: 0,
};

View File

@@ -160,7 +160,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ O \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>

View File

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

View File

@@ -6,11 +6,10 @@ import {
} from "./CodingContracts";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers";
import { SpecialServerNames } from "./Server/SpecialServerIps";
import { GetServer, GetAllServers } from "./Server/AllServers";
import { SpecialServers } from "./Server/data/SpecialServers";
import { Server } from "./Server/Server";
import { HacknetServer } from "./Hacknet/HacknetServer";
import { BaseServer } from "./Server/BaseServer";
import { getRandomInt } from "./utils/helpers/getRandomInt";
@@ -68,10 +67,7 @@ export function generateContract(params: IGenerateContractParams): void {
// Server
let server;
if (params.server != null) {
server = GetServerByHostname(params.server);
if (server == null) {
server = AllServers[params.server];
}
server = GetServer(params.server);
if (server == null) {
server = getRandomServer();
}
@@ -165,10 +161,10 @@ function getRandomReward(): ICodingContractReward {
return reward;
}
function getRandomServer(): Server | HacknetServer {
const servers = Object.keys(AllServers);
function getRandomServer(): BaseServer {
const servers = GetAllServers();
let randIndex = getRandomInt(0, servers.length - 1);
let randServer = AllServers[servers[randIndex]];
let randServer = servers[randIndex];
// An infinite loop shouldn't ever happen, but to be safe we'll use
// a for loop with a limited number of tries
@@ -176,18 +172,18 @@ function getRandomServer(): Server | HacknetServer {
if (
randServer instanceof Server &&
!randServer.purchasedByPlayer &&
randServer.hostname !== SpecialServerNames.WorldDaemon
randServer.hostname !== SpecialServers.WorldDaemon
) {
break;
}
randIndex = getRandomInt(0, servers.length - 1);
randServer = AllServers[servers[randIndex]];
randServer = servers[randIndex];
}
return randServer;
}
function getRandomFilename(server: Server | HacknetServer, reward: ICodingContractReward): string {
function getRandomFilename(server: BaseServer, reward: ICodingContractReward): string {
let contractFn = `contract-${getRandomInt(0, 1e6)}`;
for (let i = 0; i < 1000; ++i) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,13 @@ export function CityTabs(props: IProps): React.ReactElement {
</Tabs>
{city !== "Expand" ? (
<Industry rerender={props.rerender} city={city} warehouse={division.warehouses[city]} office={office} />
<Industry
key={city}
rerender={props.rerender}
city={city}
warehouse={division.warehouses[city]}
office={office}
/>
) : (
<ExpandNewCity cityStateSetter={setCity} />
)}

View File

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

View File

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

View File

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

View File

@@ -8,8 +8,7 @@ import { IndustryUpgrades } from "../IndustryUpgrades";
import { numeralWrapper } from "../../ui/numeralFormat";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { MakeProductModal } from "./MakeProductModal";
import { ResearchPopup } from "./ResearchPopup";
import { createPopup } from "../../ui/React/createPopup";
import { ResearchModal } from "./ResearchModal";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable";
@@ -96,6 +95,7 @@ function Text(): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [helpOpen, setHelpOpen] = useState(false);
const [researchOpen, setResearchOpen] = useState(false);
const vechain = corp.unlockUpgrades[4] === 1;
const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber();
@@ -116,14 +116,6 @@ function Text(): React.ReactElement {
});
}
function openResearchPopup(): void {
const popupId = "corporation-research-popup-box";
createPopup(popupId, ResearchPopup, {
industry: division,
popupId: popupId,
});
}
return (
<>
<Typography>
@@ -214,9 +206,10 @@ function Text(): React.ReactElement {
>
<Typography>Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}</Typography>
</Tooltip>
<Button sx={{ mx: 1 }} onClick={openResearchPopup}>
<Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}>
Research
</Button>
<ResearchModal open={researchOpen} onClose={() => setResearchOpen(false)} industry={division} />
</Box>
</>
);

View File

@@ -123,10 +123,21 @@ function WarehouseRoot(props: IProps): React.ReactElement {
);
}
for (const prodName in division.products) {
const prod = division.products[prodName];
if (prod === undefined) continue;
breakdown = (
<>
{breakdown}
{prodName}: {numeralWrapper.format(prod.data[props.warehouse.loc][0] * prod.siz, "0,0.0")}
</>
);
}
return (
<Paper>
<Box display="flex" alignItems="center">
<Tooltip title={props.warehouse.sizeUsed !== 0 ? breakdown : ""}>
<Tooltip title={props.warehouse.sizeUsed !== 0 ? <Typography>{breakdown}</Typography> : ""}>
<Typography color={props.warehouse.sizeUsed >= props.warehouse.size ? "error" : "primary"}>
Storage: {numeralWrapper.formatBigNumber(props.warehouse.sizeUsed)} /{" "}
{numeralWrapper.formatBigNumber(props.warehouse.size)}

View File

@@ -0,0 +1,122 @@
import React, { useState } from "react";
import { Modal } from "../../ui/React/Modal";
import { IndustryResearchTrees } from "../IndustryData";
import { CorporationConstants } from "../data/Constants";
import { IIndustry } from "../IIndustry";
import { Research } from "../Actions";
import { Node } from "../ResearchTree";
import { ResearchMap } from "../ResearchMap";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
interface INodeProps {
n: Node | null;
division: IIndustry;
}
function Upgrade({ n, division }: INodeProps): React.ReactElement {
const [open, setOpen] = useState(false);
if (n === null) return <></>;
const r = ResearchMap[n.text];
let disabled = division.sciResearch.qty < r.cost || n.researched;
const parent = n.parent;
if (parent !== null) {
disabled = disabled || !parent.researched;
}
function research(): void {
if (n === null || disabled) return;
try {
Research(division, n.text);
} catch (err) {
dialogBoxCreate(err + "");
return;
}
dialogBoxCreate(
`Researched ${n.text}. It may take a market cycle ` +
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
`the Research apply.`,
);
}
const but = (
<Box>
<Tooltip
title={
<Typography>
Research points: {r.cost}
<br />
{r.desc}
</Typography>
}
>
<span>
<Button disabled={disabled} onClick={research}>
{n.text}
</Button>
</span>
</Tooltip>
</Box>
);
if (n.children.length === 0) return but;
return (
<Box>
<Box display="flex">
{but}
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
</Box>
<Collapse in={open} unmountOnExit>
<Box m={4}>
{n.children.map((m) => (
<Upgrade key={m.text} division={division} n={m} />
))}
</Box>
</Collapse>
</Box>
);
}
interface IProps {
open: boolean;
onClose: () => void;
industry: IIndustry;
}
// Create the Research Tree UI for this Industry
export function ResearchModal(props: IProps): React.ReactElement {
const researchTree = IndustryResearchTrees[props.industry.type];
if (researchTree === undefined) return <></>;
return (
<Modal open={props.open} onClose={props.onClose}>
<Upgrade division={props.industry} n={researchTree.root} />
<Typography>
Research points: {props.industry.sciResearch.qty.toFixed(3)}
<br />
Multipliers from research:
<br />* Advertising Multiplier: x{researchTree.getAdvertisingMultiplier()}
<br />* Employee Charisma Multiplier: x{researchTree.getEmployeeChaMultiplier()}
<br />* Employee Creativity Multiplier: x{researchTree.getEmployeeCreMultiplier()}
<br />* Employee Efficiency Multiplier: x{researchTree.getEmployeeEffMultiplier()}
<br />* Employee Intelligence Multiplier: x{researchTree.getEmployeeIntMultiplier()}
<br />* Production Multiplier: x{researchTree.getProductionMultiplier()}
<br />* Sales Multiplier: x{researchTree.getSalesMultiplier()}
<br />* Scientific Research Multiplier: x{researchTree.getScientificResearchMultiplier()}
<br />* Storage Multiplier: x{researchTree.getStorageMultiplier()}
</Typography>
</Modal>
);
}

View File

@@ -1,96 +0,0 @@
import React, { useEffect } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { IndustryResearchTrees } from "../IndustryData";
import { CorporationConstants } from "../data/Constants";
import { Treant } from "treant-js";
import { IIndustry } from "../IIndustry";
import { Research } from "../Actions";
interface IProps {
industry: IIndustry;
popupId: string;
}
// Create the Research Tree UI for this Industry
export function ResearchPopup(props: IProps): React.ReactElement {
const researchTree = IndustryResearchTrees[props.industry.type];
if (researchTree === undefined) return <></>;
useEffect(() => {
{
const boxContent = document.getElementById(`${props.popupId}-content`);
if (boxContent != null) {
boxContent.style.minHeight = "80vh";
}
}
// Get the tree's markup (i.e. config) for Treant
const markup = researchTree.createTreantMarkup();
markup.chart.container = "#" + props.popupId + "-content";
markup.chart.nodeAlign = "BOTTOM";
markup.chart.rootOrientation = "WEST";
markup.chart.siblingSeparation = 40;
markup.chart.connectors = {
type: "step",
style: {
"arrow-end": "block-wide-long",
stroke: "white",
"stroke-width": 2,
},
};
Treant(markup);
// Add Event Listeners for all Nodes
const allResearch = researchTree.getAllNodes();
for (let i = 0; i < allResearch.length; ++i) {
// If this is already Researched, skip it
if (props.industry.researched[allResearch[i]] === true) {
continue;
}
// Get the DOM Element to add a click listener to it
const sanitizedName = allResearch[i].replace(/\s/g, "");
const div = document.getElementById(sanitizedName + "-corp-research-click-listener");
if (div == null) {
console.warn(`Could not find Research Tree div for ${sanitizedName}`);
continue;
}
div.addEventListener("click", () => {
try {
Research(props.industry, allResearch[i]);
} catch (err) {
dialogBoxCreate(err + "");
return;
}
dialogBoxCreate(
`Researched ${allResearch[i]}. It may take a market cycle ` +
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
`the Research apply.`,
);
removePopup(props.popupId);
});
}
});
return (
<div id={props.popupId}>
<div>
Research points: {props.industry.sciResearch.qty}
<br />
Multipliers from research:
<br />* Advertising Multiplier: x{researchTree.getAdvertisingMultiplier()}
<br />* Employee Charisma Multiplier: x{researchTree.getEmployeeChaMultiplier()}
<br />* Employee Creativity Multiplier: x{researchTree.getEmployeeCreMultiplier()}
<br />* Employee Efficiency Multiplier: x{researchTree.getEmployeeEffMultiplier()}
<br />* Employee Intelligence Multiplier: x{researchTree.getEmployeeIntMultiplier()}
<br />* Production Multiplier: x{researchTree.getProductionMultiplier()}
<br />* Sales Multiplier: x{researchTree.getSalesMultiplier()}
<br />* Scientific Research Multiplier: x{researchTree.getScientificResearchMultiplier()}
<br />* Storage Multiplier: x{researchTree.getStorageMultiplier()}
</div>
</div>
);
}

View File

@@ -9,7 +9,7 @@ import { ICorporation } from "../ICorporation";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money";
interface IProps {
open: boolean;
onClose: () => void;
@@ -73,10 +73,11 @@ export function SellSharesModal(props: IProps): React.ReactElement {
player.recordMoneySource(profit, "corporation");
props.onClose();
dialogBoxCreate(
`Sold {numeralWrapper.formatMoney(shares)} shares for ` +
`${numeralWrapper.formatMoney(profit)}. ` +
`The corporation's stock price fell to ${numeralWrapper.formatMoney(corp.sharePrice)} ` +
`as a result of dilution.`,
<>
Sold {numeralWrapper.formatMoney(shares)} shares for
<Money money={profit} />. The corporation's stock price fell to&nbsp; <Money money={corp.sharePrice} />
as a result of dilution.
</>,
);
props.rerender();

View File

@@ -0,0 +1,90 @@
import { Fragment, FragmentById } from "./Fragment";
import { FragmentType } from "./FragmentType";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
const noCharge = [FragmentType.None, FragmentType.Delete, FragmentType.Booster];
export interface IActiveFragmentParams {
x: number;
y: number;
fragment: Fragment;
}
export class ActiveFragment {
id: number;
charge: number;
x: number;
y: number;
constructor(params?: IActiveFragmentParams) {
if (params) {
this.id = params.fragment.id;
this.x = params.x;
this.y = params.y;
this.charge = 1;
if (noCharge.includes(params.fragment.type)) this.charge = 0;
} else {
this.id = -1;
this.x = -1;
this.y = -1;
this.charge = -1;
}
}
collide(other: ActiveFragment): boolean {
const thisFragment = this.fragment();
const otherFragment = other.fragment();
// These 2 variables converts 'this' local coordinates to world to other local.
const dx: number = other.x - this.x;
const dy: number = other.y - this.y;
for (let j = 0; j < thisFragment.shape.length; j++) {
for (let i = 0; i < thisFragment.shape[j].length; i++) {
if (thisFragment.fullAt(i, j) && otherFragment.fullAt(i - dx, j - dy)) return true;
}
}
return false;
}
fragment(): Fragment {
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
return fragment;
}
fullAt(worldX: number, worldY: number): boolean {
return this.fragment().fullAt(worldX - this.x, worldY - this.y);
}
neighboors(): number[][] {
return this.fragment()
.neighboors()
.map((cell) => [this.x + cell[0], this.y + cell[1]]);
}
copy(): ActiveFragment {
// We have to do a round trip because the constructor.
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
const c = new ActiveFragment({ x: this.x, y: this.y, fragment: fragment });
c.charge = this.charge;
return c;
}
/**
* Serialize an active fragment to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("ActiveFragment", this);
}
/**
* Initializes an acive fragment from a JSON save state
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): ActiveFragment {
return Generic_fromJSON(ActiveFragment, value.data);
}
}
Reviver.constructors.ActiveFragment = ActiveFragment;

329
src/CotMG/Fragment.ts Normal file
View File

@@ -0,0 +1,329 @@
import { FragmentType } from "./FragmentType";
export const Fragments: Fragment[] = [];
export class Fragment {
id: number;
shape: boolean[][];
type: FragmentType;
power: number;
limit: number;
constructor(id: number, shape: boolean[][], type: FragmentType, power: number, limit: number) {
this.id = id;
this.shape = shape;
this.type = type;
this.power = power;
this.limit = limit;
}
fullAt(x: number, y: number): boolean {
if (y < 0) return false;
if (y >= this.shape.length) return false;
if (x < 0) return false;
if (x >= this.shape[y].length) return false;
// Yes it's ordered y first.
return this.shape[y][x];
}
width(): number {
// check every line for robustness.
return Math.max(...this.shape.map((line) => line.length));
}
height(): number {
return this.shape.length;
}
// List of direct neighboors of this fragment.
neighboors(): number[][] {
const candidates: number[][] = [];
const add = (x: number, y: number): void => {
if (this.fullAt(x, y)) return;
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
candidates.push([x, y]);
};
for (let y = 0; y < this.shape.length; y++) {
for (let x = 0; x < this.shape[y].length; x++) {
// This cell is full, add all it's neighboors.
if (!this.shape[y][x]) continue;
add(x - 1, y);
add(x + 1, y);
add(x, y - 1);
add(x, y + 1);
}
}
const cells: number[][] = [];
for (const candidate of candidates) {
if (cells.some((cell) => cell[0] === candidate[0] && cell[1] === candidate[1])) continue;
cells.push(candidate);
}
return cells;
}
copy(): Fragment {
return new Fragment(
this.id,
this.shape.map((a) => a.slice()),
this.type,
this.power,
this.limit,
);
}
}
export function FragmentById(id: number): Fragment | null {
for (const fragment of Fragments) {
if (fragment.id === id) return fragment;
}
return null;
}
(function () {
const _ = false;
const X = true;
Fragments.push(
new Fragment(
0, // id
[
// shape
[X, X, X],
[_, _, X],
[_, _, X],
],
FragmentType.Hacking, // type
1,
1, // limit
),
);
Fragments.push(
new Fragment(
1, // id
[
// shape
[_, X, _],
[X, X, X],
[_, X, _],
],
FragmentType.Hacking, // type
1,
1, // limit
),
);
Fragments.push(
new Fragment(
5, // id
[
// shape
[X, X],
],
FragmentType.HackingSpeed, // type
1.3,
1, // limit
),
);
Fragments.push(
new Fragment(
6, // id
[
[X, _],
[X, X],
], // shape
FragmentType.HackingMoney, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
7, // id
[
[X, X],
[X, X],
], // shape
FragmentType.HackingGrow, // type
0.5, // power
1, // limit
),
);
Fragments.push(
new Fragment(
8, // id
[
[X, X, X],
[_, X, _],
[X, X, X],
], // shape
FragmentType.Hacking, // type
1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
10, // id
[
[X, X],
[_, X],
], // shape
FragmentType.Strength, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
12, // id
[
[_, X],
[X, X],
], // shape
FragmentType.Defense, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
14, // id
[
[X, X],
[X, _],
], // shape
FragmentType.Dexterity, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
16, // id
[
[X, _],
[X, X],
], // shape
FragmentType.Agility, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
18, // id
[
[X, X],
[X, _],
], // shape
FragmentType.Charisma, // type
3, // power
1, // limit
),
);
Fragments.push(
new Fragment(
20, // id
[
[X, _, _],
[X, X, _],
[X, X, X],
], // shape
FragmentType.HacknetMoney, // type
1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
21, // id
[
[X, X],
[_, X],
[_, X],
], // shape
FragmentType.HacknetCost, // type
-1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
25, // id
[
[X, X, X],
[_, X, _],
], // shape
FragmentType.Rep, // type
0.5, // power
1, // limit
),
);
Fragments.push(
new Fragment(
27, // id
[
[X, _],
[_, X],
], // shape
FragmentType.WorkMoney, // type
10, // power
1, // limit
),
);
Fragments.push(
new Fragment(
28, // id
[[X, X]], // shape
FragmentType.Crime, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
30, // id
[
[X, X, X],
[X, X, X],
[X, X, X],
], // shape
FragmentType.Bladeburner, // type
1.3, // power
1, // limit
),
);
Fragments.push(
new Fragment(
2, // id
[
// shape
[X, X, X],
[X, _, X],
[X, X, X],
],
FragmentType.Booster, // type
1.1, // power
3, // limit
),
);
Fragments.push(
new Fragment(
31, // id
[
// shape
[X],
[X],
[X],
[X],
],
FragmentType.Booster, // type
1.1, // power
3, // limit
),
);
})();
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
export const DeleteFragment = new Fragment(-2, [], FragmentType.Delete, 0, Infinity);

96
src/CotMG/FragmentType.ts Normal file
View File

@@ -0,0 +1,96 @@
export enum FragmentType {
// Special fragments for the UI
None,
Delete,
// Stats boosting fragments
HackingChance,
HackingSpeed,
HackingMoney,
HackingGrow,
Hacking,
Strength,
Defense,
Dexterity,
Agility,
Charisma,
HacknetMoney,
HacknetCost,
Rep,
WorkMoney,
Crime,
Bladeburner,
// utility fragments.
Booster,
}
export function Effect(tpe: FragmentType): string {
switch (tpe) {
case FragmentType.HackingChance: {
return "+x% hack() success chance";
break;
}
case FragmentType.HackingSpeed: {
return "+x% faster hack(), grow(), and weaken()";
break;
}
case FragmentType.HackingMoney: {
return "+x% hack() power";
break;
}
case FragmentType.HackingGrow: {
return "+x% grow() power";
break;
}
case FragmentType.Hacking: {
return "+x% hacking skill";
break;
}
case FragmentType.Strength: {
return "+x% strength skill";
break;
}
case FragmentType.Defense: {
return "+x% defense skill";
break;
}
case FragmentType.Dexterity: {
return "+x% dexterity skill";
break;
}
case FragmentType.Agility: {
return "+x% agility skill";
break;
}
case FragmentType.Charisma: {
return "+x% charisma skill";
break;
}
case FragmentType.HacknetMoney: {
return "+x% hacknet production";
break;
}
case FragmentType.HacknetCost: {
return "-x% all hacknet cost";
break;
}
case FragmentType.Rep: {
return "+x% reputation from factions and companies";
break;
}
case FragmentType.WorkMoney: {
return "+x% work money";
break;
}
case FragmentType.Crime: {
return "+x% crime money";
break;
}
case FragmentType.Bladeburner: {
return "+x% all bladeburner stats";
break;
}
}
throw new Error("Calling effect for fragment type that doesn't have an effect " + tpe);
}

14
src/CotMG/Helper.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Reviver } from "../utils/JSONReviver";
import { IStaneksGift } from "./IStaneksGift";
import { StaneksGift } from "./StaneksGift";
export let staneksGift: IStaneksGift = new StaneksGift();
export function loadStaneksGift(saveString: string): void {
if (saveString) {
staneksGift = JSON.parse(saveString, Reviver);
} else {
staneksGift = new StaneksGift();
}
}

22
src/CotMG/IStaneksGift.ts Normal file
View File

@@ -0,0 +1,22 @@
import { ActiveFragment } from "./ActiveFragment";
import { Fragment } from "./Fragment";
import { IPlayer } from "../PersonObjects/IPlayer";
export interface IStaneksGift {
storedCycles: number;
fragments: ActiveFragment[];
width(): number;
height(): number;
charge(worldX: number, worldY: number, ram: number): number;
process(p: IPlayer, n: number): void;
effect(fragment: ActiveFragment): number;
canPlace(x: number, y: number, fragment: Fragment): boolean;
place(x: number, y: number, fragment: Fragment): boolean;
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
deleteAt(worldX: number, worldY: number): boolean;
clear(): void;
count(fragment: Fragment): number;
inBonus(): boolean;
prestigeAugmentation(): void;
prestigeSourceFile(): void;
}

222
src/CotMG/StaneksGift.ts Normal file
View File

@@ -0,0 +1,222 @@
import { Fragment } from "./Fragment";
import { ActiveFragment } from "./ActiveFragment";
import { FragmentType } from "./FragmentType";
import { IStaneksGift } from "./IStaneksGift";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Factions } from "../Faction/Factions";
import { CalculateEffect } from "./formulas/effect";
import { CalculateCharge } from "./formulas/charge";
import { StaneksGiftEvents } from "./StaneksGiftEvents";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { CONSTANTS } from "../Constants";
import { StanekConstants } from "./data/Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Player } from "../Player";
export class StaneksGift implements IStaneksGift {
storedCycles = 0;
fragments: ActiveFragment[] = [];
baseSize(): number {
return StanekConstants.BaseSize + BitNodeMultipliers.StaneksGiftExtraSize + Player.sourceFileLvl(13);
}
width(): number {
return Math.floor(this.baseSize() / 2 + 1);
}
height(): number {
return Math.floor(this.baseSize() / 2 + 0.6);
}
charge(worldX: number, worldY: number, ram: number): number {
const af = this.fragmentAt(worldX, worldY);
if (af === null) return 0;
const charge = CalculateCharge(ram);
af.charge += charge;
Factions["Church of the Machine God"].playerReputation += Math.log(ram) / Math.log(2);
return ram;
}
inBonus(): boolean {
return (this.storedCycles * CONSTANTS._idleSpeed) / 1000 > 1;
}
process(p: IPlayer, numCycles = 1): void {
this.storedCycles += numCycles;
this.storedCycles -= 5;
this.storedCycles = Math.max(0, this.storedCycles);
this.updateMults(p);
StaneksGiftEvents.emit();
}
effect(fragment: ActiveFragment): number {
// Find all the neighbooring cells
const cells = fragment.neighboors();
// find the neighbooring active fragments.
const maybeFragments = cells.map((n) => this.fragmentAt(n[0], n[1]));
// Filter out nulls with typescript "Type guard". Whatever
let neighboors = maybeFragments.filter((v: ActiveFragment | null): v is ActiveFragment => !!v);
neighboors = neighboors.filter((fragment) => fragment.fragment().type === FragmentType.Booster);
let boost = 1;
for (const neighboor of neighboors) {
boost *= neighboor.fragment().power;
}
return CalculateEffect(fragment.charge, fragment.fragment().power, boost);
}
canPlace(x: number, y: number, fragment: Fragment): boolean {
if (x + fragment.width() > this.width()) return false;
if (y + fragment.height() > this.height()) return false;
if (this.count(fragment) >= fragment.limit) return false;
const newFrag = new ActiveFragment({ x: x, y: y, fragment: fragment });
for (const aFrag of this.fragments) {
if (aFrag.collide(newFrag)) return false;
}
return true;
}
place(x: number, y: number, fragment: Fragment): boolean {
if (!this.canPlace(x, y, fragment)) return false;
this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment }));
return true;
}
fragmentAt(worldX: number, worldY: number): ActiveFragment | null {
for (const aFrag of this.fragments) {
if (aFrag.fullAt(worldX, worldY)) {
return aFrag;
}
}
return null;
}
count(fragment: Fragment): number {
let amt = 0;
for (const aFrag of this.fragments) {
if (aFrag.fragment().id === fragment.id) amt++;
}
return amt;
}
deleteAt(worldX: number, worldY: number): boolean {
for (let i = 0; i < this.fragments.length; i++) {
if (this.fragments[i].fullAt(worldX, worldY)) {
this.fragments.splice(i, 1);
return true;
}
}
return false;
}
clear(): void {
this.fragments = [];
}
updateMults(p: IPlayer): void {
p.reapplyAllAugmentations(true);
p.reapplyAllSourceFiles();
for (const aFrag of this.fragments) {
const fragment = aFrag.fragment();
const power = this.effect(aFrag);
switch (fragment.type) {
case FragmentType.HackingChance:
p.hacking_chance_mult *= power;
break;
case FragmentType.HackingSpeed:
p.hacking_speed_mult *= power;
break;
case FragmentType.HackingMoney:
p.hacking_money_mult *= power;
break;
case FragmentType.HackingGrow:
p.hacking_grow_mult *= power;
break;
case FragmentType.Hacking:
p.hacking_mult *= power;
p.hacking_exp_mult *= power;
break;
case FragmentType.Strength:
p.strength_mult *= power;
p.strength_exp_mult *= power;
break;
case FragmentType.Defense:
p.defense_mult *= power;
p.defense_exp_mult *= power;
break;
case FragmentType.Dexterity:
p.dexterity_mult *= power;
p.dexterity_exp_mult *= power;
break;
case FragmentType.Agility:
p.agility_mult *= power;
p.agility_exp_mult *= power;
break;
case FragmentType.Charisma:
p.charisma_mult *= power;
p.charisma_exp_mult *= power;
break;
case FragmentType.HacknetMoney:
p.hacknet_node_money_mult *= power;
break;
case FragmentType.HacknetCost:
p.hacknet_node_purchase_cost_mult *= power;
p.hacknet_node_ram_cost_mult *= power;
p.hacknet_node_core_cost_mult *= power;
p.hacknet_node_level_cost_mult *= power;
break;
case FragmentType.Rep:
p.company_rep_mult *= power;
p.faction_rep_mult *= power;
break;
case FragmentType.WorkMoney:
p.work_money_mult *= power;
break;
case FragmentType.Crime:
p.crime_success_mult *= power;
p.crime_money_mult *= power;
break;
case FragmentType.Bladeburner:
p.bladeburner_max_stamina_mult *= power;
p.bladeburner_stamina_gain_mult *= power;
p.bladeburner_analysis_mult *= power;
p.bladeburner_success_chance_mult *= power;
break;
}
}
}
prestigeAugmentation(): void {
this.clear();
}
prestigeSourceFile(): void {
this.clear();
}
/**
* Serialize Staneks Gift to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("StaneksGift", this);
}
/**
* Initializes Staneks Gift from a JSON save state
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): StaneksGift {
return Generic_fromJSON(StaneksGift, value.data);
}
}
Reviver.constructors.StaneksGift = StaneksGift;

View File

@@ -0,0 +1,2 @@
import { EventEmitter } from "../utils/EventEmitter";
export const StaneksGiftEvents = new EventEmitter<[]>();

View File

@@ -0,0 +1,7 @@
export const StanekConstants: {
RAMBonus: number;
BaseSize: number;
} = {
RAMBonus: 0.1,
BaseSize: 12,
};

View File

@@ -0,0 +1,5 @@
import { StanekConstants } from "../data/Constants";
export function CalculateCharge(ram: number): number {
return ram * Math.pow(1 + Math.log2(ram) * StanekConstants.RAMBonus, 0.7);
}

View File

@@ -0,0 +1,3 @@
export function CalculateEffect(charge: number, power: number, boost: number): number {
return 1 + (Math.log(charge + 1) / (Math.log(3) * 100)) * power * boost;
}

4
src/CotMG/notes Normal file
View File

@@ -0,0 +1,4 @@
incentive for more threads
boosters just multiply output, eg 20% * 1.5 = 30%
git remote add danielyxie git@github.com:danielyxie/bitburner.git

40
src/CotMG/ui/Cell.tsx Normal file
View File

@@ -0,0 +1,40 @@
import * as React from "react";
import makeStyles from "@mui/styles/makeStyles";
import { TableCell as MuiTableCell, TableCellProps } from "@mui/material";
const useStyles = makeStyles({
root: {
border: "1px solid white",
width: "5px",
height: "5px",
},
});
export const TableCell: React.FC<TableCellProps> = (props: TableCellProps) => {
return (
<MuiTableCell
{...props}
classes={{
root: useStyles().root,
...props.classes,
}}
/>
);
};
type IProps = {
onMouseEnter?: () => void;
onClick?: () => void;
color: string;
};
export function Cell(cellProps: IProps): React.ReactElement {
return (
<TableCell
style={{ backgroundColor: cellProps.color }}
onMouseEnter={cellProps.onMouseEnter}
onClick={cellProps.onClick}
></TableCell>
);
}

View File

@@ -0,0 +1,79 @@
import React, { useState, useEffect } from "react";
import { ActiveFragment } from "../ActiveFragment";
import { IStaneksGift } from "../IStaneksGift";
import { FragmentType, Effect } from "../FragmentType";
import { numeralWrapper } from "../../ui/numeralFormat";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
type IProps = {
gift: IStaneksGift;
fragment: ActiveFragment | null;
x: number;
y: number;
};
export function FragmentInspector(props: IProps): React.ReactElement {
const [, setC] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setC(new Date()), 250);
return () => clearInterval(id);
}, []);
if (props.fragment === null) {
return (
<Paper>
<Typography>
ID: N/A
<br />
Effect: N/A
<br />
Magnitude: N/A
<br />
Charge: N/A
<br />
Heat: N/A
<br />
Effect: N/A
<br />
[X, Y] N/A
<br />
[X, Y] {props.x}, {props.y}
</Typography>
</Paper>
);
}
const f = props.fragment.fragment();
let charge = numeralWrapper.formatStaneksGiftCharge(props.fragment.charge);
let effect = "N/A";
// Boosters and cooling don't deal with heat.
if ([FragmentType.Booster, FragmentType.None, FragmentType.Delete].includes(f.type)) {
charge = "N/A";
effect = `${f.power}x adjacent fragment power`;
} else {
effect = Effect(f.type).replace("x%", numeralWrapper.formatPercentage(props.gift.effect(props.fragment) - 1));
}
return (
<Paper>
<Typography>
ID: {props.fragment.id}
<br />
Effect: {effect}
<br />
Power: {numeralWrapper.formatStaneksGiftPower(f.power)}
<br />
Charge: {charge}
<br />
<br />
root [X, Y] {props.fragment.x}, {props.fragment.y}
<br />
[X, Y] {props.x}, {props.y}
</Typography>
</Paper>
);
}

View File

@@ -0,0 +1,31 @@
import * as React from "react";
import { Cell } from "./Cell";
import TableRow from "@mui/material/TableRow";
import TableBody from "@mui/material/TableBody";
import { Table } from "../../ui/React/Table";
type IProps = {
width: number;
height: number;
colorAt: (x: number, y: number) => string;
};
export function FragmentPreview(props: IProps): React.ReactElement {
// switch the width/length to make axis consistent.
const elems = [];
for (let j = 0; j < props.height; j++) {
const cells = [];
for (let i = 0; i < props.width; i++) {
cells.push(<Cell key={i} color={props.colorAt(i, j)} />);
}
elems.push(<TableRow key={j}>{cells}</TableRow>);
}
return (
<Table>
<TableBody>{elems}</TableBody>
</Table>
);
}

View File

@@ -0,0 +1,91 @@
import React, { useState } from "react";
import { Fragments, Fragment, NoneFragment, DeleteFragment } from "../Fragment";
import { FragmentType, Effect } from "../FragmentType";
import { IStaneksGift } from "../IStaneksGift";
import { FragmentPreview } from "./FragmentPreview";
import { numeralWrapper } from "../../ui/numeralFormat";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
type IOptionProps = {
gift: IStaneksGift;
fragment: Fragment;
selectFragment: (fragment: Fragment) => void;
};
function FragmentOption(props: IOptionProps): React.ReactElement {
const remaining =
props.fragment.limit !== Infinity ? (
<>{props.fragment.limit - props.gift.count(props.fragment)} remaining</>
) : (
<></>
);
return (
<Box display="flex">
<Box sx={{ mx: 2 }}>
<FragmentPreview
width={props.fragment.width()}
height={props.fragment.height()}
colorAt={(x, y) => {
return !props.fragment.fullAt(x, y) ? "" : props.fragment.type === FragmentType.Booster ? "blue" : "green";
}}
/>
</Box>
<Typography>
{props.fragment.type === FragmentType.Booster
? `${props.fragment.power}x adjacent fragment power`
: Effect(props.fragment.type)}
<br />
power: {numeralWrapper.formatStaneksGiftPower(props.fragment.power)}
<br />
{remaining}
</Typography>
</Box>
);
}
type IProps = {
gift: IStaneksGift;
selectFragment: (fragment: Fragment) => void;
};
export function FragmentSelector(props: IProps): React.ReactElement {
const [value, setValue] = useState<string | number>("None");
function onChange(event: SelectChangeEvent<string | number>): void {
const v = event.target.value;
setValue(v);
if (v === "None") {
props.selectFragment(NoneFragment);
return;
} else if (v === "Delete") {
props.selectFragment(DeleteFragment);
return;
}
const fragment = Fragments.find((f) => f.id === v);
if (fragment === undefined) throw new Error("Fragment selector selected an undefined fragment with id " + v);
if (typeof v === "number") props.selectFragment(fragment);
}
return (
<Select sx={{ width: "100%" }} onChange={onChange} value={value}>
<MenuItem value="None">
<Typography>None</Typography>
</MenuItem>
<MenuItem value="Delete">
<Typography>Delete</Typography>
</MenuItem>
{Fragments.map((fragment) => (
<MenuItem key={fragment.id} value={fragment.id}>
<FragmentOption
key={fragment.id}
gift={props.gift}
selectFragment={props.selectFragment}
fragment={fragment}
/>
</MenuItem>
))}
</Select>
);
}

145
src/CotMG/ui/Grid.tsx Normal file
View File

@@ -0,0 +1,145 @@
import * as React from "react";
import { Fragment, NoneFragment } from "../Fragment";
import { ActiveFragment } from "../ActiveFragment";
import { FragmentType } from "../FragmentType";
import { IStaneksGift } from "../IStaneksGift";
import { Cell } from "./Cell";
import { FragmentInspector } from "./FragmentInspector";
import { FragmentSelector } from "./FragmentSelector";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import TableRow from "@mui/material/TableRow";
import TableBody from "@mui/material/TableBody";
import { Table } from "../../ui/React/Table";
function zeros(dimensions: number[]): any {
const array = [];
for (let i = 0; i < dimensions[0]; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
}
return array;
}
function randomColor(fragment: ActiveFragment): string {
// Can't set Math.random seed so copy casino. TODO refactor both RNG later.
let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10);
let s2 = s1;
let s3 = s1;
const colors = [];
for (let i = 0; i < 3; i++) {
s1 = (171 * s1) % 30269;
s2 = (172 * s2) % 30307;
s3 = (170 * s3) % 30323;
colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0);
}
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
}
type GridProps = {
gift: IStaneksGift;
};
export function Grid(props: GridProps): React.ReactElement {
function calculateGrid(gift: IStaneksGift): any {
const newgrid = zeros([gift.width(), gift.height()]);
for (let i = 0; i < gift.width(); i++) {
for (let j = 0; j < gift.height(); j++) {
const fragment = gift.fragmentAt(i, j);
if (fragment === null) continue;
newgrid[i][j] = 1;
}
}
return newgrid;
}
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
const [pos, setPos] = React.useState([0, 0]);
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
function moveGhost(worldX: number, worldY: number): void {
const newgrid = zeros([props.gift.width(), props.gift.height()]);
for (let i = 0; i < selectedFragment.shape.length; i++) {
for (let j = 0; j < selectedFragment.shape[i].length; j++) {
if (!selectedFragment.shape[i][j]) continue;
if (worldX + j > newgrid.length - 1) continue;
if (worldY + i > newgrid[worldX + j].length - 1) continue;
newgrid[worldX + j][worldY + i] = 1;
}
}
setGhostGrid(newgrid);
setPos([worldX, worldY]);
}
function deleteAt(worldX: number, worldY: number): boolean {
return props.gift.deleteAt(worldX, worldY);
}
function clickAt(worldX: number, worldY: number): void {
if (selectedFragment.type == FragmentType.None) return;
if (selectedFragment.type == FragmentType.Delete) {
deleteAt(worldX, worldY);
} else {
if (!props.gift.canPlace(worldX, worldY, selectedFragment)) return;
props.gift.place(worldX, worldY, selectedFragment);
}
setGrid(calculateGrid(props.gift));
}
function color(worldX: number, worldY: number): string {
if (ghostGrid[worldX][worldY] && grid[worldX][worldY]) return "red";
if (ghostGrid[worldX][worldY]) return "white";
if (grid[worldX][worldY]) {
const fragment = props.gift.fragmentAt(worldX, worldY);
if (fragment === null) throw new Error("ActiveFragment should not be null");
return randomColor(fragment);
}
return "";
}
function clear(): void {
props.gift.clear();
setGrid(zeros([props.gift.width(), props.gift.height()]));
}
// switch the width/length to make axis consistent.
const elems = [];
for (let j = 0; j < props.gift.height(); j++) {
const cells = [];
for (let i = 0; i < props.gift.width(); i++) {
cells.push(
<Cell key={i} onMouseEnter={() => moveGhost(i, j)} onClick={() => clickAt(i, j)} color={color(i, j)} />,
);
}
elems.push(
<TableRow key={j} className="staneksgift_row">
{cells}
</TableRow>,
);
}
function updateSelectedFragment(fragment: Fragment): void {
setSelectedFragment(fragment);
const newgrid = zeros([props.gift.width(), props.gift.height()]);
setGhostGrid(newgrid);
}
return (
<>
<Button onClick={clear}>Clear</Button>
<Box display="flex">
<Table>
<TableBody>{elems}</TableBody>
</Table>
<FragmentInspector gift={props.gift} x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
</Box>
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
</>
);
}

View File

@@ -0,0 +1,36 @@
import React, { useState, useEffect } from "react";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../Constants";
import { StaneksGiftEvents } from "../StaneksGiftEvents";
import { Grid } from "./Grid";
import { IStaneksGift } from "../IStaneksGift";
import Typography from "@mui/material/Typography";
type IProps = {
staneksGift: IStaneksGift;
};
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
const setRerender = useState(true)[1];
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
return (
<>
<Typography variant="h4">Stanek's Gift</Typography>
<Typography>
The gift is a grid on which you can place upgrades called fragments. The main type of fragment increases a stat,
like your hacking skill or agility exp. Once a stat fragment is placed it then needs to be charged via scripts
in order to become useful. The other kind of fragment is called booster fragments. They increase the efficiency
of the charged happening on fragments neighboring them (no diagonal)
</Typography>
{staneksGift.storedCycles > 5 && (
<Typography>
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
</Typography>
)}
<Grid gift={staneksGift} />
</>
);
}

View File

@@ -2,26 +2,18 @@ import { DarkWebItems } from "./DarkWebItems";
import { Player } from "../Player";
import { Terminal } from "../Terminal";
import { SpecialServerIps } from "../Server/SpecialServerIps";
import { SpecialServers } from "../Server/data/SpecialServers";
import { numeralWrapper } from "../ui/numeralFormat";
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
//Posts a "help" message if connected to DarkWeb
export function checkIfConnectedToDarkweb(): void {
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
const darkwebIp = SpecialServerIps.getIp("Darkweb Server");
if (!isValidIPAddress(darkwebIp)) {
return;
}
const server = Player.getCurrentServer();
if (server !== null && darkwebIp == server.ip) {
Terminal.print(
"You are now connected to the dark web. From the dark web you can purchase illegal items. " +
"Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " +
"to purchase an item.",
);
}
const server = Player.getCurrentServer();
if (server !== null && SpecialServers.DarkWeb == server.hostname) {
Terminal.print(
"You are now connected to the dark web. From the dark web you can purchase illegal items. " +
"Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " +
"to purchase an item.",
);
}
}

View File

@@ -2,6 +2,7 @@ import { IPlayer } from "./PersonObjects/IPlayer";
import { Bladeburner } from "./Bladeburner/Bladeburner";
import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import React from "react";
@@ -19,6 +20,7 @@ import { Corporation } from "./DevMenu/ui/Corporation";
import { CodingContracts } from "./DevMenu/ui/CodingContracts";
import { StockMarket } from "./DevMenu/ui/StockMarket";
import { Sleeves } from "./DevMenu/ui/Sleeves";
import { Stanek } from "./DevMenu/ui/Stanek";
import { TimeSkip } from "./DevMenu/ui/TimeSkip";
import Typography from "@mui/material/Typography";
@@ -52,6 +54,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
{props.player.hasWseAccount && <StockMarket />}
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
{props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1) && <Stanek />}
<TimeSkip player={props.player} engine={props.engine} />
</>

View File

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

View File

@@ -8,9 +8,8 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { AllServers } from "../../Server/AllServers";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import { GetServerByHostname } from "../../Server/ServerHelpers";
import { GetServer, GetAllServers } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import MenuItem from "@mui/material/MenuItem";
export function Servers(): React.ReactElement {
@@ -19,9 +18,9 @@ export function Servers(): React.ReactElement {
setServer(event.target.value as string);
}
function rootServer(): void {
const s = GetServerByHostname(server);
const s = GetServer(server);
if (s === null) return;
if (s instanceof HacknetServer) return;
if (!(s instanceof Server)) return;
s.hasAdminRights = true;
s.sshPortOpen = true;
s.ftpPortOpen = true;
@@ -32,9 +31,8 @@ export function Servers(): React.ReactElement {
}
function rootAllServers(): void {
for (const i in AllServers) {
const s = AllServers[i];
if (s instanceof HacknetServer) return;
for (const s of GetAllServers()) {
if (!(s instanceof Server)) return;
s.hasAdminRights = true;
s.sshPortOpen = true;
s.ftpPortOpen = true;
@@ -46,32 +44,30 @@ export function Servers(): React.ReactElement {
}
function minSecurity(): void {
const s = GetServerByHostname(server);
const s = GetServer(server);
if (s === null) return;
if (s instanceof HacknetServer) return;
if (!(s instanceof Server)) return;
s.hackDifficulty = s.minDifficulty;
}
function minAllSecurity(): void {
for (const i in AllServers) {
const server = AllServers[i];
if (server instanceof HacknetServer) continue;
server.hackDifficulty = server.minDifficulty;
for (const s of GetAllServers()) {
if (!(s instanceof Server)) return;
s.hackDifficulty = s.minDifficulty;
}
}
function maxMoney(): void {
const s = GetServerByHostname(server);
const s = GetServer(server);
if (s === null) return;
if (s instanceof HacknetServer) return;
if (!(s instanceof Server)) return;
s.moneyAvailable = s.moneyMax;
}
function maxAllMoney(): void {
for (const i in AllServers) {
const server = AllServers[i];
if (server instanceof HacknetServer) continue;
server.moneyAvailable = server.moneyMax;
for (const s of GetAllServers()) {
if (!(s instanceof Server)) return;
s.moneyAvailable = s.moneyMax;
}
}
@@ -89,7 +85,7 @@ export function Servers(): React.ReactElement {
</td>
<td colSpan={2}>
<Select id="dev-servers-dropdown" onChange={setServerDropdown} value={server}>
{Object.values(AllServers).map((server) => (
{GetAllServers().map((server) => (
<MenuItem key={server.hostname} value={server.hostname}>
{server.hostname}
</MenuItem>

79
src/DevMenu/ui/Stanek.tsx Normal file
View File

@@ -0,0 +1,79 @@
import React from "react";
import { staneksGift } from "../../CotMG/Helper";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import { Adjuster } from "./Adjuster";
export function Stanek(): React.ReactElement {
function addCycles(): void {
staneksGift.storedCycles = 1e6;
}
function modCycles(modify: number): (x: number) => void {
return function (cycles: number): void {
staneksGift.storedCycles += cycles * modify;
};
}
function resetCycles(): void {
staneksGift.storedCycles = 0;
}
function addCharge(): void {
staneksGift.fragments.forEach((f) => (f.charge = 1e21));
}
function modCharge(modify: number): (x: number) => void {
return function (cycles: number): void {
staneksGift.fragments.forEach((f) => (f.charge += cycles * modify));
};
}
function resetCharge(): void {
staneksGift.fragments.forEach((f) => (f.charge = 0));
}
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>Stanek's Gift</Typography>
</AccordionSummary>
<AccordionDetails>
<table>
<tbody>
<tr>
<td>
<Adjuster
label="cycles"
placeholder="amt"
tons={addCycles}
add={modCycles(1)}
subtract={modCycles(-1)}
reset={resetCycles}
/>
</td>
</tr>
<tr>
<td>
<Adjuster
label="all charge"
placeholder="amt"
tons={addCharge}
add={modCharge(1)}
subtract={modCharge(-1)}
reset={resetCharge}
/>
</td>
</tr>
</tbody>
</table>
</AccordionDetails>
</Accordion>
);
}

View File

@@ -1,5 +1,5 @@
import React from "react";
import { AllServers } from "../Server/AllServers";
import { GetServer, GetAllServers } from "../Server/AllServers";
import { Modal } from "../ui/React/Modal";
import { numeralWrapper } from "../ui/numeralFormat";
@@ -17,11 +17,12 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
interface IServerProps {
ip: string;
hostname: string;
}
function ServerAccordion(props: IServerProps): React.ReactElement {
const server = AllServers[props.ip];
const server = GetServer(props.hostname);
if (server === null) throw new Error("server should not be null");
let totalSize = 0;
for (const f of server.scripts) {
totalSize += f.code.length;
@@ -98,9 +99,9 @@ interface IProps {
}
export function FileDiagnosticModal(props: IProps): React.ReactElement {
const ips: string[] = [];
for (const ip of Object.keys(AllServers)) {
ips.push(ip);
const keys: string[] = [];
for (const key in GetAllServers()) {
keys.push(key);
}
return (
@@ -110,8 +111,8 @@ export function FileDiagnosticModal(props: IProps): React.ReactElement {
Welcome to the file diagnostic! If your save file is really big it's likely because you have too many
text/scripts. This tool can help you narrow down where they are.
</Typography>
{ips.map((ip: string) => (
<ServerAccordion key={ip} ip={ip} />
{keys.map((hostname: string) => (
<ServerAccordion key={hostname} hostname={hostname} />
))}
</>
</Modal>

View File

@@ -11,10 +11,11 @@ Source-File minus 1 is extremely weak because it can be fully level up quickly.
*/
export enum Exploit {
UndocumentedFunctionCall = "UndocumentedFunctionCall",
Unclickable = "Unclickable",
PrototypeTampering = "PrototypeTampering",
Bypass = "Bypass",
PrototypeTampering = "PrototypeTampering",
Unclickable = "Unclickable",
UndocumentedFunctionCall = "UndocumentedFunctionCall",
TimeCompression = "TimeCompression",
// To the players reading this. Yes you're supposed to add EditSaveFile by
// editing your save file, yes you could add them all, no we don't care
// that's not the point.
@@ -24,11 +25,12 @@ export enum Exploit {
const names: {
[key: string]: string;
} = {
UndocumentedFunctionCall: "by looking beyond the documentation.",
Bypass: "by circumventing the ram cost of document.",
EditSaveFile: "by editing your save file.",
PrototypeTampering: "by tampering with Numbers prototype.",
TimeCompression: "by compressing time",
Unclickable: "by clicking the unclickable.",
Bypass: "by circumventing the ram cost of document.",
UndocumentedFunctionCall: "by looking beyond the documentation.",
};
export function ExploitName(exploit: string): string {

View File

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

35
src/Exploits/loops.ts Normal file
View File

@@ -0,0 +1,35 @@
import { Player } from "../Player";
import { Exploit } from "./Exploit";
function tampering(): void {
if (Player.exploits.includes(Exploit.PrototypeTampering)) return;
// Tampering
const a = 55;
setInterval(function () {
if (a.toExponential() !== "5.5e+1") {
Player.giveExploit(Exploit.PrototypeTampering);
}
}, 15 * 60 * 1000); // 15 minutes
}
function timeCompression(): void {
if (Player.exploits.includes(Exploit.TimeCompression)) return;
// Time compression
let last = new Date().getTime();
function minute(): void {
const now = new Date().getTime();
if (now - last < 500) {
// time has been compressed.
Player.giveExploit(Exploit.TimeCompression);
return;
}
last = now;
window.setTimeout(minute, 1000);
}
window.setTimeout(minute, 1000);
}
export function startExploits(): void {
tampering();
timeCompression();
}

View File

@@ -1,11 +0,0 @@
import { Player } from "../Player";
import { Exploit } from "./Exploit";
export function startTampering(): void {
const a = 55;
setInterval(function () {
if (a.toExponential() !== "5.5e+1") {
Player.giveExploit(Exploit.PrototypeTampering);
}
}, 15 * 60 * 1000); // 15 minutes
}

View File

@@ -50,6 +50,11 @@ export class FactionInfo {
*/
keep: boolean;
/**
* Special faction
*/
special: boolean;
constructor(
infoText: JSX.Element,
enemies: string[],
@@ -57,6 +62,7 @@ export class FactionInfo {
offerHackingWork: boolean,
offerFieldWork: boolean,
offerSecurityWork: boolean,
special: boolean,
keep: boolean,
) {
this.infoText = infoText;
@@ -70,6 +76,7 @@ export class FactionInfo {
this.augmentationPriceMult = 1;
this.augmentationRepRequirementMult = 1;
this.keep = keep;
this.special = special;
}
offersWork(): boolean {
@@ -96,6 +103,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
Daedalus: new FactionInfo(
@@ -106,6 +114,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
"The Covenant": new FactionInfo(
@@ -124,6 +133,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
// Megacorporations, each forms its own faction
@@ -139,6 +149,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@@ -158,6 +169,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@@ -175,10 +187,11 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"Blade Industries": new FactionInfo(<>Augmentation is Salvation.</>, [], true, true, true, true, true),
"Blade Industries": new FactionInfo(<>Augmentation is Salvation.</>, [], true, true, true, true, false, true),
NWO: new FactionInfo(
(
@@ -193,10 +206,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"Clarke Incorporated": new FactionInfo(<>The Power of the Genome - Unlocked.</>, [], true, true, true, true, true),
"Clarke Incorporated": new FactionInfo(
<>The Power of the Genome - Unlocked.</>,
[],
true,
true,
true,
true,
false,
true,
),
"OmniTek Incorporated": new FactionInfo(
<>Simply put, our mission is to design and build robots that make a difference.</>,
@@ -205,6 +228,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
@@ -220,10 +244,20 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
true,
false,
true,
),
"KuaiGong International": new FactionInfo(<>Dream big. Work hard. Make history.</>, [], true, true, true, true, true),
"KuaiGong International": new FactionInfo(
<>Dream big. Work hard. Make history.</>,
[],
true,
true,
true,
true,
false,
true,
),
// Other Corporations
"Fulcrum Secret Technologies": new FactionInfo(
@@ -238,6 +272,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
true,
false,
true,
),
@@ -261,6 +296,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
"The Black Hand": new FactionInfo(
@@ -280,6 +316,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
// prettier-ignore
@@ -325,6 +362,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
// City factions, essentially governments
@@ -336,8 +374,18 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
Chongqing: new FactionInfo(
<>Serve the People.</>,
["Sector-12", "Aevum", "Volhaven"],
true,
true,
true,
true,
false,
false,
),
Chongqing: new FactionInfo(<>Serve the People.</>, ["Sector-12", "Aevum", "Volhaven"], true, true, true, true, false),
Ishima: new FactionInfo(
<>The East Asian Order of the Future.</>,
["Sector-12", "Aevum", "Volhaven"],
@@ -346,6 +394,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"New Tokyo": new FactionInfo(
<>Asia's World City.</>,
@@ -355,6 +404,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"Sector-12": new FactionInfo(
<>The City of the Future.</>,
@@ -364,6 +414,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
Volhaven: new FactionInfo(
<>Benefit, Honor, and Glory.</>,
@@ -373,6 +424,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
// Criminal Organizations/Gangs
@@ -384,6 +436,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"The Dark Army": new FactionInfo(
@@ -394,9 +447,10 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
"The Syndicate": new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false),
"The Syndicate": new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false, false),
Silhouette: new FactionInfo(
(
@@ -415,6 +469,7 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
false,
false,
),
Tetrads: new FactionInfo(
@@ -425,14 +480,15 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
true,
false,
false,
),
"Slum Snakes": new FactionInfo(<>Slum Snakes rule!</>, [], false, false, true, true, false),
"Slum Snakes": new FactionInfo(<>Slum Snakes rule!</>, [], false, false, true, true, false, false),
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
Netburners: new FactionInfo(<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>, [], true, true, false, false, false),
Netburners: new FactionInfo(<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>, [], true, true, false, false, false, false),
"Tian Di Hui": new FactionInfo(<>Obey Heaven and work righteously.</>, [], true, true, false, true, false),
"Tian Di Hui": new FactionInfo(<>Obey Heaven and work righteously.</>, [], true, true, false, true, false, false),
CyberSec: new FactionInfo(
(
@@ -448,6 +504,7 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
false,
),
// Special Factions
@@ -466,6 +523,50 @@ export const FactionInfos: IMap<FactionInfo> = {
false,
false,
false,
true,
false,
),
// prettier-ignore
"Church of the Machine God": new FactionInfo(<>
{" `` "}<br />
{" -odmmNmds: "}<br />
{" `hNmo:..-omNh. "}<br />
{" yMd` `hNh "}<br />
{" mMd oNm "}<br />
{" oMNo .mM/ "}<br />
{" `dMN+ -mM+ "}<br />
{" -mMNo -mN+ "}<br />
{" .+- :mMNo/mN/ "}<br />
{":yNMd. :NMNNN/ "}<br />
{"-mMMMh. /NMMh` "}<br />
{" .dMMMd. /NMMMy` "}<br />
{" `yMMMd. /NNyNMMh` "}<br />
{" `sMMMd. +Nm: +NMMh. "}<br />
{" oMMMm- oNm: /NMMd. "}<br />
{" +NMMmsMm- :mMMd. "}<br />
{" /NMMMm- -mMMd. "}<br />
{" /MMMm- -mMMd. "}<br />
{" `sMNMMm- .mMmo "}<br />
{" `sMd:hMMm. ./. "}<br />
{" `yMy` `yNMd` "}<br />
{" `hMs` oMMy "}<br />
{" `hMh sMN- "}<br />
{" /MM- .NMo "}<br />
{" +MM: :MM+ "}<br />
{" sNNo-.`.-omNy` "}<br />
{" -smNNNNmdo- "}<br />
{" `..` "}<br /><br />
Many cultures predict an end to humanity in the near future, a final
Armageddon that will end the world; but we disagree.
<br /><br />Note that for this faction, reputation can
only be gained by charging Stanek's gift.</>,
[],
false,
false,
false,
false,
true,
true,
),
};

View File

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

View File

@@ -19,8 +19,7 @@ import { HashUpgrades } from "./HashUpgrades";
import { generateRandomContract } from "../CodingContractGenerator";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../InteractiveTutorial";
import { IPlayer } from "../PersonObjects/IPlayer";
import { AllServers } from "../Server/AllServers";
import { GetServerByHostname } from "../Server/ServerHelpers";
import { GetServer } from "../Server/AllServers";
import { Server } from "../Server/Server";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
@@ -416,11 +415,10 @@ function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): nu
// Also, update the hash rate before processing
const ip = player.hacknetNodes[i];
if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`);
const hserver = AllServers[ip];
if (hserver instanceof Server) throw new Error(`player nodes shoud not be Server`);
const hserver = GetServer(ip);
if (!(hserver instanceof HacknetServer)) throw new Error(`player nodes shoud not be Server`);
hserver.updateHashRate(player.hacknet_node_money_mult);
const h = hserver.process(numCycles);
hserver.totalHashesGenerated += h;
hashes += h;
}
@@ -449,7 +447,7 @@ export function updateHashManagerCapacity(player: IPlayer): void {
}
const ip = nodes[i];
if (ip instanceof HacknetNode) throw new Error(`player nodes should be string but isn't`);
const h = AllServers[ip];
const h = GetServer(ip);
if (!(h instanceof HacknetServer)) {
player.hashManager.updateCapacity(0);
return;
@@ -489,7 +487,7 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
}
case "Reduce Minimum Security": {
try {
const target = GetServerByHostname(upgTarget);
const target = GetServer(upgTarget);
if (target == null) {
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
return false;
@@ -505,14 +503,16 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
}
case "Increase Maximum Money": {
try {
const target = GetServerByHostname(upgTarget);
const target = GetServer(upgTarget);
if (target == null) {
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
return false;
}
if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`);
target.changeMaximumMoney(upg.value, true);
const old = target.moneyMax;
target.changeMaximumMoney(upg.value);
console.log(target.moneyMax / old);
} catch (e) {
player.hashManager.refundUpgrade(upgName);
return false;

View File

@@ -77,8 +77,12 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
// Process this Hacknet Server in the game loop. Returns the number of hashes generated
process(numCycles = 1): number {
const seconds = (numCycles * CONSTANTS.MilliPerCycle) / 1000;
this.onlineTimeSeconds += seconds;
return this.hashRate * seconds;
const hashes = this.hashRate * seconds;
this.totalHashesGenerated += hashes;
return hashes;
}
upgradeCache(levels: number): void {

View File

@@ -18,7 +18,7 @@ export const HacknetNodeConstants: {
MaxRam: number;
MaxCores: number;
} = {
MoneyGainPerLevel: 1.6,
MoneyGainPerLevel: 1.5,
BaseCost: 1000,
LevelBaseCost: 1,

View File

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

View File

@@ -7,6 +7,7 @@ import { GeneralInfo } from "./GeneralInfo";
import { HacknetNodeElem } from "./HacknetNodeElem";
import { HacknetServerElem } from "./HacknetServerElem";
import { HacknetNode } from "../HacknetNode";
import { HacknetServer } from "../HacknetServer";
import { HashUpgradeModal } from "./HashUpgradeModal";
import { MultiplierButtons } from "./MultiplierButtons";
import { PlayerInfo } from "./PlayerInfo";
@@ -21,8 +22,7 @@ import {
} from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { AllServers } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { GetServer } from "../../Server/AllServers";
import Typography from "@mui/material/Typography";
import Grid from "@mui/material/Grid";
@@ -50,8 +50,8 @@ export function HacknetRoot(props: IProps): React.ReactElement {
const node = props.player.hacknetNodes[i];
if (hasHacknetServers(props.player)) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = AllServers[node];
if (hserver instanceof Server) throw new Error("node was a normal server"); // should never happen
const hserver = GetServer(node);
if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen
if (hserver) {
totalProduction += hserver.hashRate;
} else {
@@ -88,11 +88,11 @@ export function HacknetRoot(props: IProps): React.ReactElement {
const nodes = props.player.hacknetNodes.map((node: string | HacknetNode) => {
if (hasHacknetServers(props.player)) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = AllServers[node];
const hserver = GetServer(node);
if (hserver == null) {
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
}
if (hserver instanceof Server) throw new Error("node was normal server"); // should never happen
if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen
return (
<HacknetServerElem
player={props.player}

View File

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

View File

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

View File

@@ -96,7 +96,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
<br />
{grid.map((line, y) => (
<div key={y}>
<pre>
<Typography>
{line.map((cell, x) => {
if (x == pos[0] && y == pos[1])
return (
@@ -110,7 +110,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
</span>
);
})}
</pre>
</Typography>
<br />
</div>
))}

View File

@@ -85,7 +85,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
<Typography variant="h4">{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}</Typography>
{minefield.map((line, y) => (
<div key={y}>
<pre>
<Typography>
{line.map((cell, x) => {
if (memoryPhase) {
if (minefield[y][x]) return <span key={x}>[?]&nbsp;</span>;
@@ -96,7 +96,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
return <span key={x}>[&nbsp;]&nbsp;</span>;
}
})}
</pre>
</Typography>
<br />
</div>
))}

View File

@@ -98,14 +98,14 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
{questions.map((question, i) => (
<Typography key={i}>{question.toString()}</Typography>
))}
<pre>
<Typography>
{new Array(wires.length).fill(0).map((_, i) => (
<span key={i}>&nbsp;{i + 1}&nbsp;&nbsp;&nbsp;&nbsp;</span>
))}
</pre>
</Typography>
{new Array(8).fill(0).map((_, i) => (
<div key={i}>
<pre>
<Typography>
{wires.map((wire, j) => {
if ((i === 3 || i === 4) && cutWires[j])
return <span key={j}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>;
@@ -115,7 +115,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
</span>
);
})}
</pre>
</Typography>
</div>
))}
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />

View File

@@ -84,8 +84,8 @@ Cities[CityName.Chongqing].asciiArt = `
[world stock exchange] F |
\\ o 78 [kuaigong international]
\\ /
38 o----x--x------x------A---------
/ 39 | 41
38 o----x--x------x------A------G--
/ 39 | 41 [church]
37 o + 79 o--x--x-C-0
/ | /
/ x-----+-----x-----0 [hospital]

View File

@@ -7,7 +7,6 @@ import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer";
import { AddToAllServers, createUniqueRandomIp } from "../Server/AllServers";
import { safetlyCreateUniqueServer } from "../Server/ServerHelpers";
import { SpecialServerIps } from "../Server/SpecialServerIps";
import { dialogBoxCreate } from "../ui/React/DialogBox";
@@ -36,10 +35,9 @@ export function purchaseTorRouter(p: IPlayer): void {
maxRam: 1,
});
AddToAllServers(darkweb);
SpecialServerIps.addIp("Darkweb Server", darkweb.ip);
p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
p.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
darkweb.serversOnNetwork.push(p.getHomeComputer().hostname);
dialogBoxCreate(
"You have purchased a TOR router!<br>" +
"You now have access to the dark web from your home computer.<br>" +

View File

@@ -29,6 +29,7 @@ export enum LocationName {
// Chongqing locations
ChongqingKuaiGongInternational = "KuaiGong International",
ChongqingSolarisSpaceSystems = "Solaris Space Systems",
ChongqingChurchOfTheMachineGod = "Church of the Machine God",
// Sector 12
Sector12AlphaEnterprises = "Alpha Enterprises",

View File

@@ -440,4 +440,9 @@ export const LocationsMetadata: IConstructorParams[] = [
name: LocationName.WorldStockExchange,
types: [LocationType.StockMarket],
},
{
city: CityName.Chongqing,
name: LocationName.ChongqingChurchOfTheMachineGod,
types: [LocationType.Special],
},
];

View File

@@ -23,8 +23,8 @@ import { LocationType } from "../LocationTypeEnum";
import { Settings } from "../../Settings/Settings";
import { SpecialServerIps } from "../../Server/SpecialServerIps";
import { getServer, isBackdoorInstalled } from "../../Server/ServerHelpers";
import { isBackdoorInstalled } from "../../Server/ServerHelpers";
import { GetServer } from "../../Server/AllServers";
import { CorruptableText } from "../../ui/React/CorruptableText";
import { use } from "../../ui/Context";
@@ -83,8 +83,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
}
const locContent: React.ReactNode[] = getLocationSpecificContent();
const ip = SpecialServerIps.getIp(loc.name);
const server = getServer(ip);
const server = GetServer(loc.name);
const backdoorInstalled = server !== null && isBackdoorInstalled(server);
return (

View File

@@ -10,9 +10,8 @@ import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getServer } from "../../Server/ServerHelpers";
import { GetServer } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { SpecialServerIps } from "../../Server/SpecialServerIps";
import { Money } from "../../ui/React/Money";
import { IRouter } from "../../ui/Router";
@@ -25,8 +24,7 @@ type IProps = {
export function GymLocation(props: IProps): React.ReactElement {
function calculateCost(): number {
const ip = SpecialServerIps.getIp(props.loc.name);
const server = getServer(ip);
const server = GetServer(props.loc.name);
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult;
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
return props.loc.costMult * discount;

View File

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

View File

@@ -17,6 +17,9 @@ import Button from "@mui/material/Button";
import { Location } from "../Location";
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
import { LocationName } from "../data/LocationNames";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Factions } from "../../Faction/Factions";
import { joinFaction } from "../../Faction/FactionHelpers";
import { use } from "../../ui/Context";
@@ -109,6 +112,107 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return <Button onClick={handleResleeving}>Re-Sleeve</Button>;
}
function handleCotMG(): void {
const faction = Factions["Church of the Machine God"];
if (!player.factions.includes("Church of the Machine God")) {
joinFaction(faction);
}
if (
!player.augmentations.some((a) => a.name === AugmentationNames.StaneksGift1) &&
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
) {
player.queueAugmentation(AugmentationNames.StaneksGift1);
}
router.toFaction(faction);
}
function renderCotMG(): React.ReactElement {
// prettier-ignore
const symbol = <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>
{" `` "}<br />
{" -odmmNmds: "}<br />
{" `hNmo:..-omNh. "}<br />
{" yMd` `hNh "}<br />
{" mMd oNm "}<br />
{" oMNo .mM/ "}<br />
{" `dMN+ -mM+ "}<br />
{" -mMNo -mN+ "}<br />
{" .+- :mMNo/mN/ "}<br />
{":yNMd. :NMNNN/ "}<br />
{"-mMMMh. /NMMh` "}<br />
{" .dMMMd. /NMMMy` "}<br />
{" `yMMMd. /NNyNMMh` "}<br />
{" `sMMMd. +Nm: +NMMh. "}<br />
{" oMMMm- oNm: /NMMd. "}<br />
{" +NMMmsMm- :mMMd. "}<br />
{" /NMMMm- -mMMd. "}<br />
{" /MMMm- -mMMd. "}<br />
{" `sMNMMm- .mMmo "}<br />
{" `sMd:hMMm. ./. "}<br />
{" `yMy` `yNMd` "}<br />
{" `hMs` oMMy "}<br />
{" `hMh sMN- "}<br />
{" /MM- .NMo "}<br />
{" +MM: :MM+ "}<br />
{" sNNo-.`.-omNy` "}<br />
{" -smNNNNmdo- "}<br />
{" `..` "}</Typography>
if (player.factions.includes("Church of the Machine God")) {
return (
<>
<Typography>
<i>Allison "Mother" Stanek: Welcome back my child!</i>
</Typography>
{symbol}
</>
);
}
if (!player.canAccessCotMG()) {
return (
<>
<Typography>
A decrepit altar stands in the middle of a dilapidated church.
<br />
<br />A symbol is carved in the altar.
</Typography>
<br />
{symbol}
</>
);
}
if (
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0 ||
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0
) {
return (
<>
<Typography>
<i>
Allison "Mother" Stanek: Begone you filth! My gift must be the first modification that your body should
have!
</i>
</Typography>
</>
);
}
return (
<>
<Typography>
<i>
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
form? If you are, accept my gift.
</i>
</Typography>
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
{symbol}
</>
);
}
switch (props.loc.name) {
case LocationName.NewTokyoVitaLife: {
return renderResleeving();
@@ -122,6 +226,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
case LocationName.NewTokyoNoodleBar: {
return renderNoodleBar();
}
case LocationName.ChongqingChurchOfTheMachineGod: {
return renderCotMG();
}
default:
console.error(`Location ${props.loc.name} doesn't have any special properties`);
return <></>;

View File

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

Some files were not shown because too many files have changed in this diff Show More