Compare commits

..

79 Commits

Author SHA1 Message Date
Olivier Gagnon
d96ad9fa6e v0.51.8 2021-05-07 22:26:02 -04:00
Olivier Gagnon
5dd32ef4e4 Scripts transfer stats to parent on death. 2021-05-07 22:01:52 -04:00
Olivier Gagnon
6dde89f3c4 0518 2021-05-07 22:01:52 -04:00
Olivier Gagnon
9a9d2e1f81 ls-changes 2021-05-07 22:01:23 -04:00
Pimvgd
d347bf568e Update Overview.jsx
fix space
2021-05-07 19:34:57 -04:00
Daniel Ferri
62e2f0582a Fixed bug with faction invitations not getting deleted 2021-05-07 19:12:30 -04:00
Daniel Ferri
68eb68a89e Added accepting numbers in 1b format to faction donations 2021-05-07 19:12:30 -04:00
Daniel Ferri
13206a8c3e Fixed casing on import
Did not want to compile without the change
2021-05-07 19:12:30 -04:00
Olivier Gagnon
8b7f140115 Merge branch 'dev' of github.com:danielyxie/bitburner into dev 2021-05-07 02:14:34 -04:00
Olivier Gagnon
16c8c78b54 Add installBackdoor 2021-05-07 02:13:33 -04:00
Olivier Gagnon
cbb47772db Add installBackdoor 2021-05-07 02:09:30 -04:00
Olivier Gagnon
0642491402 hotfix backdooring the world daemon not resetting flags 2021-05-06 11:12:41 -04:00
Olivier Gagnon
c7e34d4a56 hotfix backdooring the world daemon not resetting flags 2021-05-06 11:12:04 -04:00
hydroflame
8be63be17b Merge pull request #942 from danielyxie/dev
Another build for the quick and easy stuff.
2021-05-05 21:40:14 -04:00
Olivier Gagnon
08207ac79b another build 2021-05-05 21:39:48 -04:00
Olivier Gagnon
6747a509ff Reword the Milestones 2021-05-05 21:36:43 -04:00
Olivier Gagnon
c7c86240db Faction Donation does not appear on special or gang factions, passive rep doesnt work on gang or special factions 2021-05-05 21:32:06 -04:00
hydroflame
ce0c3d71f4 Merge pull request #941 from danielyxie/dev
hotfix for some stuff
2021-05-05 21:07:09 -04:00
Olivier Gagnon
5bb6a72c90 build 2021-05-05 21:02:54 -04:00
hydroflame
5a06c60c27 Merge pull request #940 from danielyxie/0517-2
0517 hotfix
2021-05-05 21:00:14 -04:00
Olivier Gagnon
764c2533b4 hashGainRate updated to use the correct usedRam and maxRam 2021-05-05 19:18:47 -04:00
Olivier Gagnon
a9cee6f907 Fix analyze Root Access displaying always Yes 2021-05-05 19:15:56 -04:00
Olivier Gagnon
a10100bf60 You cna now cd in dir that only contain text files. 2021-05-05 19:09:14 -04:00
Olivier Gagnon
cedac23dfa CodingContractPopup now correctly displays innerHTML, desc should be changed to return jsx though. 2021-05-05 19:06:08 -04:00
Olivier Gagnon
f7c1d64033 Delete Active Scripts actually deletes active scripts on all servers, not just home 2021-05-05 18:58:53 -04:00
hydroflame
7d6d0eac32 Merge pull request #939 from ModdedGamers/dev
Make it clear that maxNumNodes returns Infinity
2021-05-05 11:03:22 -04:00
Modded Gamers
34768591b7 Make it clear that maxNumNodes returns Infinity
this function returns Infinity in some cases, so this makes that clearer.
2021-05-05 10:00:16 -04:00
Daniel Xie
827c79f70c Add build for hotfix 2021-05-05 04:31:54 -07:00
Daniel Xie
0415c7aa7c Give NeuroFlux for SF-12 before re-calculating multipliers 2021-05-05 04:23:10 -07:00
hydroflame
0c744d98b9 Merge pull request #933 from danielyxie/dev
* .ns script no longer needlessly recompile
* scping the exact same content over doesn't trigger an actual scp
* world daemon can be backdoored
* Coding contract title is click-to-copy
* Sleeve memory covenant upgrade reacts better.
2021-05-03 19:54:31 -04:00
Olivier Gagnon
11cbda6974 build 2021-05-03 19:54:11 -04:00
Olivier Gagnon
e284f49747 Covenant sleeve memory upgrade styling and more reactive text field 2021-05-03 19:52:19 -04:00
Olivier Gagnon
7777c400a5 Make coding contract title click-to-copy 2021-05-03 19:46:04 -04:00
Olivier Gagnon
dae0448744 world daemon can now be backdoored 2021-05-03 19:00:01 -04:00
Olivier Gagnon
02a4e85353 Build nsjs recompile hotfix 2021-05-03 18:54:54 -04:00
hydroflame
3cbf225c98 Fix script needlessly being recompiled (#932)
* Fix nsjs recompiling needlesly.
2021-05-03 18:53:10 -04:00
hydroflame
8006e976a5 Merge pull request #929 from danielyxie/dev
Fix research tree not appearing
2021-05-03 18:25:04 -04:00
Olivier Gagnon
5613d371c9 Fix research tree not appearing 2021-05-03 18:24:22 -04:00
hydroflame
7c9d6669f7 Merge pull request #928 from danielyxie/dev
Hotfix Mission.jsx fake unused import
2021-05-03 17:25:19 -04:00
Olivier Gagnon
c6f0551709 Hotfix Mission.jsx fake unused import 2021-05-03 17:24:39 -04:00
hydroflame
f57eed4de1 Merge pull request #926 from danielyxie/dev
Hotfix
2021-05-03 16:03:12 -04:00
Olivier Gagnon
b99711788f Hotfix negative money, n00dles metadata and guide 2021-05-03 16:02:26 -04:00
Olivier Gagnon
08aac8e35d change noodles metadata. 2021-05-03 12:56:27 -04:00
hydroflame
e3579b7229 Merge pull request #925 from danielyxie/dev
did I forget the build again?
2021-05-03 12:27:21 -04:00
Olivier Gagnon
662d0dd5fb did I forget the build again 2021-05-03 12:26:37 -04:00
hydroflame
77c40b5d67 Merge pull request #924 from danielyxie/dev
Forgot to push the build
2021-05-03 12:21:28 -04:00
Olivier Gagnon
ee759a8dd6 Merge branch 'dev' of github.com:danielyxie/bitburner into dev 2021-05-03 12:20:54 -04:00
hydroflame
ad9c7ec696 Merge pull request #923 from danielyxie/dev
0.51.7
2021-05-03 12:18:18 -04:00
hydroflame
b554328a77 Merge pull request #915 from danielyxie/noodles
Noodles
2021-05-03 12:01:39 -04:00
Olivier Gagnon
c421c57e56 address comments. 2021-05-03 11:58:11 -04:00
hydroflame
fb42d5cd79 Merge pull request #918 from Pimvgd/patch-4
typo fixes for blackops
2021-05-03 02:26:47 -04:00
hydroflame
6203d8d6f4 Merge pull request #919 from Daniferrito/patch-2
Update ps documentation
2021-05-03 02:21:22 -04:00
Olivier Gagnon
d422028737 analyze and ServerProfiler.exe handle hacknet servers better. 2021-05-03 02:20:31 -04:00
Olivier Gagnon
136d769d55 Change faction invitation Yes,No to say Join and Decide later 2021-05-03 02:01:45 -04:00
Olivier Gagnon
daaab1d6f1 Fix forgotten text for Source-File 12 2021-05-03 01:55:04 -04:00
Olivier Gagnon
4f7befb639 Fix miscalculation of source file flags on the bitverse screen. 2021-05-03 01:50:09 -04:00
Olivier Gagnon
9976ed136c fix money formatting for amount under 1000 2021-05-03 01:44:02 -04:00
Olivier Gagnon
8c2e661e08 Remove console.log 2021-05-03 01:41:16 -04:00
Olivier Gagnon
e6e598eeb4 moved augmentations between city faction to be more even. 2021-05-03 01:40:22 -04:00
Olivier Gagnon
4d9439a007 Added getServerMaxRam and getServerUsedRam, deprecated getServerRam 2021-05-03 01:07:46 -04:00
Olivier Gagnon
9951c8b18f Fix mistake in hacknet API example 2021-05-03 00:51:12 -04:00
Olivier Gagnon
800c673839 Fix character-overview-text being nested twice. 2021-05-03 00:47:56 -04:00
Olivier Gagnon
065544909b thread count formatted with commas every thousands. 2021-05-03 00:45:21 -04:00
Olivier Gagnon
4eaf68c940 Donation is always visible but locked before favor requirements are fulfilled. 2021-05-03 00:37:53 -04:00
Olivier Gagnon
a14a694df3 Fix sleeves not being to work out at volhaven 2021-05-03 00:27:12 -04:00
Dani
3ab61cbaf3 Update ps documentation 2021-05-02 23:48:20 +02:00
Pimvgd
f49aff05d7 typo fixes for blackops 2021-05-02 23:44:59 +02:00
hydroflame
e2371a0ef6 Merge pull request #917 from Daniferrito/patch-1
Fixed #916
2021-05-02 17:38:47 -04:00
Dani
6a2b3ee52e Fixed #916 2021-05-02 23:37:09 +02:00
Olivier Gagnon
5a45e3584d Formatted time in netscript hgw display in human time, milliFormat now displays milli sometimes 2021-05-02 01:04:49 -04:00
Olivier Gagnon
56ce83cce5 Fix cashroot not displaying properly 2021-05-02 00:07:04 -04:00
Olivier Gagnon
d126b6d8c5 Fix sf minus 1 bad constant 2021-05-01 23:44:23 -04:00
Olivier Gagnon
7172ef6dae SF12 now lets player start with Neuroflux 2021-05-01 23:42:52 -04:00
Olivier Gagnon
a8d0b6e13d Tail now accepts PID 2021-05-01 23:33:44 -04:00
Olivier Gagnon
d2d6453a78 quick b1tflum3 2021-05-01 23:23:04 -04:00
Olivier Gagnon
89cdecb05f getBitnodeMultipliers is available inside BN5 2021-05-01 23:08:51 -04:00
Olivier Gagnon
81fdff9068 Revert tutorial servers and add the n00dles server. 2021-05-01 23:08:51 -04:00
hydroflame
b2ac383b69 Merge pull request #909 from danielyxie/dev
When you hotfix you need to build
2021-04-29 13:41:48 -04:00
hydroflame
7df4aac8e6 Merge pull request #908 from danielyxie/dev
hotfix netscript access to formulas and getServer
2021-04-29 13:39:35 -04:00
84 changed files with 899 additions and 637 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([840,0]),o()}({779:function(n,t,o){},781:function(n,t,o){},783:function(n,t,o){},785:function(n,t,o){},787:function(n,t,o){},789:function(n,t,o){},791:function(n,t,o){},793:function(n,t,o){},795:function(n,t,o){},797:function(n,t,o){},799:function(n,t,o){},801:function(n,t,o){},803:function(n,t,o){},805:function(n,t,o){},807:function(n,t,o){},809:function(n,t,o){},811:function(n,t,o){},813:function(n,t,o){},815:function(n,t,o){},817:function(n,t,o){},819:function(n,t,o){},821:function(n,t,o){},823:function(n,t,o){},825:function(n,t,o){},827:function(n,t,o){},829:function(n,t,o){},831:function(n,t,o){},833:function(n,t,o){},835:function(n,t,o){},837:function(n,t,o){},840:function(n,t,o){"use strict";o.r(t);o(839),o(837),o(835),o(833),o(831),o(829),o(827),o(825),o(823),o(821),o(819),o(817),o(815),o(813),o(811),o(809),o(807),o(805),o(803),o(801),o(799),o(797),o(795),o(793),o(791),o(789),o(787),o(785),o(783),o(781),o(779)}});
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([841,0]),o()}({780:function(n,t,o){},782:function(n,t,o){},784:function(n,t,o){},786:function(n,t,o){},788:function(n,t,o){},790:function(n,t,o){},792:function(n,t,o){},794:function(n,t,o){},796:function(n,t,o){},798:function(n,t,o){},800:function(n,t,o){},802:function(n,t,o){},804:function(n,t,o){},806:function(n,t,o){},808:function(n,t,o){},810:function(n,t,o){},812:function(n,t,o){},814:function(n,t,o){},816:function(n,t,o){},818:function(n,t,o){},820:function(n,t,o){},822:function(n,t,o){},824:function(n,t,o){},826:function(n,t,o){},828:function(n,t,o){},830:function(n,t,o){},832:function(n,t,o){},834:function(n,t,o){},836:function(n,t,o){},838:function(n,t,o){},841:function(n,t,o){"use strict";o.r(t);o(840),o(838),o(836),o(834),o(832),o(830),o(828),o(826),o(824),o(822),o(820),o(818),o(816),o(814),o(812),o(810),o(808),o(806),o(804),o(802),o(800),o(798),o(796),o(794),o(792),o(790),o(788),o(786),o(784),o(782),o(780)}});
//# sourceMappingURL=engineStyle.bundle.js.map

38
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -56,8 +56,3 @@ through destroying BitNodes is:
* Source-Files
* Scripts on the home computer
BitNode Details
^^^^^^^^^^^^^^^
TODO

View File

@@ -47,7 +47,7 @@ List of all Source-Files
+------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-9: Coming Soon | |
+------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-10: Digital Carbon | * Each level of this grants a Duplicate Sleeve |
| BitNode-10: Digital Carbon | * Each level of this grants a Duplicate Sleeve. |
| | * Allows the player to access the :ref:`netscript_sleeveapi` in other BitNodes |
+------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-11: The Big Crash | * Company favor increases both the player's salary and reputation gain at that |

View File

@@ -3,6 +3,63 @@
Changelog
=========
v0.51.8 - 2021-05-07 It was there all along (hydroflame)
--------------------------------------------------------
**Servers**
* Update n00dles metadata
**Netscript**
* 'hashGainRate' use the correct 'usedRam' and 'maxRam'
* Fix 'setActionAutolevel' logging.
* Fix 'setActionLevel' not working at all.
* Add 'installBackdoor' singularity function.
**Hacknet**
* Fix Hacknet Servers total production always displaying 0
**Documentation**
* Updated guide to no longer recommend BN12.
* Fix documentation for maxNumNodes (@ModdedGamers)
* Fix typo in 'sourcefiles.rst'
* Fix typo in 'recommendedbitnodeorder.rst'
* Fix 'getServer' documentation missing 'server' argument.
* Fix missing ram cost in 'getData.rst'
* Fix basic formulas examples.
* Fix typo in BN11 description.
* Fix formatting issue in Bladeburner (@Pimgd)
**Misc.**
* Fix negative money being displayed in full.
* Fix Hacking Missions not working.
* Fix Corporation tree not rendering.
* Fix script being needlessly recompiled. This should save real ram (not game ram)
* w0r1d_d43m0n can be backdoored
* Coding Contracts title is click-to-copy (@Rodeth)
* Covenant memory upgrade works better.
* Fix Neuroflux not being correctly calculated when entering BN with SF12.
* Delete Active Script now delete all active scripts, not just home.
* Now you can 'cd' in directories that only contain '.txt' files.
* Fix 'analyze' always saying players had root access
* Passive faction rep no longer builds for special factions.
* Donation option no longer appears for special factions.
* Rephrased some milestones.
* donation textbox now accepts money in the format '1b' and the like (@Dawe)
* Fix being able to join hated factions simultaneously. (@Dawe)
* 'ls' now displays files in multiple column. (Helps players with many files)
* Bladeburner multiplers now appear under Character>Stats and
Character>Augmentation when they are relevant.
* Fix missing functions syntax highlight in codemirror.
* Fix infiltration number formatting.
* script income transfers to parent on death. This helps keep track of
income for scripts that spawn short lived scripts.
v0.51.7 - 2021-04-28 n00dles
----------------------------

View File

@@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
# The short X.Y version.
version = '0.51'
# The full version, including alpha/beta/rc tags.
release = '0.51.7'
release = '0.51.8'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -323,7 +323,7 @@ Source-File
:Max Level: 3
This Source-File unlocks Sleeve technology in other BitNodes.
Each level of this Source-File also grants you a Duplicate Sleeve
Each level of this Source-File also grants you a Duplicate Sleeve.
Difficulty
Hard
@@ -373,9 +373,10 @@ Description
Source-File
:Max Level: Infinity
Each level of Source-File 12 will increase all of your multipliers by 1%. This effect
is multiplicative with itself. In other words, level N of this Source-File will result
in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)
Each level of Source-File 12 will let you start with Neuroflux Governor
equal to the level of this Source-File.
This BitNode is meant to be done passively or when waiting for new content.
Difficulty
Initially very easy, but then it (obviously) becomes harder as you continue to do it.
@@ -396,27 +397,22 @@ For fast progression
1. Repeat **BitNode-1: Source Genesis** until you max out its Source-File. Its Source-File
is extremely powerful, as it raises all multipliers by a significant amount.
2. Repeat **BitNode-12: The Recursion** several times. This BitNode will be extremely easy the
first few times you tackle it, and its Source-File raises all multipliers. Furthermore,
its effect stacks multiplicatively with itself and other Source-Files/Augmentations,
which gets better as time goes on
3. Do **BitNode-5: Artificial Intelligence** once or twice. The intelligence stat it unlocks
2. Do **BitNode-5: Artificial Intelligence** once or twice. The intelligence stat it unlocks
will gradually build up as you continue to play the game, and will be helpful
in the future. The Source-File also provides hacking multipliers, which are
strong because hacking is typically one of the best ways of earning money.
4. (Optional) Consider doing **BitNode-4: The Singularity**. Its Source-File does not directly make you
3. (Optional) Consider doing **BitNode-4: The Singularity**. Its Source-File does not directly make you
more powerful in any way, but it does unlock :ref:`netscript_singularityfunctions` which
let you automate significantly more aspects of the game.
5. Do **BitNode-3: Corporatocracy** once to unlock the Corporation mechanic. This mechanic
4. Do **BitNode-3: Corporatocracy** once to unlock the Corporation mechanic. This mechanic
has high profit potential.
6. Do **BitNode-6: Bladeburners** once to unlock the Bladeburners mechanic. The Bladeburner
5. Do **BitNode-6: Bladeburners** once to unlock the Bladeburners mechanic. The Bladeburner
mechanic is useful for some of the future BitNodes (such as 9 and 10).
7. Do **BitNode-9: Hacktocracy** to unlock the Hacknet Server mechanic. You can
6. Do **BitNode-9: Hacktocracy** to unlock the Hacknet Server mechanic. You can
consider repeating it as well, as its Level 2 and 3 effects are pretty helpful as well.
.. todo:: To be continued as more BitNodes get added

View File

@@ -1,9 +1,10 @@
getServer() Netscript Function
==========================================
.. js:function:: getServer()
.. js:function:: getServer([hostname])
:RAM cost: 4 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.

View File

@@ -0,0 +1,15 @@
getServerMaxRam() Netscript Function
====================================
.. js:function:: getServerMaxRam(hostname)
:RAM cost: 0.05 GB
:param string hostname: Hostname of target server.
:returns: Total ram available on that server. In GB.
Example:
.. code-block:: javascript
maxRam = getServerMaxRam("helios"); // returns: 16
print("helios has "+maxRam + "GB");

View File

@@ -3,6 +3,8 @@ getServerRam() Netscript Function
.. js:function:: getServerRam(hostname)
.. warning:: This function is deprecated.
:RAM cost: 0.1 GB
:param string hostname: Hostname of target server.
:returns: An array of 2 number, first number is the total RAM, second the

View File

@@ -0,0 +1,15 @@
getServerUsedRam() Netscript Function
=====================================
.. js:function:: getServerUsedRam(hostname)
:RAM cost: 0.05 GB
:param string hostname: Hostname of target server.
:returns: Used ram on that server. In GB.
Example:
.. code-block:: javascript
usedRam = getServerUsedRam("harakiri-sushi"); // returns: 5.6
print("harakiri-sushi uses "+usedRam + "GB");

View File

@@ -15,7 +15,8 @@ ps() Netscript Function
{
filename: Script name,
threads: Number of threads script is running with,
args: Script's arguments
args: Script's arguments,
pid: Script's pid
}
Example:
@@ -23,7 +24,8 @@ ps() Netscript Function
.. code-block:: javascript
processes = ps("home");
for (let i = 0; i < ps.length; ++i) {
tprint(ps[i].filename + ' ' + ps[i].threads);
tprint(ps[i].args);
for (let i = 0; i < processes.length; ++i) {
tprint(processes[i].filename + ' ' + processes[i].threads);
tprint(processes[i].args);
tprint(processes[i].pid);
}

View File

@@ -3,6 +3,7 @@ getData() Netscript Function
.. js:function:: getData(filename[, hostname=current hostname])
:RAM cost: 5 GB
:param string filename: Filename of the contract
:param string hostname: Hostname of the server containing the contract.
Optional. Defaults to current server if not provided

View File

@@ -20,4 +20,4 @@ growPercent() Netscript Function
.. code-block:: javascript
tprint(growPercent(getServer(), 50, getPlayer()))
tprint(formulas.basic.growPercent(getServer(), 50, getPlayer()))

View File

@@ -21,4 +21,4 @@ growTime() Netscript Function
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(growTime(server, getPlayer()));
tprint(formulas.basic.growTime(server, getPlayer()));

View File

@@ -21,4 +21,4 @@ hackChance() Netscript Function
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackChance(server, getPlayer()));
tprint(formulas.basic.hackChance(server, getPlayer()));

View File

@@ -21,4 +21,4 @@ hackExp() Netscript Function
server = getServer();
server.hackDifficulty = 99.9;
tprint(hackExp(server, getPlayer()));
tprint(formulas.basic.hackExp(server, getPlayer()));

View File

@@ -22,4 +22,4 @@ hackPercent() Netscript Function
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackPercent(server, getPlayer()));
tprint(formulas.basic.hackPercent(server, getPlayer()));

View File

@@ -21,4 +21,4 @@ hackTime() Netscript Function
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackTime(server, getPlayer()));
tprint(formulas.basic.hackTime(server, getPlayer()));

View File

@@ -21,4 +21,4 @@ weakenTime() Netscript Function
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(weakenTime(server, getPlayer()));
tprint(formulas.basic.weakenTime(server, getPlayer()));

View File

@@ -1,11 +1,12 @@
hashGainRate() Netscript Function
==========================================
.. js:function:: hashGainRate(level, ram, core[, mult])
.. js:function:: hashGainRate(level, ramUsed, maxRam, core[, mult])
:RAM cost: 0 GB
:param number level: level of the server.
:param number ram: ram of the server.
:param number ramUsed: ram used on the server.
:param number maxRam: max ram of the server.
:param number core: cores of the server.
:returns: Money per second that a server with those stats would gain per second.
@@ -18,7 +19,7 @@ hashGainRate() Netscript Function
.. code-block:: javascript
server = hacknet.getNodeStats(1);
currentRate = formulas.hacknetNodes.hashGainRate(server.level, server.ram, server.cores);
levelRate = formulas.hacknetNodes.hashGainRate(server.level+1, server.ram, server.cores);
ramRate = formulas.hacknetNodes.hashGainRate(server.level, server.ram*2, server.cores);
coresRate = formulas.hacknetNodes.hashGainRate(server.level, server.ram, server.cores+1);
currentRate = formulas.hacknetNodes.hashGainRate(server.level, 0, server.ram, server.cores);
levelRate = formulas.hacknetNodes.hashGainRate(server.level+1, 0, server.ram, server.cores);
ramRate = formulas.hacknetNodes.hashGainRate(server.level, 0, server.ram*2, server.cores);
coresRate = formulas.hacknetNodes.hashGainRate(server.level, 0, server.ram, server.cores+1);

View File

@@ -4,4 +4,4 @@ maxNumNodes() Netscript Function
.. js:function:: maxNumNodes()
:RAM cost: 0 GB
:returns: Maximum number of Hacknet Nodes you can own.
:returns: Maximum number of Hacknet Nodes you can own, unless you have not unlocked HackNet servers. Then, it returns Infinity.

View File

@@ -54,7 +54,8 @@ This includes information such as function signatures, what they do, and their r
getServerMinSecurityLevel() <basicfunctions/getServerMinSecurityLevel>
getServerRequiredHackingLevel() <basicfunctions/getServerRequiredHackingLevel>
getServerNumPortsRequired() <basicfunctions/getServerNumPortsRequired>
getServerRam() <basicfunctions/getServerRam>
getServerMaxRam() <basicfunctions/getServerMaxRam>
getServerUsedRam() <basicfunctions/getServerUsedRam>
serverExists() <basicfunctions/serverExists>
fileExists() <basicfunctions/fileExists>
isRunning() <basicfunctions/isRunning>
@@ -90,3 +91,8 @@ This includes information such as function signatures, what they do, and their r
wget() <basicfunctions/wget>
getFavorToDonate() <basicfunctions/getFavorToDonate>
flags() <basicfunctions/flags>
.. toctree::
:caption: Deprecated:
getServerRam() <basicfunctions/getServerRam>

View File

@@ -86,7 +86,7 @@ The following is an example of one way a script can be used to automate the
purchasing and upgrading of Hacknet Nodes.
This script attempts to purchase Hacknet Nodes until the player has a total of 8. Then
it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 cores
it gradually upgrades those Node's to a minimum of level 80, 16 GB RAM, and 8 cores
.. code:: javascript
@@ -129,3 +129,16 @@ it gradually upgrades those Node's to a minimum of level 140, 64 GB RAM, and 8 c
};
print("All nodes upgraded to 16GB RAM");
for (var i = 0; i < cnt; i++) {
while (hacknet.getNodeStats(i).cores < 8) {
var cost = hacknet.getCoreUpgradeCost(i, 1);
while (myMoney() < cost) {
print("Need $" + cost + " . Have $" + myMoney());
sleep(3000);
}
res = hacknet.upgradeCore(i, 1);
};
};
print("All nodes upgraded to 8 cores");

View File

@@ -27,6 +27,7 @@ level 3, then you will be able to access all of the Singularity Functions.
getCurrentServer() <singularityfunctions/getCurrentServer>
connect() <singularityfunctions/connect>
manualHack() <singularityfunctions/manualHack>
installBackdoor() <singularityfunctions/installBackdoor>
getPlayer() <singularityfunctions/getPlayer>
hospitalize() <singularityfunctions/hospitalize>
isBusy() <singularityfunctions/isBusy>

View File

@@ -0,0 +1,22 @@
installBackdoor() Netscript Function
====================================
.. js:function:: installBackdoor()
:RAM cost: 2 GB
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
This function will install a backdoor on the server you are currently connected to.
Examples:
.. code-block:: javascript
connect("foodnstuff");
installBackdoor();
.. warning::
For NS2 users:
This function is async.

View File

@@ -327,8 +327,8 @@
Would you like to join? <br/> <br/>
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
<button id="faction-invitation-box-no" class="popup-box-button"> No </button>
<button id="faction-invitation-box-yes" class="popup-box-button"> Join! </button>
<button id="faction-invitation-box-no" class="popup-box-button"> Decide later </button>
</div>
</div>
@@ -587,14 +587,14 @@
Copy Save data to Clipboard
</button>
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
(DEBUG) Delete Active Scripts
Delete all active scripts
<span class="tooltiptextleft">
Debug option used to forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
using this, save the game and then reload the page.
</span>
</button>
<button id="debug-soft-reset" class="a-link-button tooltip">
(DEBUG) Soft Reset
Soft Reset
<span class="tooltiptextleft">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>

View File

@@ -124,5 +124,5 @@
"watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development"
},
"version": "0.51.7"
"version": "0.51.8"
}

View File

@@ -9,7 +9,7 @@ import { Factions } from "../Faction/Factions";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
interface IConstructorParams {
info: string;
info: string | JSX.Element;
isSpecial?: boolean;
moneyCost: number;
name: string;
@@ -57,7 +57,7 @@ export class Augmentation {
baseRepRequirement = 0;
// Description of what this Aug is and what it does
info = "";
info: string | JSX.Element;
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial = false;

View File

@@ -60,7 +60,7 @@ function initAugmentations() {
"This augmentation increases the player's dexterity by 10%.",
dexterity_mult: 1.1,
});
Targeting1.addToFactions(["Slum Snakes", "The Dark Army", "The Syndicate", "Sector-12", "Volhaven", "Ishima",
Targeting1.addToFactions(["Slum Snakes", "The Dark Army", "The Syndicate", "Sector-12", "Ishima",
"OmniTek Incorporated", "KuaiGong International", "Blade Industries"]);
if (augmentationExists(AugmentationNames.Targeting1)) {
delete Augmentations[AugmentationNames.Targeting1];
@@ -75,7 +75,7 @@ function initAugmentations() {
prereqs:[AugmentationNames.Targeting1],
dexterity_mult: 1.2,
});
Targeting2.addToFactions(["The Dark Army", "The Syndicate", "Sector-12", "Volhaven", "Ishima",
Targeting2.addToFactions(["The Dark Army", "The Syndicate", "Sector-12",
"OmniTek Incorporated", "KuaiGong International", "Blade Industries"]);
if (augmentationExists(AugmentationNames.Targeting2)) {
delete Augmentations[AugmentationNames.Targeting2];
@@ -136,7 +136,7 @@ function initAugmentations() {
strength_mult: 1.1,
defense_mult: 1.1,
});
CombatRib1.addToFactions(["Slum Snakes", "The Dark Army", "The Syndicate", "Sector-12", "Volhaven", "Ishima",
CombatRib1.addToFactions(["Slum Snakes", "The Dark Army", "The Syndicate", "Volhaven", "Ishima",
"OmniTek Incorporated", "KuaiGong International", "Blade Industries"]);
if (augmentationExists(AugmentationNames.CombatRib1)) {
delete Augmentations[AugmentationNames.CombatRib1];
@@ -152,7 +152,7 @@ function initAugmentations() {
strength_mult: 1.14,
defense_mult: 1.14,
});
CombatRib2.addToFactions(["The Dark Army", "The Syndicate", "Sector-12", "Volhaven", "Ishima",
CombatRib2.addToFactions(["The Dark Army", "The Syndicate", "Volhaven",
"OmniTek Incorporated", "KuaiGong International", "Blade Industries"]);
if (augmentationExists(AugmentationNames.CombatRib2)) {
delete Augmentations[AugmentationNames.CombatRib2];
@@ -428,7 +428,7 @@ function initAugmentations() {
"This augmentation increases the player's hacking speed by 3%.",
hacking_speed_mult: 1.03,
});
SynapticEnhancement.addToFactions(["CyberSec"]);
SynapticEnhancement.addToFactions(["CyberSec", "Aevum"]);
if (augmentationExists(AugmentationNames.SynapticEnhancement)) {
delete Augmentations[AugmentationNames.SynapticEnhancement];
}
@@ -756,7 +756,7 @@ function initAugmentations() {
"when working for a company by 20%.",
company_rep_mult: 1.2,
});
NuoptimalInjectorImplant.addToFactions(["Tian Di Hui", "Volhaven", "New Tokyo", "Chongqing", "Ishima",
NuoptimalInjectorImplant.addToFactions(["Tian Di Hui", "Volhaven", "New Tokyo", "Chongqing",
"Clarke Incorporated", "Four Sigma", "Bachman & Associates"]);
if (augmentationExists(AugmentationNames.NuoptimalInjectorImplant)) {
delete Augmentations[AugmentationNames.NuoptimalInjectorImplant];
@@ -1064,7 +1064,7 @@ function initAugmentations() {
agility_exp_mult: 1.1,
charisma_exp_mult: 1.1,
});
Neurotrainer1.addToFactions(["CyberSec"]);
Neurotrainer1.addToFactions(["CyberSec", "Aevum"]);
if (augmentationExists(AugmentationNames.Neurotrainer1)) {
delete Augmentations[AugmentationNames.Neurotrainer1];
}

View File

@@ -44,6 +44,18 @@ export function PlayerMultipliers(): React.ReactElement {
</table>
}
function BladeburnerMults(): React.ReactElement {
if(!Player.canAccessBladeburner()) return (<></>);
return (<>
{MultiplierTable([
['Bladeburner Success Chance', Player.bladeburner_max_stamina_mult, Player.bladeburner_max_stamina_mult*mults.bladeburner_max_stamina_mult],
['Bladeburner Max Stamina', Player.bladeburner_max_stamina_mult, Player.bladeburner_max_stamina_mult*mults.bladeburner_max_stamina_mult],
['Bladeburner Stamina Gain', Player.bladeburner_max_stamina_mult, Player.bladeburner_max_stamina_mult*mults.bladeburner_max_stamina_mult],
['Bladeburner Field Analysis', Player.bladeburner_max_stamina_mult, Player.bladeburner_max_stamina_mult*mults.bladeburner_max_stamina_mult],
])}<br />
</>);
}
return (
<>
<p><strong><u>Multipliers:</u></strong></p><br />
@@ -103,6 +115,8 @@ export function PlayerMultipliers(): React.ReactElement {
['Crime success ', Player.crime_success_mult, Player.crime_success_mult*mults.crime_success_mult],
['Crime money ', Player.crime_money_mult, Player.crime_money_mult*mults.crime_money_mult],
])}<br />
<BladeburnerMults />
</>
)
}

View File

@@ -221,7 +221,7 @@ BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"The growth rate of servers is significantly reduced<br>" +
"Weakening a server is twice as effective<br>" +
"Company wages are decreased by 50%<br>" +
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
"Corporation valuations are 90% lower and are therefore significantly less profitable<br>" +
"Hacknet Node production is significantly decreased<br>" +
"Crime and Infiltration are more lucrative<br>" +
"Augmentations are twice as expensive<br><br>" +
@@ -236,8 +236,7 @@ BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
"To iterate is human, to recurse divine.<br><br>" +
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you Source-File 12, or " +
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
"of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the level of this source file.");
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");

View File

@@ -736,7 +736,7 @@ Bladeburner.prototype.completeAction = function() {
}
}
var logLossText = "";
if (loss > 0) {logLossText += "Lost " + formatNumber(loss, 3) + " rank.";}
if (loss > 0) {logLossText += "Lost " + formatNumber(loss, 3) + " rank. ";}
if (damage > 0) {logLossText += "Took " + formatNumber(damage, 0) + " damage.";}
if (isOperation && this.logging.ops) {
this.log(action.name + " failed! " + logLossText);

View File

@@ -160,7 +160,7 @@ export const BlackOperations: IMap<BlackOperation> = {};
"technology in Operation K, we've discovered that a small group of " +
"MK-VI Synthoids were able to make off with the schematics and design " +
"of the technology before the Operation. It is almost a certainty that " +
"these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising." +
"these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising.<br><br>" +
"The goal of Operation Deckard is to hunt down these Synthoids and retire " +
"them. I don't need to tell you how critical this mission is.",
baseDifficulty:20e3, reqdRank:40e3,
@@ -231,7 +231,7 @@ export const BlackOperations: IMap<BlackOperation> = {};
"means that the supercomputer may be able to reason abstractly " +
"and become self-aware.<br><br>" +
"I do not need to remind you why sentient-level AIs pose a serious " +
"thread to all of mankind.<br><br>" +
"threat to all of mankind.<br><br>" +
"The research for this project is being conducted at one of Fulcrum " +
"Technologies secret facilities in Aevum, codenamed 'Alpha Ranch'. " +
"Infiltrate the compound, delete and destroy the work, and then find and kill the " +
@@ -342,4 +342,4 @@ export const BlackOperations: IMap<BlackOperation> = {};
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
});
})()
})()

View File

@@ -14,8 +14,10 @@ import {
} from "../utils/JSONReviver";
import { KEY } from "../utils/helpers/keyCodes";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { createPopup, removePopup } from "./ui/React/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
@@ -171,57 +173,26 @@ export class CodingContract {
* Creates a popup to prompt the player to solve the problem
*/
async prompt(): Promise<CodingContractResult> {
// tslint:disable-next-line
return new Promise<CodingContractResult>((resolve) => {
const contractType: CodingContractType = CodingContractTypes[this.type];
const popupId = `coding-contract-prompt-popup-${this.fn}`;
const title: HTMLElement = createElement("h1", {
innerHTML: this.type,
});
const txt: HTMLElement = createElement("p", {
innerHTML: ["You are attempting to solve a Coding Contract. You have",
`${this.getMaxNumTries() - this.tries} tries remaining,`,
"after which the contract will self-destruct.<br><br>",
`${contractType.desc(this.data).replace(/\n/g, "<br>")}`].join(" "),
});
let solveBtn: HTMLElement;
const cancelBtn = createElement("a", {
class: "a-link-button",
clickListener: () => {
const popupId = `coding-contract-prompt-popup-${this.fn}`;
return new Promise<CodingContractResult>((resolve, reject) => {
let popup = new CodingContractPopup({
c: this,
popupId: popupId,
onClose: () => {
resolve(CodingContractResult.Cancelled);
removeElementById(popupId);
removePopup(popupId);
},
innerText: "Cancel",
});
const answerInput = createElement("input", {
onkeydown: (e: any) => {
if (e.keyCode === KEY.ENTER && answerInput.value !== "") {
e.preventDefault();
solveBtn.click();
} else if (e.keyCode === KEY.ESC) {
e.preventDefault();
cancelBtn.click();
}
},
placeholder: "Enter Solution here",
width: "50%",
}) as HTMLInputElement;
solveBtn = createElement("a", {
class: "a-link-button",
clickListener: () => {
const answer: string = answerInput.value;
if (this.isSolution(answer)) {
onAttempt: (val: string) => {
console.log(`top; ${val}`);
if (this.isSolution(val)) {
resolve(CodingContractResult.Success);
} else {
resolve(CodingContractResult.Failure);
}
removeElementById(popupId);
},
innerText: "Solve",
removePopup(popupId);
}
});
const lineBreak: HTMLElement = createElement("br");
createPopup(popupId, [title, lineBreak, txt, lineBreak, lineBreak, answerInput, solveBtn, cancelBtn]);
answerInput.focus();
createPopup(popupId, CodingContractPopup, popup.props);
});
}

View File

@@ -6,7 +6,7 @@
import { IMap } from "./types";
export const CONSTANTS: IMap<any> = {
Version: "0.51.7",
Version: "0.51.8",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -228,61 +228,60 @@ export const CONSTANTS: IMap<any> = {
LatestUpdate:
`
v0.51.7 - 2021-04-28 n00dles
v0.51.8 - 2021-05-07 It was there all along (hydroflame)
-------
Tutorial servers
* All the tutorial servers have been reverted to their original value
* The new server n00dles has been added as tutorial server.
Servers
Terminal
* 'tail' now accepts Pid.
* 'analyze' now handles Hacknet Servers correctly.
* 'ServerProfiler.exe' now handles Hacknet Servers correctly.
SF12
* Now makes you start with Neuroflux Governor equal to the level of the SF.
* Update n00dles metadata
Netscript
* Deprecated 'getServerRam'.
* 'getServerMaxRam' added to replace 'getServerRam'
* 'getServerUsedRam' added to replace 'getServerRam'
* 'getBitnodeMultipliers' is available inside BN5
* Time logged by hack/grow/weaken now displays in human time.
* thread count logged by hack/grow/weaken now displays with commas every
thousands place.
* 'hashGainRate' use the correct 'usedRam' and 'maxRam'
* Fix 'setActionAutolevel' logging.
* Fix 'setActionLevel' not working at all.
* Add 'installBackdoor' singularity function.
Donation
Hacknet
* Always visible but locked until favor requirements are reached.
* Fix Hacknet Servers total production always displaying 0
Augmentations
Documentation
* City factions has been rebalanced to give a reason to visit them all.
Sleeves
* Fix sleeves not being able to work at Volhavens gym.
Lint
* This shouldn't change anything but was like 10h of work.
* Updated guide to no longer recommend BN12.
* Fix documentation for maxNumNodes (@ModdedGamers)
* Fix typo in 'sourcefiles.rst'
* Fix typo in 'recommendedbitnodeorder.rst'
* Fix 'getServer' documentation missing 'server' argument.
* Fix missing ram cost in 'getData.rst'
* Fix basic formulas examples.
* Fix typo in BN11 description.
* Fix formatting issue in Bladeburner (@Pimgd)
Misc.
* Plethora of typo fixed (@Pimgd)
* ps documentation fix (@Dawe)
* The dev menu now has a quick bitflume option.
* Fix SF -1 not being as powerful as intended.
* Fix cashroot starter kit not displaying correctly.
* Fix DOM element 'character-overview-text' being nested twice.
* Hacknet documentation example fix.
* Money amount under 1000 dont display 3 decimal anymore.
* Fix nextSourceFile flag miscalculation on the bitverse (for Bn12)
* Faction invite text says "Decide later"/"Join!" instead of "No"/"Yes"
* Fix negative money being displayed in full.
* Fix Hacking Missions not working.
* Fix Corporation tree not rendering.
* Fix script being needlessly recompiled. This should save real ram (not game ram)
* w0r1d_d43m0n can be backdoored
* Coding Contracts title is click-to-copy (@Rodeth)
* Covenant memory upgrade works better.
* Fix Neuroflux not being correctly calculated when entering BN with SF12.
* Delete Active Script now delete all active scripts, not just home.
* Now you can 'cd' in directories that only contain '.txt' files.
* Fix 'analyze' always saying players had root access
* Passive faction rep no longer builds for special factions.
* Donation option no longer appears for special factions.
* Rephrased some milestones.
* donation textbox now accepts money in the format '1b' and the like (@Dawe)
* Fix being able to join hated factions simultaneously. (@Dawe)
* 'ls' now displays files in multiple column. (Helps players with many files)
* Bladeburner multiplers now appear under Character>Stats and
Character>Augmentation when they are relevant.
* Fix missing functions syntax highlight in codemirror.
* Fix infiltration number formatting.
* script income transfers to parent on death. This helps keep track of
income for scripts that spawn short lived scripts.
`,
}

View File

@@ -1365,6 +1365,11 @@ Industry.prototype.createResearchBox = function() {
},
}
// Construct the tree with Treant
// This is required for side effect.
// eslint-disable-next-line no-new
new Treant(markup);
// Add Event Listeners for all Nodes
const allResearch = researchTree.getAllNodes();
for (let i = 0; i < allResearch.length; ++i) {

View File

@@ -60,7 +60,7 @@ export class Overview extends BaseReactComponent {
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
"Total Revenue: " + numeralWrapper.format(this.corp().revenue.toNumber(), "$0.000a") + " / s<br>" +
"Total Expenses: " + numeralWrapper.format(this.corp().expenses.toNumber(), "$0.000a") + "/ s<br>" +
"Total Expenses: " + numeralWrapper.format(this.corp().expenses.toNumber(), "$0.000a") + " / s<br>" +
"Total Profits: " + profitStr + " / s<br>" +
dividendStr +
"Publicly Traded: " + (this.corp().public ? "Yes" : "No") + "<br>" +

View File

@@ -135,6 +135,10 @@ class DevMenuComponent extends Component {
Player.getHomeComputer().maxRam *= 2;
}
quickB1tFlum3() {
hackWorldDaemon(Player.bitNodeN, true, true);
}
b1tflum3() {
hackWorldDaemon(Player.bitNodeN, true);
}
@@ -705,7 +709,8 @@ class DevMenuComponent extends Component {
<button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button>
</div>
<div className="row">
<button className="std-button" onClick={this.b1tflum3}>Run bit_flum3.exe</button>
<button className="std-button" onClick={this.quickB1tFlum3}>Quick b1t_flum3.exe</button>
<button className="std-button" onClick={this.b1tflum3}>Run b1t_flum3.exe</button>
<button className="std-button" onClick={this.hackW0r1dD43m0n}>Hack w0rld_d34m0n</button>
</div>
<div className="row">

View File

@@ -4,8 +4,8 @@ export function applyExploit(): void {
if (Player.exploits && Player.exploits.length === 0) {
return;
}
const inc = Math.pow(1.0001, Player.exploits.length);
const dec = Math.pow(0.9999, Player.exploits.length);
const inc = Math.pow(1.001, Player.exploits.length);
const dec = Math.pow(0.999, Player.exploits.length);
Player.hacking_chance_mult *= inc;
Player.hacking_speed_mult *= inc;

View File

@@ -56,6 +56,12 @@ export function joinFaction(faction) {
Factions[enemy].isBanned = true;
}
}
for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == faction.name || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
i--;
}
}
}
export function startHackingMission(faction) {
@@ -238,6 +244,11 @@ export function processPassiveFactionRepGain(numCycles) {
if (!Factions.hasOwnProperty(name)) continue;
const faction = Factions[name];
if (!faction.isMember) continue;
// No passive rep for special factions
const info = faction.getInfo();
if(!info.offersWork()) continue;
// No passive rep for gangs.
if(Player.getGangName() === name) continue;
// 0 favor = 1%/s
// 50 favor = 6%/s
// 100 favor = 11%/s

View File

@@ -57,6 +57,13 @@ export class FactionInfo {
this.augmentationPriceMult = 1;
this.augmentationRepRequirementMult = 1;
}
offersWork(): boolean {
return this.offerFieldWork ||
this.offerHackingMission ||
this.offerHackingWork ||
this.offerSecurityWork;
}
}
/**

View File

@@ -12,10 +12,14 @@ import { Reputation } from "../../ui/React/Reputation";
import { StdButton } from "../../ui/React/StdButton";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = {
faction: Faction;
disabled: boolean;
favorToDonate: number;
p: IPlayer;
rerender: () => void;
}
@@ -36,9 +40,10 @@ export class DonateOption extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
donateAmt: 0,
status: <></>,
status: props.disabled ? <>Unlocked at {props.favorToDonate} favor with {props.faction.name}</> : <></>,
}
this.calculateRepGain = this.calculateRepGain.bind(this);
@@ -70,7 +75,7 @@ export class DonateOption extends React.Component<IProps, IState> {
}
handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
const amt = parseFloat(e.target.value);
const amt = numeralWrapper.parseMoney(e.target.value);
if (isNaN(amt)) {
this.setState({
@@ -90,10 +95,17 @@ export class DonateOption extends React.Component<IProps, IState> {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<input className="text-input" onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
<input
className="text-input"
onChange={this.handleChange}
placeholder={"Donation amount"}
style={inputStyleMarkup}
disabled={this.props.disabled}
/>
<StdButton
onClick={this.donate}
text={"Donate Money"}
disabled={this.props.disabled}
/>
<p style={this.blockStyle}>{this.state.status}</p>
</div>

View File

@@ -264,11 +264,13 @@ export class FactionRoot extends React.Component<IProps, IState> {
/>
}
{
(!isPlayersGang && canDonate) &&
!isPlayersGang && factionInfo.offersWork() &&
<DonateOption
faction={this.props.faction}
p={this.props.p}
rerender={this.rerender}
favorToDonate={favorToDonate}
disabled={!canDonate}
/>
}
<Option

View File

@@ -421,7 +421,9 @@ function processAllHacknetServerEarnings(numCycles) {
// Also, update the hash rate before processing
const hserver = AllServers[Player.hacknetNodes[i]];
hserver.updateHashRate(Player.hacknet_node_money_mult);
hashes += hserver.process(numCycles);
const h = hserver.process(numCycles);
hserver.totalHashesGenerated += h;
hashes += h;
}
Player.hashManager.storeHashes(hashes);

View File

@@ -30,7 +30,7 @@ export const Milestones: Milestone[] = [
},
},
{
title: "Join the faction hinted at in j1.msg",
title: "Join the faction hinted at in csec-test.msg",
fulfilled: (p: IPlayer): boolean => {
return p.factions.includes("CyberSec");
},
@@ -42,7 +42,7 @@ export const Milestones: Milestone[] = [
},
},
{
title: "Join the faction hinted at in j2.msg",
title: "Join the faction hinted at in nitesec-test.msg",
fulfilled: (p: IPlayer): boolean => {
return p.factions.includes("NiteSec");
},
@@ -66,7 +66,7 @@ export const Milestones: Milestone[] = [
},
},
{
title: "Join the faction hinted at in j4.msg",
title: "Join the faction hinted at in 19dfj3l1nd.msg",
fulfilled: (p: IPlayer): boolean => {
return p.factions.includes("BitRunners");
},
@@ -78,8 +78,9 @@ export const Milestones: Milestone[] = [
},
},
{
title: "Join the final faction",
title: "Complete fl1ght.exe",
fulfilled: (p: IPlayer): boolean => {
// technically wrong but whatever
return p.factions.includes("Daedalus");
},
},

View File

@@ -13,6 +13,11 @@ import { isString } from "../utils/helpers/isString";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
// For some reason `jsplumb` needs to be imported exactly like this,
// lowercase p, and later in the code used as `jsPlumb` uppercase P. wtf.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import jsplumb from "jsplumb";
import React from "react";
import ReactDOM from "react-dom";
@@ -822,7 +827,7 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
}
/**
* Gets a Node DOM element's corresponding Node object using its
* Gets a Node DOM elements corresponding Node object using its
* element id. Function accepts either the DOM element object or the ID as
* an argument
*/

View File

@@ -23,6 +23,8 @@ export const RamCostConstants: IMap<number> = {
ScriptGetHackingLevelRamCost: 0.05,
ScriptGetMultipliersRamCost: 4.0,
ScriptGetServerRamCost: 0.1,
ScriptGetServerMaxRam: 0.05,
ScriptGetServerUsedRam: 0.05,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptHacknetNodesRamCost: 4.0,
@@ -122,6 +124,8 @@ export const RamCosts: IMap<any> = {
getServerGrowth: () => RamCostConstants.ScriptGetServerRamCost,
getServerNumPortsRequired: () => RamCostConstants.ScriptGetServerRamCost,
getServerRam: () => RamCostConstants.ScriptGetServerRamCost,
getServerMaxRam: () => RamCostConstants.ScriptGetServerMaxRam,
getServerUsedRam: () => RamCostConstants.ScriptGetServerUsedRam,
serverExists: () => RamCostConstants.ScriptGetServerRamCost,
fileExists: () => RamCostConstants.ScriptFileExistsRamCost,
isRunning: () => RamCostConstants.ScriptIsRunningRamCost,
@@ -182,6 +186,7 @@ export const RamCosts: IMap<any> = {
getCurrentServer: () => RamCostConstants.ScriptSingularityFn1RamCost,
connect: () => RamCostConstants.ScriptSingularityFn1RamCost,
manualHack: () => RamCostConstants.ScriptSingularityFn1RamCost,
installBackdoor: () => RamCostConstants.ScriptSingularityFn1RamCost,
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getPlayer: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,

View File

@@ -670,7 +670,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg('hack', canHack.msg);
}
workerScript.log("hack", `Executing ${ip} in ${hackingTime.toFixed(3)} seconds (t=${threads})`);
workerScript.log("hack", `Executing ${ip} in ${convertTimeMsToTimeElapsedString(hackingTime*1000, true)} (t=${numeralWrapper.formatThreads(threads)})`);
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
@@ -706,7 +706,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.recordHack(server.ip, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney(moneyGained)} and ${numeralWrapper.formatExp(expGainedOnSuccess)} exp (t=${threads})`);
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney(moneyGained)} and ${numeralWrapper.formatExp(expGainedOnSuccess)} exp (t=${numeralWrapper.formatThreads(threads)})`);
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
if (stock) {
influenceStockThroughServerHack(server, moneyGained);
@@ -719,7 +719,7 @@ function NetscriptFunctions(workerScript) {
// Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} exp (t=${threads})`);
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} exp (t=${numeralWrapper.formatThreads(threads)})`);
return Promise.resolve(0);
}
});
@@ -937,7 +937,7 @@ function NetscriptFunctions(workerScript) {
}
var growTime = calculateGrowTime(server, Player);
workerScript.log("grow", `Executing on '${server.hostname}' in ${formatNumber(growTime, 3)} seconds (t=${threads}).`);
workerScript.log("grow", `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(growTime*1000, true)} (t=${numeralWrapper.formatThreads(threads)}).`);
return netscriptDelay(growTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
@@ -950,7 +950,7 @@ function NetscriptFunctions(workerScript) {
expGain = 0;
}
const logGrowPercent = (moneyAfter/moneyBefore)*100 - 100;
workerScript.log("grow", `Available money on '${server.hostname}' grown by ${formatNumber(logGrowPercent, 6)}%. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${threads}).`);
workerScript.log("grow", `Available money on '${server.hostname}' grown by ${formatNumber(logGrowPercent, 6)}%. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)}).`);
workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain);
if (stock) {
@@ -988,13 +988,13 @@ function NetscriptFunctions(workerScript) {
}
var weakenTime = calculateWeakenTime(server, Player);
workerScript.log("weaken", `Executing on '${server.hostname}' in ${formatNumber(weakenTime, 3)} seconds (t=${threads})`);
workerScript.log("weaken", `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(weakenTime*1000, true)} (t=${numeralWrapper.formatThreads(threads)})`);
return netscriptDelay(weakenTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
server.weaken(CONSTANTS.ServerWeakenAmount * threads);
workerScript.scriptRef.recordWeaken(server.ip, threads);
var expGain = calculateHackingExpGain(server, Player) * threads;
workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${threads})`);
workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`);
workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain);
return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads);
@@ -1453,6 +1453,11 @@ function NetscriptFunctions(workerScript) {
if (scriptname == destServer.scripts[i].filename) {
workerScript.log("scp", `WARNING: File '${scriptname}' overwritten on '${destServer.hostname}'`);
const oldScript = destServer.scripts[i];
// If it's the exact same file don't actually perform the
// copy to avoid recompiling uselessly. Players tend to scp
// liberally.
if(oldScript.code === sourceScript.code)
return true;
oldScript.code = sourceScript.code;
oldScript.ramUsage = sourceScript.ramUsage;
oldScript.markUpdated();
@@ -1619,7 +1624,7 @@ function NetscriptFunctions(workerScript) {
},
getBitNodeMultipliers: function() {
updateDynamicRam("getBitNodeMultipliers", getRamCost("getBitNodeMultipliers"));
if (SourceFileFlags[5] <= 0) {
if (SourceFileFlags[5] <= 0 && Player.bitNodeN !== 5) {
throw makeRuntimeErrorMsg("getBitNodeMultipliers", "Requires Source-File 5 to run.");
}
let copy = Object.assign({}, BitNodeMultipliers);
@@ -1709,6 +1714,18 @@ function NetscriptFunctions(workerScript) {
workerScript.log("getServerRam", `returned [${formatNumber(server.maxRam, 2)}GB, ${formatNumber(server.ramUsed, 2)}GB]`);
return [server.maxRam, server.ramUsed];
},
getServerMaxRam: function(ip) {
updateDynamicRam("getServerMaxRam", getRamCost("getServerMaxRam"));
const server = safeGetServer(ip, "getServerMaxRam");
workerScript.log("getServerMaxRam", `returned ${formatNumber(server.maxRam, 2)}GB`);
return server.maxRam;
},
getServerUsedRam: function(ip) {
updateDynamicRam("getServerUsedRam", getRamCost("getServerUsedRam"));
const server = safeGetServer(ip, "getServerUsedRam");
workerScript.log("getServerUsedRam", `returned ${formatNumber(server.ramUsed, 2)}GB`);
return server.ramUsed;
},
serverExists: function(ip) {
updateDynamicRam("serverExists", getRamCost("serverExists"));
return (getServer(ip) !== null);
@@ -2920,6 +2937,28 @@ function NetscriptFunctions(workerScript) {
const server = Player.getCurrentServer();
return hack(server.hostname, true);
},
installBackdoor: function() {
updateDynamicRam("installBackdoor", getRamCost("installBackdoor"));
checkSingularityAccess("installBackdoor", 1);
const server = Player.getCurrentServer();
const installTime = calculateHackingTime(server, Player) / 4 * 1000;
// No root access or skill level too low
const canHack = netscriptCanHack(server, Player);
if (!canHack.res) {
throw makeRuntimeErrorMsg('installBackdoor', canHack.msg);
}
workerScript.log("installBackdoor", `Installing backdoor on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(installTime, true)}`);
return netscriptDelay(installTime, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
workerScript.log("installBackdoor", `Successfully installed backdoor on '${server.hostname}'`);
server.backdoorInstalled = true;
return Promise.resolve();
});
},
getStats: function() {
updateDynamicRam("getStats", getRamCost("getStats"));
checkSingularityAccess("getStats", 1);
@@ -3942,7 +3981,7 @@ function NetscriptFunctions(workerScript) {
setActionAutolevel: function(type="", name="", autoLevel=true) {
updateDynamicRam("setActionAutolevel", getRamCost("bladeburner", "setActionAutolevel"));
checkBladeburnerAccess("setActionAutolevel");
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name);
const action = getBladeburnerActionObject("setActionAutolevel", type, name);
action.autoLevel = autoLevel;
},
setActionLevel: function(type="", name="", level=1) {
@@ -3952,7 +3991,7 @@ function NetscriptFunctions(workerScript) {
if(level < 1 || level > action.maxLevel) {
throw makeRuntimeErrorMsg("bladeburner.setActionLevel", `Level must be between 1 and ${action.maxLevel}, is ${level}`)
}
actionObj.level = level;
action.level = level;
},
getRank: function() {
updateDynamicRam("getRank", getRamCost("bladeburner", "getRank"));
@@ -4430,9 +4469,9 @@ function NetscriptFunctions(workerScript) {
},
},
hacknetServers: {
hashGainRate: function(level, ram, cores, mult=1) {
hashGainRate: function(level, ramUsed, maxRam, cores, mult=1) {
checkFormulasAccess("hacknetServers.hashGainRate", 9);
return HScalculateHashGainRate(level, ram, cores, mult);
return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult=1);
},
levelUpgradeCost: function(startingLevel, extraLevels=1, costMult=1) {
checkFormulasAccess("hacknetServers.levelUpgradeCost", 9);

View File

@@ -27,6 +27,7 @@ export async function executeJSScript(scripts = [], workerScript) {
// but not really behaves like import. Particularly, it cannot
// load fully dynamic content. So we hide the import from webpack
// by placing it inside an eval call.
script.markUpdated();
urls = _getScriptUrls(script, scripts, []);
script.url = urls[urls.length - 1].url;
script.module = new Promise(resolve => resolve(eval('import(urls[urls.length - 1].url)')));
@@ -59,13 +60,13 @@ export async function executeJSScript(scripts = [], workerScript) {
function shouldCompile(script, scripts) {
if (script.module === "") return true;
return script.dependencies.some(dep => {
const depScript = scripts.find(s => s.filename == dep.url);
const depScript = scripts.find(s => s.filename == dep.filename);
// If the script is not present on the server, we should recompile, if only to get any necessary
// compilation errors.
if (!depScript) return true;
const depIsMoreRecent = depScript.moduleSequenceNumber > script.moduleSequenceNumber
const depIsMoreRecent = depScript.moduleSequenceNumber > script.moduleSequenceNumber;
return depIsMoreRecent;
});
}

View File

@@ -417,8 +417,8 @@ function processNetscript1Imports(code, workerScript) {
* @param {Server} server - Server on which the script is to be run
* @returns {number} pid of started script
*/
export function startWorkerScript(runningScript, server) {
if (createAndAddWorkerScript(runningScript, server)) {
export function startWorkerScript(runningScript, server, parent) {
if (createAndAddWorkerScript(runningScript, server, parent)) {
// Push onto runningScripts.
// This has to come after createAndAddWorkerScript() because that fn updates RAM usage
server.runScript(runningScript, Player.hacknet_node_money_mult);
@@ -438,7 +438,7 @@ export function startWorkerScript(runningScript, server) {
* @param {Server} server - Server on which the script is to be run
* returns {boolean} indicating whether or not the workerScript was successfully added
*/
export function createAndAddWorkerScript(runningScriptObj, server) {
export function createAndAddWorkerScript(runningScriptObj, server, parent) {
// Update server's ram usage
let threads = 1;
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
@@ -489,6 +489,12 @@ export function createAndAddWorkerScript(runningScriptObj, server) {
// Once the code finishes (either resolved or rejected, doesnt matter), set its
// running status to false
p.then(function(w) {
// On natural death, the earnings are transfered to the parent if it still exists.
if(parent && parent.running) {
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
}
// If the WorkerScript is no longer "running", then this means its execution was
// already stopped somewhere else (maybe by something like exit()). This prevents
// the script from being cleaned up twice
@@ -643,7 +649,7 @@ export function runScriptFromScript(caller, server, scriptname, args, workerScri
let runningScriptObj = new RunningScript(script, args);
runningScriptObj.threads = threads;
return startWorkerScript(runningScriptObj, server);
return startWorkerScript(runningScriptObj, server, workerScript);
}
}
}

View File

@@ -368,5 +368,10 @@ function updateAugDescription(elems: IResleeveUIElems): void {
return;
}
elems.augDescription.innerHTML = aug.info;
let innerHTML = aug.info;
if(typeof innerHTML !== 'string') {
innerHTML = renderToStaticMarkup(innerHTML);
}
elems.augDescription.innerHTML = innerHTML;
}

View File

@@ -907,7 +907,7 @@ export class Sleeve extends Person {
this.currentTaskLocation = LocationName.Sector12PowerhouseGym;
costMult = 20;
break;
case LocationName.VolhavenMilleniumFitnessGym:
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
if (this.city != CityName.Volhaven) { return false; }
this.currentTaskLocation = LocationName.VolhavenMilleniumFitnessGym;
costMult = 7;

View File

@@ -55,10 +55,15 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer): void
continue;
}
let tooltip = aug.info;
if(typeof tooltip !== 'string') {
tooltip = renderToStaticMarkup(tooltip);
}
ownedAugsDiv.appendChild(createElement("div", {
class: "gang-owned-upgrade", // Reusing a class from the Gang UI
innerText: ownedAug,
tooltip: aug.info,
tooltip: tooltip,
}))
}
popupElems.push(ownedAugsDiv);
@@ -83,13 +88,18 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer): void
class: "cmpy-mgmt-upgrade-div", // We'll reuse this CSS class
});
let info = aug.info;
if(typeof info !== 'string') {
info = renderToStaticMarkup(info);
}
div.appendChild(createElement("p", {
fontSize: "12px",
innerHTML:
[
`<h2>${aug.name}</h2><br>`,
`Cost: ${renderToStaticMarkup(Money(aug.startingCost))}<br><br>`,
`${aug.info}`,
`${info}`,
].join(" "),
padding: "2px",
clickListener: () => {

View File

@@ -35,7 +35,11 @@ export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState>
}
changePurchaseAmount(e: React.ChangeEvent<HTMLInputElement>): void {
const n: number = parseInt(e.target.value);
let n: number = parseInt(e.target.value);
if(isNaN(n)) n = 1;
const maxMemory = 100 - this.props.sleeve.memory;
if (n > maxMemory) n = maxMemory;
this.setState({
amt: n,
@@ -89,7 +93,7 @@ export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState>
<label htmlFor={inputId}>
Amount of memory to purchase (must be an integer):
</label>
<input id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={isNaN(this.state.amt) ? this.state.amt.toString() : this.state.amt} />
<input className="text-input" id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={this.state.amt} />
<br />
<StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnContent} />
</div>

View File

@@ -234,6 +234,11 @@ function prestigeSourceFile() {
}
}
// Give levels of NeuroFluxGoverner for Source-File 12. Must be done here before Augmentations are recalculated
if (SourceFileFlags[12] > 0) {
Player.augmentations.push({name: AugmentationNames.NeuroFluxGovernor, level: SourceFileFlags[12]})
}
// Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers

View File

@@ -1,5 +1,5 @@
import { Program } from "./Program";
import { programsMetadata } from "./data/programsMetadata";
import { programsMetadata } from "./data/ProgramsMetadata";
import { IMap } from "../types";
export const Programs: IMap<Program> = {};

View File

@@ -58,13 +58,17 @@ function writeRedPillLetter(pElem, line, i=0) {
}
let redPillFlag = false;
function hackWorldDaemon(currentNodeNumber, flume=false) {
function hackWorldDaemon(currentNodeNumber, flume=false, quick=false) {
// Clear Red Pill screen first
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
redPillFlag = true;
Engine.loadRedPillContent();
if(quick) {
return loadBitVerse(currentNodeNumber, flume, quick);
}
return writeRedPillLine("[ERROR] SEMPOOL INVALID").then(function() {
return writeRedPillLine("[ERROR] Segmentation Fault");
}).then(function() {
@@ -143,7 +147,7 @@ function giveSourceFile(bitNodeNumber) {
// is destroyed. Updated every time loadBitVerse() is called
let nextSourceFileFlags = [];
function loadBitVerse(destroyedBitNodeNum, flume=false) {
function loadBitVerse(destroyedBitNodeNum, flume=false, quick=false) {
// Clear the screen
const container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
@@ -151,7 +155,7 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
// Update NextSourceFileFlags
nextSourceFileFlags = SourceFileFlags.slice();
if (!flume) {
if (nextSourceFileFlags[destroyedBitNodeNum] < 3 && destroyedBitNodeNum !== 12)
if (nextSourceFileFlags[destroyedBitNodeNum] < 3)
++nextSourceFileFlags[destroyedBitNodeNum];
}
@@ -221,6 +225,10 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
}(i)); // Immediate invocation closure
}
if(quick) {
return Promise.resolve(true);
}
// Create lore text
return writeRedPillLine("Many decades ago, a humanoid extraterrestial species which we call the Enders descended on the Earth...violently").then(function() {
return writeRedPillLine("Our species fought back, but it was futile. The Enders had technology far beyond our own...");
@@ -316,8 +324,6 @@ function createBitNodeYesNoEventListener(newBitNode, destroyedBitNode, flume=fal
document.getElementById("terminal-input-td").innerHTML = '$ <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
$('input[class=terminal-input]').prop('disabled', false);
Terminal.hackFlag = false;
prestigeSourceFile();
yesNoBoxClose();
});

View File

@@ -178,7 +178,7 @@ class CodeMirrorEditorWrapper extends ScriptEditor {
netscriptFns.push(name);
//Get functions from namespaces
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang", "sleeve"];
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang", "sleeve", "heart", "formulas"];
if (namespaces.includes(name)) {
let namespace = fnsObj[name];
if (typeof namespace !== "object") {continue;}

View File

@@ -1,5 +1,7 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
import CodeMirror from "codemirror/lib/codemirror.js";
import { NetscriptFunctions } from "../NetscriptFunctions";
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -26,7 +28,7 @@ CodeMirror.defineMode("netscript", function(config, parserConfig) {
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
return {
const ret = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
"debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
@@ -40,234 +42,16 @@ CodeMirror.defineMode("netscript", function(config, parserConfig) {
// Netscript Basic Functions
"hack": atom,
"hackAnalyzeThreads": atom,
"hackAnalyzePercent": atom,
"hackChance": atom,
"sleep": atom,
"grow": atom,
"weaken": atom,
"growthAnalyze": atom,
"print": atom,
"tprint": atom,
"scan": atom,
"nuke": atom,
"brutessh": atom,
"ftpcrack": atom,
"clearLog": atom,
"disableLog": atom,
"enableLog": atom,
"isLogEnabled": atom,
"getScriptLogs": atom,
"tail": atom,
"relaysmtp": atom,
"httpworm": atom,
"sqlinject": atom,
"run": atom,
"exec": atom,
"spawn": atom,
"kill": atom,
"killall": atom,
"exit": atom,
"scp": atom,
"ls": atom,
"ps": atom,
"hasRootAccess": atom,
"getIp": atom,
"getHackingMultipliers": atom,
"getBitNodeMultipliers": atom,
"getStats": atom,
"isBusy": atom,
"getHacknetMultipliers": atom,
"getHostname": atom,
"getHackingLevel": atom,
"getServerMoneyAvailable": atom,
"getServerMaxMoney": atom,
"getServerGrowth": atom,
"getServerSecurityLevel": atom,
"getServerBaseSecurityLevel": atom,
"getServerMinSecurityLevel": atom,
"getServerRequiredHackingLevel": atom,
"getServerNumPortsRequired": atom,
"getServerRam": atom,
"serverExists": atom,
"fileExists": atom,
"isRunning": atom,
"deleteServer": atom,
"getPurchasedServers": atom,
"getPurchasedServerLimit": atom,
"getPurchasedServerMaxRam": atom,
"getPurchasedServerCost": atom,
"purchaseServer": atom,
"round": atom,
"write": atom,
"tryWrite": atom,
"read": atom,
"peek": atom,
"clear": atom,
"rm": atom,
"getPortHandle": atom,
"scriptRunning": atom,
"scriptKill": atom,
"getScriptName": atom,
"getScriptRam": atom,
"getHackTime": atom,
"getGrowTime": atom,
"getWeakenTime": atom,
"getScriptIncome": atom,
"getScriptExpGain": atom,
"getTimeSinceLastAug": atom,
"prompt": atom,
// Netscript Singularity Functions
"universityCourse": atom,
"getCharacterInformation": atom,
"gymWorkout": atom,
"travelToCity": atom,
"purchaseTor": atom,
"purchaseProgram": atom,
"upgradeHomeRam": atom,
"getUpgradeHomeRamCost": atom,
"workForCompany": atom,
"applyToCompany": atom,
"getCompanyRep": atom,
"getCompanyFavor": atom,
"stopAction": atom,
"getFactionFavor": atom,
"getFavorToDonate": atom,
"getFactionFavorGain": atom,
"getCompanyFavorGain": atom,
"checkFactionInvitations": atom,
"joinFaction": atom,
"workForFaction": atom,
"getFactionRep": atom,
"donateToFaction": atom,
"createProgram": atom,
"commitCrime": atom,
"getCrimeChance": atom,
"getOwnedAugmentations": atom,
"getOwnedSourceFiles": atom,
"getAugmentationsFromFaction": atom,
"getAugmentationPrereq": atom,
"getAugmentationCost": atom,
"purchaseAugmentation": atom,
"installAugmentations": atom,
// Netscript TIX API
"getStockPrice": atom,
"getStockAskPrice": atom,
"getStockBidPrice": atom,
"getStockPosition": atom,
"getStockSymbols": atom,
"getStockMaxShares": atom,
"getStockPurchaseCost": atom,
"getStockSaleGain": atom,
"buyStock": atom,
"sellStock": atom,
"shortStock": atom,
"sellShort": atom,
"placeOrder": atom,
"cancelOrder": atom,
"getOrders": atom,
"getStockVolatility": atom,
"getStockForecast": atom,
"purchase4SMarketData": atom,
"purchase4SMarketDataTixApi": atom,
// Netscript Hacknet Node API
"hacknet": atom,
"numNodes": atom,
"purchaseNode": atom,
"getPurchaseNodeCost": atom,
"getNodeStats": atom,
"upgradeLevel": atom,
"upgradeRam": atom,
"upgradeCore": atom,
"upgradeCache": atom,
"getLevelUpgradeCost": atom,
"getRamUpgradeCost": atom,
"getCoreUpgradeCost": atom,
"getCacheUpgradeCost": atom,
// Netscript Gang API
"gang": atom,
"getMemberNames": atom,
"getGangInformation": atom,
"getMemberInformation": atom,
"canRecruitMember": atom,
"recruitMember": atom,
"getTaskNames": atom,
"setMemberTask": atom,
"getEquipmentNames": atom,
"getEquipmentCost": atom,
"getEquipmentType": atom,
"purchaseEquipment": atom,
"ascendMember": atom,
"setTerritoryWarfare": atom,
"getChanceToWinClash": atom,
"getBonusTime": atom,
// Netscript Bladeburner API
"bladeburner": atom,
"getContractNames": atom,
"getOperationNames": atom,
"getBlackOpNames": atom,
"getGeneralActionNames": atom,
"getSkillNames": atom,
"startAction": atom,
"stopBladeburnerAction": atom,
"getCurrentAction": atom,
"getActionTime": atom,
"getActionEstimatedSuccessChance": atom,
"getActionCountRemaining": atom,
"getActionMaxLevel": atom,
"getActionCurrentLevel": atom,
"getActionAutolevel": atom,
"getActionRepGain": atom,
"setActionAutolevel": atom,
"setActionLevel": atom,
"getRank": atom,
"getBlackOpRank": atom,
"getSkillPoints": atom,
"getSkillLevel": atom,
"getSkillUpgradeCost": atom,
"upgradeSkill": atom,
"getTeamSize": atom,
"getCity": atom,
"setTeamSize": atom,
"getCityEstimatedPopulation": atom,
"getCityEstimatedCommunities": atom,
"getCityChaos": atom,
"switchCity": atom,
"getStamina": atom,
"joinBladeburnerFaction": atom,
// Repeat of above "getBonusTime": atom,
// Netscript Coding Contract API
"codingcontract": atom,
"attempt": atom,
"getContractType": atom,
"getData": atom,
"getDescription": atom,
"getNumTriesRemaining": atom,
// Sleeve API
"sleeve": atom,
"getNumSleeves": atom,
"setToShockRecovery": atom,
"setToSynchronize": atom,
"setToCommitCrime": atom,
"setToUniversityCourse": atom,
"travel": atom,
"setToCompanyWork": atom,
"setToFactionWork": atom,
"setToGymWorkout": atom,
"getSleeveStats": atom,
"getTask": atom,
"getInformation": atom,
"getSleeveAugmentations": atom,
"getSleevePurchasableAugs": atom,
"purchaseSleeveAug": atom,
};
function push(obj) {
for(const key of Object.keys(obj)) {
if(typeof obj[key] === 'function') ret[key] = atom;
if(typeof obj[key] === 'object') push(obj[key]);
}
}
push(NetscriptFunctions(null));
return ret;
}());
var isOperatorChar = /[+\-*&%=<>!?|~^@]/;

View File

@@ -1203,38 +1203,51 @@ export const serverMetadata: IServerMetadata[] = [
},
{
hackDifficulty: 1,
hostname: "n00dles",
literature: [],
maxRamExponent: 2,
moneyAvailable: 70000,
networkLayer: 1,
numOpenPortsRequired: 0,
organizationName: LocationName.NewTokyoNoodleBar,
requiredHackingSkill: 1,
serverGrowth: 3000,
specialName: LocationName.NewTokyoNoodleBar,
},
{
hackDifficulty: 10,
hostname: "foodnstuff",
literature: [LiteratureNames.Sector12Crime],
maxRamExponent: 4,
moneyAvailable: 40000,
moneyAvailable: 2000000,
networkLayer: 1,
numOpenPortsRequired: 0,
organizationName: LocationName.Sector12FoodNStuff,
requiredHackingSkill: 1,
serverGrowth: 3000,
serverGrowth: 5,
specialName: LocationName.Sector12FoodNStuff,
},
{
hackDifficulty: 3,
hackDifficulty: 10,
hostname: "sigma-cosmetics",
maxRamExponent: 4,
moneyAvailable: 70000,
moneyAvailable: 2300000,
networkLayer: 1,
numOpenPortsRequired: 0,
organizationName: "Sigma Cosmetics",
requiredHackingSkill: 5,
serverGrowth: 3000,
serverGrowth: 10,
},
{
hackDifficulty: 9,
hackDifficulty: 15,
hostname: "joesguns",
maxRamExponent: 4,
moneyAvailable: 600000,
moneyAvailable: 2500000,
networkLayer: 1,
numOpenPortsRequired: 0,
organizationName: LocationName.Sector12JoesGuns,
requiredHackingSkill: 10,
serverGrowth: 500,
serverGrowth: 20,
specialName: LocationName.Sector12JoesGuns,
},
{
@@ -1569,4 +1582,4 @@ export const serverMetadata: IServerMetadata[] = [
serverGrowth: 0,
specialName: "w0r1d_d43m0n",
},
];
];

View File

@@ -60,5 +60,4 @@ SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so t
"Level 1: 32%<br>" +
"Level 2: 48%<br>" +
"Level 3: 56%<br>");
SourceFiles["SourceFile12"] = new SourceFile(12, "This Source-File increases all your multipliers by 1% per level. This effect is multiplicative with itself. " +
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
SourceFiles["SourceFile12"] = new SourceFile(12, "This Source-File lets the player start with Neuroflux Governor equal to the level of this Source-File.");

View File

@@ -141,44 +141,9 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
Player.company_rep_mult *= incMult;
break;
}
case 12: { // The Recursion
const inc = Math.pow(1.01, srcFile.lvl);
const dec = Math.pow(0.99, srcFile.lvl);
Player.hacking_chance_mult *= inc;
Player.hacking_speed_mult *= inc;
Player.hacking_money_mult *= inc;
Player.hacking_grow_mult *= inc;
Player.hacking_mult *= inc;
Player.strength_mult *= inc;
Player.defense_mult *= inc;
Player.dexterity_mult *= inc;
Player.agility_mult *= inc;
Player.charisma_mult *= inc;
Player.hacking_exp_mult *= inc;
Player.strength_exp_mult *= inc;
Player.defense_exp_mult *= inc;
Player.dexterity_exp_mult *= inc;
Player.agility_exp_mult *= inc;
Player.charisma_exp_mult *= inc;
Player.company_rep_mult *= inc;
Player.faction_rep_mult *= inc;
Player.crime_money_mult *= inc;
Player.crime_success_mult *= inc;
Player.hacknet_node_money_mult *= inc;
Player.hacknet_node_purchase_cost_mult *= dec;
Player.hacknet_node_ram_cost_mult *= dec;
Player.hacknet_node_core_cost_mult *= dec;
Player.hacknet_node_level_cost_mult *= dec;
Player.work_money_mult *= inc;
case 12: // The Recursion
// No effects, grants neuroflux.
break;
}
default:
console.error(`Invalid source file number: ${srcFile.n}`);
break;

View File

@@ -53,7 +53,11 @@ import { Player } from "./Player";
import { hackWorldDaemon } from "./RedPill";
import { RunningScript } from "./Script/RunningScript";
import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers";
import { getCurrentEditor, findRunningScript } from "./Script/ScriptHelpers";
import {
getCurrentEditor,
findRunningScript,
findRunningScriptByPid,
} from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS";
import { AllServers } from "./Server/AllServers";
import {
@@ -531,6 +535,7 @@ let Terminal = {
Player.bitNodeN = 1;
}
hackWorldDaemon(Player.bitNodeN);
Terminal.hackFlag = false;
return;
}
server.backdoorInstalled = true;
@@ -559,7 +564,16 @@ let Terminal = {
finishBackdoor: function(cancelled = false) {
if(!cancelled){
let server = Player.getCurrentServer();
const server = Player.getCurrentServer();
if (SpecialServerIps[SpecialServerNames.WorldDaemon] &&
SpecialServerIps[SpecialServerNames.WorldDaemon] == server.ip) {
if (Player.bitNodeN == null) {
Player.bitNodeN = 1;
}
hackWorldDaemon(Player.bitNodeN);
Terminal.backdoorFlag = false;
return;
}
server.backdoorInstalled = true;
postElement(<>Backdoor successful!</>);
}
@@ -571,47 +585,27 @@ let Terminal = {
let currServ = Player.getCurrentServer();
const isHacknet = currServ instanceof HacknetServer;
post(currServ.hostname + ": ");
post("Organization name: " + currServ.organizationName);
var rootAccess = "";
if (currServ.hasAdminRights) {rootAccess = "YES";}
else {rootAccess = "NO";}
post("Root Access: " + rootAccess);
if (!isHacknet) { post("Required hacking skill: " + currServ.requiredHackingSkill); }
post("Server security level: " + numeralWrapper.formatServerSecurity(currServ.hackDifficulty));
post("Chance to hack: " + numeralWrapper.formatPercentage(calculateHackingChance(currServ, Player)));
post("Time to hack: " + convertTimeMsToTimeElapsedString(calculateHackingTime(currServ, Player)*1000));
postElement(<>Total money available on server: {Money(currServ.moneyAvailable)}</>);
if (!isHacknet) { post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired); }
if (currServ.sshPortOpen) {
post("SSH port: Open")
} else {
post("SSH port: Closed")
}
if (currServ.ftpPortOpen) {
post("FTP port: Open")
} else {
post("FTP port: Closed")
}
if (currServ.smtpPortOpen) {
post("SMTP port: Open")
} else {
post("SMTP port: Closed")
}
if (currServ.httpPortOpen) {
post("HTTP port: Open")
} else {
post("HTTP port: Closed")
}
if (currServ.sqlPortOpen) {
post("SQL port: Open")
} else {
post("SQL port: Closed")
}
const org = currServ.organizationName
post("Organization name: " + (!isHacknet ? org : "Player"));
const admin = currServ.hasAdminRights;
let hasAdminRights = !isHacknet && currServ.hasAdminRights || isHacknet;
post("Root Access: " + (hasAdminRights ? "YES" : "NO"));
const hackingSkill = currServ.requiredHackingSkill
post("Required hacking skill: " + (!isHacknet ? hackingSkill : "N/A"));
const security = currServ.hackDifficulty;
post("Server security level: " + (!isHacknet ? numeralWrapper.formatServerSecurity(security) : "N/A"));
const hackingChance = calculateHackingChance(currServ, Player)
post("Chance to hack: " + (!isHacknet ? numeralWrapper.formatPercentage(hackingChance) : "N/A"));
const hackingTime = calculateHackingTime(currServ, Player)*1000;
post("Time to hack: " + (!isHacknet ? convertTimeMsToTimeElapsedString(hackingTime, true) : "N/A"));
postElement(<>Total money available on server: {!isHacknet ? Money(currServ.moneyAvailable) : "N/A"}</>);
const numPort = currServ.numOpenPortsRequired;
post("Required number of open ports for NUKE: " + (!isHacknet ? numPort : "N/A"));
post("SSH port: "+ (currServ.sshPortOpen ? "Open" : "Closed"))
post("FTP port: "+ (currServ.ftpPortOpen ? "Open" : "Closed"))
post("SMTP port: "+ (currServ.smtpPortOpen ? "Open" : "Closed"))
post("HTTP port: "+ (currServ.httpPortOpen ? "Open" : "Closed"))
post("SQL port: "+ (currServ.sqlPortOpen ? "Open" : "Closed"))
}
Terminal.analyzeFlag = false;
},
@@ -974,7 +968,8 @@ let Terminal = {
}
const server = Player.getCurrentServer();
if(!server.scripts.some(script => script.filename.startsWith(evaledDir))) {
if(!server.scripts.some(script => script.filename.startsWith(evaledDir)) &&
!server.textFiles.some(file => file.fn.startsWith(evaledDir))) {
postError("Invalid path. Failed to change directories");
return;
}
@@ -1405,10 +1400,10 @@ let Terminal = {
try {
if (commandArray.length < 2) {
postError("Incorrect number of arguments. Usage: tail [script] [arg1] [arg2]...");
} else {
} else if(typeof commandArray[1] === 'string') {
const scriptName = Terminal.getFilepath(commandArray[1]);
if (!isScriptFilename(scriptName)) {
postError("tail can only be called on .script files (filename must end with .script)");
postError("tail can only be called on .script, .ns, .js files, or by pid");
return;
}
@@ -1425,6 +1420,13 @@ let Terminal = {
return;
}
logBoxCreate(runningScript);
} else {
const runningScript = findRunningScriptByPid(commandArray[1], Player.getCurrentServer());
if (runningScript == null) {
postError("No such script exists");
return;
}
logBoxCreate(runningScript);
}
} catch(e) {
Terminal.postThrownError(e);
@@ -1700,10 +1702,14 @@ let Terminal = {
}
// Display all programs and scripts
let allFiles = [];
let folders = [];
const allPrograms = [];
const allScripts = [];
const allTextFiles = [];
const allContracts = [];
const allMessages = [];
const folders = [];
function handleFn(fn) {
function handleFn(fn, dest) {
let parsedFn = fn;
if (prefix) {
if (!fn.startsWith(prefix)) {
@@ -1732,29 +1738,53 @@ let Terminal = {
return;
}
allFiles.push(parsedFn);
dest.push(parsedFn);
}
// Get all of the programs and scripts on the machine into one temporary array
const s = Player.getCurrentServer();
for (const program of s.programs) handleFn(program);
for (const script of s.scripts) handleFn(script.filename);
for (const txt of s.textFiles) handleFn(txt.fn);
for (const contract of s.contracts) handleFn(contract.fn);
for (const msgOrLit of s.messages) (msgOrLit instanceof Message) ? handleFn(msgOrLit.filename) : handleFn(msgOrLit);
for (const program of s.programs) handleFn(program, allPrograms);
for (const script of s.scripts) handleFn(script.filename, allScripts);
for (const txt of s.textFiles) handleFn(txt.fn, allTextFiles);
for (const contract of s.contracts) handleFn(contract.fn, allContracts);
for (const msgOrLit of s.messages) (msgOrLit instanceof Message) ? handleFn(msgOrLit.filename, allMessages) : handleFn(msgOrLit, allMessages);
// Sort the files/folders alphabetically then print each
allFiles.sort();
allPrograms.sort();
allScripts.sort();
allTextFiles.sort();
allContracts.sort();
allMessages.sort();
folders.sort();
const config = { color: "#0000FF" };
for (const dir of folders) {
postContent(dir, config);
function postSegments(segments, config) {
const maxLength = Math.max(...segments.map(s => s.length))+1;
const filesPerRow = Math.floor(80 / maxLength);
for(let i = 0; i < segments.length; i++) {
let row = '';
for(let col = 0; col < filesPerRow; col++) {
if(!(i < segments.length)) break;
row += segments[i];
row += " ".repeat((maxLength * (col+1)) - row.length);
i++
}
postContent(row, config);
}
}
for (const file of allFiles) {
postContent(file);
}
const config = { color: "#0000FF" };
postSegments(folders, config);
postElement(<br />);
postSegments(allMessages);
postElement(<br />);
postSegments(allTextFiles);
postElement(<br />);
postSegments(allPrograms);
postElement(<br />);
postSegments(allContracts);
postElement(<br />);
postSegments(allScripts);
},
executeMemCommand: function(commandArray) {
@@ -2136,13 +2166,19 @@ let Terminal = {
post("Invalid server IP/hostname");
return;
}
if(targetServer instanceof HacknetServer) {
post(`${Programs.ServerProfiler.name} cannot be run on a Hacknet Server.`);
return
}
post(targetServer.hostname + ":");
post("Server base security level: " + targetServer.baseDifficulty);
post("Server current security level: " + targetServer.hackDifficulty);
post("Server growth rate: " + targetServer.serverGrowth);
post(`Netscript hack() execution time: ${convertTimeMsToTimeElapsedString(calculateHackingTime(targetServer, Player)*1000)}`);
post(`Netscript grow() execution time: ${convertTimeMsToTimeElapsedString(calculateGrowTime(targetServer, Player)*1000)}`);
post(`Netscript weaken() execution time: ${convertTimeMsToTimeElapsedString(calculateWeakenTime(targetServer, Player)*1000)}`);
post(`Netscript hack() execution time: ${convertTimeMsToTimeElapsedString(calculateHackingTime(targetServer, Player)*1000, true)}`);
post(`Netscript grow() execution time: ${convertTimeMsToTimeElapsedString(calculateGrowTime(targetServer, Player)*1000, true)}`);
post(`Netscript weaken() execution time: ${convertTimeMsToTimeElapsedString(calculateWeakenTime(targetServer, Player)*1000, true)}`);
};
programHandlers[Programs.AutoLink.name] = () => {
post("This executable cannot be run.");

View File

@@ -107,10 +107,10 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
{
desc: (n: number): string => {
return ["It is possible write four as a sum in exactly four different ways:\n\n",
" 3 + 1\n",
" 2 + 2\n",
" 2 + 1 + 1\n",
" 1 + 1 + 1 + 1\n\n",
"&nbsp;&nbsp;&nbsp;&nbsp;3 + 1\n",
"&nbsp;&nbsp;&nbsp;&nbsp;2 + 2\n",
"&nbsp;&nbsp;&nbsp;&nbsp;2 + 1 + 1\n",
"&nbsp;&nbsp;&nbsp;&nbsp;1 + 1 + 1 + 1\n\n",
`How many different ways can the number ${n} be written as a sum of at least`,
"two positive integers?"].join(" ");
},
@@ -142,17 +142,17 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
}
d += ["\nHere is an example of what spiral order should be:",
"\nExample:",
" [\n",
" [1, 2, 3],\n",
" [4, 5, 6],\n",
" [7, 8, 9]\n",
" ] should result in [1, 2, 3, 6, 9, 8 ,7, 4, 5]\n\n",
"&nbsp;&nbsp;&nbsp;&nbsp;[\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[1, 2, 3],\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[4, 5, 6],\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[7, 8, 9]\n",
"&nbsp;&nbsp;&nbsp;&nbsp;] should result in [1, 2, 3, 6, 9, 8 ,7, 4, 5]\n\n",
"Note that the matrix will not always be square:\n",
" [\n",
" [1, 2, 3, 4]\n",
" [5, 6, 7, 8]\n",
" [9, 10, 11, 12]\n",
" ] should result in [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7"].join(" ");
"&nbsp;&nbsp;&nbsp;&nbsp;[\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[1, 2, 3, 4]\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[5, 6, 7, 8]\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[9, 10, 11, 12]\n",
"&nbsp;&nbsp;&nbsp;&nbsp;] should result in [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7"].join(" ");
return d;
},

View File

@@ -63,7 +63,7 @@ import {
scriptEditorInit,
updateScriptEditorContent,
} from "./Script/ScriptHelpers";
import { initForeignServers } from "./Server/AllServers";
import { initForeignServers, AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSpecialServerIps } from "./Server/SpecialServerIps";
@@ -643,12 +643,6 @@ const Engine = {
clickListener: (e) => {
if (!e.isTrusted) { return false; }
joinFaction(Factions[factionName]);
for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == factionName || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
i--;
}
}
Engine.displayFactionsInfo();
return false;
},
@@ -1542,8 +1536,10 @@ const Engine = {
// DEBUG Delete active Scripts on home
document.getElementById("debug-delete-scripts-link").addEventListener("click", function() {
Player.getHomeComputer().runningScripts = [];
dialogBoxCreate("Forcefully deleted all running scripts on home computer. Please save and refresh page");
for(const hostname of Object.keys(AllServers)) {
AllServers[hostname].runningScripts = [];
}
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
gameOptionsBoxClose();
return false;
});

View File

@@ -340,8 +340,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
Would you like to join? <br /> <br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
<button id="faction-invitation-box-no" class="popup-box-button"> No </button>
<button id="faction-invitation-box-yes" class="popup-box-button"> Join! </button>
<button id="faction-invitation-box-no" class="popup-box-button"> Decide later </button>
</div>
</div>
@@ -600,14 +600,14 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
Copy Save data to Clipboard
</button>
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
(DEBUG) Delete Active Scripts
Delete all active scripts
<span class="tooltiptextleft">
Debug option used to forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
using this, save the game and then reload the page.
</span>
</button>
<button id="debug-soft-reset" class="a-link-button tooltip">
(DEBUG) Soft Reset
Soft Reset
<span class="tooltiptextleft">
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>

View File

@@ -49,7 +49,7 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
panelClass="active-scripts-script-panel"
panelContent={
<>
<pre>Threads: {props.workerScript.scriptRef.threads}</pre>
<pre>Threads: {numeralWrapper.formatThreads(props.workerScript.scriptRef.threads)}</pre>
<pre>Args: {arrayToString(props.workerScript.args)}</pre>
<pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
<pre>Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}</pre>

View File

@@ -108,6 +108,18 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
</>
}
function BladeburnerMults(): React.ReactElement {
if(!p.canAccessBladeburner()) return (<></>);
return (<>
<MultiplierTable rows={[
['Bladeburner Success Chance', p.bladeburner_max_stamina_mult],
['Bladeburner Max Stamina', p.bladeburner_stamina_gain_mult],
['Bladeburner Stamina Gain', p.bladeburner_analysis_mult],
['Bladeburner Field Analysis', p.bladeburner_success_chance_mult],
]} /><br />
</>);
}
function CurrentBitNode(): React.ReactElement {
if(p.sourceFiles.length > 0) {
@@ -233,7 +245,9 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<MultiplierTable rows={[
['Crime success', p.crime_success_mult],
['Crime money', p.crime_money_mult, p.crime_money_mult*BitNodeMultipliers.CrimeMoney],
]} /><br /><br />
]} /><br />
<BladeburnerMults /><br />
<b>Misc.</b><br /><br />
<span>{`Servers owned: ${p.purchasedServers.length} / ${getPurchaseServerLimit()}`}</span><br />

View File

@@ -24,10 +24,19 @@ export function AugmentationAccordion(props: IProps): React.ReactElement {
}
}
if(typeof props.aug.info === 'string') {
return (
<Accordion
headerContent={<>{displayName}</>}
panelContent={<p dangerouslySetInnerHTML={{__html: props.aug.info}}></p>}
/>
)
}
return (
<Accordion
headerContent={<>{displayName}</>}
panelContent={<p dangerouslySetInnerHTML={{__html: props.aug.info}}></p>}
panelContent={<p>{props.aug.info}</p>}
/>
)
}

View File

@@ -15,8 +15,7 @@ export class CharacterOverviewComponent extends Component {
);
return (
<div id="character-overview-text">
<table>
<table>
<tbody>
<tr id="character-hp-wrapper">
<td className="character-hp-cell">Hp:</td><td id="character-hp-text" className="character-hp-cell character-stat-cell">{numeralWrapper.formatHp(Player.hp) + " / " + numeralWrapper.formatHp(Player.max_hp)}</td>
@@ -47,8 +46,7 @@ export class CharacterOverviewComponent extends Component {
intelligence
}
</tbody>
</table>
</div>
</table>
)
}
}

View File

@@ -0,0 +1,61 @@
import * as React from "react";
import { KEY } from "../../../utils/helpers/keyCodes";
import { CodingContract, CodingContractType, CodingContractTypes } from "../../CodingContracts";
import { ClickableTag, CopyableText } from "./CopyableText";
import { PopupCloseButton } from "./PopupCloseButton";
type IProps = {
c: CodingContract;
popupId: string;
onClose: () => void;
onAttempt: (answer: string) => void;
}
type IState = {
answer: string;
}
export class CodingContractPopup extends React.Component<IProps, IState>{
constructor(props: IProps) {
super(props);
this.state = { answer: ''};
this.setAnswer = this.setAnswer.bind(this);
this.onInputKeydown = this.onInputKeydown.bind(this);
}
setAnswer(event: any) {
this.setState({ answer: event.target.value });
}
onInputKeydown(e:any){
if (e.keyCode === KEY.ENTER && e.target.value !== "") {
e.preventDefault();
this.props.onAttempt(this.state.answer);
} else if (e.keyCode === KEY.ESC) {
e.preventDefault();
this.props.onClose();
}
}
render(): React.ReactNode {
const contractType: CodingContractType = CodingContractTypes[this.props.c.type];
let description = [];
for (const [i, value] of contractType.desc(this.props.c.data).split('\n').entries())
description.push(<span key={i} dangerouslySetInnerHTML={{__html: value+'<br />'}}></span>);
return (
<div>
<CopyableText value={this.props.c.type} tag={ClickableTag.Tag_h1} />
<br/><br/>
<p>You are attempting to solve a Coding Contract. You have {this.props.c.getMaxNumTries() - this.props.c.tries} tries remaining, after which the contract will self-destruct.</p>
<br/>
<p>{description}</p>
<br/>
<input className="text-input" style={{ width:"50%",marginTop:"8px" }} autoFocus={true} placeholder="Enter Solution here" value={this.state.answer}
onChange={this.setAnswer} onKeyDown={this.onInputKeydown} />
<PopupCloseButton popup={this.props.popupId} onClose={() => this.props.onAttempt(this.state.answer)} text={"Solve"} />
<PopupCloseButton popup={this.props.popupId} onClose={this.props.onClose} text={"Close"} />
</div>
)
}
}

View File

@@ -1,7 +1,13 @@
import * as React from "react";
export enum ClickableTag{
Tag_span,
Tag_h1
}
type IProps = {
value: string;
tag: ClickableTag;
}
type IState = {
@@ -9,6 +15,11 @@ type IState = {
}
export class CopyableText extends React.Component<IProps, IState> {
public static defaultProps = {
//Default span to prevent destroying current clickables
tag: ClickableTag.Tag_span
};
constructor(props: IProps) {
super(props);
@@ -53,9 +64,19 @@ export class CopyableText extends React.Component<IProps, IState> {
render(): React.ReactNode {
return (<span className={this.textClasses()} onClick={this.copy}>
<b>{this.props.value}</b>
<span className={this.tooltipClasses()}>Copied!</span>
</span>);
switch (this.props.tag) {
case ClickableTag.Tag_h1:
return (
<h1 className={this.textClasses()} onClick={this.copy}>
{this.props.value}
<span className={this.tooltipClasses()}>Copied!</span>
</h1>)
case ClickableTag.Tag_span:
return (
<span className={this.textClasses()} onClick={this.copy}>
<b>{this.props.value}</b>
<span className={this.tooltipClasses()}>Copied!</span>
</span>)
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Basic button for popup dialog boxes
* It creates an event handler such that pressing Esc will perform the click handler.
*
* Should only be used in other React components, otherwise it may not be properly
* unmounted
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import { KEY } from "../../../utils/helpers/keyCodes";
import { removeElement } from "../../../utils/uiHelpers/removeElement";
export interface IPopupButtonProps {
class?: string;
popup: HTMLElement | string;
style?: object;
text: string;
onClose?: () => void;
}
export class PopupButton extends React.Component<IPopupButtonProps, any> {
constructor(props: IPopupButtonProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.keyListener = this.keyListener.bind(this);
}
componentDidMount() {
document.addEventListener("keydown", this.keyListener);
}
componentWillUnmount() {
document.removeEventListener("keydown", this.keyListener);
}
handleClick() {
if(this.props.onClose)
this.props.onClose();
//We might be able to remove this?
//Clickhandler from the props will override this anyhow.
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
} else {
popup = this.props.popup;
}
// TODO Check if this is okay? This is essentially calling to unmount a parent component
if (popup instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(popup); // Removes everything inside the wrapper container
removeElement(popup); // Removes the wrapper container
}
}
keyListener(e: KeyboardEvent) {
//This doesn't really make sense, a button doesnt have to listen to escape IMO
//Too affraid to remove it since im not sure what it will break.. But yuck..
if (e.keyCode === KEY.ESC) {
this.handleClick();
}
}
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
return (
<button className={className} onClick={this.handleClick} style={this.props.style}>
{this.props.text}
</button>
)
}
}

View File

@@ -8,33 +8,27 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { KEY } from "../../../utils/helpers/keyCodes";
import { removeElement } from "../../../utils/uiHelpers/removeElement";
import { IPopupButtonProps, PopupButton } from "./PopupButton";
export interface IPopupCloseButtonProps {
export interface IPopupCloseButtonProps extends IPopupButtonProps {
class?: string;
popup: HTMLElement | string;
style?: any;
text: string;
onClose: () => void;
}
export class PopupCloseButton extends React.Component<IPopupCloseButtonProps, any> {
export class PopupCloseButton extends PopupButton {
constructor(props: IPopupCloseButtonProps) {
super(props);
this.closePopup = this.closePopup.bind(this);
this.keyListener = this.keyListener.bind(this);
}
componentDidMount(): void {
document.addEventListener("keydown", this.keyListener);
}
componentWillUnmount(): void {
document.removeEventListener("keydown", this.keyListener);
}
closePopup(): void {
if(this.props.onClose)
this.props.onClose();
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
@@ -42,24 +36,23 @@ export class PopupCloseButton extends React.Component<IPopupCloseButtonProps, an
popup = this.props.popup;
}
// TODO Check if this is okay? This is essentially calling to unmount a parent component
// TODO Check if this is okay? This is essentially calling to unmount a
// parent component
if (popup instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(popup); // Removes everything inside the wrapper container
// Removes everything inside the wrapper container
ReactDOM.unmountComponentAtNode(popup);
removeElement(popup); // Removes the wrapper container
}
}
keyListener(e: KeyboardEvent): void {
if (e.keyCode === KEY.ESC) {
this.closePopup();
}
}
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
return (
<button className={className} onClick={this.closePopup} style={this.props.style}>
<button
className={className}
onClick={this.closePopup}
style={this.props.style}>
{this.props.text}
</button>
)

View File

@@ -53,6 +53,9 @@ class NumeralFormatter {
}
formatMoney(n: number): string {
if(Math.abs(n) < 1000) {
return this.format(n, "$0.00");
}
return this.format(n, "$0.000a");
}
@@ -131,6 +134,26 @@ class NumeralFormatter {
formatInfiltrationSecurity(n: number): string {
return this.format(n, "0.000a");
}
formatThreads(n: number): string {
return this.format(n, "0,0");
}
parseMoney(s: string): number {
// numeral library does not handle formats like 1e10 well (returns 110),
// so if both return a valid number, return the biggest one
const numeralValue = numeral(s).value();
const parsed = parseFloat(s);
if (isNaN(parsed) && numeralValue === null) {
return NaN;
} else if (isNaN(parsed)) {
return numeralValue;
} else if (numeralValue === null) {
return parsed;
} else {
return Math.max(numeralValue, parsed);
}
}
}
export const numeralWrapper = new NumeralFormatter();

View File

@@ -394,6 +394,16 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
await testNonzeroDynamicRamCost(f);
});
it("getServerMaxRam()", async function() {
const f = ["getServerMaxRam"];
await testNonzeroDynamicRamCost(f);
});
it("getServerUsedRam()", async function() {
const f = ["getServerUsedRam"];
await testNonzeroDynamicRamCost(f);
});
it("serverExists()", async function() {
const f = ["serverExists"];
await testNonzeroDynamicRamCost(f);

View File

@@ -284,6 +284,16 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
await expectNonZeroRamCost(f);
});
it("getServerMaxRam()", async function() {
const f = ["getServerMaxRam"];
await expectNonZeroRamCost(f);
});
it("getServerUsedRam()", async function() {
const f = ["getServerUsedRam"];
await expectNonZeroRamCost(f);
});
it("serverExists()", async function() {
const f = ["serverExists"];
await expectNonZeroRamCost(f);

View File

@@ -0,0 +1,11 @@
import { expect } from "chai";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
describe("StringHelperFunctions Tests", function() {
expect(convertTimeMsToTimeElapsedString(1000)).to.equal("1 seconds");
expect(convertTimeMsToTimeElapsedString(5*60*1000+34*1000)).to.equal("5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000)).to.equal("2 days 5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000, true)).to.equal("2 days 5 minutes 34.000 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123, true)).to.equal("2 days 5 minutes 34.123 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123.888, true)).to.equal("2 days 5 minutes 34.123 seconds");
})

View File

@@ -1,4 +1,5 @@
export * from "./Netscript/DynamicRamCalculationTests";
export * from "./Netscript/StaticRamCalculationTests";
export * from "./StockMarketTests";
export * from "./StringHelperFunctionsTests";
export * from "./Terminal/DirectoryTests";

View File

@@ -36,8 +36,6 @@ function factionInvitationBoxCreate(faction) {
var i = Player.factionInvitations.findIndex((facName)=>{return facName === faction.name});
if (i === -1) {
console.error("Could not find faction in Player.factionInvitations");
} else {
Player.factionInvitations.splice(i, 1);
}
joinFaction(faction);
factionInvitationBoxClose();

View File

@@ -1,6 +1,6 @@
import { dialogBoxCreate } from "./DialogBox";
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
import { formatNumber } from "./StringHelperFunctions";
import { numeralWrapper } from "../src/ui/numeralFormat";
import { BitNodeMultipliers } from "../src/BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../src/Constants";
@@ -38,12 +38,12 @@ function infiltrationBoxCreate(inst) {
Player.gainIntelligenceExp(inst.calcGainedIntelligenceExp());
const expGainText = ["You gained:",
`${formatNumber(inst.calcGainedHackingExp(), 3)} hacking exp`,
`${formatNumber(inst.calcGainedStrengthExp(), 3)} str exp`,
`${formatNumber(inst.calcGainedDefenseExp(), 3)} def exp`,
`${formatNumber(inst.calcGainedDexterityExp(), 3)} dex exp`,
`${formatNumber(inst.calcGainedAgilityExp(), 3)} agi exp`,
`${formatNumber(inst.calcGainedCharismaExp(), 3)} cha exp`].join("\n");
`${numeralWrapper.formatExp(inst.calcGainedHackingExp(), 3)} hacking exp`,
`${numeralWrapper.formatExp(inst.calcGainedStrengthExp(), 3)} str exp`,
`${numeralWrapper.formatExp(inst.calcGainedDefenseExp(), 3)} def exp`,
`${numeralWrapper.formatExp(inst.calcGainedDexterityExp(), 3)} dex exp`,
`${numeralWrapper.formatExp(inst.calcGainedAgilityExp(), 3)} agi exp`,
`${numeralWrapper.formatExp(inst.calcGainedCharismaExp(), 3)} cha exp`].join("\n");
var totalValue = 0;
for (var i = 0; i < inst.secretsStolen.length; ++i) {
@@ -58,9 +58,9 @@ function infiltrationBoxCreate(inst) {
CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue * BitNodeMultipliers.InfiltrationMoney;
infiltrationSetText("You can sell the classified documents and secrets " +
"you stole from " + inst.companyName + " for <span class='money-gold'>$" +
formatNumber(moneyValue, 2) + "</span> on the black market or you can give it " +
"to a faction to gain <span class='light-yellow'>" + formatNumber(facValue, 3) + " reputation</span> with " +
"you stole from " + inst.companyName + " for <span class='money-gold'>" +
numeralWrapper.formatMoney(moneyValue) + "</span> on the black market or you can give it " +
"to a faction to gain <span class='light-yellow'>" + numeralWrapper.formatReputation(facValue) + " reputation</span> with " +
"that faction.");
var selector = document.getElementById("infiltration-faction-select");
selector.innerHTML = "";
@@ -88,7 +88,7 @@ function infiltrationBoxCreate(inst) {
Player.gainMoney(moneyValue);
Player.recordMoneySource(moneyValue, "infiltration");
dialogBoxCreate("You sold the classified information you stole from " + inst.companyName +
" for <span class='money-gold'>$" + formatNumber(moneyValue, 2) + "</span> on the black market!<br><br>" +
" for <span class='money-gold'>" + numeralWrapper.formatMoney(moneyValue) + "</span> on the black market!<br><br>" +
expGainText);
infiltrationBoxClose();
return false;
@@ -108,7 +108,7 @@ function infiltrationBoxCreate(inst) {
}
faction.playerReputation += facValue;
dialogBoxCreate("You gave the classified information you stole from " + inst.companyName +
" to " + facName + " and gained <span class='light-yellow'>" + formatNumber(facValue, 3) + " reputation</span> with the faction. <br><br>" +
" to " + facName + " and gained <span class='light-yellow'>" + numeralWrapper.formatReputation(facValue) + " reputation</span> with the faction. <br><br>" +
expGainText);
infiltrationBoxClose();
return false;

View File

@@ -13,7 +13,8 @@ Converts a date representing time in milliseconds to a string with the format H
e.g. 10000 -> "10 seconds"
120000 -> "2 minutes and 0 seconds"
*/
function convertTimeMsToTimeElapsedString(time: number): string {
function convertTimeMsToTimeElapsedString(time: number, showMilli=false): string {
time = Math.floor(time);
const millisecondsPerSecond = 1000;
const secondPerMinute = 60;
const minutesPerHours = 60;
@@ -33,7 +34,13 @@ function convertTimeMsToTimeElapsedString(time: number): string {
const minutes: number = Math.floor(secTruncHours / secondPerMinute);
const secTruncMinutes: number = secTruncHours % secondPerMinute;
const seconds: number = secTruncMinutes;
const milliTruncSec: string = (() => {
let str = `${time % millisecondsPerSecond}`;
while(str.length < 3) str = "0"+str;
return str;
})()
const seconds: string = showMilli ? `${secTruncMinutes}.${milliTruncSec}` : `${secTruncMinutes}`;
let res = "";
if (days > 0) {res += `${days} days `; }